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 4cd94ace20ad..5e49668a9454 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 @@ -21,6 +21,8 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +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; @@ -28,6 +30,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; @@ -293,7 +297,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 308aa69ea487..b2c891c30691 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 @@ -159,6 +159,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..7d4e22e98f18 --- /dev/null +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules2/endpoint-provider-unknown-property-class.java @@ -0,0 +1,107 @@ +/* + * 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.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, new LocalState()); + 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, LocalState locals) { + RuleResult result = endpointRule1(params, locals); + if (result.isResolved()) { + return result; + } + return endpointRule3(params, locals); + } + + private static RuleResult endpointRule1(QueryEndpointParams params, LocalState locals) { + if (params.endpoint() != null) { + return endpointRule2(params, locals); + } + return RuleResult.carryOn(); + } + + private static RuleResult endpointRule2(QueryEndpointParams params, LocalState locals) { + return RuleResult.endpoint(Endpoint.builder().url(URI.create(params.endpoint())).build()); + } + + private static RuleResult endpointRule3(QueryEndpointParams params, LocalState locals) { + return RuleResult.error("Invalid Configuration: Missing Endpoint"); + } + + @Override + public boolean equals(Object rhs) { + return rhs != null && getClass().equals(rhs.getClass()); + } + + @Override + public int hashCode() { + return getClass().hashCode(); + } + + private static final class LocalState { + LocalState() { + } + + LocalState(LocalStateBuilder builder) { + } + + public LocalStateBuilder toBuilder() { + return new LocalStateBuilder(this); + } + } + + private static final class LocalStateBuilder { + LocalStateBuilder() { + } + + LocalStateBuilder(LocalState locals) { + } + + LocalState build() { + return new LocalState(this); + } + } +}