Skip to content

Commit

Permalink
Search Support New Builder Functionality (Azure#15965)
Browse files Browse the repository at this point in the history
* Support HttpPipelinePosition

* Add support for ClientOptions
  • Loading branch information
alzimmermsft authored Oct 6, 2020
1 parent 16332b9 commit 61e8c15
Show file tree
Hide file tree
Showing 8 changed files with 270 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
import com.azure.core.credential.AzureKeyCredential;
import com.azure.core.http.HttpClient;
import com.azure.core.http.HttpPipeline;
import com.azure.core.http.HttpPipelinePosition;
import com.azure.core.http.policy.HttpLogOptions;
import com.azure.core.http.policy.HttpPipelinePolicy;
import com.azure.core.http.policy.RetryPolicy;
import com.azure.core.util.ClientOptions;
import com.azure.core.util.Configuration;
import com.azure.core.util.CoreUtils;
import com.azure.core.util.logging.ClientLogger;
Expand Down Expand Up @@ -49,13 +51,16 @@
@ServiceClientBuilder(serviceClients = {SearchClient.class, SearchAsyncClient.class})
public final class SearchClientBuilder {
private final ClientLogger logger = new ClientLogger(SearchClientBuilder.class);
private final List<HttpPipelinePolicy> policies = new ArrayList<>();

private final List<HttpPipelinePolicy> perCallPolicies = new ArrayList<>();
private final List<HttpPipelinePolicy> perRetryPolicies = new ArrayList<>();

private AzureKeyCredential credential;
private SearchServiceVersion serviceVersion;
private String endpoint;
private HttpClient httpClient;
private HttpPipeline httpPipeline;
private ClientOptions clientOptions;
private HttpLogOptions httpLogOptions;
private Configuration configuration;
private String indexName;
Expand Down Expand Up @@ -107,8 +112,8 @@ public SearchAsyncClient buildAsyncClient() {
}

Objects.requireNonNull(credential, "'credential' cannot be null.");
HttpPipeline pipeline = Utility.buildHttpPipeline(httpLogOptions, configuration, retryPolicy, credential,
policies, httpClient);
HttpPipeline pipeline = Utility.buildHttpPipeline(clientOptions, httpLogOptions, configuration, retryPolicy,
credential, perCallPolicies, perRetryPolicies, httpClient);

return new SearchAsyncClient(endpoint, indexName, buildVersion, pipeline, jsonSerializer);
}
Expand Down Expand Up @@ -181,6 +186,17 @@ public static HttpLogOptions getDefaultLogOptions() {
return Constants.DEFAULT_LOG_OPTIONS_SUPPLIER.get();
}

/**
* Sets the client options such as application ID and custom headers to set on a request.
*
* @param clientOptions The client options.
* @return The updated SearchClientBuilder object.
*/
public SearchClientBuilder clientOptions(ClientOptions clientOptions) {
this.clientOptions = clientOptions;
return this;
}

/**
* Adds a pipeline policy to apply to each request sent.
* <p>
Expand All @@ -192,7 +208,14 @@ public static HttpLogOptions getDefaultLogOptions() {
* @throws NullPointerException If {@code policy} is {@code null}.
*/
public SearchClientBuilder addPolicy(HttpPipelinePolicy policy) {
policies.add(Objects.requireNonNull(policy));
Objects.requireNonNull(policy, "'policy' cannot be null.");

if (policy.getPipelinePosition() == HttpPipelinePosition.PER_CALL) {
perCallPolicies.add(policy);
} else {
perRetryPolicies.add(policy);
}

return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.azure.core.http.policy.RequestIdPolicy;
import com.azure.core.http.policy.RetryPolicy;
import com.azure.core.http.policy.UserAgentPolicy;
import com.azure.core.util.ClientOptions;
import com.azure.core.util.Configuration;
import com.azure.core.util.CoreUtils;
import com.azure.core.util.serializer.JacksonAdapter;
Expand All @@ -40,6 +41,7 @@ public final class Utility {
public static final TypeReference<Map<String, Object>> MAP_STRING_OBJECT_TYPE_REFERENCE =
new TypeReference<Map<String, Object>>() { };

private static final ClientOptions DEFAULT_CLIENT_OPTIONS = new ClientOptions();
private static final HttpLogOptions DEFAULT_LOG_OPTIONS = Constants.DEFAULT_LOG_OPTIONS_SUPPLIER.get();
private static final HttpHeaders HTTP_HEADERS = new HttpHeaders().put("return-client-request-id", "true");

Expand All @@ -65,34 +67,47 @@ public static SerializerAdapter initializeSerializerAdapter() {
return adapter;
}

public static HttpPipeline buildHttpPipeline(HttpLogOptions logOptions, Configuration configuration,
RetryPolicy retryPolicy, AzureKeyCredential credential, List<HttpPipelinePolicy> policies,
HttpClient httpClient) {
public static HttpPipeline buildHttpPipeline(ClientOptions clientOptions, HttpLogOptions logOptions,
Configuration configuration, RetryPolicy retryPolicy, AzureKeyCredential credential,
List<HttpPipelinePolicy> perCallPolicies, List<HttpPipelinePolicy> perRetryPolicies, HttpClient httpClient) {
Configuration buildConfiguration = (configuration == null)
? Configuration.getGlobalConfiguration()
: configuration;

ClientOptions buildClientOptions = (clientOptions == null) ? DEFAULT_CLIENT_OPTIONS : clientOptions;
HttpLogOptions buildLogOptions = (logOptions == null) ? DEFAULT_LOG_OPTIONS : logOptions;

String applicationId = null;
if (!CoreUtils.isNullOrEmpty(buildClientOptions.getApplicationId())) {
applicationId = buildClientOptions.getApplicationId();
} else if (!CoreUtils.isNullOrEmpty(buildLogOptions.getApplicationId())) {
applicationId = buildLogOptions.getApplicationId();
}

// Closest to API goes first, closest to wire goes last.
final List<HttpPipelinePolicy> httpPipelinePolicies = new ArrayList<>();
httpPipelinePolicies.add(new AddHeadersPolicy(HTTP_HEADERS));
httpPipelinePolicies.add(new AddHeadersFromContextPolicy());
httpPipelinePolicies.add(new UserAgentPolicy(buildLogOptions.getApplicationId(), CLIENT_NAME, CLIENT_VERSION,
buildConfiguration));
httpPipelinePolicies.add(new UserAgentPolicy(applicationId, CLIENT_NAME, CLIENT_VERSION, buildConfiguration));
httpPipelinePolicies.add(new RequestIdPolicy());

httpPipelinePolicies.addAll(perCallPolicies);
HttpPolicyProviders.addBeforeRetryPolicies(httpPipelinePolicies);
httpPipelinePolicies.add(retryPolicy == null ? new RetryPolicy() : retryPolicy);

httpPipelinePolicies.add(new AddDatePolicy());

httpPipelinePolicies.add(new AzureKeyCredentialPolicy("api-key", credential));

httpPipelinePolicies.addAll(policies);

httpPipelinePolicies.addAll(perRetryPolicies);
HttpPolicyProviders.addAfterRetryPolicies(httpPipelinePolicies);

HttpHeaders headers = new HttpHeaders();
buildClientOptions.getHeaders().forEach(header -> headers.put(header.getName(), header.getValue()));
if (headers.getSize() > 0) {
httpPipelinePolicies.add(new AddHeadersPolicy(headers));
}

httpPipelinePolicies.add(new HttpLoggingPolicy(buildLogOptions));

return new HttpPipelineBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import com.azure.core.credential.AzureKeyCredential;
import com.azure.core.http.HttpClient;
import com.azure.core.http.HttpPipeline;
import com.azure.core.http.HttpPipelinePosition;
import com.azure.core.http.policy.HttpLogOptions;
import com.azure.core.http.policy.HttpPipelinePolicy;
import com.azure.core.http.policy.RetryPolicy;
import com.azure.core.util.ClientOptions;
import com.azure.core.util.Configuration;
import com.azure.core.util.logging.ClientLogger;
import com.azure.core.util.serializer.JsonSerializer;
Expand Down Expand Up @@ -48,14 +50,17 @@
@ServiceClientBuilder(serviceClients = {SearchIndexClient.class, SearchIndexAsyncClient.class})
public final class SearchIndexClientBuilder {
private final ClientLogger logger = new ClientLogger(SearchIndexClientBuilder.class);
private final List<HttpPipelinePolicy> policies = new ArrayList<>();

private final List<HttpPipelinePolicy> perCallPolicies = new ArrayList<>();
private final List<HttpPipelinePolicy> perRetryPolicies = new ArrayList<>();

private AzureKeyCredential credential;
private SearchServiceVersion serviceVersion;
private String endpoint;
private HttpClient httpClient;
private HttpPipeline httpPipeline;
private HttpLogOptions httpLogOptions;
private ClientOptions clientOptions;
private Configuration configuration;
private RetryPolicy retryPolicy;
private JsonSerializer jsonSerializer;
Expand Down Expand Up @@ -105,8 +110,8 @@ public SearchIndexAsyncClient buildAsyncClient() {

Objects.requireNonNull(credential, "'credential' cannot be null.");

HttpPipeline pipeline = Utility.buildHttpPipeline(httpLogOptions, configuration, retryPolicy, credential,
policies, httpClient);
HttpPipeline pipeline = Utility.buildHttpPipeline(clientOptions, httpLogOptions, configuration, retryPolicy,
credential, perCallPolicies, perRetryPolicies, httpClient);

return new SearchIndexAsyncClient(endpoint, buildVersion, pipeline, jsonSerializer);
}
Expand Down Expand Up @@ -164,6 +169,17 @@ public static HttpLogOptions getDefaultLogOptions() {
return Constants.DEFAULT_LOG_OPTIONS_SUPPLIER.get();
}

/**
* Sets the client options such as application ID and custom headers to set on a request.
*
* @param clientOptions The client options.
* @return The updated SearchIndexClientBuilder object.
*/
public SearchIndexClientBuilder clientOptions(ClientOptions clientOptions) {
this.clientOptions = clientOptions;
return this;
}

/**
* Adds a pipeline policy to apply to each request sent.
* <p>
Expand All @@ -175,7 +191,14 @@ public static HttpLogOptions getDefaultLogOptions() {
* @throws NullPointerException If {@code policy} is {@code null}.
*/
public SearchIndexClientBuilder addPolicy(HttpPipelinePolicy policy) {
policies.add(Objects.requireNonNull(policy));
Objects.requireNonNull(policy, "'policy' cannot be null.");

if (policy.getPipelinePosition() == HttpPipelinePosition.PER_CALL) {
perCallPolicies.add(policy);
} else {
perRetryPolicies.add(policy);
}

return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
import com.azure.core.credential.AzureKeyCredential;
import com.azure.core.http.HttpClient;
import com.azure.core.http.HttpPipeline;
import com.azure.core.http.HttpPipelinePosition;
import com.azure.core.http.policy.HttpLogOptions;
import com.azure.core.http.policy.HttpPipelinePolicy;
import com.azure.core.http.policy.RetryPolicy;
import com.azure.core.util.ClientOptions;
import com.azure.core.util.Configuration;
import com.azure.core.util.logging.ClientLogger;
import com.azure.search.documents.SearchServiceVersion;
Expand Down Expand Up @@ -48,13 +50,16 @@
@ServiceClientBuilder(serviceClients = {SearchIndexerClient.class, SearchIndexerAsyncClient.class})
public class SearchIndexerClientBuilder {
private final ClientLogger logger = new ClientLogger(SearchIndexerClientBuilder.class);
private final List<HttpPipelinePolicy> policies = new ArrayList<>();

private final List<HttpPipelinePolicy> perCallPolicies = new ArrayList<>();
private final List<HttpPipelinePolicy> perRetryPolicies = new ArrayList<>();

private AzureKeyCredential credential;
private SearchServiceVersion serviceVersion;
private String endpoint;
private HttpClient httpClient;
private HttpPipeline httpPipeline;
private ClientOptions clientOptions;
private HttpLogOptions httpLogOptions;
private Configuration configuration;
private RetryPolicy retryPolicy;
Expand Down Expand Up @@ -105,8 +110,8 @@ public SearchIndexerAsyncClient buildAsyncClient() {

Objects.requireNonNull(credential, "'credential' cannot be null.");

HttpPipeline pipeline = Utility.buildHttpPipeline(httpLogOptions, configuration, retryPolicy, credential,
policies, httpClient);
HttpPipeline pipeline = Utility.buildHttpPipeline(clientOptions, httpLogOptions, configuration, retryPolicy,
credential, perCallPolicies, perRetryPolicies, httpClient);

return new SearchIndexerAsyncClient(endpoint, buildVersion, pipeline);
}
Expand Down Expand Up @@ -164,6 +169,17 @@ public static HttpLogOptions getDefaultLogOptions() {
return Constants.DEFAULT_LOG_OPTIONS_SUPPLIER.get();
}

/**
* Sets the client options such as application ID and custom headers to set on a request.
*
* @param clientOptions The client options.
* @return The updated SearchIndexerClientBuilder object.
*/
public SearchIndexerClientBuilder clientOptions(ClientOptions clientOptions) {
this.clientOptions = clientOptions;
return this;
}

/**
* Adds a pipeline policy to apply to each request sent.
* <p>
Expand All @@ -175,7 +191,14 @@ public static HttpLogOptions getDefaultLogOptions() {
* @throws NullPointerException If {@code policy} is {@code null}.
*/
public SearchIndexerClientBuilder addPolicy(HttpPipelinePolicy policy) {
policies.add(Objects.requireNonNull(policy));
Objects.requireNonNull(policy, "'policy' cannot be null.");

if (policy.getPipelinePosition() == HttpPipelinePosition.PER_CALL) {
perCallPolicies.add(policy);
} else {
perRetryPolicies.add(policy);
}

return this;
}

Expand Down
6 changes: 3 additions & 3 deletions sdk/search/azure-search-documents/src/samples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ Getting started explained in detail [here][SDK_README_GETTING_STARTED].

Maven dependency for Azure Cognitive Search Client library. Add it to your project's pom file.

[//]: # {x-version-update-start;com.azure:azure-search;current}
[//]: # {x-version-update-start;com.azure:azure-search-documents;current}

```xml
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-search</artifactId>
<version>1.0.0-preview.2</version>
<artifactId>azure-search-documents</artifactId>
<version>11.2.0-beta.1</version>
</dependency>
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,25 @@
package com.azure.search.documents;

import com.azure.core.credential.AzureKeyCredential;
import com.azure.core.exception.HttpResponseException;
import com.azure.core.http.policy.HttpLogOptions;
import com.azure.core.test.http.MockHttpResponse;
import com.azure.core.util.ClientOptions;
import com.azure.core.util.Header;
import com.azure.search.documents.indexes.SearchIndexClientBuilderTests;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import java.net.MalformedURLException;
import java.security.SecureRandom;
import java.util.Collections;

import static com.azure.search.documents.indexes.SearchIndexClientBuilderTests.request;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class SearchClientBuilderTests {
private final AzureKeyCredential searchApiKeyCredential = new AzureKeyCredential("0123");
Expand Down Expand Up @@ -135,4 +143,54 @@ public void indexClientFreshDateOnRetry() throws MalformedURLException {
.assertNext(response -> assertEquals(200, response.getStatusCode()))
.verifyComplete();
}

@Test
public void clientOptionsIsPreferredOverLogOptions() {
SearchClient searchClient = new SearchClientBuilder()
.endpoint(searchEndpoint)
.credential(searchApiKeyCredential)
.indexName("test_builder")
.httpLogOptions(new HttpLogOptions().setApplicationId("anOldApplication"))
.clientOptions(new ClientOptions().setApplicationId("aNewApplication"))
.httpClient(httpRequest -> {
assertTrue(httpRequest.getHeaders().getValue("User-Agent").contains("aNewApplication"));
return Mono.error(new HttpResponseException(new MockHttpResponse(httpRequest, 400)));
})
.buildClient();

assertThrows(RuntimeException.class, searchClient::getDocumentCount);
}

@Test
public void applicationIdFallsBackToLogOptions() {
SearchClient searchClient = new SearchClientBuilder()
.endpoint(searchEndpoint)
.credential(searchApiKeyCredential)
.indexName("test_builder")
.httpLogOptions(new HttpLogOptions().setApplicationId("anOldApplication"))
.httpClient(httpRequest -> {
assertTrue(httpRequest.getHeaders().getValue("User-Agent").contains("anOldApplication"));
return Mono.error(new HttpResponseException(new MockHttpResponse(httpRequest, 400)));
})
.buildClient();

assertThrows(RuntimeException.class, searchClient::getDocumentCount);
}

@Test
public void clientOptionHeadersAreAddedLast() {
SearchClient searchClient = new SearchClientBuilder()
.endpoint(searchEndpoint)
.credential(searchApiKeyCredential)
.indexName("test_builder")
.clientOptions(new ClientOptions()
.setHeaders(Collections.singletonList(new Header("User-Agent", "custom"))))
.httpClient(httpRequest -> {
assertEquals("custom", httpRequest.getHeaders().getValue("User-Agent"));
return Mono.error(new HttpResponseException(new MockHttpResponse(httpRequest, 400)));
})
.buildClient();

assertThrows(RuntimeException.class, searchClient::getDocumentCount);
}
}
Loading

0 comments on commit 61e8c15

Please sign in to comment.