From 6e6435908db9e864315b52140af302cbe852fa81 Mon Sep 17 00:00:00 2001
From: Jianghao Lu
Date: Wed, 16 Jun 2021 15:57:21 -0700
Subject: [PATCH 01/11] Add RequestOptions & handling in RestProxy
---
sdk/core/azure-core/pom.xml | 6 +
.../com/azure/core/http/RequestOptions.java | 152 ++++++++++++++++++
.../azure/core/http/ResponseStatusOption.java | 19 +++
.../com/azure/core/http/rest/RestProxy.java | 24 ++-
.../core/http/rest/SwaggerMethodParser.java | 11 ++
.../RequestOptionsJavaDocCodeSnippets.java | 75 +++++++++
.../azure/core/http/RequestOptionsTests.java | 76 +++++++++
.../http/rest/SwaggerMethodParserTests.java | 37 +++++
8 files changed, 393 insertions(+), 7 deletions(-)
create mode 100644 sdk/core/azure-core/src/main/java/com/azure/core/http/RequestOptions.java
create mode 100644 sdk/core/azure-core/src/main/java/com/azure/core/http/ResponseStatusOption.java
create mode 100644 sdk/core/azure-core/src/samples/java/com/azure/core/http/RequestOptionsJavaDocCodeSnippets.java
create mode 100644 sdk/core/azure-core/src/test/java/com/azure/core/http/RequestOptionsTests.java
diff --git a/sdk/core/azure-core/pom.xml b/sdk/core/azure-core/pom.xml
index 9576ce59ab03a..5fd3dc37a9612 100644
--- a/sdk/core/azure-core/pom.xml
+++ b/sdk/core/azure-core/pom.xml
@@ -162,6 +162,12 @@
1.22
test
+
+ javax.json
+ javax.json-api
+ 1.1.4
+ test
+
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/http/RequestOptions.java b/sdk/core/azure-core/src/main/java/com/azure/core/http/RequestOptions.java
new file mode 100644
index 0000000000000..bec960419ff8f
--- /dev/null
+++ b/sdk/core/azure-core/src/main/java/com/azure/core/http/RequestOptions.java
@@ -0,0 +1,152 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.core.http;
+
+import com.azure.core.util.BinaryData;
+
+import java.util.function.Consumer;
+
+/**
+ * This class contains the options to customize a HTTP request. {@link RequestOptions} can be
+ * used to configure the request headers, query params, the request body, or add a callback
+ * to modify all aspects of the HTTP request.
+ *
+ *
+ * An instance of fully configured {@link RequestOptions} can be passed to a service method that
+ * preconfigures known components of the request like URL, path params etc, further modifying both
+ * un-configured, or preconfigured components.
+ *
+ *
+ *
+ * To demonstrate how this class can be used to construct a request, let's use a Pet Store service as an example. The
+ * list of APIs available on this service are documented in the swagger definition.
+ *
+ *
+ * Creating an instance of DynamicRequest using the constructor
+ * {@codesnippet com.azure.core.experimental.http.requestoptions.instantiation}
+ **
+ * Configuring the request with a path param and making a HTTP GET request
+ * Continuing with the pet store example, getting information about a pet requires making a
+ * HTTP GET call
+ * to the pet service and setting the pet id in path param as shown in the sample below.
+ *
+ * {@codesnippet com.azure.core.experimental.http.dynamicrequest.getrequest}
+ *
+ * Configuring the request with JSON body and making a HTTP POST request
+ * To add a new pet to the pet store , a HTTP POST call should
+ * be made to the service with the details of the pet that is to be added. The details of the pet are included as the
+ * request body in JSON format.
+ *
+ * The JSON structure for the request is defined as follows:
+ * {@code
+ * {
+ * "id": 0,
+ * "category": {
+ * "id": 0,
+ * "name": "string"
+ * },
+ * "name": "doggie",
+ * "photoUrls": [
+ * "string"
+ * ],
+ * "tags": [
+ * {
+ * "id": 0,
+ * "name": "string"
+ * }
+ * ],
+ * "status": "available"
+ * }
+ * }
+ *
+ * To create a concrete request, Json builder provided in javax package is used here for demonstration. However, any
+ * other Json building library can be used to achieve similar results.
+ *
+ * {@codesnippet com.azure.core.experimental.http.requestoptions.createjsonrequest}
+ *
+ * Now, this string representation of the JSON request can be set as body of RequestOptions
+ *
+ * {@codesnippet com.azure.core.experimental.http.requestoptions.postrequest}
+ */
+public class RequestOptions {
+ private Consumer requestCallback = request -> { };
+ private ResponseStatusOption statusOption = ResponseStatusOption.DEFAULT;
+
+ /**
+ * Gets the request callback, applying all the configurations set on this RequestOptions.
+ * @return the request callback
+ */
+ public Consumer getRequestCallback() {
+ return this.requestCallback;
+ }
+
+ /**
+ * Gets under what conditions the operation raises an exception if the underlying response indicates a failure.
+ * @return the configured option
+ */
+ public ResponseStatusOption getStatusOption() {
+ return this.statusOption;
+ }
+
+ /**
+ * Adds a header to the HTTP request.
+ * @param header the header key
+ * @param value the header value
+ *
+ * @return the modified RequestOptions object
+ */
+ public RequestOptions addHeader(String header, String value) {
+ this.requestCallback = this.requestCallback.andThen(request ->
+ request.getHeaders().set(header, value));
+ return this;
+ }
+
+ /**
+ * Adds a query parameter to the request URL.
+ * @param parameterName the name of the query parameter
+ * @param value the value of the query parameter
+ * @return the modified RequestOptions object
+ */
+ public RequestOptions addQueryParam(String parameterName, String value) {
+ this.requestCallback = this.requestCallback.andThen(request -> {
+ String url = request.getUrl().toString();
+ request.setUrl(url + (url.contains("?") ? "&" : "?") + parameterName + "=" + value);
+ });
+ return this;
+ }
+
+ /**
+ * Adds a custom request callback to modify the HTTP request before it's sent by the HttpClient.
+ * The modifications made on a RequestOptions object is applied in order on the request.
+ *
+ * @param requestCallback the request callback
+ * @return the modified RequestOptions object
+ */
+ public RequestOptions addRequestCallback(Consumer requestCallback) {
+ this.requestCallback = this.requestCallback.andThen(requestCallback);
+ return this;
+ }
+
+ /**
+ * Sets the body to send as part of the HTTP request.
+ * @param requestBody the request body data
+ * @return the modified RequestOptions object
+ */
+ public RequestOptions setBody(BinaryData requestBody) {
+ this.requestCallback = this.requestCallback.andThen(request -> {
+ request.setBody(requestBody.toBytes());
+ });
+ return this;
+ }
+
+ /**
+ * Sets under what conditions the operation raises an exception if the underlying response indicates a failure.
+ * @param statusOption the option to control under what conditions the operation raises an exception
+ * @return the modified RequestOptions object
+ */
+ public RequestOptions setStatusOption(ResponseStatusOption statusOption) {
+ this.statusOption = statusOption;
+ return this;
+ }
+}
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/http/ResponseStatusOption.java b/sdk/core/azure-core/src/main/java/com/azure/core/http/ResponseStatusOption.java
new file mode 100644
index 0000000000000..dd7ae7d604ae2
--- /dev/null
+++ b/sdk/core/azure-core/src/main/java/com/azure/core/http/ResponseStatusOption.java
@@ -0,0 +1,19 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.core.http;
+
+/**
+ * ResponseStatusOption controls the behavior of an operation based on the status code of a response.
+ */
+public enum ResponseStatusOption {
+ /**
+ * Indicates that an operation should throw an exception when the response indicates a failure.
+ */
+ DEFAULT,
+
+ /**
+ * Indicates that an operation should not throw an exception when the response indicates a failure.
+ */
+ NO_THROW;
+}
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RestProxy.java b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RestProxy.java
index 4e1bf7b482c37..3ac807b4dfa6c 100644
--- a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RestProxy.java
+++ b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RestProxy.java
@@ -13,6 +13,8 @@
import com.azure.core.http.HttpPipelineBuilder;
import com.azure.core.http.HttpRequest;
import com.azure.core.http.HttpResponse;
+import com.azure.core.http.RequestOptions;
+import com.azure.core.http.ResponseStatusOption;
import com.azure.core.http.policy.CookiePolicy;
import com.azure.core.http.policy.HttpPipelinePolicy;
import com.azure.core.http.policy.RetryPolicy;
@@ -134,11 +136,17 @@ public Object invoke(Object proxy, final Method method, Object[] args) {
request.setBody(validateLength(request));
}
+ RequestOptions options = methodParser.setRequestOptions(args);
+ if (options != null) {
+ options.getRequestCallback().accept(request);
+ }
+
final Mono asyncResponse = send(request, context);
Mono asyncDecodedResponse = this.decoder.decode(asyncResponse, methodParser);
- return handleRestReturnType(asyncDecodedResponse, methodParser, methodParser.getReturnType(), context);
+ return handleRestReturnType(asyncDecodedResponse, methodParser,
+ methodParser.getReturnType(), context, options);
} catch (IOException e) {
throw logger.logExceptionAsError(Exceptions.propagate(e));
}
@@ -318,9 +326,9 @@ private HttpRequest configRequest(final HttpRequest request, final SwaggerMethod
}
private Mono ensureExpectedStatus(final Mono asyncDecodedResponse,
- final SwaggerMethodParser methodParser) {
+ final SwaggerMethodParser methodParser, RequestOptions options) {
return asyncDecodedResponse
- .flatMap(decodedHttpResponse -> ensureExpectedStatus(decodedHttpResponse, methodParser));
+ .flatMap(decodedHttpResponse -> ensureExpectedStatus(decodedHttpResponse, methodParser, options));
}
private static Exception instantiateUnexpectedException(final UnexpectedExceptionInformation exception,
@@ -365,10 +373,11 @@ private static Exception instantiateUnexpectedException(final UnexpectedExceptio
* @return An async-version of the provided decodedResponse.
*/
private Mono ensureExpectedStatus(final HttpDecodedResponse decodedResponse,
- final SwaggerMethodParser methodParser) {
+ final SwaggerMethodParser methodParser, RequestOptions options) {
final int responseStatusCode = decodedResponse.getSourceResponse().getStatusCode();
final Mono asyncResult;
- if (!methodParser.isExpectedResponseStatusCode(responseStatusCode)) {
+ if (!methodParser.isExpectedResponseStatusCode(responseStatusCode)
+ && (options == null || options.getStatusOption() != ResponseStatusOption.NO_THROW)) {
Mono bodyAsBytes = decodedResponse.getSourceResponse().getBodyAsByteArray();
asyncResult = bodyAsBytes.flatMap((Function>) responseContent -> {
@@ -490,9 +499,10 @@ private Mono> handleBodyReturnType(final HttpDecodedResponse response,
private Object handleRestReturnType(final Mono asyncHttpDecodedResponse,
final SwaggerMethodParser methodParser,
final Type returnType,
- final Context context) {
+ final Context context,
+ final RequestOptions options) {
final Mono asyncExpectedResponse =
- ensureExpectedStatus(asyncHttpDecodedResponse, methodParser)
+ ensureExpectedStatus(asyncHttpDecodedResponse, methodParser, options)
.doOnEach(RestProxy::endTracingSpan)
.contextWrite(reactor.util.context.Context.of("TRACING_CONTEXT", context));
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/SwaggerMethodParser.java b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/SwaggerMethodParser.java
index 4a8fee390c050..f41006ed078d6 100644
--- a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/SwaggerMethodParser.java
+++ b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/SwaggerMethodParser.java
@@ -24,6 +24,7 @@
import com.azure.core.http.HttpHeader;
import com.azure.core.http.HttpHeaders;
import com.azure.core.http.HttpMethod;
+import com.azure.core.http.RequestOptions;
import com.azure.core.implementation.TypeUtil;
import com.azure.core.implementation.UnixTime;
import com.azure.core.implementation.http.UnexpectedExceptionInformation;
@@ -347,6 +348,16 @@ public Context setContext(Object[] swaggerMethodArguments) {
return (context != null) ? context : Context.NONE;
}
+ /**
+ * Get the {@link RequestOptions} passed into the proxy method.
+ *
+ * @param swaggerMethodArguments the arguments passed to the proxy method
+ * @return the request options
+ */
+ public RequestOptions setRequestOptions(Object[] swaggerMethodArguments) {
+ return CoreUtils.findFirstOfType(swaggerMethodArguments, RequestOptions.class);
+ }
+
/**
* Get whether or not the provided response status code is one of the expected status codes for this Swagger
* method.
diff --git a/sdk/core/azure-core/src/samples/java/com/azure/core/http/RequestOptionsJavaDocCodeSnippets.java b/sdk/core/azure-core/src/samples/java/com/azure/core/http/RequestOptionsJavaDocCodeSnippets.java
new file mode 100644
index 0000000000000..461cb51e5c063
--- /dev/null
+++ b/sdk/core/azure-core/src/samples/java/com/azure/core/http/RequestOptionsJavaDocCodeSnippets.java
@@ -0,0 +1,75 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.core.http;
+
+import com.azure.core.util.BinaryData;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+
+/**
+ * JavaDoc code snippets for {@link RequestOptions}.
+ */
+public class RequestOptionsJavaDocCodeSnippets {
+
+ /**
+ * Sample to demonstrate how to create an instance of {@link RequestOptions}.
+ * @return An instance of {@link RequestOptions}.
+ */
+ public RequestOptions createInstance() {
+ // BEGIN: com.azure.core.experimental.http.requestoptions.instantiation
+ RequestOptions options = new RequestOptions()
+ .setBody(BinaryData.fromString("{\"name\":\"Fluffy\"}"))
+ .addHeader("x-ms-pet-version", "2021-06-01");
+ // END: com.azure.core.experimental.http.requestoptions.instantiation
+ return options;
+ }
+
+ /**
+ * Sample to demonstrate setting the JSON request body in a {@link RequestOptions}.
+ * @return An instance of {@link RequestOptions}.
+ */
+ public RequestOptions setJsonRequestBodyInRequestOptions() {
+ // BEGIN: com.azure.core.experimental.http.requestoptions.createjsonrequest
+ JsonArray photoUrls = Json.createArrayBuilder()
+ .add("https://imgur.com/pet1")
+ .add("https://imgur.com/pet2")
+ .build();
+
+ JsonArray tags = Json.createArrayBuilder()
+ .add(Json.createObjectBuilder()
+ .add("id", 0)
+ .add("name", "Labrador")
+ .build())
+ .add(Json.createObjectBuilder()
+ .add("id", 1)
+ .add("name", "2021")
+ .build())
+ .build();
+
+ JsonObject requestBody = Json.createObjectBuilder()
+ .add("id", 0)
+ .add("name", "foo")
+ .add("status", "available")
+ .add("category", Json.createObjectBuilder().add("id", 0).add("name", "dog"))
+ .add("photoUrls", photoUrls)
+ .add("tags", tags)
+ .build();
+
+ String requestBodyStr = requestBody.toString();
+ // END: com.azure.core.experimental.http.requestoptions.createjsonrequest
+
+ // BEGIN: com.azure.core.experimental.http.requestoptions.postrequest
+ RequestOptions options = new RequestOptions()
+ .addRequestCallback(request -> request
+ // may already be set if request is created from a client
+ .setUrl("https://petstore.example.com/pet")
+ .setHttpMethod(HttpMethod.POST)
+ .setBody(requestBodyStr)
+ .setHeader("Content-Type", "application/json"));
+ // END: com.azure.core.experimental.http.requestoptions.postrequest
+ return options;
+ }
+}
diff --git a/sdk/core/azure-core/src/test/java/com/azure/core/http/RequestOptionsTests.java b/sdk/core/azure-core/src/test/java/com/azure/core/http/RequestOptionsTests.java
new file mode 100644
index 0000000000000..7d541385307b7
--- /dev/null
+++ b/sdk/core/azure-core/src/test/java/com/azure/core/http/RequestOptionsTests.java
@@ -0,0 +1,76 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.core.http;
+
+import com.azure.core.util.BinaryData;
+import org.junit.jupiter.api.Test;
+import reactor.test.StepVerifier;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class RequestOptionsTests {
+ @Test
+ public void addQueryParam() throws MalformedURLException {
+ final HttpRequest request = new HttpRequest(HttpMethod.POST, new URL("http://request.url"));
+
+ RequestOptions options = new RequestOptions()
+ .addQueryParam("foo", "bar")
+ .addQueryParam("$skipToken", "1");
+ options.getRequestCallback().accept(request);
+
+ assertTrue(request.getUrl().toString().contains("?foo=bar&$skipToken=1"));
+ }
+
+ @Test
+ public void addHeader() throws MalformedURLException {
+ final HttpRequest request = new HttpRequest(HttpMethod.POST, new URL("http://request.url"));
+
+ RequestOptions options = new RequestOptions()
+ .addHeader("x-ms-foo", "bar")
+ .addHeader("Content-Type", "application/json");
+ options.getRequestCallback().accept(request);
+
+ HttpHeaders headers = request.getHeaders();
+ assertEquals("bar", headers.getValue("x-ms-foo"));
+ assertEquals("application/json", headers.getValue("Content-Type"));
+ }
+
+ @Test
+ public void setBody() throws MalformedURLException {
+ final HttpRequest request = new HttpRequest(HttpMethod.POST, new URL("http://request.url"));
+
+ String expected = "{\"id\":\"123\"}";
+
+ RequestOptions options = new RequestOptions()
+ .setBody(BinaryData.fromString(expected));
+ options.getRequestCallback().accept(request);
+
+ StepVerifier.create(BinaryData.fromFlux(request.getBody()).map(BinaryData::toString))
+ .expectNext(expected)
+ .verifyComplete();
+ }
+
+ @Test
+ public void addRequestCallback() throws MalformedURLException {
+ final HttpRequest request = new HttpRequest(HttpMethod.POST, new URL("http://request.url"));
+
+ RequestOptions options = new RequestOptions()
+ .addHeader("x-ms-foo", "bar")
+ .addRequestCallback(r -> r.setHttpMethod(HttpMethod.GET))
+ .addRequestCallback(r -> r.setUrl("https://request.url"))
+ .addQueryParam("$skipToken", "1")
+ .addRequestCallback(r -> r.setHeader("x-ms-foo", "baz"));
+
+ options.getRequestCallback().accept(request);
+
+ HttpHeaders headers = request.getHeaders();
+ assertEquals("baz", headers.getValue("x-ms-foo"));
+ assertEquals(HttpMethod.GET, request.getHttpMethod());
+ assertEquals("https://request.url?$skipToken=1", request.getUrl().toString());
+ }
+}
diff --git a/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/SwaggerMethodParserTests.java b/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/SwaggerMethodParserTests.java
index 67f9367b275ce..b3f2f1042db34 100644
--- a/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/SwaggerMethodParserTests.java
+++ b/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/SwaggerMethodParserTests.java
@@ -26,8 +26,11 @@
import com.azure.core.http.HttpHeader;
import com.azure.core.http.HttpHeaders;
import com.azure.core.http.HttpMethod;
+import com.azure.core.http.RequestOptions;
+import com.azure.core.http.ResponseStatusOption;
import com.azure.core.implementation.UnixTime;
import com.azure.core.util.Base64Url;
+import com.azure.core.util.BinaryData;
import com.azure.core.util.Context;
import com.azure.core.util.DateTimeRfc1123;
import com.azure.core.util.UrlBuilder;
@@ -480,6 +483,12 @@ public void setContext(SwaggerMethodParser swaggerMethodParser, Object[] argumen
assertEquals(expectedContext, swaggerMethodParser.setContext(arguments));
}
+ @ParameterizedTest
+ @MethodSource("setRequestOptionsSupplier")
+ public void setRequestOptions(SwaggerMethodParser swaggerMethodParser, Object[] arguments, RequestOptions expectedRequestOptions) {
+ assertEquals(expectedRequestOptions, swaggerMethodParser.setRequestOptions(arguments));
+ }
+
private static Stream setContextSupplier() throws NoSuchMethodException {
Method method = OperationMethods.class.getDeclaredMethod("getMethod");
SwaggerMethodParser swaggerMethodParser = new SwaggerMethodParser(method, "https://raw.host.com");
@@ -494,6 +503,34 @@ private static Stream setContextSupplier() throws NoSuchMethodExcepti
);
}
+ private static Stream setRequestOptionsSupplier() throws NoSuchMethodException {
+ Method method = OperationMethods.class.getDeclaredMethod("getMethod");
+ SwaggerMethodParser swaggerMethodParser = new SwaggerMethodParser(method, "https://raw.host.com");
+
+ RequestOptions bodyOptions = new RequestOptions()
+ .setBody(BinaryData.fromString("{\"id\":\"123\"}"));
+
+ RequestOptions headerQueryOptions = new RequestOptions()
+ .addHeader("x-ms-foo", "bar")
+ .addQueryParam("foo", "bar");
+
+ RequestOptions urlOptions = new RequestOptions()
+ .addRequestCallback(httpRequest -> httpRequest.setUrl("https://foo.host.com"));
+
+ RequestOptions statusOptionOptions = new RequestOptions()
+ .setStatusOption(ResponseStatusOption.NO_THROW);
+
+ return Stream.of(
+ Arguments.of(swaggerMethodParser, null, null),
+ Arguments.of(swaggerMethodParser, toObjectArray(), null),
+ Arguments.of(swaggerMethodParser, toObjectArray("string"), null),
+ Arguments.of(swaggerMethodParser, toObjectArray(bodyOptions), bodyOptions),
+ Arguments.of(swaggerMethodParser, toObjectArray("string", headerQueryOptions), headerQueryOptions),
+ Arguments.of(swaggerMethodParser, toObjectArray("string1", "string2", urlOptions), urlOptions),
+ Arguments.of(swaggerMethodParser, toObjectArray(statusOptionOptions), statusOptionOptions)
+ );
+ }
+
interface ExpectedStatusCodeMethods {
@Get("test")
void noExpectedStatusCodes();
From 73731538005e293f97357a8d2a58b5053b101b56 Mon Sep 17 00:00:00 2001
From: Jianghao Lu
Date: Fri, 18 Jun 2021 16:41:34 -0700
Subject: [PATCH 02/11] Handle BinaryData request & response types
---
.../main/java/com/azure/core/http/rest/RestProxy.java | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RestProxy.java b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RestProxy.java
index 3ac807b4dfa6c..af637ad0d6f1f 100644
--- a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RestProxy.java
+++ b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RestProxy.java
@@ -25,6 +25,7 @@
import com.azure.core.implementation.serializer.HttpResponseDecoder;
import com.azure.core.implementation.serializer.HttpResponseDecoder.HttpDecodedResponse;
import com.azure.core.util.Base64Url;
+import com.azure.core.util.BinaryData;
import com.azure.core.util.Context;
import com.azure.core.util.FluxUtil;
import com.azure.core.util.UrlBuilder;
@@ -294,7 +295,11 @@ private HttpRequest configRequest(final HttpRequest request, final SwaggerMethod
}
}
- if (isJson) {
+ if (bodyContentObject instanceof BinaryData) {
+ byte[] body = ((BinaryData) bodyContentObject).toBytes();
+ request.setBody(body);
+ request.setHeader("Content-Length", String.valueOf(body.length));
+ } else if (isJson) {
ByteArrayOutputStream stream = new AccessibleByteArrayOutputStream();
serializer.serialize(bodyContentObject, SerializerEncoding.JSON, stream);
@@ -480,6 +485,9 @@ private Mono> handleBodyReturnType(final HttpDecodedResponse response,
} else if (FluxUtil.isFluxByteBuffer(entityType)) {
// Mono>
asyncResult = Mono.just(response.getSourceResponse().getBody());
+ } else if (TypeUtil.isTypeOrSubTypeOf(entityType, BinaryData.class)) {
+ // Mono
+ asyncResult = BinaryData.fromFlux(response.getSourceResponse().getBody());
} else {
// Mono or Mono>
asyncResult = response.getDecodedBody((byte[]) null);
From ba8aa050e5567f46c58229e179aa760035c1c166 Mon Sep 17 00:00:00 2001
From: Jianghao Lu
Date: Tue, 22 Jun 2021 17:43:12 -0700
Subject: [PATCH 03/11] Address some feedback
---
.../com/azure/core/http/RequestOptions.java | 152 ------------------
.../com/azure/core/http/rest/RestProxy.java | 5 +-
.../core/http/rest/SwaggerMethodParser.java | 1 -
.../azure-core/src/main/java/module-info.java | 1 +
.../RequestOptionsJavaDocCodeSnippets.java | 1 +
.../azure/core/http/RequestOptionsTests.java | 76 ---------
.../http/rest/SwaggerMethodParserTests.java | 1 -
7 files changed, 3 insertions(+), 234 deletions(-)
delete mode 100644 sdk/core/azure-core/src/main/java/com/azure/core/http/RequestOptions.java
delete mode 100644 sdk/core/azure-core/src/test/java/com/azure/core/http/RequestOptionsTests.java
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/http/RequestOptions.java b/sdk/core/azure-core/src/main/java/com/azure/core/http/RequestOptions.java
deleted file mode 100644
index bec960419ff8f..0000000000000
--- a/sdk/core/azure-core/src/main/java/com/azure/core/http/RequestOptions.java
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-package com.azure.core.http;
-
-import com.azure.core.util.BinaryData;
-
-import java.util.function.Consumer;
-
-/**
- * This class contains the options to customize a HTTP request. {@link RequestOptions} can be
- * used to configure the request headers, query params, the request body, or add a callback
- * to modify all aspects of the HTTP request.
- *
- *
- * An instance of fully configured {@link RequestOptions} can be passed to a service method that
- * preconfigures known components of the request like URL, path params etc, further modifying both
- * un-configured, or preconfigured components.
- *
- *
- *
- * To demonstrate how this class can be used to construct a request, let's use a Pet Store service as an example. The
- * list of APIs available on this service are documented in the swagger definition.
- *
- *
- * Creating an instance of DynamicRequest using the constructor
- * {@codesnippet com.azure.core.experimental.http.requestoptions.instantiation}
- **
- * Configuring the request with a path param and making a HTTP GET request
- * Continuing with the pet store example, getting information about a pet requires making a
- * HTTP GET call
- * to the pet service and setting the pet id in path param as shown in the sample below.
- *
- * {@codesnippet com.azure.core.experimental.http.dynamicrequest.getrequest}
- *
- * Configuring the request with JSON body and making a HTTP POST request
- * To add a new pet to the pet store , a HTTP POST call should
- * be made to the service with the details of the pet that is to be added. The details of the pet are included as the
- * request body in JSON format.
- *
- * The JSON structure for the request is defined as follows:
- * {@code
- * {
- * "id": 0,
- * "category": {
- * "id": 0,
- * "name": "string"
- * },
- * "name": "doggie",
- * "photoUrls": [
- * "string"
- * ],
- * "tags": [
- * {
- * "id": 0,
- * "name": "string"
- * }
- * ],
- * "status": "available"
- * }
- * }
- *
- * To create a concrete request, Json builder provided in javax package is used here for demonstration. However, any
- * other Json building library can be used to achieve similar results.
- *
- * {@codesnippet com.azure.core.experimental.http.requestoptions.createjsonrequest}
- *
- * Now, this string representation of the JSON request can be set as body of RequestOptions
- *
- * {@codesnippet com.azure.core.experimental.http.requestoptions.postrequest}
- */
-public class RequestOptions {
- private Consumer requestCallback = request -> { };
- private ResponseStatusOption statusOption = ResponseStatusOption.DEFAULT;
-
- /**
- * Gets the request callback, applying all the configurations set on this RequestOptions.
- * @return the request callback
- */
- public Consumer getRequestCallback() {
- return this.requestCallback;
- }
-
- /**
- * Gets under what conditions the operation raises an exception if the underlying response indicates a failure.
- * @return the configured option
- */
- public ResponseStatusOption getStatusOption() {
- return this.statusOption;
- }
-
- /**
- * Adds a header to the HTTP request.
- * @param header the header key
- * @param value the header value
- *
- * @return the modified RequestOptions object
- */
- public RequestOptions addHeader(String header, String value) {
- this.requestCallback = this.requestCallback.andThen(request ->
- request.getHeaders().set(header, value));
- return this;
- }
-
- /**
- * Adds a query parameter to the request URL.
- * @param parameterName the name of the query parameter
- * @param value the value of the query parameter
- * @return the modified RequestOptions object
- */
- public RequestOptions addQueryParam(String parameterName, String value) {
- this.requestCallback = this.requestCallback.andThen(request -> {
- String url = request.getUrl().toString();
- request.setUrl(url + (url.contains("?") ? "&" : "?") + parameterName + "=" + value);
- });
- return this;
- }
-
- /**
- * Adds a custom request callback to modify the HTTP request before it's sent by the HttpClient.
- * The modifications made on a RequestOptions object is applied in order on the request.
- *
- * @param requestCallback the request callback
- * @return the modified RequestOptions object
- */
- public RequestOptions addRequestCallback(Consumer requestCallback) {
- this.requestCallback = this.requestCallback.andThen(requestCallback);
- return this;
- }
-
- /**
- * Sets the body to send as part of the HTTP request.
- * @param requestBody the request body data
- * @return the modified RequestOptions object
- */
- public RequestOptions setBody(BinaryData requestBody) {
- this.requestCallback = this.requestCallback.andThen(request -> {
- request.setBody(requestBody.toBytes());
- });
- return this;
- }
-
- /**
- * Sets under what conditions the operation raises an exception if the underlying response indicates a failure.
- * @param statusOption the option to control under what conditions the operation raises an exception
- * @return the modified RequestOptions object
- */
- public RequestOptions setStatusOption(ResponseStatusOption statusOption) {
- this.statusOption = statusOption;
- return this;
- }
-}
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RestProxy.java b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RestProxy.java
index af637ad0d6f1f..e450337ef7d2a 100644
--- a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RestProxy.java
+++ b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RestProxy.java
@@ -13,7 +13,6 @@
import com.azure.core.http.HttpPipelineBuilder;
import com.azure.core.http.HttpRequest;
import com.azure.core.http.HttpResponse;
-import com.azure.core.http.RequestOptions;
import com.azure.core.http.ResponseStatusOption;
import com.azure.core.http.policy.CookiePolicy;
import com.azure.core.http.policy.HttpPipelinePolicy;
@@ -296,9 +295,7 @@ private HttpRequest configRequest(final HttpRequest request, final SwaggerMethod
}
if (bodyContentObject instanceof BinaryData) {
- byte[] body = ((BinaryData) bodyContentObject).toBytes();
- request.setBody(body);
- request.setHeader("Content-Length", String.valueOf(body.length));
+ request.setBody(((BinaryData) bodyContentObject).toBytes());
} else if (isJson) {
ByteArrayOutputStream stream = new AccessibleByteArrayOutputStream();
serializer.serialize(bodyContentObject, SerializerEncoding.JSON, stream);
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/SwaggerMethodParser.java b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/SwaggerMethodParser.java
index f41006ed078d6..ba8c9c0085dcf 100644
--- a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/SwaggerMethodParser.java
+++ b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/SwaggerMethodParser.java
@@ -24,7 +24,6 @@
import com.azure.core.http.HttpHeader;
import com.azure.core.http.HttpHeaders;
import com.azure.core.http.HttpMethod;
-import com.azure.core.http.RequestOptions;
import com.azure.core.implementation.TypeUtil;
import com.azure.core.implementation.UnixTime;
import com.azure.core.implementation.http.UnexpectedExceptionInformation;
diff --git a/sdk/core/azure-core/src/main/java/module-info.java b/sdk/core/azure-core/src/main/java/module-info.java
index e67c0592af2a0..fbdc6c2a747b7 100644
--- a/sdk/core/azure-core/src/main/java/module-info.java
+++ b/sdk/core/azure-core/src/main/java/module-info.java
@@ -38,6 +38,7 @@
opens com.azure.core.implementation to com.fasterxml.jackson.databind;
opens com.azure.core.implementation.logging to com.fasterxml.jackson.databind;
opens com.azure.core.implementation.serializer to com.fasterxml.jackson.databind;
+ opens com.azure.core.http.rest to com.fasterxml.jackson.databind;
// Service Provider Interfaces
uses com.azure.core.http.HttpClientProvider;
diff --git a/sdk/core/azure-core/src/samples/java/com/azure/core/http/RequestOptionsJavaDocCodeSnippets.java b/sdk/core/azure-core/src/samples/java/com/azure/core/http/RequestOptionsJavaDocCodeSnippets.java
index 461cb51e5c063..757c28d12b0c3 100644
--- a/sdk/core/azure-core/src/samples/java/com/azure/core/http/RequestOptionsJavaDocCodeSnippets.java
+++ b/sdk/core/azure-core/src/samples/java/com/azure/core/http/RequestOptionsJavaDocCodeSnippets.java
@@ -3,6 +3,7 @@
package com.azure.core.http;
+import com.azure.core.http.rest.RequestOptions;
import com.azure.core.util.BinaryData;
import javax.json.Json;
diff --git a/sdk/core/azure-core/src/test/java/com/azure/core/http/RequestOptionsTests.java b/sdk/core/azure-core/src/test/java/com/azure/core/http/RequestOptionsTests.java
deleted file mode 100644
index 7d541385307b7..0000000000000
--- a/sdk/core/azure-core/src/test/java/com/azure/core/http/RequestOptionsTests.java
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-package com.azure.core.http;
-
-import com.azure.core.util.BinaryData;
-import org.junit.jupiter.api.Test;
-import reactor.test.StepVerifier;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-public class RequestOptionsTests {
- @Test
- public void addQueryParam() throws MalformedURLException {
- final HttpRequest request = new HttpRequest(HttpMethod.POST, new URL("http://request.url"));
-
- RequestOptions options = new RequestOptions()
- .addQueryParam("foo", "bar")
- .addQueryParam("$skipToken", "1");
- options.getRequestCallback().accept(request);
-
- assertTrue(request.getUrl().toString().contains("?foo=bar&$skipToken=1"));
- }
-
- @Test
- public void addHeader() throws MalformedURLException {
- final HttpRequest request = new HttpRequest(HttpMethod.POST, new URL("http://request.url"));
-
- RequestOptions options = new RequestOptions()
- .addHeader("x-ms-foo", "bar")
- .addHeader("Content-Type", "application/json");
- options.getRequestCallback().accept(request);
-
- HttpHeaders headers = request.getHeaders();
- assertEquals("bar", headers.getValue("x-ms-foo"));
- assertEquals("application/json", headers.getValue("Content-Type"));
- }
-
- @Test
- public void setBody() throws MalformedURLException {
- final HttpRequest request = new HttpRequest(HttpMethod.POST, new URL("http://request.url"));
-
- String expected = "{\"id\":\"123\"}";
-
- RequestOptions options = new RequestOptions()
- .setBody(BinaryData.fromString(expected));
- options.getRequestCallback().accept(request);
-
- StepVerifier.create(BinaryData.fromFlux(request.getBody()).map(BinaryData::toString))
- .expectNext(expected)
- .verifyComplete();
- }
-
- @Test
- public void addRequestCallback() throws MalformedURLException {
- final HttpRequest request = new HttpRequest(HttpMethod.POST, new URL("http://request.url"));
-
- RequestOptions options = new RequestOptions()
- .addHeader("x-ms-foo", "bar")
- .addRequestCallback(r -> r.setHttpMethod(HttpMethod.GET))
- .addRequestCallback(r -> r.setUrl("https://request.url"))
- .addQueryParam("$skipToken", "1")
- .addRequestCallback(r -> r.setHeader("x-ms-foo", "baz"));
-
- options.getRequestCallback().accept(request);
-
- HttpHeaders headers = request.getHeaders();
- assertEquals("baz", headers.getValue("x-ms-foo"));
- assertEquals(HttpMethod.GET, request.getHttpMethod());
- assertEquals("https://request.url?$skipToken=1", request.getUrl().toString());
- }
-}
diff --git a/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/SwaggerMethodParserTests.java b/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/SwaggerMethodParserTests.java
index b3f2f1042db34..944de9b5dd3be 100644
--- a/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/SwaggerMethodParserTests.java
+++ b/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/SwaggerMethodParserTests.java
@@ -26,7 +26,6 @@
import com.azure.core.http.HttpHeader;
import com.azure.core.http.HttpHeaders;
import com.azure.core.http.HttpMethod;
-import com.azure.core.http.RequestOptions;
import com.azure.core.http.ResponseStatusOption;
import com.azure.core.implementation.UnixTime;
import com.azure.core.util.Base64Url;
From 3aaf0c42c8a0c20d912d96793d21d5fa347adef7 Mon Sep 17 00:00:00 2001
From: Jianghao Lu
Date: Tue, 22 Jun 2021 17:48:20 -0700
Subject: [PATCH 04/11] Move RequestOptions
---
.../azure/core/http/rest/RequestOptions.java | 173 ++++++++++++++++++
.../core/http/rest/RequestOptionsTests.java | 79 ++++++++
2 files changed, 252 insertions(+)
create mode 100644 sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RequestOptions.java
create mode 100644 sdk/core/azure-core/src/test/java/com/azure/core/http/rest/RequestOptionsTests.java
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RequestOptions.java b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RequestOptions.java
new file mode 100644
index 0000000000000..f8056580127d7
--- /dev/null
+++ b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RequestOptions.java
@@ -0,0 +1,173 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.core.http.rest;
+
+import com.azure.core.annotation.QueryParam;
+import com.azure.core.http.HttpRequest;
+import com.azure.core.http.ResponseStatusOption;
+import com.azure.core.util.BinaryData;
+
+import java.util.function.Consumer;
+
+/**
+ * This class contains the options to customize a HTTP request. {@link RequestOptions} can be
+ * used to configure the request headers, query params, the request body, or add a callback
+ * to modify all aspects of the HTTP request.
+ *
+ *
+ * An instance of fully configured {@link RequestOptions} can be passed to a service method that
+ * preconfigures known components of the request like URL, path params etc, further modifying both
+ * un-configured, or preconfigured components.
+ *
+ *
+ *
+ * To demonstrate how this class can be used to construct a request, let's use a Pet Store service as an example. The
+ * list of APIs available on this service are documented in the swagger definition.
+ *
+ *
+ * Creating an instance of DynamicRequest using the constructor
+ * {@codesnippet com.azure.core.http.requestoptions.instantiation}
+ **
+ * Configuring the request with a path param and making a HTTP GET request
+ * Continuing with the pet store example, getting information about a pet requires making a
+ * HTTP GET call
+ * to the pet service and setting the pet id in path param as shown in the sample below.
+ *
+ * {@codesnippet com.azure.core.http.dynamicrequest.getrequest}
+ *
+ * Configuring the request with JSON body and making a HTTP POST request
+ * To add a new pet to the pet store , a HTTP POST call should
+ * be made to the service with the details of the pet that is to be added. The details of the pet are included as the
+ * request body in JSON format.
+ *
+ * The JSON structure for the request is defined as follows:
+ * {@code
+ * {
+ * "id": 0,
+ * "category": {
+ * "id": 0,
+ * "name": "string"
+ * },
+ * "name": "doggie",
+ * "photoUrls": [
+ * "string"
+ * ],
+ * "tags": [
+ * {
+ * "id": 0,
+ * "name": "string"
+ * }
+ * ],
+ * "status": "available"
+ * }
+ * }
+ *
+ * To create a concrete request, Json builder provided in javax package is used here for demonstration. However, any
+ * other Json building library can be used to achieve similar results.
+ *
+ * {@codesnippet com.azure.core.http.requestoptions.createjsonrequest}
+ *
+ * Now, this string representation of the JSON request can be set as body of RequestOptions
+ *
+ * {@codesnippet com.azure.core.http.requestoptions.postrequest}
+ */
+public final class RequestOptions {
+ private Consumer requestCallback = request -> { };
+ private ResponseStatusOption statusOption = ResponseStatusOption.DEFAULT;
+ private BinaryData requestBody;
+
+ /**
+ * Gets the request callback, applying all the configurations set on this RequestOptions.
+ * @return the request callback
+ */
+ Consumer getRequestCallback() {
+ return this.requestCallback;
+ }
+
+ /**
+ * Gets under what conditions the operation raises an exception if the underlying response indicates a failure.
+ * @return the configured option
+ */
+ ResponseStatusOption getStatusOption() {
+ return this.statusOption;
+ }
+
+ /**
+ * Adds a header to the HTTP request.
+ * @param header the header key
+ * @param value the header value
+ *
+ * @return the modified RequestOptions object
+ */
+ public RequestOptions addHeader(String header, String value) {
+ this.requestCallback = this.requestCallback.andThen(request ->
+ request.getHeaders().get(header).addValue(value));
+ return this;
+ }
+
+ /**
+ * Adds a query parameter to the request URL.
+ *
+ * @param parameterName the name of the query parameter
+ * @param value the value of the query parameter
+ * @return the modified RequestOptions object
+ */
+ public RequestOptions addQueryParam(String parameterName, String value) {
+ return addQueryParam(parameterName, value, false);
+ }
+
+ /**
+ * Adds a query parameter to the request URL, specifying whether the parameter is already encoded.
+ * A value true for this argument indicates that value of {@link QueryParam#value()} is already encoded
+ * hence engine should not encode it, by default value will be encoded.
+ *
+ * @param parameterName the name of the query parameter
+ * @param value the value of the query parameter
+ * @param encoded whether or not this query parameter is already encoded
+ * @return the modified RequestOptions object
+ */
+ public RequestOptions addQueryParam(String parameterName, String value, boolean encoded) {
+ this.requestCallback = this.requestCallback.andThen(request -> {
+ String url = request.getUrl().toString();
+ request.setUrl(url + (url.contains("?") ? "&" : "?")
+ + UrlEscapers.QUERY_ESCAPER.escape(parameterName) + "="
+ + UrlEscapers.QUERY_ESCAPER.escape(value));
+ });
+ return this;
+ }
+
+ /**
+ * Adds a custom request callback to modify the HTTP request before it's sent by the HttpClient.
+ * The modifications made on a RequestOptions object is applied in order on the request.
+ *
+ * @param requestCallback the request callback
+ * @return the modified RequestOptions object
+ */
+ public RequestOptions addRequestCallback(Consumer requestCallback) {
+ this.requestCallback = this.requestCallback.andThen(requestCallback);
+ return this;
+ }
+
+ /**
+ * Sets the body to send as part of the HTTP request.
+ * @param requestBody the request body data
+ * @return the modified RequestOptions object
+ */
+ public RequestOptions setBody(BinaryData requestBody) {
+ this.requestCallback = this.requestCallback.andThen(request -> {
+ request.setBody(requestBody.toBytes());
+ });
+ return this;
+ }
+
+ /**
+ * Sets under what conditions the operation raises an exception if the underlying response indicates a failure.
+ * @param statusOption the option to control under what conditions the operation raises an exception
+ * @return the modified RequestOptions object
+ */
+ public RequestOptions setStatusOption(ResponseStatusOption statusOption) {
+ this.statusOption = statusOption;
+ return this;
+ }
+}
diff --git a/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/RequestOptionsTests.java b/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/RequestOptionsTests.java
new file mode 100644
index 0000000000000..a19e230aad539
--- /dev/null
+++ b/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/RequestOptionsTests.java
@@ -0,0 +1,79 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.core.http.rest;
+
+import com.azure.core.http.HttpHeaders;
+import com.azure.core.http.HttpMethod;
+import com.azure.core.http.HttpRequest;
+import com.azure.core.util.BinaryData;
+import org.junit.jupiter.api.Test;
+import reactor.test.StepVerifier;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class RequestOptionsTests {
+ @Test
+ public void addQueryParam() throws MalformedURLException {
+ final HttpRequest request = new HttpRequest(HttpMethod.POST, new URL("http://request.url"));
+
+ RequestOptions options = new RequestOptions()
+ .addQueryParam("foo", "bar")
+ .addQueryParam("$skipToken", "1");
+ options.getRequestCallback().accept(request);
+
+ assertTrue(request.getUrl().toString().contains("?foo=bar&$skipToken=1"));
+ }
+
+ @Test
+ public void addHeader() throws MalformedURLException {
+ final HttpRequest request = new HttpRequest(HttpMethod.POST, new URL("http://request.url"));
+
+ RequestOptions options = new RequestOptions()
+ .addHeader("x-ms-foo", "bar")
+ .addHeader("Content-Type", "application/json");
+ options.getRequestCallback().accept(request);
+
+ HttpHeaders headers = request.getHeaders();
+ assertEquals("bar", headers.getValue("x-ms-foo"));
+ assertEquals("application/json", headers.getValue("Content-Type"));
+ }
+
+ @Test
+ public void setBody() throws MalformedURLException {
+ final HttpRequest request = new HttpRequest(HttpMethod.POST, new URL("http://request.url"));
+
+ String expected = "{\"id\":\"123\"}";
+
+ RequestOptions options = new RequestOptions()
+ .setBody(BinaryData.fromString(expected));
+ options.getRequestCallback().accept(request);
+
+ StepVerifier.create(BinaryData.fromFlux(request.getBody()).map(BinaryData::toString))
+ .expectNext(expected)
+ .verifyComplete();
+ }
+
+ @Test
+ public void addRequestCallback() throws MalformedURLException {
+ final HttpRequest request = new HttpRequest(HttpMethod.POST, new URL("http://request.url"));
+
+ RequestOptions options = new RequestOptions()
+ .addHeader("x-ms-foo", "bar")
+ .addRequestCallback(r -> r.setHttpMethod(HttpMethod.GET))
+ .addRequestCallback(r -> r.setUrl("https://request.url"))
+ .addQueryParam("$skipToken", "1")
+ .addRequestCallback(r -> r.setHeader("x-ms-foo", "baz"));
+
+ options.getRequestCallback().accept(request);
+
+ HttpHeaders headers = request.getHeaders();
+ assertEquals("baz", headers.getValue("x-ms-foo"));
+ assertEquals(HttpMethod.GET, request.getHttpMethod());
+ assertEquals("https://request.url?$skipToken=1", request.getUrl().toString());
+ }
+}
From 34056504a5482848e53dfc0e083ceee85e26c512 Mon Sep 17 00:00:00 2001
From: Jianghao Lu
Date: Wed, 23 Jun 2021 11:26:29 -0700
Subject: [PATCH 05/11] Change ResponseStatusOption to boolean
---
.../azure/core/http/ResponseStatusOption.java | 19 ------------
.../azure/core/http/rest/RequestOptions.java | 29 ++++++++++---------
.../com/azure/core/http/rest/RestProxy.java | 3 +-
.../http/rest/SwaggerMethodParserTests.java | 1 -
4 files changed, 17 insertions(+), 35 deletions(-)
delete mode 100644 sdk/core/azure-core/src/main/java/com/azure/core/http/ResponseStatusOption.java
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/http/ResponseStatusOption.java b/sdk/core/azure-core/src/main/java/com/azure/core/http/ResponseStatusOption.java
deleted file mode 100644
index dd7ae7d604ae2..0000000000000
--- a/sdk/core/azure-core/src/main/java/com/azure/core/http/ResponseStatusOption.java
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-package com.azure.core.http;
-
-/**
- * ResponseStatusOption controls the behavior of an operation based on the status code of a response.
- */
-public enum ResponseStatusOption {
- /**
- * Indicates that an operation should throw an exception when the response indicates a failure.
- */
- DEFAULT,
-
- /**
- * Indicates that an operation should not throw an exception when the response indicates a failure.
- */
- NO_THROW;
-}
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RequestOptions.java b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RequestOptions.java
index f8056580127d7..5e0f8f096cec2 100644
--- a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RequestOptions.java
+++ b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RequestOptions.java
@@ -5,7 +5,6 @@
import com.azure.core.annotation.QueryParam;
import com.azure.core.http.HttpRequest;
-import com.azure.core.http.ResponseStatusOption;
import com.azure.core.util.BinaryData;
import java.util.function.Consumer;
@@ -74,7 +73,7 @@
*/
public final class RequestOptions {
private Consumer requestCallback = request -> { };
- private ResponseStatusOption statusOption = ResponseStatusOption.DEFAULT;
+ private boolean throwOnError = true;
private BinaryData requestBody;
/**
@@ -86,11 +85,13 @@ Consumer getRequestCallback() {
}
/**
- * Gets under what conditions the operation raises an exception if the underlying response indicates a failure.
- * @return the configured option
+ * Gets whether or not to throw an exception when an HTTP response with a status code indicating an error
+ * (400 or above) is received.
+ *
+ * @return true if to throw on status codes of 400 or above, false if not. Default is true.
*/
- ResponseStatusOption getStatusOption() {
- return this.statusOption;
+ boolean isThrowOnError() {
+ return this.throwOnError;
}
/**
@@ -130,9 +131,9 @@ public RequestOptions addQueryParam(String parameterName, String value) {
public RequestOptions addQueryParam(String parameterName, String value, boolean encoded) {
this.requestCallback = this.requestCallback.andThen(request -> {
String url = request.getUrl().toString();
- request.setUrl(url + (url.contains("?") ? "&" : "?")
- + UrlEscapers.QUERY_ESCAPER.escape(parameterName) + "="
- + UrlEscapers.QUERY_ESCAPER.escape(value));
+ String encodedParameterName = encoded ? parameterName : UrlEscapers.QUERY_ESCAPER.escape(parameterName);
+ String encodedParameterValue = encoded ? value : UrlEscapers.QUERY_ESCAPER.escape(value);
+ request.setUrl(url + (url.contains("?") ? "&" : "?") + encodedParameterName + "=" + encodedParameterValue);
});
return this;
}
@@ -162,12 +163,14 @@ public RequestOptions setBody(BinaryData requestBody) {
}
/**
- * Sets under what conditions the operation raises an exception if the underlying response indicates a failure.
- * @param statusOption the option to control under what conditions the operation raises an exception
+ * Sets whether or not to throw an exception when an HTTP response with a status code indicating an error
+ * (400 or above) is received. By default an exception will be thrown when an error response is received.
+ *
+ * @param throwOnError true if to throw on status codes of 400 or above, false if not. Default is true.
* @return the modified RequestOptions object
*/
- public RequestOptions setStatusOption(ResponseStatusOption statusOption) {
- this.statusOption = statusOption;
+ public RequestOptions setThrowOnError(boolean throwOnError) {
+ this.throwOnError = throwOnError;
return this;
}
}
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RestProxy.java b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RestProxy.java
index e450337ef7d2a..e52deecbc1cb4 100644
--- a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RestProxy.java
+++ b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RestProxy.java
@@ -13,7 +13,6 @@
import com.azure.core.http.HttpPipelineBuilder;
import com.azure.core.http.HttpRequest;
import com.azure.core.http.HttpResponse;
-import com.azure.core.http.ResponseStatusOption;
import com.azure.core.http.policy.CookiePolicy;
import com.azure.core.http.policy.HttpPipelinePolicy;
import com.azure.core.http.policy.RetryPolicy;
@@ -379,7 +378,7 @@ private Mono ensureExpectedStatus(final HttpDecodedResponse
final int responseStatusCode = decodedResponse.getSourceResponse().getStatusCode();
final Mono asyncResult;
if (!methodParser.isExpectedResponseStatusCode(responseStatusCode)
- && (options == null || options.getStatusOption() != ResponseStatusOption.NO_THROW)) {
+ && (options == null || options.isThrowOnError())) {
Mono bodyAsBytes = decodedResponse.getSourceResponse().getBodyAsByteArray();
asyncResult = bodyAsBytes.flatMap((Function>) responseContent -> {
diff --git a/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/SwaggerMethodParserTests.java b/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/SwaggerMethodParserTests.java
index 944de9b5dd3be..f63b09e325568 100644
--- a/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/SwaggerMethodParserTests.java
+++ b/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/SwaggerMethodParserTests.java
@@ -26,7 +26,6 @@
import com.azure.core.http.HttpHeader;
import com.azure.core.http.HttpHeaders;
import com.azure.core.http.HttpMethod;
-import com.azure.core.http.ResponseStatusOption;
import com.azure.core.implementation.UnixTime;
import com.azure.core.util.Base64Url;
import com.azure.core.util.BinaryData;
From 548ad2c091a8f741d2d7121746dc3bdab7a1fffb Mon Sep 17 00:00:00 2001
From: Jianghao Lu
Date: Wed, 23 Jun 2021 13:36:14 -0700
Subject: [PATCH 06/11] Fix issues from cr feedback
---
.../azure/core/http/rest/RequestOptions.java | 27 ++++++++++---------
.../RequestOptionsJavaDocCodeSnippets.java | 12 ++++-----
2 files changed, 20 insertions(+), 19 deletions(-)
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RequestOptions.java b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RequestOptions.java
index 5e0f8f096cec2..aab56b854339a 100644
--- a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RequestOptions.java
+++ b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RequestOptions.java
@@ -4,6 +4,7 @@
package com.azure.core.http.rest;
import com.azure.core.annotation.QueryParam;
+import com.azure.core.http.HttpHeader;
import com.azure.core.http.HttpRequest;
import com.azure.core.util.BinaryData;
@@ -26,14 +27,7 @@
*
*
* Creating an instance of DynamicRequest using the constructor
- * {@codesnippet com.azure.core.http.requestoptions.instantiation}
- **
- * Configuring the request with a path param and making a HTTP GET request
- * Continuing with the pet store example, getting information about a pet requires making a
- * HTTP GET call
- * to the pet service and setting the pet id in path param as shown in the sample below.
- *
- * {@codesnippet com.azure.core.http.dynamicrequest.getrequest}
+ * {@codesnippet com.azure.core.http.rest.requestoptions.instantiation}
*
* Configuring the request with JSON body and making a HTTP POST request
* To add a new pet to the pet store , a HTTP POST call should
@@ -65,11 +59,11 @@
* To create a concrete request, Json builder provided in javax package is used here for demonstration. However, any
* other Json building library can be used to achieve similar results.
*
- * {@codesnippet com.azure.core.http.requestoptions.createjsonrequest}
+ * {@codesnippet com.azure.core.http.rest.requestoptions.createjsonrequest}
*
* Now, this string representation of the JSON request can be set as body of RequestOptions
*
- * {@codesnippet com.azure.core.http.requestoptions.postrequest}
+ * {@codesnippet com.azure.core.http.rest.requestoptions.postrequest}
*/
public final class RequestOptions {
private Consumer requestCallback = request -> { };
@@ -102,13 +96,20 @@ boolean isThrowOnError() {
* @return the modified RequestOptions object
*/
public RequestOptions addHeader(String header, String value) {
- this.requestCallback = this.requestCallback.andThen(request ->
- request.getHeaders().get(header).addValue(value));
+ this.requestCallback = this.requestCallback.andThen(request -> {
+ HttpHeader httpHeader = request.getHeaders().get(header);
+ if (httpHeader == null) {
+ request.getHeaders().set(header, value);
+ } else {
+ httpHeader.addValue(value);
+ }
+ });
return this;
}
/**
- * Adds a query parameter to the request URL.
+ * Adds a query parameter to the request URL. The parameter name and value will be URL encoded.
+ * To use an already encoded parameter name and value, call {@code addQueryParam("name", "value", true)}.
*
* @param parameterName the name of the query parameter
* @param value the value of the query parameter
diff --git a/sdk/core/azure-core/src/samples/java/com/azure/core/http/RequestOptionsJavaDocCodeSnippets.java b/sdk/core/azure-core/src/samples/java/com/azure/core/http/RequestOptionsJavaDocCodeSnippets.java
index 757c28d12b0c3..a3b57cc880644 100644
--- a/sdk/core/azure-core/src/samples/java/com/azure/core/http/RequestOptionsJavaDocCodeSnippets.java
+++ b/sdk/core/azure-core/src/samples/java/com/azure/core/http/RequestOptionsJavaDocCodeSnippets.java
@@ -20,11 +20,11 @@ public class RequestOptionsJavaDocCodeSnippets {
* @return An instance of {@link RequestOptions}.
*/
public RequestOptions createInstance() {
- // BEGIN: com.azure.core.experimental.http.requestoptions.instantiation
+ // BEGIN: com.azure.core.http.rest.requestoptions.instantiation
RequestOptions options = new RequestOptions()
.setBody(BinaryData.fromString("{\"name\":\"Fluffy\"}"))
.addHeader("x-ms-pet-version", "2021-06-01");
- // END: com.azure.core.experimental.http.requestoptions.instantiation
+ // END: com.azure.core.http.rest.requestoptions.instantiation
return options;
}
@@ -33,7 +33,7 @@ public RequestOptions createInstance() {
* @return An instance of {@link RequestOptions}.
*/
public RequestOptions setJsonRequestBodyInRequestOptions() {
- // BEGIN: com.azure.core.experimental.http.requestoptions.createjsonrequest
+ // BEGIN: com.azure.core.http.rest.requestoptions.createjsonrequest
JsonArray photoUrls = Json.createArrayBuilder()
.add("https://imgur.com/pet1")
.add("https://imgur.com/pet2")
@@ -60,9 +60,9 @@ public RequestOptions setJsonRequestBodyInRequestOptions() {
.build();
String requestBodyStr = requestBody.toString();
- // END: com.azure.core.experimental.http.requestoptions.createjsonrequest
+ // END: com.azure.core.http.rest.requestoptions.createjsonrequest
- // BEGIN: com.azure.core.experimental.http.requestoptions.postrequest
+ // BEGIN: com.azure.core.http.rest.requestoptions.postrequest
RequestOptions options = new RequestOptions()
.addRequestCallback(request -> request
// may already be set if request is created from a client
@@ -70,7 +70,7 @@ public RequestOptions setJsonRequestBodyInRequestOptions() {
.setHttpMethod(HttpMethod.POST)
.setBody(requestBodyStr)
.setHeader("Content-Type", "application/json"));
- // END: com.azure.core.experimental.http.requestoptions.postrequest
+ // END: com.azure.core.http.rest.requestoptions.postrequest
return options;
}
}
From 97952416c6a43592a43fcf4df3003a26e8d24d84 Mon Sep 17 00:00:00 2001
From: Jianghao Lu
Date: Wed, 23 Jun 2021 13:51:12 -0700
Subject: [PATCH 07/11] Move javadoc samples
---
.../http/{ => rest}/RequestOptionsJavaDocCodeSnippets.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
rename sdk/core/azure-core/src/samples/java/com/azure/core/http/{ => rest}/RequestOptionsJavaDocCodeSnippets.java (97%)
diff --git a/sdk/core/azure-core/src/samples/java/com/azure/core/http/RequestOptionsJavaDocCodeSnippets.java b/sdk/core/azure-core/src/samples/java/com/azure/core/http/rest/RequestOptionsJavaDocCodeSnippets.java
similarity index 97%
rename from sdk/core/azure-core/src/samples/java/com/azure/core/http/RequestOptionsJavaDocCodeSnippets.java
rename to sdk/core/azure-core/src/samples/java/com/azure/core/http/rest/RequestOptionsJavaDocCodeSnippets.java
index a3b57cc880644..9b938fb000fcf 100644
--- a/sdk/core/azure-core/src/samples/java/com/azure/core/http/RequestOptionsJavaDocCodeSnippets.java
+++ b/sdk/core/azure-core/src/samples/java/com/azure/core/http/rest/RequestOptionsJavaDocCodeSnippets.java
@@ -1,8 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
-package com.azure.core.http;
+package com.azure.core.http.rest;
+import com.azure.core.http.HttpMethod;
import com.azure.core.http.rest.RequestOptions;
import com.azure.core.util.BinaryData;
From 0620138aeb34fd62b820e41c14a33647e5e6d5b0 Mon Sep 17 00:00:00 2001
From: Jianghao Lu
Date: Wed, 23 Jun 2021 14:04:42 -0700
Subject: [PATCH 08/11] Fix copy paste error in javadocs
---
.../src/main/java/com/azure/core/http/rest/RequestOptions.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RequestOptions.java b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RequestOptions.java
index aab56b854339a..627fd5dac86f8 100644
--- a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RequestOptions.java
+++ b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RequestOptions.java
@@ -26,7 +26,7 @@
* list of APIs available on this service are documented in the swagger definition.
*
*
- * Creating an instance of DynamicRequest using the constructor
+ * Creating an instance of RequestOptions using the constructor
* {@codesnippet com.azure.core.http.rest.requestoptions.instantiation}
*
* Configuring the request with JSON body and making a HTTP POST request
From fef0373a1c6089bf27c177220e319a6fda27ad2f Mon Sep 17 00:00:00 2001
From: Jianghao Lu
Date: Wed, 23 Jun 2021 15:45:29 -0700
Subject: [PATCH 09/11] Fix swagger method parser test
---
.../src/main/java/com/azure/core/http/rest/RequestOptions.java | 2 +-
.../java/com/azure/core/http/rest/SwaggerMethodParserTests.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RequestOptions.java b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RequestOptions.java
index 627fd5dac86f8..9ea4770041051 100644
--- a/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RequestOptions.java
+++ b/sdk/core/azure-core/src/main/java/com/azure/core/http/rest/RequestOptions.java
@@ -26,7 +26,7 @@
* list of APIs available on this service are documented in the swagger definition.
*
*
- * Creating an instance of RequestOptions using the constructor
+ * Creating an instance of RequestOptions
* {@codesnippet com.azure.core.http.rest.requestoptions.instantiation}
*
* Configuring the request with JSON body and making a HTTP POST request
diff --git a/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/SwaggerMethodParserTests.java b/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/SwaggerMethodParserTests.java
index f63b09e325568..964a3fd35725f 100644
--- a/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/SwaggerMethodParserTests.java
+++ b/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/SwaggerMethodParserTests.java
@@ -516,7 +516,7 @@ private static Stream setRequestOptionsSupplier() throws NoSuchMethod
.addRequestCallback(httpRequest -> httpRequest.setUrl("https://foo.host.com"));
RequestOptions statusOptionOptions = new RequestOptions()
- .setStatusOption(ResponseStatusOption.NO_THROW);
+ .setThrowOnError(false);
return Stream.of(
Arguments.of(swaggerMethodParser, null, null),
From b0689bbea1eab4a6ef1b80175df7dc33712b1d79 Mon Sep 17 00:00:00 2001
From: Jianghao Lu
Date: Wed, 23 Jun 2021 16:11:14 -0700
Subject: [PATCH 10/11] Fix test broken by encoding
---
.../java/com/azure/core/http/rest/RequestOptionsTests.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/RequestOptionsTests.java b/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/RequestOptionsTests.java
index a19e230aad539..ec0ec5694e13c 100644
--- a/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/RequestOptionsTests.java
+++ b/sdk/core/azure-core/src/test/java/com/azure/core/http/rest/RequestOptionsTests.java
@@ -26,7 +26,7 @@ public void addQueryParam() throws MalformedURLException {
.addQueryParam("$skipToken", "1");
options.getRequestCallback().accept(request);
- assertTrue(request.getUrl().toString().contains("?foo=bar&$skipToken=1"));
+ assertTrue(request.getUrl().toString().contains("?foo=bar&%24skipToken=1"));
}
@Test
@@ -74,6 +74,6 @@ public void addRequestCallback() throws MalformedURLException {
HttpHeaders headers = request.getHeaders();
assertEquals("baz", headers.getValue("x-ms-foo"));
assertEquals(HttpMethod.GET, request.getHttpMethod());
- assertEquals("https://request.url?$skipToken=1", request.getUrl().toString());
+ assertEquals("https://request.url?%24skipToken=1", request.getUrl().toString());
}
}
From 281629d0b3f9956cf6172dc2840a0b19573672e0 Mon Sep 17 00:00:00 2001
From: Jianghao Lu
Date: Wed, 23 Jun 2021 16:37:54 -0700
Subject: [PATCH 11/11] Fix checkstyle error
---
.../azure/core/http/rest/RequestOptionsJavaDocCodeSnippets.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/sdk/core/azure-core/src/samples/java/com/azure/core/http/rest/RequestOptionsJavaDocCodeSnippets.java b/sdk/core/azure-core/src/samples/java/com/azure/core/http/rest/RequestOptionsJavaDocCodeSnippets.java
index 9b938fb000fcf..46a49c2d672ad 100644
--- a/sdk/core/azure-core/src/samples/java/com/azure/core/http/rest/RequestOptionsJavaDocCodeSnippets.java
+++ b/sdk/core/azure-core/src/samples/java/com/azure/core/http/rest/RequestOptionsJavaDocCodeSnippets.java
@@ -4,7 +4,6 @@
package com.azure.core.http.rest;
import com.azure.core.http.HttpMethod;
-import com.azure.core.http.rest.RequestOptions;
import com.azure.core.util.BinaryData;
import javax.json.Json;