diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/Metadata.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/Metadata.java index c320a5a21e5d..d4724384f55e 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/Metadata.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/Metadata.java @@ -519,6 +519,11 @@ public boolean isXmlProtocol() { protocol == Protocol.REST_XML; } + public boolean isQueryProtocol() { + return protocol == Protocol.EC2 || + protocol == Protocol.QUERY; + } + /** * @return True for RESTful protocols. False for all other protocols (RPC, Query, etc). */ diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClass.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClass.java index c2a0e8aaac35..70570438a9de 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClass.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClass.java @@ -24,8 +24,12 @@ import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterizedTypeName; +import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.TypeVariableName; + +import java.util.Collections; import java.util.List; import javax.lang.model.element.Modifier; import software.amazon.awssdk.annotations.SdkInternalApi; @@ -46,6 +50,7 @@ import software.amazon.awssdk.core.signer.Signer; import software.amazon.awssdk.http.Protocol; import software.amazon.awssdk.http.SdkHttpConfigurationOption; +import software.amazon.awssdk.protocols.query.interceptor.QueryParametersToBodyInterceptor; import software.amazon.awssdk.utils.AttributeMap; import software.amazon.awssdk.utils.CollectionUtils; import software.amazon.awssdk.utils.StringUtils; @@ -177,6 +182,16 @@ private MethodSpec finalizeServiceConfigurationMethod() { .addCode("interceptors = $T.mergeLists(interceptors, config.option($T.EXECUTION_INTERCEPTORS));\n", CollectionUtils.class, SdkClientOption.class); + if (model.getMetadata().isQueryProtocol()) { + TypeName listType = ParameterizedTypeName.get(List.class, ExecutionInterceptor.class); + builder.addStatement("$T protocolInterceptors = $T.singletonList(new $T())", + listType, + Collections.class, + QueryParametersToBodyInterceptor.class); + builder.addStatement("interceptors = $T.mergeLists(interceptors, protocolInterceptors)", + CollectionUtils.class); + } + if (model.getEndpointOperation().isPresent()) { builder.beginControlFlow("if (!endpointDiscoveryEnabled)") .addStatement("endpointDiscoveryEnabled = CHAIN.resolveEndpointDiscovery()") diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/interceptor/QueryParametersToBodyInterceptor.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/interceptor/QueryParametersToBodyInterceptor.java new file mode 100644 index 000000000000..f094df4b6346 --- /dev/null +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/interceptor/QueryParametersToBodyInterceptor.java @@ -0,0 +1,82 @@ +/* + * Copyright 2010-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.protocols.query.interceptor; + +import static java.util.Collections.singletonList; +import static software.amazon.awssdk.utils.StringUtils.lowerCase; + +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; + +import software.amazon.awssdk.annotations.SdkProtectedApi; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.http.SdkHttpMethod; +import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.utils.CollectionUtils; +import software.amazon.awssdk.utils.http.SdkHttpUtils; + +/** + * Modifies an HTTP request by moving query parameters to the body under the following conditions: + * - It is a POST request + * - There is no content stream provider + * - There are query parameters to transfer + *

+ * This interceptor is automatically inserted by codegen for services using Query Protocol + */ +@SdkProtectedApi +public final class QueryParametersToBodyInterceptor implements ExecutionInterceptor { + + private static final String DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded; charset=" + + lowerCase(StandardCharsets.UTF_8.toString()); + + @Override + public SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context, + ExecutionAttributes executionAttributes) { + + SdkHttpRequest httpRequest = context.httpRequest(); + + if (!(httpRequest instanceof SdkHttpFullRequest)) { + return httpRequest; + } + + SdkHttpFullRequest httpFullRequest = (SdkHttpFullRequest) httpRequest; + if (shouldPutParamsInBody(httpFullRequest)) { + return changeQueryParametersToFormData(httpFullRequest); + } + return httpFullRequest; + } + + private boolean shouldPutParamsInBody(SdkHttpFullRequest input) { + return input.method() == SdkHttpMethod.POST && + !input.contentStreamProvider().isPresent() && + !CollectionUtils.isNullOrEmpty(input.rawQueryParameters()); + } + + private SdkHttpRequest changeQueryParametersToFormData(SdkHttpFullRequest input) { + byte[] params = SdkHttpUtils.encodeAndFlattenFormData(input.rawQueryParameters()).orElse("") + .getBytes(StandardCharsets.UTF_8); + + return input.toBuilder().clearQueryParameters() + .contentStreamProvider(() -> new ByteArrayInputStream(params)) + .putHeader("Content-Length", singletonList(String.valueOf(params.length))) + .putHeader("Content-Type", singletonList(DEFAULT_CONTENT_TYPE)) + .build(); + } + +} diff --git a/core/protocols/aws-query-protocol/src/test/java/software/amazon/awssdk/protocols/query/interceptor/QueryParametersToBodyInterceptorTest.java b/core/protocols/aws-query-protocol/src/test/java/software/amazon/awssdk/protocols/query/interceptor/QueryParametersToBodyInterceptorTest.java new file mode 100644 index 000000000000..604674f6b11b --- /dev/null +++ b/core/protocols/aws-query-protocol/src/test/java/software/amazon/awssdk/protocols/query/interceptor/QueryParametersToBodyInterceptorTest.java @@ -0,0 +1,154 @@ +/* + * Copyright 2010-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.protocols.query.interceptor; + +import org.junit.Before; +import org.junit.Test; +import software.amazon.awssdk.core.Protocol; +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.async.AsyncRequestBody; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.http.ContentStreamProvider; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.http.SdkHttpMethod; +import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.utils.IoUtils; + +import java.io.ByteArrayInputStream; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.Optional; +import java.util.stream.Stream; + +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; + +public class QueryParametersToBodyInterceptorTest { + + public static final URI HTTP_LOCALHOST = URI.create("http://localhost:8080"); + + private QueryParametersToBodyInterceptor interceptor; + private ExecutionAttributes executionAttributes; + + private SdkHttpFullRequest.Builder requestBuilder; + + @Before + public void setup() { + + interceptor = new QueryParametersToBodyInterceptor(); + executionAttributes = new ExecutionAttributes(); + + requestBuilder = SdkHttpFullRequest.builder() + .protocol(Protocol.HTTPS.toString()) + .method(SdkHttpMethod.POST) + .putRawQueryParameter("key", singletonList("value")) + .uri(HTTP_LOCALHOST); + } + + @Test + public void postRequestsWithNoBodyHaveTheirParametersMovedToTheBody() throws Exception { + + SdkHttpFullRequest request = requestBuilder.build(); + + SdkHttpFullRequest output = (SdkHttpFullRequest) interceptor.modifyHttpRequest( + new HttpRequestOnlyContext(request, null), executionAttributes); + + assertThat(output.rawQueryParameters()).hasSize(0); + assertThat(output.headers()) + .containsKey("Content-Length") + .containsEntry("Content-Type", singletonList("application/x-www-form-urlencoded; charset=utf-8")); + assertThat(output.contentStreamProvider()).isNotEmpty(); + } + + @Test + public void nonPostRequestsWithNoBodyAreUnaltered() throws Exception { + Stream.of(SdkHttpMethod.values()) + .filter(m -> !m.equals(SdkHttpMethod.POST)) + .forEach(this::nonPostRequestsUnaltered); + } + + @Test + public void postWithContentIsUnaltered() throws Exception { + byte[] contentBytes = "hello".getBytes(StandardCharsets.UTF_8); + ContentStreamProvider contentProvider = () -> new ByteArrayInputStream(contentBytes); + + SdkHttpFullRequest request = requestBuilder.contentStreamProvider(contentProvider).build(); + + SdkHttpFullRequest output = (SdkHttpFullRequest) interceptor.modifyHttpRequest( + new HttpRequestOnlyContext(request, null), executionAttributes); + + assertThat(output.rawQueryParameters()).hasSize(1); + assertThat(output.headers()).hasSize(0); + assertThat(IoUtils.toByteArray(output.contentStreamProvider().get().newStream())).isEqualTo(contentBytes); + } + + @Test + public void onlyAlterRequestsIfParamsArePresent() throws Exception { + SdkHttpFullRequest request = requestBuilder.clearQueryParameters().build(); + + SdkHttpFullRequest output = (SdkHttpFullRequest) interceptor.modifyHttpRequest( + new HttpRequestOnlyContext(request, null), executionAttributes); + + assertThat(output.rawQueryParameters()).hasSize(0); + assertThat(output.headers()).hasSize(0); + assertThat(output.contentStreamProvider()).isEmpty(); + } + + private void nonPostRequestsUnaltered(SdkHttpMethod method) { + + SdkHttpFullRequest request = requestBuilder.method(method).build(); + + SdkHttpFullRequest output = (SdkHttpFullRequest) interceptor.modifyHttpRequest( + new HttpRequestOnlyContext(request, null), executionAttributes); + + assertThat(output.rawQueryParameters()).hasSize(1); + assertThat(output.headers()).hasSize(0); + assertThat(output.contentStreamProvider()).isEmpty(); + } + + public final class HttpRequestOnlyContext implements software.amazon.awssdk.core.interceptor.Context.ModifyHttpRequest { + + private final SdkHttpRequest request; + private final RequestBody requestBody; + + public HttpRequestOnlyContext(SdkHttpRequest request, + RequestBody requestBody) { + this.request = request; + this.requestBody = requestBody; + } + + @Override + public SdkRequest request() { + return null; + } + + @Override + public SdkHttpRequest httpRequest() { + return request; + } + + @Override + public Optional requestBody() { + return Optional.ofNullable(requestBody); + } + + @Override + public Optional asyncRequestBody() { + return Optional.empty(); + } + } +} diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/AmazonAsyncHttpClient.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/AmazonAsyncHttpClient.java index 04ec7d53ba1c..90ab4fad1b7c 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/AmazonAsyncHttpClient.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/AmazonAsyncHttpClient.java @@ -40,7 +40,6 @@ import software.amazon.awssdk.core.internal.http.pipeline.stages.MakeRequestMutableStage; import software.amazon.awssdk.core.internal.http.pipeline.stages.MergeCustomHeadersStage; import software.amazon.awssdk.core.internal.http.pipeline.stages.MergeCustomQueryParamsStage; -import software.amazon.awssdk.core.internal.http.pipeline.stages.MoveParametersToBodyStage; import software.amazon.awssdk.core.internal.http.pipeline.stages.SigningStage; import software.amazon.awssdk.core.internal.http.pipeline.stages.UnwrapResponseContainer; import software.amazon.awssdk.core.internal.retry.SdkDefaultRetrySetting; @@ -178,7 +177,6 @@ public CompletableFuture execute( .then(ApplyUserAgentStage::new) .then(MergeCustomHeadersStage::new) .then(MergeCustomQueryParamsStage::new) - .then(MoveParametersToBodyStage::new) .then(MakeRequestImmutableStage::new) .then(RequestPipelineBuilder .first(SigningStage::new) diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/AmazonSyncHttpClient.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/AmazonSyncHttpClient.java index b98361021d3f..3bdcc23d5966 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/AmazonSyncHttpClient.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/AmazonSyncHttpClient.java @@ -40,7 +40,6 @@ import software.amazon.awssdk.core.internal.http.pipeline.stages.MakeRequestMutableStage; import software.amazon.awssdk.core.internal.http.pipeline.stages.MergeCustomHeadersStage; import software.amazon.awssdk.core.internal.http.pipeline.stages.MergeCustomQueryParamsStage; -import software.amazon.awssdk.core.internal.http.pipeline.stages.MoveParametersToBodyStage; import software.amazon.awssdk.core.internal.http.pipeline.stages.RetryableStage; import software.amazon.awssdk.core.internal.http.pipeline.stages.SigningStage; import software.amazon.awssdk.core.internal.http.pipeline.stages.TimeoutExceptionHandlingStage; @@ -178,7 +177,6 @@ public OutputT execute(HttpResponseHandler> response .then(ApplyUserAgentStage::new) .then(MergeCustomHeadersStage::new) .then(MergeCustomQueryParamsStage::new) - .then(MoveParametersToBodyStage::new) .then(MakeRequestImmutableStage::new) // End of mutating request .then(RequestPipelineBuilder diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/MoveParametersToBodyStage.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/MoveParametersToBodyStage.java deleted file mode 100644 index d7b02781998a..000000000000 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/MoveParametersToBodyStage.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2010-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.core.internal.http.pipeline.stages; - -import static java.util.Collections.singletonList; -import static software.amazon.awssdk.utils.StringUtils.lowerCase; - -import java.io.ByteArrayInputStream; -import java.nio.charset.StandardCharsets; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.core.internal.http.RequestExecutionContext; -import software.amazon.awssdk.core.internal.http.pipeline.MutableRequestToRequestPipeline; -import software.amazon.awssdk.http.SdkHttpFullRequest; -import software.amazon.awssdk.http.SdkHttpMethod; -import software.amazon.awssdk.utils.CollectionUtils; -import software.amazon.awssdk.utils.http.SdkHttpUtils; - -@SdkInternalApi -//TODO Might only need to do this for certain protocols - ie query? -public final class MoveParametersToBodyStage implements MutableRequestToRequestPipeline { - @Override - public SdkHttpFullRequest.Builder execute(SdkHttpFullRequest.Builder input, RequestExecutionContext context) { - if (shouldPutParamsInBody(input)) { - return changeQueryParametersToFormData(input); - } - return input; - } - - private boolean shouldPutParamsInBody(SdkHttpFullRequest.Builder input) { - return input.method() == SdkHttpMethod.POST && - input.contentStreamProvider() == null && - !CollectionUtils.isNullOrEmpty(input.rawQueryParameters()); - } - - private static SdkHttpFullRequest.Builder changeQueryParametersToFormData(SdkHttpFullRequest.Builder input) { - byte[] params = SdkHttpUtils.encodeAndFlattenFormData(input.rawQueryParameters()).orElse("") - .getBytes(StandardCharsets.UTF_8); - - return input.clearQueryParameters() - .contentStreamProvider(() -> new ByteArrayInputStream(params)) - .putHeader("Content-Length", singletonList(String.valueOf(params.length))) - .putHeader("Content-Type", singletonList("application/x-www-form-urlencoded; charset=" + - lowerCase(StandardCharsets.UTF_8.toString()))); - } -} diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/MoveParametersToBodyStageTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/MoveParametersToBodyStageTest.java deleted file mode 100644 index c4f481b363b2..000000000000 --- a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/MoveParametersToBodyStageTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2010-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.core.internal.http.pipeline.stages; - -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.ByteArrayInputStream; -import java.nio.charset.StandardCharsets; -import java.util.stream.Stream; -import org.junit.Test; -import software.amazon.awssdk.core.http.ExecutionContext; -import software.amazon.awssdk.core.http.NoopTestRequest; -import software.amazon.awssdk.core.internal.http.RequestExecutionContext; -import software.amazon.awssdk.core.internal.http.timers.ClientExecutionAndRequestTimerTestUtils; -import software.amazon.awssdk.http.ContentStreamProvider; -import software.amazon.awssdk.http.SdkHttpFullRequest; -import software.amazon.awssdk.http.SdkHttpMethod; -import software.amazon.awssdk.utils.IoUtils; -import utils.ValidSdkObjects; - -public class MoveParametersToBodyStageTest { - - private final MoveParametersToBodyStage sut = new MoveParametersToBodyStage(); - - @Test - public void postRequestsWithNoBodyHaveTheirParametersMovedToTheBody() throws Exception { - SdkHttpFullRequest.Builder mutableRequest = ValidSdkObjects.sdkHttpFullRequest() - .method(SdkHttpMethod.POST) - .putRawQueryParameter("key", singletonList("value")); - - SdkHttpFullRequest output = sut.execute(mutableRequest, requestContext(mutableRequest)).build(); - - assertThat(output.rawQueryParameters()).hasSize(0); - assertThat(output.headers()) - .containsKey("Content-Length") - .containsEntry("Content-Type", singletonList("application/x-www-form-urlencoded; charset=utf-8")); - assertThat(output.contentStreamProvider()).isNotEmpty(); - } - - @Test - public void nonPostRequestsWithNoBodyAreUnaltered() throws Exception { - Stream.of(SdkHttpMethod.values()) - .filter(m -> !m.equals(SdkHttpMethod.POST)) - .forEach(this::nonPostRequestsUnaltered); - } - - @Test - public void postWithContentIsUnaltered() throws Exception { - byte[] contentBytes = "hello".getBytes(StandardCharsets.UTF_8); - ContentStreamProvider contentProvider = () -> new ByteArrayInputStream(contentBytes); - SdkHttpFullRequest.Builder mutableRequest = ValidSdkObjects.sdkHttpFullRequest() - .contentStreamProvider(contentProvider) - .method(SdkHttpMethod.POST) - .clearHeaders() - .putRawQueryParameter("key", singletonList("value")); - - SdkHttpFullRequest output = sut.execute(mutableRequest, requestContext(mutableRequest)).build(); - - assertThat(output.rawQueryParameters()).hasSize(1); - assertThat(output.headers()).hasSize(0); - assertThat(IoUtils.toByteArray(output.contentStreamProvider().get().newStream())).isEqualTo(contentBytes); - } - - @Test - public void onlyAlterRequestsIfParamsArePresent() throws Exception { - SdkHttpFullRequest.Builder mutableRequest = ValidSdkObjects.sdkHttpFullRequest() - .clearHeaders() - .method(SdkHttpMethod.POST); - - SdkHttpFullRequest output = sut.execute(mutableRequest, requestContext(mutableRequest)).build(); - - assertThat(output.rawQueryParameters()).hasSize(0); - assertThat(output.headers()).hasSize(0); - assertThat(output.contentStreamProvider()).isEmpty(); - } - - private void nonPostRequestsUnaltered(SdkHttpMethod method) { - SdkHttpFullRequest.Builder mutableRequest = ValidSdkObjects.sdkHttpFullRequest() - .method(method) - .clearHeaders() - .putRawQueryParameter("key", singletonList("value")); - - SdkHttpFullRequest output = sut.execute(mutableRequest, requestContext(mutableRequest)).build(); - - assertThat(output.rawQueryParameters()).hasSize(1); - assertThat(output.headers()).hasSize(0); - assertThat(output.contentStreamProvider()).isEmpty(); - } - - private RequestExecutionContext requestContext(SdkHttpFullRequest.Builder mutableRequest) { - ExecutionContext executionContext = ClientExecutionAndRequestTimerTestUtils.executionContext(mutableRequest.build()); - return RequestExecutionContext.builder() - .executionContext(executionContext) - .originalRequest(NoopTestRequest.builder().build()) - .build(); - } -} diff --git a/test/protocol-tests/src/test/java/software/amazon/awssdk/protocol/tests/QueryRequestTransformTest.java b/test/protocol-tests/src/test/java/software/amazon/awssdk/protocol/tests/QueryRequestTransformTest.java new file mode 100644 index 000000000000..5be7f51a4460 --- /dev/null +++ b/test/protocol-tests/src/test/java/software/amazon/awssdk/protocol/tests/QueryRequestTransformTest.java @@ -0,0 +1,102 @@ +/* + * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.protocol.tests; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; +import static com.github.tomakehurst.wiremock.client.WireMock.containing; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.getAllServeEvents; +import static com.github.tomakehurst.wiremock.client.WireMock.matching; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.verify; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.stubbing.ServeEvent; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.http.Header; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.protocolquery.ProtocolQueryAsyncClient; +import software.amazon.awssdk.services.protocolquery.ProtocolQueryClient; +import software.amazon.awssdk.services.protocolquery.model.IdempotentOperationRequest; + +import java.net.URI; +import java.util.List; + +public class QueryRequestTransformTest { + + @Rule + public WireMockRule wireMock = new WireMockRule(0); + + private ProtocolQueryClient client; + private ProtocolQueryAsyncClient asyncClient; + + @Before + public void setupClient() { + client = ProtocolQueryClient.builder() + .credentialsProvider(StaticCredentialsProvider.create( + AwsBasicCredentials.create("akid", "skid"))) + .region(Region.US_EAST_1) + .endpointOverride(URI.create("http://localhost:" + wireMock.port())) + .build(); + + asyncClient = ProtocolQueryAsyncClient.builder() + .credentialsProvider(StaticCredentialsProvider.create( + AwsBasicCredentials.create("akid", "skid"))) + .region(Region.US_EAST_1) + .endpointOverride(URI.create("http://localhost:" + wireMock.port())) + .build(); + } + + @Test + public void syncRequest_isMovingParamsToBodyStage() { + stubSimpleResponse(); + IdempotentOperationRequest request = IdempotentOperationRequest.builder().idempotencyToken("test").build(); + client.idempotentOperation(request); + verifyResponseMetadata(); + } + + @Test + public void asyncRequest_isMovingParamsToBodyStage() { + stubSimpleResponse(); + IdempotentOperationRequest request = IdempotentOperationRequest.builder().idempotencyToken("test").build(); + asyncClient.idempotentOperation(request).join(); + verifyResponseMetadata(); + } + + private void verifyResponseMetadata() { + verify(postRequestedFor(anyUrl()) + .withHeader(Header.CONTENT_TYPE, equalTo("application/x-www-form-urlencoded; charset=UTF-8")) + .withHeader(Header.CONTENT_LENGTH, matching("\\d+")) + .withUrl("/") + .withRequestBody(containing("Action=IdempotentOperation")) + .withRequestBody(containing("Version=")) + .withRequestBody(containing("IdempotencyToken=test"))); + } + + private void stubSimpleResponse() { + stubFor(post(anyUrl()).willReturn(aResponse() + .withStatus(200) + .withBody(""))); + } + +}