diff --git a/src/main/java/org/springframework/data/elasticsearch/client/ClientConfiguration.java b/src/main/java/org/springframework/data/elasticsearch/client/ClientConfiguration.java index 8a429fd96..ad5ceb32f 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/ClientConfiguration.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/ClientConfiguration.java @@ -31,6 +31,7 @@ import org.springframework.http.HttpHeaders; * * @author Mark Paluch * @author Peter-Josef Meisch + * @author Huw Ayling-Miller * @author Henrique Amaral * @since 3.2 */ @@ -144,11 +145,19 @@ public interface ClientConfiguration { */ Duration getSocketTimeout(); + /** + * Returns the path prefix that should be prepended to HTTP(s) requests for Elasticsearch behind a proxy. + * + * @return the path prefix. + * @since 3.2.4 + */ + String getPathPrefix(); + /** * returns an optionally set proxy in the form host:port * * @return the optional proxy - * @since 4.0 + * @since 3.2.4 */ Optional getProxy(); @@ -294,6 +303,15 @@ public interface ClientConfiguration { */ TerminalClientConfigurationBuilder withBasicAuth(String username, String password); + /** + * Configure the path prefix that will be prepended to any HTTP(s) requests + * + * @param pathPrefix the pathPrefix. + * @return the {@link TerminalClientConfigurationBuilder} + * @since 3.2.4 + */ + TerminalClientConfigurationBuilder withPathPrefix(String pathPrefix); + /** * @param proxy a proxy formatted as String {@literal host:port}. * @return the {@link MaybeSecureClientConfigurationBuilder}. diff --git a/src/main/java/org/springframework/data/elasticsearch/client/ClientConfigurationBuilder.java b/src/main/java/org/springframework/data/elasticsearch/client/ClientConfigurationBuilder.java index 96f83ec9a..5d5907b38 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/ClientConfigurationBuilder.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/ClientConfigurationBuilder.java @@ -38,6 +38,7 @@ import org.springframework.util.Assert; * @author Christoph Strobl * @author Mark Paluch * @author Peter-Josef Meisch + * @author Huw Ayling-Miller * @author Henrique Amaral * @since 3.2 */ @@ -53,7 +54,8 @@ class ClientConfigurationBuilder private Duration soTimeout = Duration.ofSeconds(5); private String username; private String password; - private String proxy; + private String pathPrefix; + private String proxy; /* * (non-Javadoc) @@ -183,6 +185,14 @@ class ClientConfigurationBuilder return this; } + @Override + public TerminalClientConfigurationBuilder withPathPrefix(String pathPrefix) { + + this.pathPrefix = pathPrefix; + + return this; + } + /* * (non-Javadoc) * @see org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationBuilderWithOptionalDefaultHeaders#build() @@ -197,7 +207,7 @@ class ClientConfigurationBuilder headers.setBasicAuth(username, password); } - return new DefaultClientConfiguration(hosts, headers, useSsl, sslContext, soTimeout, connectTimeout, + return new DefaultClientConfiguration(hosts, headers, useSsl, sslContext, soTimeout, connectTimeout, pathPrefix, hostnameVerifier, proxy); } diff --git a/src/main/java/org/springframework/data/elasticsearch/client/DefaultClientConfiguration.java b/src/main/java/org/springframework/data/elasticsearch/client/DefaultClientConfiguration.java index d52b4654d..78ac4a6dd 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/DefaultClientConfiguration.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/DefaultClientConfiguration.java @@ -33,6 +33,8 @@ import org.springframework.lang.Nullable; * * @author Mark Paluch * @author Christoph Strobl + * @author Huw Ayling-Miller + * @author Peter-Josef Meisch * @since 3.2 */ class DefaultClientConfiguration implements ClientConfiguration { @@ -43,11 +45,12 @@ class DefaultClientConfiguration implements ClientConfiguration { private final @Nullable SSLContext sslContext; private final Duration soTimeout; private final Duration connectTimeout; + private final String pathPrefix; private final @Nullable HostnameVerifier hostnameVerifier; private final String proxy; DefaultClientConfiguration(List hosts, HttpHeaders headers, boolean useSsl, - @Nullable SSLContext sslContext, Duration soTimeout, Duration connectTimeout, + @Nullable SSLContext sslContext, Duration soTimeout, Duration connectTimeout, @Nullable String pathPrefix, @Nullable HostnameVerifier hostnameVerifier, String proxy) { this.hosts = Collections.unmodifiableList(new ArrayList<>(hosts)); @@ -56,6 +59,7 @@ class DefaultClientConfiguration implements ClientConfiguration { this.sslContext = sslContext; this.soTimeout = soTimeout; this.connectTimeout = connectTimeout; + this.pathPrefix = pathPrefix; this.hostnameVerifier = hostnameVerifier; this.proxy = proxy; } @@ -123,12 +127,21 @@ class DefaultClientConfiguration implements ClientConfiguration { return this.soTimeout; } - /* - * (non-Javadoc) - * @see org.springframework.data.elasticsearch.client.ClientConfiguration#getProxy() - */ - @Override - public Optional getProxy() { - return Optional.ofNullable(proxy); - } + /* + * (non-Javadoc) + * @see org.springframework.data.elasticsearch.client.ClientConfiguration#getPathPrefix() + */ + @Override + public String getPathPrefix() { + return this.pathPrefix; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.elasticsearch.client.ClientConfiguration#getProxy() + */ + @Override + public Optional getProxy() { + return Optional.ofNullable(proxy); + } } diff --git a/src/main/java/org/springframework/data/elasticsearch/client/RestClients.java b/src/main/java/org/springframework/data/elasticsearch/client/RestClients.java index dfa5442dd..bf931a1a6 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/RestClients.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/RestClients.java @@ -53,7 +53,9 @@ import org.springframework.util.Assert; * * @author Christoph Strobl * @author Mark Paluch + * @author Huw Ayling-Miller * @author Henrique Amaral + * @author Peter-Josef Meisch * @since 3.2 */ public final class RestClients { @@ -77,6 +79,11 @@ public final class RestClients { HttpHost[] httpHosts = formattedHosts(clientConfiguration.getEndpoints(), clientConfiguration.useSsl()).stream() .map(HttpHost::create).toArray(HttpHost[]::new); RestClientBuilder builder = RestClient.builder(httpHosts); + + if (clientConfiguration.getPathPrefix() != null) { + builder.setPathPrefix(clientConfiguration.getPathPrefix()); + } + HttpHeaders headers = clientConfiguration.getDefaultHeaders(); if (!headers.isEmpty()) { diff --git a/src/main/java/org/springframework/data/elasticsearch/client/reactive/DefaultReactiveElasticsearchClient.java b/src/main/java/org/springframework/data/elasticsearch/client/reactive/DefaultReactiveElasticsearchClient.java index 33a842119..db15bf141 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/reactive/DefaultReactiveElasticsearchClient.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/reactive/DefaultReactiveElasticsearchClient.java @@ -125,6 +125,8 @@ import org.springframework.web.reactive.function.client.WebClient.RequestBodySpe * * @author Christoph Strobl * @author Mark Paluch + * @author Peter-Josef Meisch + * @author Huw Ayling-Miller * @author Henrique Amaral * @since 3.2 * @see ClientConfiguration @@ -220,6 +222,10 @@ public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearch ReactorClientHttpConnector connector = new ReactorClientHttpConnector(httpClient); WebClientProvider provider = WebClientProvider.create(scheme, connector); + if (clientConfiguration.getPathPrefix() != null) { + provider = provider.withPathPrefix(clientConfiguration.getPathPrefix()); + } + return provider.withDefaultHeaders(clientConfiguration.getDefaultHeaders()); } diff --git a/src/main/java/org/springframework/data/elasticsearch/client/reactive/DefaultWebClientProvider.java b/src/main/java/org/springframework/data/elasticsearch/client/reactive/DefaultWebClientProvider.java index 74120d60b..155ca2d45 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/reactive/DefaultWebClientProvider.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/reactive/DefaultWebClientProvider.java @@ -32,6 +32,8 @@ import org.springframework.web.reactive.function.client.WebClient.Builder; * * @author Mark Paluch * @author Christoph Strobl + * @author Huw Ayling-Miller + * @author Peter-Josef Meisch * @since 3.2 */ class DefaultWebClientProvider implements WebClientProvider { @@ -42,6 +44,7 @@ class DefaultWebClientProvider implements WebClientProvider { private final @Nullable ClientHttpConnector connector; private final Consumer errorListener; private final HttpHeaders headers; + private final String pathPrefix; /** * Create new {@link DefaultWebClientProvider} with empty {@link HttpHeaders} and no-op {@literal error listener}. @@ -50,19 +53,20 @@ class DefaultWebClientProvider implements WebClientProvider { * @param connector can be {@literal null}. */ DefaultWebClientProvider(String scheme, @Nullable ClientHttpConnector connector) { - this(scheme, connector, e -> {}, HttpHeaders.EMPTY); + this(scheme, connector, e -> {}, HttpHeaders.EMPTY, null); } /** * Create new {@link DefaultWebClientProvider} with empty {@link HttpHeaders} and no-op {@literal error listener}. - * + * * @param scheme must not be {@literal null}. * @param connector can be {@literal null}. * @param errorListener must not be {@literal null}. * @param headers must not be {@literal null}. + * @param pathPrefixcan be {@literal null} */ private DefaultWebClientProvider(String scheme, @Nullable ClientHttpConnector connector, - Consumer errorListener, HttpHeaders headers) { + Consumer errorListener, HttpHeaders headers, @Nullable String pathPrefix) { Assert.notNull(scheme, "Scheme must not be null! A common scheme would be 'http'."); Assert.notNull(errorListener, "ErrorListener must not be null! You may want use a no-op one 'e -> {}' instead."); @@ -73,6 +77,7 @@ class DefaultWebClientProvider implements WebClientProvider { this.connector = connector; this.errorListener = errorListener; this.headers = headers; + this.pathPrefix = pathPrefix; } /* @@ -109,7 +114,7 @@ class DefaultWebClientProvider implements WebClientProvider { merged.addAll(this.headers); merged.addAll(headers); - return new DefaultWebClientProvider(this.scheme, this.connector, errorListener, merged); + return new DefaultWebClientProvider(this.scheme, this.connector, errorListener, merged, this.pathPrefix); } /* @@ -121,6 +126,15 @@ class DefaultWebClientProvider implements WebClientProvider { return this.errorListener; } + /* + * (non-Javadoc) + * @see org.springframework.data.elasticsearch.client.reactive.WebClientProvider#getPathPrefix() + */ + @Override + public String getPathPrefix() { + return pathPrefix; + } + /* * (non-Javadoc) * @see org.springframework.data.elasticsearch.client.reactive.WebClientProvider#withErrorListener(java.util.function.Consumer) @@ -131,7 +145,18 @@ class DefaultWebClientProvider implements WebClientProvider { Assert.notNull(errorListener, "Error listener must not be null."); Consumer listener = this.errorListener.andThen(errorListener); - return new DefaultWebClientProvider(this.scheme, this.connector, listener, this.headers); + return new DefaultWebClientProvider(this.scheme, this.connector, listener, this.headers, this.pathPrefix); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.elasticsearch.client.reactive.WebClientProvider#withPathPrefix(java.lang.String) + */ + @Override + public WebClientProvider withPathPrefix(String pathPrefix) { + Assert.notNull(pathPrefix, "pathPrefix must not be null."); + + return new DefaultWebClientProvider(this.scheme, this.connector, this.errorListener, this.headers, pathPrefix); } protected WebClient createWebClientForSocketAddress(InetSocketAddress socketAddress) { @@ -142,7 +167,8 @@ class DefaultWebClientProvider implements WebClientProvider { builder = builder.clientConnector(connector); } - String baseUrl = String.format("%s://%s:%d", this.scheme, socketAddress.getHostString(), socketAddress.getPort()); + String baseUrl = String.format("%s://%s:%d%s", this.scheme, socketAddress.getHostString(), socketAddress.getPort(), + pathPrefix == null ? "" : "/" + pathPrefix); return builder.baseUrl(baseUrl).filter((request, next) -> next.exchange(request).doOnError(errorListener)).build(); } } diff --git a/src/main/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClient.java b/src/main/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClient.java index 34ceee6c5..7b9487348 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClient.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClient.java @@ -48,7 +48,6 @@ import org.elasticsearch.index.get.GetResult; import org.elasticsearch.index.reindex.BulkByScrollResponse; import org.elasticsearch.index.reindex.DeleteByQueryRequest; import org.elasticsearch.search.SearchHit; - import org.springframework.data.elasticsearch.client.ClientConfiguration; import org.springframework.data.elasticsearch.client.ElasticsearchHost; import org.springframework.http.HttpHeaders; @@ -341,7 +340,7 @@ public interface ReactiveElasticsearchClient { * @see Count API on * elastic.co * @return the {@link Mono} emitting the count result. - * @since 4.0 + * @since 3.2.4 */ default Mono count(Consumer consumer) { @@ -357,7 +356,7 @@ public interface ReactiveElasticsearchClient { * @see Count API on * elastic.co * @return the {@link Mono} emitting the count result. - * @since 4.0 + * @since 3.2.4 */ default Mono count(CountRequest countRequest) { return count(HttpHeaders.EMPTY, countRequest); @@ -371,7 +370,7 @@ public interface ReactiveElasticsearchClient { * @see Count API on * elastic.co * @return the {@link Mono} emitting the count result. - * @since 4.0 + * @since 3.2.4 */ Mono count(HttpHeaders headers, CountRequest countRequest); diff --git a/src/main/java/org/springframework/data/elasticsearch/client/reactive/WebClientProvider.java b/src/main/java/org/springframework/data/elasticsearch/client/reactive/WebClientProvider.java index ead42f3fe..1982ddb7e 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/reactive/WebClientProvider.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/reactive/WebClientProvider.java @@ -34,6 +34,8 @@ import org.springframework.web.reactive.function.client.WebClient; * * @author Christoph Strobl * @author Mark Paluch + * @author Huw Ayling-Miller + * @author Peter-Josef Meisch * @since 3.2 */ public interface WebClientProvider { @@ -96,6 +98,14 @@ public interface WebClientProvider { */ Consumer getErrorListener(); + /** + * Obtain the {@link String pathPrefix} to be used. + * + * @return the pathPrefix if set. + * @since 3.2.4 + */ + String getPathPrefix(); + /** * Create a new instance of {@link WebClientProvider} applying the given headers by default. * @@ -111,4 +121,13 @@ public interface WebClientProvider { * @return new instance of {@link WebClientProvider}. */ WebClientProvider withErrorListener(Consumer errorListener); + + /** + * Create a new instance of {@link WebClientProvider} where HTTP requests are called with the given path prefix. + * + * @param pathPrefix Path prefix to add to requests + * @return new instance of {@link WebClientProvider} + * @since 3.2.4 + */ + WebClientProvider withPathPrefix(String pathPrefix); } diff --git a/src/main/java/org/springframework/data/elasticsearch/client/util/RequestConverters.java b/src/main/java/org/springframework/data/elasticsearch/client/util/RequestConverters.java index 7cf4453f8..69b07d82b 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/util/RequestConverters.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/util/RequestConverters.java @@ -394,7 +394,7 @@ public class RequestConverters { * * @param countRequest the search defining the data to be counted * @return Elasticsearch count request - * @since 4.0 + * @since 3.2 */ public static Request count(CountRequest countRequest) { Request request = new Request(HttpMethod.POST.name(), diff --git a/src/test/java/org/springframework/data/elasticsearch/client/ClientConfigurationUnitTests.java b/src/test/java/org/springframework/data/elasticsearch/client/ClientConfigurationUnitTests.java index e764356b8..81a763cb1 100644 --- a/src/test/java/org/springframework/data/elasticsearch/client/ClientConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/client/ClientConfigurationUnitTests.java @@ -32,6 +32,7 @@ import org.springframework.http.HttpHeaders; * * @author Mark Paluch * @author Peter-Josef Meisch + * @author Huw Ayling-Miller * @author Henrique Amaral */ public class ClientConfigurationUnitTests { @@ -55,6 +56,7 @@ public class ClientConfigurationUnitTests { .usingSsl() // .withDefaultHeaders(headers) // .withConnectTimeout(Duration.ofDays(1)).withSocketTimeout(Duration.ofDays(2)) // + .withPathPrefix("myPathPrefix") // .withProxy("localhost:8080").build(); assertThat(clientConfiguration.getEndpoints()).containsOnly(InetSocketAddress.createUnresolved("foo", 9200), @@ -63,6 +65,7 @@ public class ClientConfigurationUnitTests { assertThat(clientConfiguration.getDefaultHeaders().get("foo")).containsOnly("bar"); assertThat(clientConfiguration.getConnectTimeout()).isEqualTo(Duration.ofDays(1)); assertThat(clientConfiguration.getSocketTimeout()).isEqualTo(Duration.ofDays(2)); + assertThat(clientConfiguration.getPathPrefix()).isEqualTo("myPathPrefix"); assertThat(clientConfiguration.getProxy()).contains("localhost:8080"); } diff --git a/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveMockClientTestsUtils.java b/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveMockClientTestsUtils.java index a7947cdad..86a18f5e7 100644 --- a/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveMockClientTestsUtils.java +++ b/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveMockClientTestsUtils.java @@ -39,7 +39,6 @@ import java.util.function.Supplier; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; - import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.data.elasticsearch.client.ElasticsearchHost; @@ -60,7 +59,9 @@ import org.springframework.web.util.UriBuilder; /** * @author Christoph Strobl + * @author Huw Ayling-Miller * @author Henrique Amaral + * @author Peter-Josef Meisch */ public class ReactiveMockClientTestsUtils { @@ -266,6 +267,16 @@ public class ReactiveMockClientTestsUtils { throw new UnsupportedOperationException(); } + @Override + public String getPathPrefix() { + return null; + } + + @Override + public WebClientProvider withPathPrefix(String pathPrefix) { + throw new UnsupportedOperationException(); + } + public Send when(String host) { InetSocketAddress inetSocketAddress = getInetSocketAddress(host); return new CallbackImpl(get(host), headersUriSpecMap.get(inetSocketAddress),