diff --git a/.changes/next-release/bugfix-AWSSDKforJavav2-816c928.json b/.changes/next-release/bugfix-AWSSDKforJavav2-816c928.json new file mode 100644 index 000000000000..06d11f4c2ff9 --- /dev/null +++ b/.changes/next-release/bugfix-AWSSDKforJavav2-816c928.json @@ -0,0 +1,6 @@ +{ + "type": "bugfix", + "category": "AWS SDK for Java v2", + "contributor": "", + "description": "Ignore unknown properties on endpoints in endpoint rules." +} diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules2/CodeGeneratorVisitor.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules2/CodeGeneratorVisitor.java index 04844eebaecb..609e2a5f5663 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules2/CodeGeneratorVisitor.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules2/CodeGeneratorVisitor.java @@ -22,6 +22,8 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import software.amazon.awssdk.awscore.endpoints.AwsEndpointAttribute; import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4AuthScheme; import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4aAuthScheme; @@ -29,6 +31,8 @@ import software.amazon.awssdk.endpoints.Endpoint; public class CodeGeneratorVisitor extends WalkRuleExpressionVisitor { + private static final Logger log = LoggerFactory.getLogger(CodeGeneratorVisitor.class); + private final CodeBlock.Builder builder; private final RuleRuntimeTypeMirror typeMirror; private final SymbolTable symbolTable; @@ -344,7 +348,7 @@ public Void visitPropertiesExpression(PropertiesExpression e) { } else if (knownEndpointAttributes.containsKey(k)) { addAttributeBlock(k, v); } else { - throw new RuntimeException("unknown endpoint property: " + k); + log.warn("Ignoring unknown endpoint property: {}", k); } }); return null; diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/ClientTestModels.java b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/ClientTestModels.java index 978e837fc87f..54dd331118ce 100644 --- a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/ClientTestModels.java +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/ClientTestModels.java @@ -175,6 +175,26 @@ public static IntermediateModel queryServiceModelsWithOverrideKnowProperties() { return new IntermediateModelBuilder(models).build(); } + public static IntermediateModel queryServiceModelsWithUnknownEndpointProperties() { + File serviceModel = new File(ClientTestModels.class.getResource("client/c2j/query/service-2.json").getFile()); + File waitersModel = new File(ClientTestModels.class.getResource("client/c2j/query/waiters-2.json").getFile()); + File endpointRuleSetModel = + new File(ClientTestModels.class.getResource("client/c2j/query/endpoint-rule-set-unknown-properties.json").getFile()); + File endpointTestsModel = + new File(ClientTestModels.class.getResource("client/c2j/query/endpoint-tests.json").getFile()); + + C2jModels models = C2jModels + .builder() + .serviceModel(getServiceModel(serviceModel)) + .waitersModel(getWaiters(waitersModel)) + .customizationConfig(CustomizationConfig.create()) + .endpointRuleSetModel(getEndpointRuleSet(endpointRuleSetModel)) + .endpointTestSuiteModel(getEndpointTestSuite(endpointTestsModel)) + .build(); + + return new IntermediateModelBuilder(models).build(); + } + public static IntermediateModel queryServiceModelsEndpointAuthParamsWithAllowList() { File serviceModel = new File(ClientTestModels.class.getResource("client/c2j/query/service-2.json").getFile()); File customizationModel = diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/rules/EndpointProviderCompiledRulesClassSpecTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/rules/EndpointProviderCompiledRulesClassSpecTest.java index 3ee5c8757a37..1b0961bc8b79 100644 --- a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/rules/EndpointProviderCompiledRulesClassSpecTest.java +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/rules/EndpointProviderCompiledRulesClassSpecTest.java @@ -37,4 +37,11 @@ void knowPropertiesOverride() { new EndpointProviderSpec2(ClientTestModels.queryServiceModelsWithOverrideKnowProperties()); assertThat(endpointProviderSpec, generatesTo("endpoint-provider-know-prop-override-class.java")); } + + @Test + void unknownEndpointProperties() { + ClassSpec endpointProviderSpec = + new EndpointProviderSpec2(ClientTestModels.queryServiceModelsWithUnknownEndpointProperties()); + assertThat(endpointProviderSpec, generatesTo("endpoint-provider-unknown-property-class.java")); + } } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/query/endpoint-rule-set-unknown-properties.json b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/query/endpoint-rule-set-unknown-properties.json new file mode 100644 index 000000000000..ddc397230298 --- /dev/null +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/query/endpoint-rule-set-unknown-properties.json @@ -0,0 +1,46 @@ +{ + "version": "1.0", + "parameters": { + "Endpoint": { + "builtIn": "SDK::Endpoint", + "required": false, + "documentation": "Override the endpoint used to send this request", + "type": "String" + } + }, + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "Endpoint" + } + ] + } + ], + "rules": [ + { + "conditions": [], + "endpoint": { + "url": { + "ref": "Endpoint" + }, + "properties": { + "unknownProperty": "value" + }, + "headers": {} + }, + "type": "endpoint" + } + ], + "type": "tree" + }, + { + "conditions": [], + "error": "Invalid Configuration: Missing Endpoint", + "type": "error" + } + ] +} \ No newline at end of file diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules2/endpoint-provider-unknown-property-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules2/endpoint-provider-unknown-property-class.java new file mode 100644 index 000000000000..18f0c5cb6e42 --- /dev/null +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules2/endpoint-provider-unknown-property-class.java @@ -0,0 +1,60 @@ +package software.amazon.awssdk.services.query.endpoints.internal; + +import java.net.URI; +import java.util.concurrent.CompletableFuture; +import software.amazon.awssdk.annotations.Generated; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.core.exception.SdkClientException; +import software.amazon.awssdk.endpoints.Endpoint; +import software.amazon.awssdk.services.query.endpoints.QueryEndpointParams; +import software.amazon.awssdk.services.query.endpoints.QueryEndpointProvider; +import software.amazon.awssdk.utils.CompletableFutureUtils; + +@Generated("software.amazon.awssdk:codegen") +@SdkInternalApi +public final class DefaultQueryEndpointProvider implements QueryEndpointProvider { + @Override + public CompletableFuture resolveEndpoint(QueryEndpointParams params) { + try { + RuleResult result = endpointRule0(params); + if (result.canContinue()) { + throw SdkClientException.create("Rule engine did not reach an error or endpoint result"); + } + if (result.isError()) { + String errorMsg = result.error(); + if (errorMsg.contains("Invalid ARN") && errorMsg.contains(":s3:::")) { + errorMsg += ". Use the bucket name instead of simple bucket ARNs in GetBucketLocationRequest."; + } + throw SdkClientException.create(errorMsg); + } + return CompletableFuture.completedFuture(result.endpoint()); + } catch (Exception error) { + return CompletableFutureUtils.failedFuture(error); + } + } + + private static RuleResult endpointRule0(QueryEndpointParams params) { + RuleResult result = endpointRule1(params); + if (result.isResolved()) { + return result; + } + return RuleResult.error("Invalid Configuration: Missing Endpoint"); + } + + private static RuleResult endpointRule1(QueryEndpointParams params) { + if (params.endpoint() != null) { + return RuleResult.endpoint(Endpoint.builder().url(URI.create(params.endpoint())).build()); + } + return RuleResult.carryOn(); + } + + @Override + public boolean equals(Object rhs) { + return rhs != null && getClass().equals(rhs.getClass()); + } + + @Override + public int hashCode() { + return getClass().hashCode(); + } +}