From fcf4425569ba4a14c548d7042f3ac3862f2de968 Mon Sep 17 00:00:00 2001 From: Jordon Phillips Date: Tue, 23 Mar 2021 17:43:30 +0100 Subject: [PATCH 1/6] chore: serialize rest json error code header (#2166) --- .../aws/typescript/codegen/RestJsonProtocolGenerator.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/RestJsonProtocolGenerator.java b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/RestJsonProtocolGenerator.java index 769b919321d40..4418472dc7189 100644 --- a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/RestJsonProtocolGenerator.java +++ b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/RestJsonProtocolGenerator.java @@ -90,6 +90,12 @@ protected void writeDefaultHeaders(GenerationContext context, OperationShape ope AwsProtocolUtils.generateUnsignedPayloadSigV4Header(context, operation); } + @Override + protected void writeDefaultErrorHeaders(GenerationContext context, StructureShape error) { + super.writeDefaultErrorHeaders(context, error); + context.getWriter().write("'x-amzn-errortype': $S,", error.getId().getName()); + } + @Override public void serializeInputDocument( GenerationContext context, From 5917de0d5de8cfaaaecb235ec3c40283c27faff6 Mon Sep 17 00:00:00 2001 From: Adam Thomas <61852529+adamthom-amzn@users.noreply.github.com> Date: Wed, 24 Mar 2021 09:14:10 -0700 Subject: [PATCH 2/6] chore: short-circuit ssdk-incompatible integrations (#2167) --- .../codegen/AwsEndpointGeneratorIntegration.java | 12 ++++++++++++ .../AwsPackageFixturesGeneratorIntegration.java | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsEndpointGeneratorIntegration.java b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsEndpointGeneratorIntegration.java index 35a0dde5f7735..d5656e79293e8 100644 --- a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsEndpointGeneratorIntegration.java +++ b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsEndpointGeneratorIntegration.java @@ -39,6 +39,10 @@ public void writeAdditionalFiles( SymbolProvider symbolProvider, BiConsumer> writerFactory ) { + if (!settings.generateClient()) { + return; + } + writerFactory.accept("endpoints.ts", writer -> { new EndpointGenerator(settings.getService(model), writer).run(); }); @@ -51,6 +55,10 @@ public void addConfigInterfaceFields( SymbolProvider symbolProvider, TypeScriptWriter writer ) { + if (!settings.generateClient()) { + return; + } + writer.addImport("RegionInfoProvider", "RegionInfoProvider", TypeScriptDependency.AWS_SDK_TYPES.packageName); writer.writeDocs("Fetch related hostname, signing name or signing region with given region."); writer.write("regionInfoProvider?: RegionInfoProvider;\n"); @@ -63,6 +71,10 @@ public Map> getRuntimeConfigWriters( SymbolProvider symbolProvider, LanguageTarget target ) { + if (!settings.generateClient()) { + return Collections.emptyMap(); + } + switch (target) { case SHARED: return MapUtils.of("regionInfoProvider", writer -> { diff --git a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsPackageFixturesGeneratorIntegration.java b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsPackageFixturesGeneratorIntegration.java index 791802662f0ca..54392e2efeb5a 100644 --- a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsPackageFixturesGeneratorIntegration.java +++ b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsPackageFixturesGeneratorIntegration.java @@ -55,6 +55,11 @@ public void writeAdditionalFiles( resource = resource.replace("${year}", Integer.toString(Calendar.getInstance().get(Calendar.YEAR))); writer.write(resource); }); + + if (!settings.generateClient()) { + return; + } + writerFactory.accept("README.md", writer -> { ServiceShape service = settings.getService(model); String resource = IoUtils.readUtf8Resource(getClass(), "README.md.template"); From 4d4104056e6b54d89784095152e3c5b3788daf0a Mon Sep 17 00:00:00 2001 From: Adam Thomas <61852529+adamthom-amzn@users.noreply.github.com> Date: Fri, 26 Mar 2021 09:14:27 -0700 Subject: [PATCH 3/6] chore: disable idempotency autofill import when not generating a client (#2181) --- .../smithy/aws/typescript/codegen/AwsProtocolUtils.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsProtocolUtils.java b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsProtocolUtils.java index 2500effab6f41..def50877aaf93 100644 --- a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsProtocolUtils.java +++ b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsProtocolUtils.java @@ -221,6 +221,10 @@ static boolean writeXmlNamespace(GenerationContext context, Shape shape, String * @param context The generation context. */ static void addItempotencyAutofillImport(GenerationContext context) { + // servers do not autogenerate idempotency tokens during deserialization + if (!context.getSettings().generateClient()) { + return; + } context.getModel().shapes(MemberShape.class) .filter(memberShape -> memberShape.hasTrait(IdempotencyTokenTrait.class)) .findFirst() From 45d352de33e0a176eda8e6873a3ab54007df5f58 Mon Sep 17 00:00:00 2001 From: Jaykumar Gosar <5666661+gosar@users.noreply.github.com> Date: Thu, 15 Apr 2021 12:53:22 -0700 Subject: [PATCH 4/6] Support generating non-AWS client (#2222) * feat(config-resolver): make region optional for non-AWS client * feat(codegen): skip integrations that are not relevant for non-AWS services This is an initial version to get a working version of generated code that compiles without manual edits in smithy-typescript-ssdk-demo. I expect to make updates to this logic. * chore(codegen): address code comments The minor ones from https://github.com/adamthom-amzn/aws-sdk-js-v3/pull/1 * fix(codegen): use SigV4Trait check instead of ServiceTrait AddAwsRuntimeConfigTest is checking for some behaviors from AddAwsAuthPlugin too, which was failing with missing aws.auth#sigv4 trait after my change. Added the trait for now to the test, but unit tests will need to be added/refactored for all these changes. * chore(codegen): move isAwsService check to utils class * chore(codegen): code style formatting * chore(codegen): check SigV4 trait for including region --- .../typescript/codegen/AddAwsAuthPlugin.java | 21 ++-- .../codegen/AddAwsRuntimeConfig.java | 98 ++++++++++++------- .../typescript/codegen/AddBuiltinPlugins.java | 2 + .../codegen/AddUserAgentDependency.java | 13 ++- .../AwsEndpointGeneratorIntegration.java | 8 +- ...wsPackageFixturesGeneratorIntegration.java | 5 +- .../codegen/AwsServiceIdIntegration.java | 1 + .../typescript/codegen/AwsTraitsUtils.java | 46 +++++++++ .../aws/typescript/codegen/NotSame.smithy | 1 + .../config-resolver/src/EndpointsConfig.ts | 8 +- 10 files changed, 151 insertions(+), 52 deletions(-) create mode 100644 codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsTraitsUtils.java diff --git a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddAwsAuthPlugin.java b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddAwsAuthPlugin.java index 29fe4e917e45f..a574667f67319 100644 --- a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddAwsAuthPlugin.java +++ b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddAwsAuthPlugin.java @@ -15,6 +15,7 @@ package software.amazon.smithy.aws.typescript.codegen; +import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isSigV4Service; import static software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin.Convention.HAS_CONFIG; import static software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin.Convention.HAS_MIDDLEWARE; @@ -46,6 +47,7 @@ /** * Configure clients with AWS auth configurations and plugin. */ +// TODO: Think about AWS Auth supported for only some operations and not all, when not AWS service, with say @auth([]) public final class AddAwsAuthPlugin implements TypeScriptIntegration { static final String STS_CLIENT_PREFIX = "sts-client-"; static final String ROLE_ASSUMERS_FILE = "defaultRoleAssumers"; @@ -59,6 +61,9 @@ public void addConfigInterfaceFields( TypeScriptWriter writer ) { ServiceShape service = settings.getService(model); + if (!isSigV4Service(service)) { + return; + } if (!areAllOptionalAuthOperations(model, service)) { writer.addImport("Credentials", "__Credentials", TypeScriptDependency.AWS_SDK_TYPES.packageName); writer.writeDocs("Default credentials provider; Not available in browser runtime.") @@ -71,7 +76,9 @@ public List getClientPlugins() { return ListUtils.of( RuntimeClientPlugin.builder() .withConventions(AwsDependency.MIDDLEWARE_SIGNING.dependency, "AwsAuth", HAS_CONFIG) - .servicePredicate((m, s) -> !areAllOptionalAuthOperations(m, s) && !testServiceId(s, "STS")) + .servicePredicate((m, s) -> isSigV4Service(s) + && !areAllOptionalAuthOperations(m, s) + && !testServiceId(s, "STS")) .build(), RuntimeClientPlugin.builder() .withConventions(AwsDependency.STS_MIDDLEWARE.dependency, @@ -83,7 +90,7 @@ public List getClientPlugins() { .withConventions(AwsDependency.MIDDLEWARE_SIGNING.dependency, "AwsAuth", HAS_MIDDLEWARE) // See operationUsesAwsAuth() below for AwsAuth Middleware customizations. .servicePredicate( - (m, s) -> !testServiceId(s, "STS") && !hasOptionalAuthOperation(m, s) + (m, s) -> !testServiceId(s, "STS") && isSigV4Service(s) && !hasOptionalAuthOperation(m, s) ).build(), RuntimeClientPlugin.builder() .withConventions(AwsDependency.MIDDLEWARE_SIGNING.dependency, "AwsAuth", HAS_MIDDLEWARE) @@ -100,7 +107,7 @@ public Map> getRuntimeConfigWriters( LanguageTarget target ) { ServiceShape service = settings.getService(model); - if (areAllOptionalAuthOperations(model, service)) { + if (!isSigV4Service(service) || areAllOptionalAuthOperations(model, service)) { return Collections.emptyMap(); } switch (target) { @@ -187,8 +194,8 @@ private static boolean operationUsesAwsAuth(Model model, ServiceShape service, O } // optionalAuth trait doesn't require authentication. - if (hasOptionalAuthOperation(model, service)) { - return !operation.getTrait(OptionalAuthTrait.class).isPresent(); + if (isSigV4Service(service) && hasOptionalAuthOperation(model, service)) { + return !operation.hasTrait(OptionalAuthTrait.class); } return false; } @@ -197,7 +204,7 @@ private static boolean hasOptionalAuthOperation(Model model, ServiceShape servic TopDownIndex topDownIndex = TopDownIndex.of(model); Set operations = topDownIndex.getContainedOperations(service); for (OperationShape operation : operations) { - if (operation.getTrait(OptionalAuthTrait.class).isPresent()) { + if (operation.hasTrait(OptionalAuthTrait.class)) { return true; } } @@ -208,7 +215,7 @@ private static boolean areAllOptionalAuthOperations(Model model, ServiceShape se TopDownIndex topDownIndex = TopDownIndex.of(model); Set operations = topDownIndex.getContainedOperations(service); for (OperationShape operation : operations) { - if (!operation.getTrait(OptionalAuthTrait.class).isPresent()) { + if (!operation.hasTrait(OptionalAuthTrait.class)) { return false; } } diff --git a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddAwsRuntimeConfig.java b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddAwsRuntimeConfig.java index 1d230838034db..cd3213bde6925 100644 --- a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddAwsRuntimeConfig.java +++ b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddAwsRuntimeConfig.java @@ -15,6 +15,9 @@ package software.amazon.smithy.aws.typescript.codegen; +import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isAwsService; +import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isSigV4Service; + import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -31,6 +34,9 @@ import software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration; import software.amazon.smithy.utils.MapUtils; +// TODO: This javadoc is specific to needs of AWS client. However it has elements that would be needed by non-AWS +// clients too, like logger, region for SigV4. We should refactor these into different Integration or rename this +// class to be generic. /** * AWS clients need to know the service name for collecting metrics, the * region name used to resolve endpoints, the max attempt to retry a request @@ -82,10 +88,14 @@ public void addConfigInterfaceFields( writer.addImport("Provider", "__Provider", TypeScriptDependency.AWS_SDK_TYPES.packageName); writer.addImport("Logger", "__Logger", TypeScriptDependency.AWS_SDK_TYPES.packageName); - writer.writeDocs("Unique service identifier.\n@internal") - .write("serviceId?: string;\n"); - writer.writeDocs("The AWS region to which this client will send requests") - .write("region?: string | __Provider;\n"); + if (isAwsService(settings, model)) { + writer.writeDocs("Unique service identifier.\n@internal") + .write("serviceId?: string;\n"); + } + if (isSigV4Service(settings, model)) { + writer.writeDocs("The AWS region to which this client will send requests or use as signingRegion") + .write("region?: string | __Provider;\n"); + } writer.writeDocs("Value for how many times a request will be made at most in case of retry.") .write("maxAttempts?: number | __Provider;\n"); writer.writeDocs("Optional logger for logging debug/info/warn/error.") @@ -114,11 +124,17 @@ public Map> getRuntimeConfigWriters( + "trait was found on " + service.getId()); } } - runtimeConfigs.putAll(getDefaultConfig(target)); + runtimeConfigs.putAll(getDefaultConfig(target, settings, model)); return runtimeConfigs; } - private Map> getDefaultConfig(LanguageTarget target) { + private Map> getDefaultConfig( + LanguageTarget target, + TypeScriptSettings settings, + Model model + ) { + Map> defaultConfigs = new HashMap(); + boolean isSigV4Service = isSigV4Service(settings, model); switch (target) { case SHARED: return MapUtils.of( @@ -128,40 +144,46 @@ private Map> getDefaultConfig(LanguageTarget } ); case BROWSER: - return MapUtils.of( - "region", writer -> { - writer.addDependency(TypeScriptDependency.INVALID_DEPENDENCY); - writer.addImport("invalidProvider", "invalidProvider", - TypeScriptDependency.INVALID_DEPENDENCY.packageName); - writer.write("region: invalidProvider(\"Region is missing\"),"); - }, - "maxAttempts", writer -> { - writer.addDependency(TypeScriptDependency.MIDDLEWARE_RETRY); - writer.addImport("DEFAULT_MAX_ATTEMPTS", "DEFAULT_MAX_ATTEMPTS", - TypeScriptDependency.MIDDLEWARE_RETRY.packageName); - writer.write("maxAttempts: DEFAULT_MAX_ATTEMPTS,"); - } - ); + if (isSigV4Service) { + defaultConfigs.put("region", writer -> { + writer.addDependency(TypeScriptDependency.INVALID_DEPENDENCY); + writer.addImport("invalidProvider", "invalidProvider", + TypeScriptDependency.INVALID_DEPENDENCY.packageName); + writer.write("region: invalidProvider(\"Region is missing\"),"); + }); + } + defaultConfigs.put("maxAttempts", writer -> { + writer.addDependency(TypeScriptDependency.MIDDLEWARE_RETRY); + writer.addImport("DEFAULT_MAX_ATTEMPTS", "DEFAULT_MAX_ATTEMPTS", + TypeScriptDependency.MIDDLEWARE_RETRY.packageName); + writer.write("maxAttempts: DEFAULT_MAX_ATTEMPTS,"); + }); + return defaultConfigs; case NODE: - return MapUtils.of( - "region", writer -> { - writer.addDependency(AwsDependency.NODE_CONFIG_PROVIDER); - writer.addImport("loadConfig", "loadNodeConfig", - AwsDependency.NODE_CONFIG_PROVIDER.packageName); - writer.addDependency(TypeScriptDependency.CONFIG_RESOLVER); - writer.addImport("NODE_REGION_CONFIG_OPTIONS", "NODE_REGION_CONFIG_OPTIONS", - TypeScriptDependency.CONFIG_RESOLVER.packageName); - writer.addImport("NODE_REGION_CONFIG_FILE_OPTIONS", "NODE_REGION_CONFIG_FILE_OPTIONS", - TypeScriptDependency.CONFIG_RESOLVER.packageName); - writer.write( + if (isSigV4Service) { + // TODO: For non-AWS service, figure out how the region should be configured. + defaultConfigs.put("region", writer -> { + writer.addDependency(AwsDependency.NODE_CONFIG_PROVIDER); + writer.addImport("loadConfig", "loadNodeConfig", + AwsDependency.NODE_CONFIG_PROVIDER.packageName); + writer.addDependency(TypeScriptDependency.CONFIG_RESOLVER); + writer.addImport("NODE_REGION_CONFIG_OPTIONS", "NODE_REGION_CONFIG_OPTIONS", + TypeScriptDependency.CONFIG_RESOLVER.packageName); + writer.addImport("NODE_REGION_CONFIG_FILE_OPTIONS", "NODE_REGION_CONFIG_FILE_OPTIONS", + TypeScriptDependency.CONFIG_RESOLVER.packageName); + writer.write( "region: loadNodeConfig(NODE_REGION_CONFIG_OPTIONS, NODE_REGION_CONFIG_FILE_OPTIONS),"); - }, - "maxAttempts", writer -> { - writer.addImport("NODE_MAX_ATTEMPT_CONFIG_OPTIONS", "NODE_MAX_ATTEMPT_CONFIG_OPTIONS", - TypeScriptDependency.MIDDLEWARE_RETRY.packageName); - writer.write("maxAttempts: loadNodeConfig(NODE_MAX_ATTEMPT_CONFIG_OPTIONS),"); - } - ); + }); + } + defaultConfigs.put("maxAttempts", writer -> { + writer.addDependency(AwsDependency.NODE_CONFIG_PROVIDER); + writer.addImport("loadConfig", "loadNodeConfig", + AwsDependency.NODE_CONFIG_PROVIDER.packageName); + writer.addImport("NODE_MAX_ATTEMPT_CONFIG_OPTIONS", "NODE_MAX_ATTEMPT_CONFIG_OPTIONS", + TypeScriptDependency.MIDDLEWARE_RETRY.packageName); + writer.write("maxAttempts: loadNodeConfig(NODE_MAX_ATTEMPT_CONFIG_OPTIONS),"); + }); + return defaultConfigs; default: return Collections.emptyMap(); } diff --git a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddBuiltinPlugins.java b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddBuiltinPlugins.java index c34b801ed786c..17a06fdce74d8 100644 --- a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddBuiltinPlugins.java +++ b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddBuiltinPlugins.java @@ -15,6 +15,7 @@ package software.amazon.smithy.aws.typescript.codegen; +import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isAwsService; import static software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin.Convention.HAS_CONFIG; import static software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin.Convention.HAS_MIDDLEWARE; @@ -44,6 +45,7 @@ public List getClientPlugins() { return ListUtils.of( RuntimeClientPlugin.builder() .withConventions(TypeScriptDependency.CONFIG_RESOLVER.dependency, "Region", HAS_CONFIG) + .servicePredicate((m, s) -> isAwsService(s)) .build(), RuntimeClientPlugin.builder() .withConventions(TypeScriptDependency.CONFIG_RESOLVER.dependency, "Endpoints", HAS_CONFIG) diff --git a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddUserAgentDependency.java b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddUserAgentDependency.java index 485c9f1989131..9158c06914d86 100644 --- a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddUserAgentDependency.java +++ b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddUserAgentDependency.java @@ -15,6 +15,8 @@ package software.amazon.smithy.aws.typescript.codegen; +import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isAwsService; + import java.util.Collections; import java.util.List; import java.util.Map; @@ -33,12 +35,15 @@ /** * Add client plubins and configs to support injecting user agent. */ +// TODO: Looks to add this back for non-AWS service clients, by fixing the dependency on ClientSharedValues.serviceId public class AddUserAgentDependency implements TypeScriptIntegration { @Override public List getClientPlugins() { return ListUtils.of( RuntimeClientPlugin.builder() - .withConventions(AwsDependency.MIDDLEWARE_USER_AGENT.dependency, "UserAgent").build()); + .withConventions(AwsDependency.MIDDLEWARE_USER_AGENT.dependency, "UserAgent") + .servicePredicate((m, s) -> isAwsService(s)) + .build()); } @Override @@ -48,6 +53,9 @@ public void addConfigInterfaceFields( SymbolProvider symbolProvider, TypeScriptWriter writer ) { + if (!isAwsService(settings, model)) { + return; + } writer.addImport("Provider", "Provider", TypeScriptDependency.AWS_SDK_TYPES.packageName); writer.addImport("UserAgent", "__UserAgent", TypeScriptDependency.AWS_SDK_TYPES.packageName); writer.writeDocs("The provider populating default tracking information to be sent with `user-agent`, " @@ -62,6 +70,9 @@ public Map> getRuntimeConfigWriters( SymbolProvider symbolProvider, LanguageTarget target ) { + if (!isAwsService(settings, model)) { + return Collections.emptyMap(); + } switch (target) { case NODE: return MapUtils.of( diff --git a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsEndpointGeneratorIntegration.java b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsEndpointGeneratorIntegration.java index d5656e79293e8..64c26c3a6f1e6 100644 --- a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsEndpointGeneratorIntegration.java +++ b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsEndpointGeneratorIntegration.java @@ -15,6 +15,8 @@ package software.amazon.smithy.aws.typescript.codegen; +import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isAwsService; + import java.util.Collections; import java.util.Map; import java.util.function.BiConsumer; @@ -39,7 +41,7 @@ public void writeAdditionalFiles( SymbolProvider symbolProvider, BiConsumer> writerFactory ) { - if (!settings.generateClient()) { + if (!settings.generateClient() || !isAwsService(settings, model)) { return; } @@ -55,7 +57,7 @@ public void addConfigInterfaceFields( SymbolProvider symbolProvider, TypeScriptWriter writer ) { - if (!settings.generateClient()) { + if (!settings.generateClient() || !isAwsService(settings, model)) { return; } @@ -71,7 +73,7 @@ public Map> getRuntimeConfigWriters( SymbolProvider symbolProvider, LanguageTarget target ) { - if (!settings.generateClient()) { + if (!settings.generateClient() || !isAwsService(settings, model)) { return Collections.emptyMap(); } diff --git a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsPackageFixturesGeneratorIntegration.java b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsPackageFixturesGeneratorIntegration.java index 54392e2efeb5a..167f0a3e23e7a 100644 --- a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsPackageFixturesGeneratorIntegration.java +++ b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsPackageFixturesGeneratorIntegration.java @@ -15,6 +15,8 @@ package software.amazon.smithy.aws.typescript.codegen; +import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isAwsService; + import java.util.Arrays; import java.util.Calendar; import java.util.function.BiConsumer; @@ -56,7 +58,8 @@ public void writeAdditionalFiles( writer.write(resource); }); - if (!settings.generateClient()) { + // TODO: May need to generate a different/modified README.md for these cases + if (!settings.generateClient() || !isAwsService(settings, model)) { return; } diff --git a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsServiceIdIntegration.java b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsServiceIdIntegration.java index fe00dc300c518..045391406265e 100644 --- a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsServiceIdIntegration.java +++ b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsServiceIdIntegration.java @@ -44,6 +44,7 @@ public SymbolProvider decorateSymbolProvider( return symbol; } + // TODO: Should this WARNING be avoided somehow if client is not for an AWS service? // If the SDK service ID trait is present, use that, otherwise fall back to // the default naming strategy for the service. return shape.getTrait(ServiceTrait.class) diff --git a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsTraitsUtils.java b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsTraitsUtils.java new file mode 100644 index 0000000000000..465037596e0e0 --- /dev/null +++ b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsTraitsUtils.java @@ -0,0 +1,46 @@ +/* + * Copyright 2021 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.smithy.aws.typescript.codegen; + +import software.amazon.smithy.aws.traits.ServiceTrait; +import software.amazon.smithy.aws.traits.auth.SigV4Trait; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.typescript.codegen.TypeScriptSettings; + +/** + * Utility methods related to AWS traits. + */ +final class AwsTraitsUtils { + + private AwsTraitsUtils() {} + + static boolean isAwsService(TypeScriptSettings settings, Model model) { + return isAwsService(settings.getService(model)); + } + + static boolean isAwsService(ServiceShape serviceShape) { + return serviceShape.hasTrait(ServiceTrait.class); + } + + static boolean isSigV4Service(TypeScriptSettings settings, Model model) { + return isSigV4Service(settings.getService(model)); + } + + static boolean isSigV4Service(ServiceShape serviceShape) { + return serviceShape.hasTrait(SigV4Trait.class); + } +} diff --git a/codegen/smithy-aws-typescript-codegen/src/test/resources/software/amazon/smithy/aws/typescript/codegen/NotSame.smithy b/codegen/smithy-aws-typescript-codegen/src/test/resources/software/amazon/smithy/aws/typescript/codegen/NotSame.smithy index 24c748af32e34..3401b6f3c41cd 100644 --- a/codegen/smithy-aws-typescript-codegen/src/test/resources/software/amazon/smithy/aws/typescript/codegen/NotSame.smithy +++ b/codegen/smithy-aws-typescript-codegen/src/test/resources/software/amazon/smithy/aws/typescript/codegen/NotSame.smithy @@ -4,6 +4,7 @@ use aws.protocols#restJson1 @aws.api#service(sdkId: "Not Same") @restJson1 +@aws.auth#sigv4(name: "notsame") service OriginalName { version: "2019-10-15", operations: [GetFoo] diff --git a/packages/config-resolver/src/EndpointsConfig.ts b/packages/config-resolver/src/EndpointsConfig.ts index 640d90bb54b64..579850c4bb5e4 100644 --- a/packages/config-resolver/src/EndpointsConfig.ts +++ b/packages/config-resolver/src/EndpointsConfig.ts @@ -13,9 +13,9 @@ export interface EndpointsInputConfig { } interface PreviouslyResolved { - regionInfoProvider: RegionInfoProvider; + regionInfoProvider?: RegionInfoProvider; urlParser: UrlParser; - region: Provider; + region?: Provider; } export interface EndpointsResolvedConfig extends Required { @@ -45,6 +45,10 @@ const normalizeEndpoint = (input: EndpointsInputConfig & PreviouslyResolved): Pr }; const getEndPointFromRegion = async (input: EndpointsInputConfig & PreviouslyResolved) => { + if (input.region === undefined || input.regionInfoProvider === undefined) { + throw new Error("No endpoint specified and region is not defined"); + } + const { tls = true } = input; const region = await input.region(); From 422db80e68e236f9b866d4addd90fadd8564b493 Mon Sep 17 00:00:00 2001 From: Jaykumar Gosar <5666661+gosar@users.noreply.github.com> Date: Fri, 23 Apr 2021 15:07:21 -0700 Subject: [PATCH 5/6] chore(codegen,config-resolver): refactor how endpoint is resolved for non-AWS client (#2287) * chore(config-resolver): refactor EndpointsConfig for non AWS services This reverts an earlier change to EndpointsConfig.ts and instead provides the new functionality in separate CustomEndpointsConfig.ts. This will be used as a separate plugin for endpoint resolution in non AWS clients. * feat(codegen): Use separate CustomEndpointsConfig for non-AWS clients This depends on newly added CustomEndpointsConfig in @aws-sdk/config-resolver package. --- .../typescript/codegen/AddBuiltinPlugins.java | 6 +++ .../src/CustomEndpointsConfig.ts | 45 +++++++++++++++++++ .../config-resolver/src/EndpointsConfig.ts | 8 +--- packages/config-resolver/src/index.ts | 1 + 4 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 packages/config-resolver/src/CustomEndpointsConfig.ts diff --git a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddBuiltinPlugins.java b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddBuiltinPlugins.java index 17a06fdce74d8..f9561dda282c4 100644 --- a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddBuiltinPlugins.java +++ b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddBuiltinPlugins.java @@ -47,8 +47,14 @@ public List getClientPlugins() { .withConventions(TypeScriptDependency.CONFIG_RESOLVER.dependency, "Region", HAS_CONFIG) .servicePredicate((m, s) -> isAwsService(s)) .build(), + // Only one of Endpoints or CustomEndpoints should be used RuntimeClientPlugin.builder() .withConventions(TypeScriptDependency.CONFIG_RESOLVER.dependency, "Endpoints", HAS_CONFIG) + .servicePredicate((m, s) -> isAwsService(s)) + .build(), + RuntimeClientPlugin.builder() + .withConventions(TypeScriptDependency.CONFIG_RESOLVER.dependency, "CustomEndpoints", HAS_CONFIG) + .servicePredicate((m, s) -> !isAwsService(s)) .build(), RuntimeClientPlugin.builder() .withConventions(TypeScriptDependency.MIDDLEWARE_RETRY.dependency, "Retry") diff --git a/packages/config-resolver/src/CustomEndpointsConfig.ts b/packages/config-resolver/src/CustomEndpointsConfig.ts new file mode 100644 index 0000000000000..56be461185363 --- /dev/null +++ b/packages/config-resolver/src/CustomEndpointsConfig.ts @@ -0,0 +1,45 @@ +// TODO: Create a .spec.ts for this +import { Endpoint, Provider, UrlParser } from "@aws-sdk/types"; + +export interface CustomEndpointsInputConfig { + /** + * The fully qualified endpoint of the webservice. + */ + endpoint: string | Endpoint | Provider; + + /** + * Whether TLS is enabled for requests. + */ + tls?: boolean; +} + +interface PreviouslyResolved { + urlParser: UrlParser; +} + +export interface CustomEndpointsResolvedConfig extends Required { + endpoint: Provider; + isCustomEndpoint: true; // TODO: Can this be removed or some other logic depends on this? +} + +export const resolveCustomEndpointsConfig = ( + input: T & CustomEndpointsInputConfig & PreviouslyResolved +): T & CustomEndpointsResolvedConfig => ({ + ...input, + tls: input.tls ?? true, + endpoint: normalizeEndpoint(input), + isCustomEndpoint: true, +}); + +// TODO: can this be shared with EndpointsConfig.ts +const normalizeEndpoint = (input: CustomEndpointsInputConfig & PreviouslyResolved): Provider => { + const { endpoint, urlParser } = input; + if (typeof endpoint === "string") { + const promisified = Promise.resolve(urlParser(endpoint)); + return () => promisified; + } else if (typeof endpoint === "object") { + const promisified = Promise.resolve(endpoint); + return () => promisified; + } + return endpoint; +}; diff --git a/packages/config-resolver/src/EndpointsConfig.ts b/packages/config-resolver/src/EndpointsConfig.ts index 579850c4bb5e4..640d90bb54b64 100644 --- a/packages/config-resolver/src/EndpointsConfig.ts +++ b/packages/config-resolver/src/EndpointsConfig.ts @@ -13,9 +13,9 @@ export interface EndpointsInputConfig { } interface PreviouslyResolved { - regionInfoProvider?: RegionInfoProvider; + regionInfoProvider: RegionInfoProvider; urlParser: UrlParser; - region?: Provider; + region: Provider; } export interface EndpointsResolvedConfig extends Required { @@ -45,10 +45,6 @@ const normalizeEndpoint = (input: EndpointsInputConfig & PreviouslyResolved): Pr }; const getEndPointFromRegion = async (input: EndpointsInputConfig & PreviouslyResolved) => { - if (input.region === undefined || input.regionInfoProvider === undefined) { - throw new Error("No endpoint specified and region is not defined"); - } - const { tls = true } = input; const region = await input.region(); diff --git a/packages/config-resolver/src/index.ts b/packages/config-resolver/src/index.ts index fcf9542bc29ff..47d04a39807c9 100644 --- a/packages/config-resolver/src/index.ts +++ b/packages/config-resolver/src/index.ts @@ -1,2 +1,3 @@ +export * from "./CustomEndpointsConfig"; export * from "./EndpointsConfig"; export * from "./RegionConfig"; From 3daceedbb2a4cf6658d852381e3ffdb97da3c9a3 Mon Sep 17 00:00:00 2001 From: Jaykumar Gosar <5666661+gosar@users.noreply.github.com> Date: Mon, 26 Apr 2021 09:30:22 -0700 Subject: [PATCH 6/6] test(config-resolver): add test for CustomEndpointsConfig (#2305) --- .../src/CustomEndpointsConfig.spec.ts | 64 +++++++++++++++++++ .../src/CustomEndpointsConfig.ts | 4 +- 2 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 packages/config-resolver/src/CustomEndpointsConfig.spec.ts diff --git a/packages/config-resolver/src/CustomEndpointsConfig.spec.ts b/packages/config-resolver/src/CustomEndpointsConfig.spec.ts new file mode 100644 index 0000000000000..39ea547ad38a2 --- /dev/null +++ b/packages/config-resolver/src/CustomEndpointsConfig.spec.ts @@ -0,0 +1,64 @@ +import { Endpoint } from "@aws-sdk/types"; + +import { resolveCustomEndpointsConfig } from "./CustomEndpointsConfig"; + +describe("CustomEndpointsConfig", () => { + const urlParser = jest.fn(); + + const input = { urlParser }; + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe("endpoint", () => { + const mockEndpoint: Endpoint = { protocol: "protocol", hostname: "hostname", path: "path" }; + + it("returns output of urlParser if endpoint is of type string", async () => { + const endpoint = "endpoint"; + urlParser.mockReturnValueOnce(mockEndpoint); + const { endpoint: endpointProvider, isCustomEndpoint } = resolveCustomEndpointsConfig({ ...input, endpoint }); + expect(isCustomEndpoint).toBe(true); + const endpointOutput = await endpointProvider(); + expect(endpointOutput).toStrictEqual(mockEndpoint); + expect(urlParser).toHaveBeenCalledTimes(1); + expect(urlParser).toHaveBeenCalledWith(endpoint); + }); + + it("returns promisified endpoint if it's of type object", async () => { + const endpoint = mockEndpoint; + const { endpoint: endpointProvider, isCustomEndpoint } = resolveCustomEndpointsConfig({ ...input, endpoint }); + expect(isCustomEndpoint).toBe(true); + const endpointOutput = await endpointProvider(); + expect(endpointOutput).toStrictEqual(endpoint); + expect(urlParser).not.toHaveBeenCalled(); + }); + + it("returns endpoint if it's already Provider", async () => { + const endpoint = () => Promise.resolve(mockEndpoint); + const { endpoint: endpointProvider, isCustomEndpoint } = resolveCustomEndpointsConfig({ ...input, endpoint }); + expect(isCustomEndpoint).toBe(true); + const endpointOutput = await endpointProvider(); + expect(endpointOutput).toStrictEqual(mockEndpoint); + expect(urlParser).not.toHaveBeenCalled(); + }); + }); + + describe("tls", () => { + const endpoint = "endpoint"; + + beforeEach(() => { + urlParser.mockReturnValueOnce({ protocol: "protocol", hostname: "hostname", path: "path" }); + }); + + [true, false].forEach((tls) => { + it(`returns input.tls when it's ${tls}`, () => { + expect(resolveCustomEndpointsConfig({ ...input, endpoint, tls }).tls).toStrictEqual(tls); + }); + }); + + it("returns true is input.tls is undefined", () => { + expect(resolveCustomEndpointsConfig({ ...input, endpoint }).tls).toStrictEqual(true); + }); + }); +}); diff --git a/packages/config-resolver/src/CustomEndpointsConfig.ts b/packages/config-resolver/src/CustomEndpointsConfig.ts index 56be461185363..e8c1291ade5c3 100644 --- a/packages/config-resolver/src/CustomEndpointsConfig.ts +++ b/packages/config-resolver/src/CustomEndpointsConfig.ts @@ -1,4 +1,3 @@ -// TODO: Create a .spec.ts for this import { Endpoint, Provider, UrlParser } from "@aws-sdk/types"; export interface CustomEndpointsInputConfig { @@ -19,7 +18,7 @@ interface PreviouslyResolved { export interface CustomEndpointsResolvedConfig extends Required { endpoint: Provider; - isCustomEndpoint: true; // TODO: Can this be removed or some other logic depends on this? + isCustomEndpoint: true; } export const resolveCustomEndpointsConfig = ( @@ -31,7 +30,6 @@ export const resolveCustomEndpointsConfig = ( isCustomEndpoint: true, }); -// TODO: can this be shared with EndpointsConfig.ts const normalizeEndpoint = (input: CustomEndpointsInputConfig & PreviouslyResolved): Provider => { const { endpoint, urlParser } = input; if (typeof endpoint === "string") {