diff --git a/fluentgen/src/main/java/com/azure/autorest/fluent/template/FluentClientMethodTemplate.java b/fluentgen/src/main/java/com/azure/autorest/fluent/template/FluentClientMethodTemplate.java index a5eaafc8df..a08b0953f4 100644 --- a/fluentgen/src/main/java/com/azure/autorest/fluent/template/FluentClientMethodTemplate.java +++ b/fluentgen/src/main/java/com/azure/autorest/fluent/template/FluentClientMethodTemplate.java @@ -39,7 +39,6 @@ protected void generatePagedAsyncSinglePage(ClientMethod clientMethod, JavaType addOptionalAndConstantVariables(function, clientMethod, restAPIMethod.getParameters(), settings); applyParameterTransformations(function, clientMethod, settings); convertClientTypesToWireTypes(function, clientMethod, restAPIMethod.getParameters(), clientMethod.getClientReference(), settings); - addSpecialHeadersToLocalVariables(function, clientMethod); if (mergeContextParameter) { function.line(String.format("context = %s.mergeContext(context);", clientMethod.getClientReference())); } @@ -206,7 +205,6 @@ protected void generateSimpleAsyncRestResponse(ClientMethod clientMethod, JavaTy addOptionalAndConstantVariables(function, clientMethod, restAPIMethod.getParameters(), settings); applyParameterTransformations(function, clientMethod, settings); convertClientTypesToWireTypes(function, clientMethod, restAPIMethod.getParameters(), clientMethod.getClientReference(), settings); - addSpecialHeadersToLocalVariables(function, clientMethod); String restAPIMethodArgumentList = String.join(", ", clientMethod.getProxyMethodArguments(settings)); String serviceMethodCall = String.format("service.%s(%s)", restAPIMethod.getName(), restAPIMethodArgumentList); diff --git a/javagen/src/main/java/com/azure/autorest/mapper/ProxyMethodMapper.java b/javagen/src/main/java/com/azure/autorest/mapper/ProxyMethodMapper.java index 8d2dde661b..53c4c97e4b 100644 --- a/javagen/src/main/java/com/azure/autorest/mapper/ProxyMethodMapper.java +++ b/javagen/src/main/java/com/azure/autorest/mapper/ProxyMethodMapper.java @@ -739,13 +739,13 @@ protected List getSpecialParameters(Operation operation) { specialParameters.add(commonBuilderSetting.apply(new ProxyMethodParameter.Builder() .name(MethodUtil.REPEATABILITY_REQUEST_ID_VARIABLE_NAME) - .parameterReference(MethodUtil.REPEATABILITY_REQUEST_ID_VARIABLE_NAME) + .parameterReference(MethodUtil.REPEATABILITY_REQUEST_ID_EXPRESSION) .requestParameterName(MethodUtil.REPEATABILITY_REQUEST_ID_HEADER) .description("Repeatability request ID header")) .build()); specialParameters.add(commonBuilderSetting.apply(new ProxyMethodParameter.Builder() .name(MethodUtil.REPEATABILITY_FIRST_SENT_VARIABLE_NAME) - .parameterReference(MethodUtil.REPEATABILITY_FIRST_SENT_VARIABLE_NAME) + .parameterReference(MethodUtil.REPEATABILITY_FIRST_SENT_EXPRESSION) .requestParameterName(MethodUtil.REPEATABILITY_FIRST_SENT_HEADER) .description("Repeatability first sent header as HTTP-date")) .build()); diff --git a/javagen/src/main/java/com/azure/autorest/template/ClientMethodTemplate.java b/javagen/src/main/java/com/azure/autorest/template/ClientMethodTemplate.java index 24bfd5045b..e08ff49153 100644 --- a/javagen/src/main/java/com/azure/autorest/template/ClientMethodTemplate.java +++ b/javagen/src/main/java/com/azure/autorest/template/ClientMethodTemplate.java @@ -503,9 +503,8 @@ private static boolean addSpecialHeadersToRequestOptions(JavaBlock function, Cli // repeatability headers if (repeatabilityRequestHeaders) { - addSpecialHeadersToLocalVariables(function, clientMethod); - requestOptionsSetHeaderIfAbsent(function, MethodUtil.REPEATABILITY_REQUEST_ID_VARIABLE_NAME, MethodUtil.REPEATABILITY_REQUEST_ID_HEADER); - requestOptionsSetHeaderIfAbsent(function, MethodUtil.REPEATABILITY_FIRST_SENT_VARIABLE_NAME, MethodUtil.REPEATABILITY_FIRST_SENT_HEADER); + requestOptionsSetHeaderIfAbsent(function, MethodUtil.REPEATABILITY_REQUEST_ID_EXPRESSION, MethodUtil.REPEATABILITY_REQUEST_ID_HEADER); + requestOptionsSetHeaderIfAbsent(function, MethodUtil.REPEATABILITY_FIRST_SENT_EXPRESSION, MethodUtil.REPEATABILITY_FIRST_SENT_HEADER); } // content-type headers for optional body parameter @@ -523,23 +522,16 @@ private static boolean addSpecialHeadersToRequestOptions(JavaBlock function, Cli return requestOptionsLocal; } - private static void requestOptionsSetHeaderIfAbsent(JavaBlock function, String variableName, String headerName) { + private static void requestOptionsSetHeaderIfAbsent(JavaBlock function, String expression, String headerName) { function.line("requestOptionsLocal.addRequestCallback(requestLocal -> {"); function.indent(() -> { function.ifBlock(String.format("requestLocal.getHeaders().get(HttpHeaderName.fromString(\"%1$s\")) == null", headerName), ifBlock -> { - function.line(String.format("requestLocal.getHeaders().set(HttpHeaderName.fromString(\"%1$s\"), %2$s);", headerName, variableName)); + function.line(String.format("requestLocal.getHeaders().set(HttpHeaderName.fromString(\"%1$s\"), %2$s);", headerName, expression)); }); }); function.line("});"); } - protected static void addSpecialHeadersToLocalVariables(JavaBlock function, ClientMethod clientMethod) { - if (MethodUtil.isMethodIncludeRepeatableRequestHeaders(clientMethod.getProxyMethod())) { - function.line(String.format("String %1$s = CoreUtils.randomUuid().toString();", MethodUtil.REPEATABILITY_REQUEST_ID_VARIABLE_NAME)); - function.line(String.format("String %1$s = DateTimeRfc1123.toRfc1123String(OffsetDateTime.now());", MethodUtil.REPEATABILITY_FIRST_SENT_VARIABLE_NAME)); - } - } - protected static void writeMethod(JavaType typeBlock, JavaVisibility visibility, String methodSignature, Consumer method) { if (visibility == JavaVisibility.Public) { typeBlock.publicMethod(methodSignature, method); @@ -742,8 +734,6 @@ private void generatePagedSinglePage(ClientMethod clientMethod, JavaType typeBlo boolean requestOptionsLocal = false; if (settings.isDataPlaneClient()) { requestOptionsLocal = addSpecialHeadersToRequestOptions(function, clientMethod); - } else { - addSpecialHeadersToLocalVariables(function, clientMethod); } String serviceMethodCall = checkAndReplaceParamNameCollision(clientMethod, restAPIMethod, requestOptionsLocal, settings); @@ -1063,8 +1053,6 @@ protected void generatePlainSyncMethod(ClientMethod clientMethod, JavaType typeB boolean requestOptionsLocal = false; if (settings.isDataPlaneClient()) { requestOptionsLocal = addSpecialHeadersToRequestOptions(function, clientMethod); - } else { - addSpecialHeadersToLocalVariables(function, clientMethod); } String serviceMethodCall = checkAndReplaceParamNameCollision(clientMethod, restAPIMethod.toSync(), requestOptionsLocal, @@ -1163,8 +1151,6 @@ protected void generatePagedAsyncSinglePage(ClientMethod clientMethod, JavaType boolean requestOptionsLocal = false; if (settings.isDataPlaneClient()) { requestOptionsLocal = addSpecialHeadersToRequestOptions(function, clientMethod); - } else { - addSpecialHeadersToLocalVariables(function, clientMethod); } String serviceMethodCall = checkAndReplaceParamNameCollision(clientMethod, restAPIMethod, requestOptionsLocal, settings); @@ -1300,8 +1286,6 @@ protected void generateSimpleAsyncRestResponse(ClientMethod clientMethod, JavaTy boolean requestOptionsLocal = false; if (settings.isDataPlaneClient()) { requestOptionsLocal = addSpecialHeadersToRequestOptions(function, clientMethod); - } else { - addSpecialHeadersToLocalVariables(function, clientMethod); } String serviceMethodCall = checkAndReplaceParamNameCollision(clientMethod, restAPIMethod, requestOptionsLocal, settings); diff --git a/javagen/src/main/java/com/azure/autorest/util/MethodUtil.java b/javagen/src/main/java/com/azure/autorest/util/MethodUtil.java index fc505d0a10..956f6edae9 100644 --- a/javagen/src/main/java/com/azure/autorest/util/MethodUtil.java +++ b/javagen/src/main/java/com/azure/autorest/util/MethodUtil.java @@ -47,6 +47,8 @@ public class MethodUtil { public static final String REPEATABILITY_FIRST_SENT_HEADER = "repeatability-first-sent"; public static final String REPEATABILITY_REQUEST_ID_VARIABLE_NAME = CodeNamer.toCamelCase(REPEATABILITY_REQUEST_ID_HEADER); public static final String REPEATABILITY_FIRST_SENT_VARIABLE_NAME = CodeNamer.toCamelCase(REPEATABILITY_FIRST_SENT_HEADER); + public static final String REPEATABILITY_REQUEST_ID_EXPRESSION = "CoreUtils.randomUuid().toString()"; + public static final String REPEATABILITY_FIRST_SENT_EXPRESSION = "DateTimeRfc1123.toRfc1123String(OffsetDateTime.now())"; private static final Set REPEATABILITY_REQUEST_HTTP_METHODS = EnumSet.of(HttpMethod.PUT, HttpMethod.PATCH, HttpMethod.DELETE, HttpMethod.POST); diff --git a/protocol-tests/src/main/java/fixtures/specialheader/implementation/HeadersImpl.java b/protocol-tests/src/main/java/fixtures/specialheader/implementation/HeadersImpl.java index cc9f2821a7..613dfa6e3d 100644 --- a/protocol-tests/src/main/java/fixtures/specialheader/implementation/HeadersImpl.java +++ b/protocol-tests/src/main/java/fixtures/specialheader/implementation/HeadersImpl.java @@ -237,7 +237,7 @@ public Mono> paramRepeatabilityRequestWithResponseAsync(Req requestOptionsLocal.addRequestCallback(requestLocal -> { if (requestLocal.getHeaders().get(HttpHeaderName.fromString("repeatability-first-sent")) == null) { requestLocal.getHeaders() - .set(HttpHeaderName.fromString("repeatability-first-sent"), repeatabilityFirstSent); + .set(HttpHeaderName.fromString("repeatability-first-sent"), DateTimeRfc1123.toRfc1123String(OffsetDateTime.now())); } }); return FluxUtil.withContext( diff --git a/protocol-tests/src/test/java/fixtures/specialheader/SpecialHeaderTests.java b/protocol-tests/src/test/java/fixtures/specialheader/SpecialHeaderTests.java index 69c3df576b..c69be6099f 100644 --- a/protocol-tests/src/test/java/fixtures/specialheader/SpecialHeaderTests.java +++ b/protocol-tests/src/test/java/fixtures/specialheader/SpecialHeaderTests.java @@ -6,6 +6,7 @@ import com.azure.core.exception.HttpResponseException; import com.azure.core.http.HttpClient; import com.azure.core.http.HttpHeader; +import com.azure.core.http.HttpHeaderName; import com.azure.core.http.HttpHeaders; import com.azure.core.http.HttpPipelineCallContext; import com.azure.core.http.HttpPipelineNextPolicy; @@ -15,6 +16,8 @@ import com.azure.core.http.policy.HttpLogOptions; import com.azure.core.http.policy.HttpPipelinePolicy; import com.azure.core.http.rest.RequestOptions; +import com.azure.core.http.rest.Response; +import com.azure.core.util.BinaryData; import com.azure.core.util.CoreUtils; import com.azure.core.util.DateTimeRfc1123; import fixtures.MockHttpResponse; @@ -23,17 +26,22 @@ import org.junit.jupiter.api.Test; import reactor.core.publisher.Mono; +import java.time.Duration; +import java.time.Instant; import java.time.OffsetDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.Locale; import java.util.UUID; +import java.util.concurrent.TimeUnit; public class SpecialHeaderTests { private static SpecialHeaderClient client; + private static SpecialHeaderAsyncClient asyncClient; + private static class ValidationPolicy implements HttpPipelinePolicy { private String repeatabilityRequestId; private String repeatabilityFirstSent; @@ -97,25 +105,33 @@ public Mono process(HttpPipelineCallContext context, HttpPipelineN public static void setup() { HttpClient mockHttpClient = request -> Mono.just(new MockHttpResponse(request, 500)); + HttpLogOptions httpLogOptions = new HttpLogOptions() + .setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .setRequestLogger((logger, loggingOptions) -> { + final HttpRequest request = loggingOptions.getHttpRequest(); + StringBuilder sb = new StringBuilder(); + for (HttpHeader header : request.getHeaders()) { + String headerName = header.getName(); + sb.append(headerName).append(":"); + sb.append(header.getValue()); + sb.append("; "); + } + logger.info(sb.toString()); + return Mono.empty(); + }); + client = new SpecialHeaderClientBuilder() // .host("https://httpbin.org/") .httpClient(mockHttpClient) .addPolicy(VALIDATION_POLICY) - .httpLogOptions(new HttpLogOptions() - .setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS) - .setRequestLogger((logger, loggingOptions) -> { - final HttpRequest request = loggingOptions.getHttpRequest(); - StringBuilder sb = new StringBuilder(); - for (HttpHeader header : request.getHeaders()) { - String headerName = header.getName(); - sb.append(headerName).append(":"); - sb.append(header.getValue()); - sb.append("; "); - } - logger.info(sb.toString()); - return Mono.empty(); - })) + .httpLogOptions(httpLogOptions) .buildClient(); + + asyncClient = new SpecialHeaderClientBuilder() + .httpClient(mockHttpClient) + .addPolicy(VALIDATION_POLICY) + .httpLogOptions(httpLogOptions) + .buildAsyncClient(); } @Test @@ -142,17 +158,39 @@ public void testRepeatabilityRequestPut() { public void testRepeatabilityRequestUserProvidedHeader() { VALIDATION_POLICY.clear(); - final String id = UUID.randomUUID().toString(); + final String id = CoreUtils.randomUuid().toString(); final String date = DateTimeRfc1123.toRfc1123String(OffsetDateTime.now().minusMinutes(1)); Assertions.assertThrows(HttpResponseException.class, () -> client.paramRepeatabilityRequestWithResponse(new RequestOptions() - .setHeader("Repeatability-Request-ID", id) - .setHeader("Repeatability-First-Sent", date))); + .setHeader(HttpHeaderName.fromString("Repeatability-Request-ID"), id) + .setHeader(HttpHeaderName.fromString("Repeatability-First-Sent"), date))); Assertions.assertTrue(VALIDATION_POLICY.isValidationPass()); Assertions.assertEquals(id, VALIDATION_POLICY.repeatabilityRequestId); Assertions.assertEquals(date, VALIDATION_POLICY.repeatabilityFirstSent); } + + @Test + public void testRepeatabilityRequestAsyncDate() throws InterruptedException { + // make the Mono but do not subscribe + Instant instantAtMono = OffsetDateTime.now().toInstant(); + Mono> responseMono = asyncClient.paramRepeatabilityRequestWithResponse(null); + + // wait for 3 sec + TimeUnit.SECONDS.sleep(3); + + // subscribe - send request + Instant instantAtRequest = OffsetDateTime.now().toInstant(); + Assertions.assertThrows(HttpResponseException.class, responseMono::block); + + Assertions.assertTrue(VALIDATION_POLICY.isValidationPass()); + Instant instantOfRepeatabilityFirstSent = new DateTimeRfc1123(VALIDATION_POLICY.repeatabilityFirstSent).getDateTime().toInstant(); + + // instantOfRepeatabilityFirstSent should be near instantAtRequest, not instantAtMono + Duration duration = Duration.between(instantAtMono, instantOfRepeatabilityFirstSent); + // check for 2 sec, as the precision of "repeatability-first-sent" is 1 sec + Assertions.assertTrue(duration.compareTo(Duration.ofSeconds(2)) > 0); + } }