diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/ServiceConfig.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/ServiceConfig.java index b50be4ff45df..b0c0db862df2 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/ServiceConfig.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/ServiceConfig.java @@ -47,6 +47,8 @@ public class ServiceConfig { private boolean hasAccelerateModeEnabledProperty = false; + private boolean hasCrossRegionAccessEnabledProperty = false; + public String getClassName() { return className; } @@ -95,6 +97,14 @@ public void setHasPathStyleAccessEnabledProperty(boolean hasPathStyleAccessEnabl this.hasPathStyleAccessEnabledProperty = hasPathStyleAccessEnabledProperty; } + public boolean hasCrossRegionAccessEnabledProperty() { + return hasCrossRegionAccessEnabledProperty; + } + + public void setHasCrossRegionAccessEnabledProperty(boolean hasCrossRegionAccessEnabledProperty) { + this.hasCrossRegionAccessEnabledProperty = hasCrossRegionAccessEnabledProperty; + } + public boolean hasAccelerateModeEnabledProperty() { return hasAccelerateModeEnabledProperty; } diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/AsyncClientBuilderClass.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/AsyncClientBuilderClass.java index f8731d7dad07..4e841ada78d3 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/AsyncClientBuilderClass.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/AsyncClientBuilderClass.java @@ -32,6 +32,7 @@ import software.amazon.awssdk.codegen.utils.AuthUtils; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; import software.amazon.awssdk.core.client.config.SdkClientOption; +import software.amazon.awssdk.endpoints.EndpointProvider; public class AsyncClientBuilderClass implements ClassSpec { private final IntermediateModel model; @@ -126,6 +127,9 @@ private MethodSpec buildClientMethod() { .addStatement("$T clientConfiguration = super.asyncClientConfiguration()", SdkClientConfiguration.class) .addStatement("this.validateClientOptions(clientConfiguration)") .addStatement("$T endpointOverride = null", URI.class) + .addStatement("$T endpointProvider = clientConfiguration.option($T.ENDPOINT_PROVIDER)", + EndpointProvider.class, + SdkClientOption.class) .addCode("if (clientConfiguration.option($T.ENDPOINT_OVERRIDDEN) != null" + "&& $T.TRUE.equals(clientConfiguration.option($T.ENDPOINT_OVERRIDDEN))) {" + "endpointOverride = clientConfiguration.option($T.ENDPOINT);" @@ -135,6 +139,7 @@ private MethodSpec buildClientMethod() { + ".overrideConfiguration(overrideConfiguration())" + ".region(clientConfiguration.option($T.AWS_REGION))" + ".endpointOverride(endpointOverride)" + + ".endpointProvider(endpointProvider)" + ".build()", serviceConfigClassName, serviceConfigClassName, AwsClientOption.class) .addStatement("return new $T(serviceClientConfiguration, clientConfiguration)", clientClassName) diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/SyncClientBuilderClass.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/SyncClientBuilderClass.java index 036589de04e8..a2d20fef479d 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/SyncClientBuilderClass.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/SyncClientBuilderClass.java @@ -32,6 +32,7 @@ import software.amazon.awssdk.codegen.utils.AuthUtils; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; import software.amazon.awssdk.core.client.config.SdkClientOption; +import software.amazon.awssdk.endpoints.EndpointProvider; public class SyncClientBuilderClass implements ClassSpec { private final IntermediateModel model; @@ -126,6 +127,8 @@ private MethodSpec buildClientMethod() { .addStatement("$T clientConfiguration = super.syncClientConfiguration()", SdkClientConfiguration.class) .addStatement("this.validateClientOptions(clientConfiguration)") .addStatement("$T endpointOverride = null", URI.class) + .addStatement("$T endpointProvider = clientConfiguration.option($T.ENDPOINT_PROVIDER)", + EndpointProvider.class, SdkClientOption.class) .addCode("if (clientConfiguration.option($T.ENDPOINT_OVERRIDDEN) != null" + "&& $T.TRUE.equals(clientConfiguration.option($T.ENDPOINT_OVERRIDDEN))) {" + "endpointOverride = clientConfiguration.option($T.ENDPOINT);" @@ -135,6 +138,7 @@ private MethodSpec buildClientMethod() { + ".overrideConfiguration(overrideConfiguration())" + ".region(clientConfiguration.option($T.AWS_REGION))" + ".endpointOverride(endpointOverride)" + + ".endpointProvider(endpointProvider)" + ".build()", serviceConfigClassName, serviceConfigClassName, AwsClientOption.class) .addStatement("return new $T(serviceClientConfiguration, clientConfiguration)", clientClassName) diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/model/ServiceClientConfigurationClass.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/model/ServiceClientConfigurationClass.java index 1d3d258ef01b..8d872211ea85 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/model/ServiceClientConfigurationClass.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/model/ServiceClientConfigurationClass.java @@ -31,6 +31,7 @@ import software.amazon.awssdk.codegen.poet.ClassSpec; import software.amazon.awssdk.codegen.poet.PoetUtils; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.endpoints.EndpointProvider; import software.amazon.awssdk.regions.Region; public class ServiceClientConfigurationClass implements ClassSpec { @@ -110,6 +111,13 @@ private TypeSpec builderInterfaceSpec() { .returns(className().nestedClass("Builder")) .addJavadoc("Configure the client override configuration") .build()) + .addMethod(MethodSpec.methodBuilder("endpointProvider") + .addAnnotation(Override.class) + .addModifiers(PUBLIC, ABSTRACT) + .addParameter(EndpointProvider.class, "endpointProvider") + .returns(className().nestedClass("Builder")) + .addJavadoc("Configure the endpointProvider") + .build()) .build(); } @@ -150,6 +158,14 @@ private TypeSpec builderImplSpec() { .addStatement("this.endpointOverride = endpointOverride") .addStatement("return this") .build()) + .addMethod(MethodSpec.methodBuilder("endpointProvider") + .addAnnotation(Override.class) + .addModifiers(PUBLIC) + .addParameter(EndpointProvider.class, "endpointProvider") + .returns(className().nestedClass("Builder")) + .addStatement("this.endpointProvider = endpointProvider") + .addStatement("return this") + .build()) .addMethod(MethodSpec.methodBuilder("build") .addAnnotation(Override.class) .addModifiers(PUBLIC) diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointParametersClassSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointParametersClassSpec.java index 22d25f69ba6d..a330213aa873 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointParametersClassSpec.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointParametersClassSpec.java @@ -23,6 +23,8 @@ import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterSpec; +import com.squareup.javapoet.ParameterizedTypeName; +import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import java.util.Map; import javax.lang.model.element.Modifier; @@ -33,6 +35,8 @@ import software.amazon.awssdk.codegen.poet.ClassSpec; import software.amazon.awssdk.codegen.poet.PoetUtils; import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; public class EndpointParametersClassSpec implements ClassSpec { private final IntermediateModel intermediateModel; @@ -53,6 +57,7 @@ public TypeSpec poetSpec() { .addType(builderInterfaceSpec()) .addType(builderImplSpec()) .addAnnotation(SdkPublicApi.class) + .addSuperinterface(toCopyableBuilderInterface()) .addModifiers(Modifier.PUBLIC, Modifier.FINAL); parameters().forEach((name, model) -> { @@ -60,6 +65,8 @@ public TypeSpec poetSpec() { b.addMethod(accessorMethod(name, model)); }); + b.addMethod(toBuilderMethod()); + return b.build(); } @@ -70,6 +77,7 @@ public ClassName className() { private TypeSpec builderInterfaceSpec() { TypeSpec.Builder b = TypeSpec.interfaceBuilder(builderInterfaceName()) + .addSuperinterface(copyableBuilderExtendsInterface()) .addModifiers(Modifier.PUBLIC); parameters().forEach((name, model) -> { @@ -89,6 +97,11 @@ private TypeSpec builderImplSpec() { .addModifiers(Modifier.PRIVATE, Modifier.STATIC) .addSuperinterface(builderInterfaceName()); + b.addMethod(MethodSpec.constructorBuilder() + .addModifiers(Modifier.PRIVATE) + .build()); + b.addMethod(toBuilderConstructor().build()); + parameters().forEach((name, model) -> { b.addField(fieldSpec(name, model).toBuilder().initializer(defaultValueCode(model)).build()); b.addMethod(builderSetterMethod(name, model)); @@ -183,6 +196,14 @@ private MethodSpec builderMethod() { .build(); } + private MethodSpec toBuilderMethod() { + return MethodSpec.methodBuilder("toBuilder") + .addModifiers(Modifier.PUBLIC) + .returns(builderInterfaceName()) + .addStatement("return new $T(this)", builderClassName()) + .build(); + } + private String variableName(String name) { return intermediateModel.getNamingStrategy().getVariableName(name); } @@ -224,4 +245,25 @@ private MethodSpec.Builder paramMethodBuilder(String name, ParameterModel model) } return b; } + + private MethodSpec.Builder toBuilderConstructor() { + MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder(); + constructorBuilder.addModifiers(Modifier.PRIVATE); + constructorBuilder.addParameter(className(), "builder"); + parameters().forEach((name, model) -> { + constructorBuilder.addStatement("this.$1N = builder.$1N", variableName(name)); + }); + return constructorBuilder; + } + + private TypeName toCopyableBuilderInterface() { + return ParameterizedTypeName.get(ClassName.get(ToCopyableBuilder.class), + className().nestedClass(builderInterfaceName().simpleName()), + className()); + } + + private TypeName copyableBuilderExtendsInterface() { + return ParameterizedTypeName.get(ClassName.get(CopyableBuilder.class), + builderInterfaceName(), className()); + } } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-async-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-async-client-builder-class.java index 61101024dda9..022c231111c4 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-async-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-async-client-builder-class.java @@ -7,6 +7,7 @@ import software.amazon.awssdk.awscore.client.config.AwsClientOption; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; import software.amazon.awssdk.core.client.config.SdkClientOption; +import software.amazon.awssdk.endpoints.EndpointProvider; import software.amazon.awssdk.services.json.endpoints.JsonEndpointProvider; /** @@ -33,13 +34,14 @@ protected final JsonAsyncClient buildClient() { SdkClientConfiguration clientConfiguration = super.asyncClientConfiguration(); this.validateClientOptions(clientConfiguration); URI endpointOverride = null; + EndpointProvider endpointProvider = clientConfiguration.option(SdkClientOption.ENDPOINT_PROVIDER); if (clientConfiguration.option(SdkClientOption.ENDPOINT_OVERRIDDEN) != null && Boolean.TRUE.equals(clientConfiguration.option(SdkClientOption.ENDPOINT_OVERRIDDEN))) { endpointOverride = clientConfiguration.option(SdkClientOption.ENDPOINT); } JsonServiceClientConfiguration serviceClientConfiguration = JsonServiceClientConfiguration.builder() .overrideConfiguration(overrideConfiguration()).region(clientConfiguration.option(AwsClientOption.AWS_REGION)) - .endpointOverride(endpointOverride).build(); + .endpointOverride(endpointOverride).endpointProvider(endpointProvider).build(); return new DefaultJsonAsyncClient(serviceClientConfiguration, clientConfiguration); } } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-sync-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-sync-client-builder-class.java index 64b443054e04..898aeb469ac6 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-sync-client-builder-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-sync-client-builder-class.java @@ -7,6 +7,7 @@ import software.amazon.awssdk.awscore.client.config.AwsClientOption; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; import software.amazon.awssdk.core.client.config.SdkClientOption; +import software.amazon.awssdk.endpoints.EndpointProvider; import software.amazon.awssdk.services.json.endpoints.JsonEndpointProvider; /** @@ -33,13 +34,14 @@ protected final JsonClient buildClient() { SdkClientConfiguration clientConfiguration = super.syncClientConfiguration(); this.validateClientOptions(clientConfiguration); URI endpointOverride = null; + EndpointProvider endpointProvider = clientConfiguration.option(SdkClientOption.ENDPOINT_PROVIDER); if (clientConfiguration.option(SdkClientOption.ENDPOINT_OVERRIDDEN) != null && Boolean.TRUE.equals(clientConfiguration.option(SdkClientOption.ENDPOINT_OVERRIDDEN))) { endpointOverride = clientConfiguration.option(SdkClientOption.ENDPOINT); } JsonServiceClientConfiguration serviceClientConfiguration = JsonServiceClientConfiguration.builder() .overrideConfiguration(overrideConfiguration()).region(clientConfiguration.option(AwsClientOption.AWS_REGION)) - .endpointOverride(endpointOverride).build(); + .endpointOverride(endpointOverride).endpointProvider(endpointProvider).build(); return new DefaultJsonClient(serviceClientConfiguration, clientConfiguration); } } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/model/serviceclientconfiguration.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/model/serviceclientconfiguration.java index 1220fcb9e0c0..572604798a8b 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/model/serviceclientconfiguration.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/model/serviceclientconfiguration.java @@ -5,6 +5,7 @@ import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.awscore.AwsServiceClientConfiguration; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.endpoints.EndpointProvider; import software.amazon.awssdk.regions.Region; /** @@ -45,6 +46,13 @@ public interface Builder extends AwsServiceClientConfiguration.Builder { */ @Override Builder overrideConfiguration(ClientOverrideConfiguration clientOverrideConfiguration); + + /** + * Configure the endpointProvider + */ + @Override + Builder endpointProvider(EndpointProvider endpointProvider); + } private static final class BuilderImpl extends AwsServiceClientConfiguration.BuilderImpl implements Builder { @@ -73,6 +81,12 @@ public Builder endpointOverride(URI endpointOverride) { return this; } + @Override + public Builder endpointProvider(EndpointProvider endpointProvider) { + this.endpointProvider = endpointProvider; + return this; + } + @Override public JsonProtocolTestsServiceClientConfiguration build() { return new JsonProtocolTestsServiceClientConfiguration(this); diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-parameters.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-parameters.java index 039823246232..7180601639af 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-parameters.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-parameters.java @@ -3,13 +3,15 @@ import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; /** * The parameters object used to resolve an endpoint for the Query service. */ @Generated("software.amazon.awssdk:codegen") @SdkPublicApi -public final class QueryEndpointParams { +public final class QueryEndpointParams implements ToCopyableBuilder { private final Region region; private final Boolean useDualStackEndpoint; @@ -88,7 +90,11 @@ public String operationContextParam() { return operationContextParam; } - public interface Builder { + public Builder toBuilder() { + return new BuilderImpl(this); + } + + public interface Builder extends CopyableBuilder { Builder region(Region region); Builder useDualStackEndpoint(Boolean useDualStackEndpoint); @@ -134,6 +140,22 @@ private static class BuilderImpl implements Builder { private String operationContextParam; + private BuilderImpl() { + } + + private BuilderImpl(QueryEndpointParams builder) { + this.region = builder.region; + this.useDualStackEndpoint = builder.useDualStackEndpoint; + this.useFIPSEndpoint = builder.useFIPSEndpoint; + this.endpointId = builder.endpointId; + this.defaultTrueParam = builder.defaultTrueParam; + this.defaultStringParam = builder.defaultStringParam; + this.deprecatedParam = builder.deprecatedParam; + this.booleanContextParam = builder.booleanContextParam; + this.stringContextParam = builder.stringContextParam; + this.operationContextParam = builder.operationContextParam; + } + @Override public Builder region(Region region) { this.region = region; diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsServiceClientConfiguration.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsServiceClientConfiguration.java index 30698fd6222a..97a20a1e31d5 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsServiceClientConfiguration.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsServiceClientConfiguration.java @@ -20,6 +20,7 @@ import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.core.SdkServiceClientConfiguration; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.endpoints.EndpointProvider; import software.amazon.awssdk.regions.Region; /** @@ -81,6 +82,9 @@ public interface Builder extends SdkServiceClientConfiguration.Builder { @Override Builder endpointOverride(URI endpointOverride); + @Override + Builder endpointProvider(EndpointProvider endpointProvider); + @Override AwsServiceClientConfiguration build(); } @@ -89,6 +93,7 @@ protected abstract static class BuilderImpl implements Builder { protected ClientOverrideConfiguration overrideConfiguration; protected Region region; protected URI endpointOverride; + protected EndpointProvider endpointProvider; protected BuilderImpl() { } @@ -97,6 +102,7 @@ protected BuilderImpl(AwsServiceClientConfiguration awsServiceClientConfiguratio this.overrideConfiguration = awsServiceClientConfiguration.overrideConfiguration(); this.region = awsServiceClientConfiguration.region(); this.endpointOverride = awsServiceClientConfiguration.endpointOverride().orElse(null); + this.endpointProvider = awsServiceClientConfiguration.endpointProvider().orElse(null); } @Override @@ -113,6 +119,12 @@ public final Region region() { public final URI endpointOverride() { return endpointOverride; } + + @Override + public final EndpointProvider endpointProvider() { + return endpointProvider; + } + } } diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilder.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilder.java index 32daaba3f3fb..07cff8936c7c 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilder.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilder.java @@ -43,6 +43,7 @@ import software.amazon.awssdk.core.internal.InternalCoreExecutionAttribute; import software.amazon.awssdk.core.internal.util.HttpChecksumResolver; import software.amazon.awssdk.core.signer.Signer; +import software.amazon.awssdk.endpoints.EndpointProvider; import software.amazon.awssdk.metrics.MetricCollector; @SdkInternalApi @@ -94,7 +95,8 @@ private AwsExecutionContextBuilder() { .putAttribute(SdkExecutionAttribute.OPERATION_NAME, executionParams.getOperationName()) .putAttribute(SdkExecutionAttribute.CLIENT_ENDPOINT, clientConfig.option(SdkClientOption.ENDPOINT)) .putAttribute(SdkExecutionAttribute.ENDPOINT_OVERRIDDEN, clientConfig.option(SdkClientOption.ENDPOINT_OVERRIDDEN)) - .putAttribute(SdkInternalExecutionAttribute.ENDPOINT_PROVIDER, clientConfig.option(SdkClientOption.ENDPOINT_PROVIDER)) + .putAttribute(SdkInternalExecutionAttribute.ENDPOINT_PROVIDER, + resolveEndpointProvider(originalRequest, clientConfig)) .putAttribute(SdkInternalExecutionAttribute.CLIENT_CONTEXT_PARAMS, clientConfig.option(SdkClientOption.CLIENT_CONTEXT_PARAMS)) .putAttribute(SdkInternalExecutionAttribute.DISABLE_HOST_PREFIX_INJECTION, @@ -204,4 +206,18 @@ private static boolean isAuthenticatedRequest(ExecutionAttributes executionAttri return executionAttributes.getOptionalAttribute(SdkInternalExecutionAttribute.IS_NONE_AUTH_TYPE_REQUEST).orElse(true); } + + /** + * Resolves the endpoint provider, with the request override configuration taking precedence over the + * provided default client clientConfig. + * @return The endpoint provider that will be used by the SDK to resolve endpoints. + */ + private static EndpointProvider resolveEndpointProvider(SdkRequest request, + SdkClientConfiguration clientConfig) { + return request.overrideConfiguration() + .flatMap(RequestOverrideConfiguration::endpointProvider) + .orElse(clientConfig.option(SdkClientOption.ENDPOINT_PROVIDER)); + } + + } diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/RequestOverrideConfiguration.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/RequestOverrideConfiguration.java index 1dd67b4a6ceb..cb4daf65922a 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/RequestOverrideConfiguration.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/RequestOverrideConfiguration.java @@ -31,6 +31,7 @@ import software.amazon.awssdk.core.interceptor.ExecutionAttribute; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.signer.Signer; +import software.amazon.awssdk.endpoints.EndpointProvider; import software.amazon.awssdk.metrics.MetricPublisher; import software.amazon.awssdk.utils.CollectionUtils; import software.amazon.awssdk.utils.Validate; @@ -51,6 +52,8 @@ public abstract class RequestOverrideConfiguration { private final List metricPublishers; private final ExecutionAttributes executionAttributes; + private final EndpointProvider endpointProvider; + protected RequestOverrideConfiguration(Builder builder) { this.headers = CollectionUtils.deepUnmodifiableMap(builder.headers(), () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER)); this.rawQueryParameters = CollectionUtils.deepUnmodifiableMap(builder.rawQueryParameters()); @@ -60,6 +63,7 @@ protected RequestOverrideConfiguration(Builder builder) { this.signer = builder.signer(); this.metricPublishers = Collections.unmodifiableList(new ArrayList<>(builder.metricPublishers())); this.executionAttributes = ExecutionAttributes.unmodifiableExecutionAttributes(builder.executionAttributes()); + this.endpointProvider = builder.endpointProvider(); } /** @@ -153,6 +157,14 @@ public ExecutionAttributes executionAttributes() { return executionAttributes; } + /** + * Returns the endpoint provider for resolving the endpoint for this request. This supersedes the + * endpoint provider set on the client. + */ + public Optional endpointProvider() { + return Optional.ofNullable(endpointProvider); + } + @Override public boolean equals(Object o) { if (this == o) { @@ -169,7 +181,8 @@ public boolean equals(Object o) { Objects.equals(apiCallAttemptTimeout, that.apiCallAttemptTimeout) && Objects.equals(signer, that.signer) && Objects.equals(metricPublishers, that.metricPublishers) && - Objects.equals(executionAttributes, that.executionAttributes); + Objects.equals(executionAttributes, that.executionAttributes) && + Objects.equals(endpointProvider, that.endpointProvider); } @Override @@ -183,6 +196,7 @@ public int hashCode() { hashCode = 31 * hashCode + Objects.hashCode(signer); hashCode = 31 * hashCode + Objects.hashCode(metricPublishers); hashCode = 31 * hashCode + Objects.hashCode(executionAttributes); + hashCode = 31 * hashCode + Objects.hashCode(endpointProvider); return hashCode; } @@ -412,6 +426,18 @@ default B putRawQueryParameter(String name, String value) { ExecutionAttributes executionAttributes(); + /** + * Sets the endpointProvider to use for resolving the endpoint of the request. This endpointProvider gets priority + * over the endpointProvider set on the client while resolving the endpoint for the requests. + * If this value is null, then the client level endpointProvider is used for resolving the endpoint. + * + * @param endpointProvider Endpoint Provider that will override the resolving the endpoint for the request. + * @return This object for method chaining + */ + B endpointProvider(EndpointProvider endpointProvider); + + EndpointProvider endpointProvider(); + /** * Create a new {@code SdkRequestOverrideConfiguration} with the properties set on this builder. * @@ -430,6 +456,9 @@ protected abstract static class BuilderImpl implements Builde private List metricPublishers = new ArrayList<>(); private ExecutionAttributes.Builder executionAttributesBuilder = ExecutionAttributes.builder(); + private EndpointProvider endpointProvider; + + protected BuilderImpl() { } @@ -442,6 +471,7 @@ protected BuilderImpl(RequestOverrideConfiguration sdkRequestOverrideConfig) { signer(sdkRequestOverrideConfig.signer().orElse(null)); metricPublishers(sdkRequestOverrideConfig.metricPublishers()); executionAttributes(sdkRequestOverrideConfig.executionAttributes()); + endpointProvider(sdkRequestOverrideConfig.endpointProvider); } @Override @@ -595,5 +625,21 @@ public ExecutionAttributes executionAttributes() { public void setExecutionAttributes(ExecutionAttributes executionAttributes) { executionAttributes(executionAttributes); } + + + @Override + public B endpointProvider(EndpointProvider endpointProvider) { + this.endpointProvider = endpointProvider; + return (B) this; + } + + public void setEndpointProvider(EndpointProvider endpointProvider) { + endpointProvider(endpointProvider); + } + + @Override + public EndpointProvider endpointProvider() { + return endpointProvider; + } } } diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/SdkServiceClientConfiguration.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/SdkServiceClientConfiguration.java index 26674d396b96..8928ab52d3fd 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/SdkServiceClientConfiguration.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/SdkServiceClientConfiguration.java @@ -20,6 +20,7 @@ import java.util.Optional; import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.endpoints.EndpointProvider; /** * Class to expose SDK service client settings to the user, e.g., ClientOverrideConfiguration @@ -30,9 +31,12 @@ public abstract class SdkServiceClientConfiguration { private final ClientOverrideConfiguration overrideConfiguration; private final URI endpointOverride; + private final EndpointProvider endpointProvider; + protected SdkServiceClientConfiguration(Builder builder) { this.overrideConfiguration = builder.overrideConfiguration(); this.endpointOverride = builder.endpointOverride(); + this.endpointProvider = builder.endpointProvider(); } /** @@ -53,6 +57,16 @@ public Optional endpointOverride() { return Optional.ofNullable(this.endpointOverride); } + /** + * + * @return The configured endpoint provider of the SdkClient. If the endpoint provider was not configured, the default + * endpoint provider will be returned. + */ + public Optional endpointProvider() { + return Optional.ofNullable(this.endpointProvider); + } + + @Override public boolean equals(Object o) { if (this == o) { @@ -64,13 +78,15 @@ public boolean equals(Object o) { SdkServiceClientConfiguration serviceClientConfiguration = (SdkServiceClientConfiguration) o; return Objects.equals(overrideConfiguration, serviceClientConfiguration.overrideConfiguration()) - && Objects.equals(endpointOverride, serviceClientConfiguration.endpointOverride().orElse(null)); + && Objects.equals(endpointOverride, serviceClientConfiguration.endpointOverride().orElse(null)) + && Objects.equals(endpointProvider, serviceClientConfiguration.endpointProvider().orElse(null)); } @Override public int hashCode() { int result = overrideConfiguration != null ? overrideConfiguration.hashCode() : 0; result = 31 * result + (endpointOverride != null ? endpointOverride.hashCode() : 0); + result = 31 * result + (endpointProvider != null ? endpointProvider.hashCode() : 0); return result; } @@ -88,6 +104,8 @@ public interface Builder { */ URI endpointOverride(); + EndpointProvider endpointProvider(); + /** * Configure the client override configuration */ @@ -98,6 +116,9 @@ public interface Builder { */ Builder endpointOverride(URI endpointOverride); + + Builder endpointProvider(EndpointProvider endpointProvider); + /** * Build the service client configuration using the configuration on this builder */ diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/S3Configuration.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/S3Configuration.java index 5dec8e93602f..62631b53e71b 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/S3Configuration.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/S3Configuration.java @@ -64,11 +64,16 @@ public final class S3Configuration implements ServiceConfiguration, ToCopyableBu */ private static final boolean DEFAULT_CHUNKED_ENCODING_ENABLED = true; + private static final boolean DEFAULT_CROSS_REGION_ACCESS_ENABLED = false; + + private final FieldWithDefault pathStyleAccessEnabled; private final FieldWithDefault accelerateModeEnabled; private final FieldWithDefault dualstackEnabled; private final FieldWithDefault checksumValidationEnabled; private final FieldWithDefault chunkedEncodingEnabled; + private final FieldWithDefault crossRegionAccessEnabled; + private final Boolean useArnRegionEnabled; private final Boolean multiRegionEnabled; private final FieldWithDefault> profileFile; @@ -90,6 +95,9 @@ private S3Configuration(DefaultS3ServiceConfigurationBuilder builder) { if (accelerateModeEnabled() && pathStyleAccessEnabled()) { throw new IllegalArgumentException("Accelerate mode cannot be used with path style addressing"); } + this.crossRegionAccessEnabled = FieldWithDefault.create(builder.crossRegionAccessEnabled, + DEFAULT_CROSS_REGION_ACCESS_ENABLED); + } private boolean resolveUseArnRegionEnabled() { @@ -206,6 +214,18 @@ public boolean multiRegionEnabled() { .orElseGet(this::resolveMultiRegionEnabled); } + + + /** + * Returns whether the client is allowed to make cross-region calls based on bucket names. + *

+ * @return True if cross Region Bucket Access Enabled + */ + public boolean crossRegionAccessEnabled() { + return crossRegionAccessEnabled.value(); + } + + @Override public Builder toBuilder() { return builder() @@ -217,7 +237,8 @@ public Builder toBuilder() { .chunkedEncodingEnabled(chunkedEncodingEnabled.valueOrNullIfDefault()) .useArnRegionEnabled(useArnRegionEnabled) .profileFile(profileFile.valueOrNullIfDefault()) - .profileName(profileName.valueOrNullIfDefault()); + .profileName(profileName.valueOrNullIfDefault()) + .crossRegionAccessEnabled(crossRegionAccessEnabled.valueOrNullIfDefault()); } @NotThreadSafe @@ -355,6 +376,34 @@ public interface Builder extends CopyableBuilder { *

*/ Builder profileName(String profileName); + + /** + *

Configures whether cross-region bucket access is enabled for clients using the configuration. + *

The following behavior is used when this mode is enabled: + *

    + *
  1. This method allows enabling or disabling cross-region bucket access for clients. When cross-region bucket + * access is enabled, requests that do not act on an existing bucket (e.g., createBucket API) will be routed to the + * region configured on the client
  2. + *
  3. The first time a request is made that references an existing bucket (e.g., putObject API), a request will be + * made to the client-configured region. If the bucket does not exist in this region, the service will include the + * actual region in the error responses. Subsequently, the API will be called using the correct region obtained + * from the error response.
  4. + *
  5. This location may be cached in the client for subsequent requests to the same bucket.
  6. + *
+ *

Enabling this mode has several drawbacks, as it can increase latency if the bucket's location is physically far + * from the location of the request.Therefore, it is strongly advised, whenever possible, to know the location of your + * buckets and create a region-specific client to access them + * + * @param crossRegionAccessEnabled Whether cross region bucket access should be enabled. + * @return The builder object for method chaining. + */ + Builder crossRegionAccessEnabled(Boolean crossRegionAccessEnabled); + + /** + * Returns value of crossRegionAccessEnabled value set using {@link Builder#crossRegionAccessEnabled(Boolean)} + */ + Boolean crossRegionAccessEnabled(); + } static final class DefaultS3ServiceConfigurationBuilder implements Builder { @@ -368,6 +417,8 @@ static final class DefaultS3ServiceConfigurationBuilder implements Builder { private Supplier profileFile; private String profileName; + private Boolean crossRegionAccessEnabled; + @Override public Boolean dualstackEnabled() { return dualstackEnabled; @@ -505,6 +556,26 @@ public void setUseArnRegionEnabled(Boolean useArnRegionEnabled) { useArnRegionEnabled(useArnRegionEnabled); } + + @Override + public Boolean crossRegionAccessEnabled() { + return crossRegionAccessEnabled; + } + + @Override + public Builder crossRegionAccessEnabled(Boolean crossRegionAccessEnabled) { + this.crossRegionAccessEnabled = crossRegionAccessEnabled; + return this; + } + + /** + * refer {@link Builder#crossRegionAccessEnabled(Boolean)} + * @param crossRegionAccessEnabled + */ + public void setCrossRegionAccessEnabled(Boolean crossRegionAccessEnabled) { + crossRegionAccessEnabled(crossRegionAccessEnabled); + } + @Override public S3Configuration build() { return new S3Configuration(this); diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/S3ConfigurationTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/S3ConfigurationTest.java index d9a7ed6da7e0..b7dd63e0abcf 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/S3ConfigurationTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/S3ConfigurationTest.java @@ -46,6 +46,7 @@ public void createConfiguration_minimal() { assertThat(config.multiRegionEnabled()).isEqualTo(true); assertThat(config.pathStyleAccessEnabled()).isEqualTo(false); assertThat(config.useArnRegionEnabled()).isEqualTo(false); + assertThat(config.crossRegionAccessEnabled()).isEqualTo(false); } @Test @@ -115,4 +116,13 @@ public void useArnRegionEnabled_enabledInCProfile_shouldResolveToConfigCorrectly assertThat(config.useArnRegionEnabled()).isEqualTo(false); } + @Test + public void crossRegionEnabled__enabledInConfigOnly_shouldResolveCorrectly() { + S3Configuration config = S3Configuration.builder().crossRegionAccessEnabled(true).build(); + assertThat(config.crossRegionAccessEnabled()).isEqualTo(true); + + S3Configuration configDisabled = S3Configuration.builder().crossRegionAccessEnabled(false).build(); + assertThat(configDisabled.crossRegionAccessEnabled()).isEqualTo(false); + } + } diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/endpointproviders/EndpointInterceptorTests.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/endpointproviders/EndpointInterceptorTests.java index e9e10177cc8d..4ab603f270bb 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/endpointproviders/EndpointInterceptorTests.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/endpointproviders/EndpointInterceptorTests.java @@ -27,12 +27,14 @@ import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.endpoints.Endpoint; +import software.amazon.awssdk.endpoints.EndpointProvider; import software.amazon.awssdk.http.SdkHttpRequest; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.restjsonendpointproviders.RestJsonEndpointProvidersAsyncClient; import software.amazon.awssdk.services.restjsonendpointproviders.RestJsonEndpointProvidersAsyncClientBuilder; import software.amazon.awssdk.services.restjsonendpointproviders.RestJsonEndpointProvidersClient; import software.amazon.awssdk.services.restjsonendpointproviders.RestJsonEndpointProvidersClientBuilder; +import software.amazon.awssdk.services.restjsonendpointproviders.endpoints.RestJsonEndpointProvidersEndpointProvider; public class EndpointInterceptorTests { diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/endpointproviders/RequestOverrideEndpointProviderTests.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/endpointproviders/RequestOverrideEndpointProviderTests.java new file mode 100644 index 000000000000..075976d136c8 --- /dev/null +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/endpointproviders/RequestOverrideEndpointProviderTests.java @@ -0,0 +1,164 @@ +/* + * Copyright 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.services.endpointproviders; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.net.URI; +import java.util.concurrent.CompletableFuture; +import org.junit.Test; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; +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.core.interceptor.SdkInternalExecutionAttribute; +import software.amazon.awssdk.endpoints.Endpoint; +import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.restjsonendpointproviders.RestJsonEndpointProvidersAsyncClient; +import software.amazon.awssdk.services.restjsonendpointproviders.RestJsonEndpointProvidersAsyncClientBuilder; +import software.amazon.awssdk.services.restjsonendpointproviders.RestJsonEndpointProvidersClient; +import software.amazon.awssdk.services.restjsonendpointproviders.RestJsonEndpointProvidersClientBuilder; +import software.amazon.awssdk.services.restjsonendpointproviders.endpoints.RestJsonEndpointProvidersEndpointParams; +import software.amazon.awssdk.services.restjsonendpointproviders.endpoints.RestJsonEndpointProvidersEndpointProvider; + +public class RequestOverrideEndpointProviderTests { + + @Test + public void sync_endpointOverridden_equals_requestOverride() { + CapturingInterceptor interceptor = new CapturingInterceptor(); + RestJsonEndpointProvidersClient client = syncClientBuilder() + .overrideConfiguration(o -> o.addExecutionInterceptor(interceptor) + .putAdvancedOption(SdkAdvancedClientOption.DISABLE_HOST_PREFIX_INJECTION, true)) + .build(); + + assertThatThrownBy(() -> client.operationWithHostPrefix( + r -> r.overrideConfiguration(o -> o.endpointProvider(new CustomEndpointProvider(Region.AWS_GLOBAL)))) + + ) + .hasMessageContaining("stop"); + Endpoint endpoint = interceptor.executionAttributes().getAttribute(SdkInternalExecutionAttribute.RESOLVED_ENDPOINT); + assertThat(endpoint.url().getHost()).isEqualTo("restjson.aws-global.amazonaws.com"); + } + + @Test + public void sync_endpointOverridden_equals_ClientsWhenNoRequestOverride() { + CapturingInterceptor interceptor = new CapturingInterceptor(); + RestJsonEndpointProvidersClient client = syncClientBuilder().endpointProvider(new CustomEndpointProvider(Region.EU_WEST_2)) + .overrideConfiguration(o -> o.addExecutionInterceptor(interceptor) + .putAdvancedOption(SdkAdvancedClientOption.DISABLE_HOST_PREFIX_INJECTION, true)) + .build(); + + assertThatThrownBy(() -> client.operationWithHostPrefix(r -> {})).hasMessageContaining("stop"); + + Endpoint endpoint = interceptor.executionAttributes().getAttribute(SdkInternalExecutionAttribute.RESOLVED_ENDPOINT); + + assertThat(endpoint.url().getHost()).isEqualTo("restjson.eu-west-2.amazonaws.com"); + } + + + @Test + public void async_endpointOverridden_equals_requestOverride() { + + + CapturingInterceptor interceptor = new CapturingInterceptor(); + RestJsonEndpointProvidersAsyncClient client = asyncClientBuilder() + .overrideConfiguration(o -> o.addExecutionInterceptor(interceptor) + .putAdvancedOption(SdkAdvancedClientOption.DISABLE_HOST_PREFIX_INJECTION, true)) + .build(); + + assertThatThrownBy(() -> client.operationWithHostPrefix( + r -> r.overrideConfiguration(o -> o.endpointProvider(new CustomEndpointProvider(Region.AWS_GLOBAL))) + ).join()) + .hasMessageContaining("stop"); + + Endpoint endpoint = interceptor.executionAttributes().getAttribute(SdkInternalExecutionAttribute.RESOLVED_ENDPOINT); + + assertThat(endpoint.url().getHost()).isEqualTo("restjson.aws-global.amazonaws.com"); + } + + + @Test + public void async_endpointOverridden_equals_ClientsWhenNoRequestOverride() { + + + CapturingInterceptor interceptor = new CapturingInterceptor(); + RestJsonEndpointProvidersAsyncClient client = asyncClientBuilder().endpointProvider(new CustomEndpointProvider(Region.EU_WEST_2)) + .overrideConfiguration(o -> o.addExecutionInterceptor(interceptor) + .putAdvancedOption(SdkAdvancedClientOption.DISABLE_HOST_PREFIX_INJECTION, true)) + .build(); + + assertThatThrownBy(() -> client.operationWithHostPrefix(r -> {}).join()) + .hasMessageContaining("stop"); + + Endpoint endpoint = interceptor.executionAttributes().getAttribute(SdkInternalExecutionAttribute.RESOLVED_ENDPOINT); + + assertThat(endpoint.url().getHost()).isEqualTo("restjson.eu-west-2.amazonaws.com"); + } + + public static class CapturingInterceptor implements ExecutionInterceptor { + + private ExecutionAttributes executionAttributes; + + @Override + public SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes) { + this.executionAttributes = executionAttributes; + throw new CaptureCompletedException("stop"); + } + + public ExecutionAttributes executionAttributes() { + return executionAttributes; + } + + public class CaptureCompletedException extends RuntimeException { + CaptureCompletedException(String message) { + super(message); + } + } + } + private RestJsonEndpointProvidersClientBuilder syncClientBuilder() { + return RestJsonEndpointProvidersClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider( + StaticCredentialsProvider.create( + AwsBasicCredentials.create("akid", "skid"))); + } + + private RestJsonEndpointProvidersAsyncClientBuilder asyncClientBuilder() { + return RestJsonEndpointProvidersAsyncClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider( + StaticCredentialsProvider.create( + AwsBasicCredentials.create("akid", "skid"))); + } + + + class CustomEndpointProvider implements RestJsonEndpointProvidersEndpointProvider{ + final RestJsonEndpointProvidersEndpointProvider endpointProvider; + final Region overridingRegion; + CustomEndpointProvider (Region region){ + this.endpointProvider = RestJsonEndpointProvidersEndpointProvider.defaultProvider(); + this.overridingRegion = region; + } + @Override + public CompletableFuture resolveEndpoint(RestJsonEndpointProvidersEndpointParams endpointParams) { + return endpointProvider.resolveEndpoint(endpointParams.toBuilder().region(overridingRegion).build()); + } + } +} diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/serviceclientconfiguration/ServiceClientConfigurationTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/serviceclientconfiguration/ServiceClientConfigurationTest.java index 6a0058400f27..1d682af68ec6 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/serviceclientconfiguration/ServiceClientConfigurationTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/serviceclientconfiguration/ServiceClientConfigurationTest.java @@ -21,9 +21,11 @@ import java.time.Duration; import org.junit.jupiter.api.Test; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.endpoints.EndpointProvider; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.protocolrestxml.ProtocolRestXmlAsyncClient; import software.amazon.awssdk.services.protocolrestxml.ProtocolRestXmlClient; +import software.amazon.awssdk.services.protocolrestxml.endpoints.ProtocolRestXmlEndpointProvider; public class ServiceClientConfigurationTest { @@ -91,6 +93,26 @@ public void syncClient_serviceClientConfiguration_withoutOverrideConfiguration_s assertThat(client.serviceClientConfiguration().overrideConfiguration().metricPublishers()).isEmpty(); } + @Test + public void syncClientWithEndpointProvider_serviceClientConfiguration_shouldReturnCorrectEndpointProvider() { + ProtocolRestXmlEndpointProvider clientEndpointProvider = ProtocolRestXmlEndpointProvider.defaultProvider(); + ProtocolRestXmlClient client = ProtocolRestXmlClient.builder() + .endpointProvider(clientEndpointProvider) + .build(); + + EndpointProvider endpointProvider = client.serviceClientConfiguration().endpointProvider().orElse(null); + assertThat(endpointProvider).isEqualTo(clientEndpointProvider); + } + + @Test + public void syncClientWithoutEndpointProvider_serviceClientConfiguration_shouldReturnDefaultEndpointProvider() { + ProtocolRestXmlClient client = ProtocolRestXmlClient.builder() + .build(); + + EndpointProvider endpointProvider = client.serviceClientConfiguration().endpointProvider().orElse(null); + assertThat(endpointProvider instanceof ProtocolRestXmlEndpointProvider).isTrue(); + } + @Test public void asyncClient_serviceClientConfiguration_shouldReturnCorrectRegion() { ProtocolRestXmlAsyncClient client = ProtocolRestXmlAsyncClient.builder() @@ -155,4 +177,25 @@ public void asyncClient_serviceClientConfiguration_withoutOverrideConfiguration_ assertThat(client.serviceClientConfiguration().overrideConfiguration().metricPublishers()).isEmpty(); } + + @Test + public void asyncClientWithEndpointProvider_serviceClientConfiguration_shouldReturnCorrectEndpointProvider() { + ProtocolRestXmlEndpointProvider clientEndpointProvider = ProtocolRestXmlEndpointProvider.defaultProvider(); + ProtocolRestXmlAsyncClient client = ProtocolRestXmlAsyncClient.builder() + .endpointProvider(clientEndpointProvider) + .build(); + + EndpointProvider endpointProvider = client.serviceClientConfiguration().endpointProvider().orElse(null); + assertThat(endpointProvider).isEqualTo(clientEndpointProvider); + } + + @Test + public void asyncClientWithoutEndpointProvider_serviceClientConfiguration_shouldReturnDefault() { + ProtocolRestXmlAsyncClient client = ProtocolRestXmlAsyncClient.builder() + .build(); + + EndpointProvider endpointProvider = client.serviceClientConfiguration().endpointProvider().orElse(null); + assertThat(endpointProvider instanceof ProtocolRestXmlEndpointProvider).isTrue(); + } + }