diff --git a/sdk/search/azure-search-documents/CHANGELOG.md b/sdk/search/azure-search-documents/CHANGELOG.md index 2c3981d0c3fca..673e58cc14d34 100644 --- a/sdk/search/azure-search-documents/CHANGELOG.md +++ b/sdk/search/azure-search-documents/CHANGELOG.md @@ -1,8 +1,8 @@ # Release History -## 11.0.0-beta.2 (Unreleased) - +## 11.0.0-beta.2 (2020-04-03) - Replaced `SearchApiKeyCredential` with `AzureKeyCredential`. +- Fixed a bug where the Date header wouldn't be updated with a new value on request retry. ## 11.0.0-beta.1 (2020-03-10) diff --git a/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/SearchIndexClientBuilder.java b/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/SearchIndexClientBuilder.java index 4d93ec0b33b3a..b90fdc13f445f 100644 --- a/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/SearchIndexClientBuilder.java +++ b/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/SearchIndexClientBuilder.java @@ -127,25 +127,29 @@ public SearchIndexAsyncClient buildAsyncClient() { ? Configuration.getGlobalConfiguration() : configuration; - policies.add(new AddHeadersPolicy(headers)); - policies.add(new RequestIdPolicy()); - policies.add(new AddDatePolicy()); + // Closest to API goes first, closest to wire goes last. + final List httpPipelinePolicies = new ArrayList<>(); + httpPipelinePolicies.add(new AddHeadersPolicy(headers)); + httpPipelinePolicies.add(new RequestIdPolicy()); - HttpPolicyProviders.addBeforeRetryPolicies(policies); - policies.add(retryPolicy == null ? new RetryPolicy() : retryPolicy); - HttpPolicyProviders.addAfterRetryPolicies(policies); + HttpPolicyProviders.addBeforeRetryPolicies(httpPipelinePolicies); + httpPipelinePolicies.add(retryPolicy == null ? new RetryPolicy() : retryPolicy); + httpPipelinePolicies.add(new AddDatePolicy()); if (keyCredential != null) { this.policies.add(new AzureKeyCredentialPolicy(API_KEY, keyCredential)); } + httpPipelinePolicies.addAll(this.policies); - policies.add(new UserAgentPolicy(httpLogOptions.getApplicationId(), clientName, clientVersion, + HttpPolicyProviders.addAfterRetryPolicies(httpPipelinePolicies); + + httpPipelinePolicies.add(new UserAgentPolicy(httpLogOptions.getApplicationId(), clientName, clientVersion, buildConfiguration)); - policies.add(new HttpLoggingPolicy(httpLogOptions)); + httpPipelinePolicies.add(new HttpLoggingPolicy(httpLogOptions)); HttpPipeline buildPipeline = new HttpPipelineBuilder() .httpClient(httpClient) - .policies(policies.toArray(new HttpPipelinePolicy[0])) + .policies(httpPipelinePolicies.toArray(new HttpPipelinePolicy[0])) .build(); return new SearchIndexAsyncClient(endpoint, indexName, buildVersion, buildPipeline); diff --git a/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/SearchServiceClientBuilder.java b/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/SearchServiceClientBuilder.java index 1359c474d6ad1..7540a182b9d54 100644 --- a/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/SearchServiceClientBuilder.java +++ b/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/SearchServiceClientBuilder.java @@ -120,26 +120,28 @@ public SearchServiceAsyncClient buildAsyncClient() { Configuration buildConfiguration = (configuration == null) ? Configuration.getGlobalConfiguration() : configuration; + final List httpPipelinePolicies = new ArrayList<>(); + httpPipelinePolicies.add(new AddHeadersPolicy(headers)); + httpPipelinePolicies.add(new RequestIdPolicy()); - policies.add(new AddHeadersPolicy(headers)); - policies.add(new RequestIdPolicy()); - policies.add(new AddDatePolicy()); - - HttpPolicyProviders.addBeforeRetryPolicies(policies); - policies.add(retryPolicy == null ? new RetryPolicy() : retryPolicy); - HttpPolicyProviders.addAfterRetryPolicies(policies); + HttpPolicyProviders.addBeforeRetryPolicies(httpPipelinePolicies); + httpPipelinePolicies.add(retryPolicy == null ? new RetryPolicy() : retryPolicy); + httpPipelinePolicies.add(new AddDatePolicy()); if (keyCredential != null) { this.policies.add(new AzureKeyCredentialPolicy(API_KEY, keyCredential)); } + httpPipelinePolicies.addAll(this.policies); + + HttpPolicyProviders.addAfterRetryPolicies(httpPipelinePolicies); - policies.add(new UserAgentPolicy(httpLogOptions.getApplicationId(), clientName, clientVersion, + httpPipelinePolicies.add(new UserAgentPolicy(httpLogOptions.getApplicationId(), clientName, clientVersion, buildConfiguration)); - policies.add(new HttpLoggingPolicy(httpLogOptions)); + httpPipelinePolicies.add(new HttpLoggingPolicy(httpLogOptions)); HttpPipeline buildPipeline = new HttpPipelineBuilder() .httpClient(httpClient) - .policies(policies.toArray(new HttpPipelinePolicy[0])) + .policies(httpPipelinePolicies.toArray(new HttpPipelinePolicy[0])) .build(); return new SearchServiceAsyncClient(endpoint, buildVersion, buildPipeline); diff --git a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/SearchIndexClientBuilderTests.java b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/SearchIndexClientBuilderTests.java index b31051cd46afb..0bb8f3e4f1a56 100644 --- a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/SearchIndexClientBuilderTests.java +++ b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/SearchIndexClientBuilderTests.java @@ -5,7 +5,12 @@ import com.azure.core.credential.AzureKeyCredential; import org.junit.jupiter.api.Test; +import reactor.test.StepVerifier; +import java.net.MalformedURLException; +import java.security.SecureRandom; + +import static com.azure.search.documents.SearchServiceClientBuilderTests.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; @@ -191,4 +196,23 @@ public void verifyNewBuilderSetsLatestVersionAsync() { assertEquals(SearchServiceVersion.getLatest().getVersion(), searchIndexAsyncClient.getServiceVersion().getVersion()); } + + @Test + public void indexClientFreshDateOnRetry() throws MalformedURLException { + byte[] randomData = new byte[256]; + new SecureRandom().nextBytes(randomData); + SearchIndexAsyncClient searchIndexAsyncClient = new SearchIndexClientBuilder() + .endpoint(searchEndpoint) + .credential(searchApiKeyCredential) + .indexName("test_builder") + .httpClient(new SearchServiceClientBuilderTests.FreshDateTestClient()) + .buildAsyncClient(); + + StepVerifier.create(searchIndexAsyncClient.getHttpPipeline().send( + request(searchIndexAsyncClient.getEndpoint()))) + .assertNext(response -> { + assertEquals(200, response.getStatusCode()); + }) + .verifyComplete(); + } } diff --git a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/SearchServiceClientBuilderTests.java b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/SearchServiceClientBuilderTests.java index 2fab14ccf2376..ba722657183a4 100644 --- a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/SearchServiceClientBuilderTests.java +++ b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/SearchServiceClientBuilderTests.java @@ -3,7 +3,23 @@ package com.azure.search.documents; import com.azure.core.credential.AzureKeyCredential; +import com.azure.core.http.HttpClient; +import com.azure.core.http.HttpHeaders; +import com.azure.core.http.HttpMethod; +import com.azure.core.http.HttpRequest; +import com.azure.core.http.HttpResponse; +import com.azure.core.test.http.MockHttpResponse; +import com.azure.core.util.CoreUtils; +import com.azure.core.util.DateTimeRfc1123; import org.junit.jupiter.api.Test; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.SecureRandom; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -146,4 +162,52 @@ public void verifyEmptyApiVersionSetsLatestAsync() { assertEquals(SearchServiceVersion.getLatest(), searchServiceAsyncClient.getServiceVersion()); } + + @Test + public void serviceClientFreshDateOnRetry() throws MalformedURLException { + byte[] randomData = new byte[256]; + new SecureRandom().nextBytes(randomData); + SearchServiceAsyncClient searchServiceAsyncClient = new SearchServiceClientBuilder() + .endpoint(searchEndpoint) + .credential(searchApiKeyCredential) + .httpClient(new FreshDateTestClient()) + .buildAsyncClient(); + + + StepVerifier.create(searchServiceAsyncClient.getHttpPipeline().send( + request(searchServiceAsyncClient.getEndpoint()))) + .assertNext(response -> { + assertEquals(200, response.getStatusCode()); + }) + .verifyComplete(); + } + + static HttpRequest request(String url) throws MalformedURLException { + return new HttpRequest(HttpMethod.HEAD, + new URL(url), new HttpHeaders().put("Content-Length", "0"), + Flux.empty()); + } + + static final class FreshDateTestClient implements HttpClient { + private DateTimeRfc1123 firstDate; + + @Override + public Mono send(HttpRequest request) { + if (firstDate == null) { + firstDate = convertToDateObject(request.getHeaders().getValue("Date")); + return Mono.error(new IOException("IOException!")); + } + + assert !firstDate.equals(convertToDateObject(request.getHeaders().getValue("Date"))); + return Mono.just(new MockHttpResponse(request, 200)); + } + + private static DateTimeRfc1123 convertToDateObject(String dateHeader) { + if (CoreUtils.isNullOrEmpty(dateHeader)) { + throw new RuntimeException("Failed to set 'Date' header."); + } + + return new DateTimeRfc1123(dateHeader); + } + } }