From b5ae5e4cb787170d3c0acc843a87541810b0808c Mon Sep 17 00:00:00 2001 From: Steven Yuan Date: Thu, 7 Sep 2023 10:03:47 -0700 Subject: [PATCH] feat(experimentalIdentityAndAuth): add codegen for `HttpAuthExtensionConfiguration` (#910) Add code-generation for `HttpAuthExtensionConfiguration`, which allows configuring: - `httpAuthSchemes` - `httpAuthSchemeProvider` - Any `ConfigField` registered by `HttpAuthScheme`s Also, add relative import dependencies for: - `httpAuthSchemeProvider` - `httpAuthExtensionConfiguration` --- .../typescript/codegen/ConfigField.java | 18 +- .../codegen/RuntimeConfigGenerator.java | 6 +- .../ServiceBareBonesClientGenerator.java | 7 +- .../typescript/codegen/auth/AuthUtils.java | 54 +++ .../http/HttpAuthSchemeProviderGenerator.java | 24 +- .../integration/AddHttpApiKeyAuthPlugin.java | 9 +- .../integration/AddHttpBearerAuthPlugin.java | 3 +- .../http/integration/AddSigV4AuthPlugin.java | 9 +- ...tpAuthExtensionConfigurationInterface.java | 35 ++ .../HttpAuthRuntimeExtensionIntegration.java | 388 ++++++++++++++++++ ....codegen.integration.TypeScriptIntegration | 1 + 11 files changed, 513 insertions(+), 41 deletions(-) create mode 100644 smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/HttpAuthExtensionConfigurationInterface.java create mode 100644 smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/HttpAuthRuntimeExtensionIntegration.java diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ConfigField.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ConfigField.java index 91091eb6557..9c250ff1189 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ConfigField.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ConfigField.java @@ -20,6 +20,22 @@ @SmithyUnstableApi public final record ConfigField( String name, + Type type, Consumer source, Consumer docs -) {} +) { + /** + * Defines the type of the config field. + */ + @SmithyUnstableApi + public enum Type { + /** + * Specifies the property is important, e.g. {@code apiKey} for {@code @httpApiKeyAuth} + */ + MAIN, + /** + * Specifies the property is auxiliary, e.g. {@code region} for {@code @aws.auth#sigv4} + */ + AUXILIARY + } +} diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/RuntimeConfigGenerator.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/RuntimeConfigGenerator.java index c9fd39cbf01..38ede72bede 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/RuntimeConfigGenerator.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/RuntimeConfigGenerator.java @@ -15,7 +15,6 @@ package software.amazon.smithy.typescript.codegen; -import java.nio.file.Paths; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -32,7 +31,6 @@ import software.amazon.smithy.model.shapes.ShapeId; import software.amazon.smithy.typescript.codegen.auth.AuthUtils; import software.amazon.smithy.typescript.codegen.auth.http.HttpAuthScheme; -import software.amazon.smithy.typescript.codegen.auth.http.HttpAuthSchemeProviderGenerator; import software.amazon.smithy.typescript.codegen.auth.http.SupportedHttpAuthSchemesIndex; import software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration; import software.amazon.smithy.utils.MapUtils; @@ -212,9 +210,7 @@ private void generateHttpAuthSchemeConfig( if (target.equals(LanguageTarget.SHARED)) { configs.put("httpAuthSchemeProvider", w -> { String providerName = "default" + service.toShapeId().getName() + "HttpAuthSchemeProvider"; - w.addRelativeImport(providerName, null, Paths.get(".", CodegenUtils.SOURCE_FOLDER, - HttpAuthSchemeProviderGenerator.HTTP_AUTH_FOLDER, - HttpAuthSchemeProviderGenerator.HTTP_AUTH_SCHEME_RESOLVER_MODULE)); + w.addImport(providerName, null, AuthUtils.AUTH_HTTP_PROVIDER_DEPENDENCY); w.write(providerName); }); } diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServiceBareBonesClientGenerator.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServiceBareBonesClientGenerator.java index 63161281b9b..d7e57e0c19c 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServiceBareBonesClientGenerator.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServiceBareBonesClientGenerator.java @@ -32,7 +32,7 @@ import software.amazon.smithy.model.shapes.OperationShape; import software.amazon.smithy.model.shapes.ServiceShape; import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait; -import software.amazon.smithy.typescript.codegen.auth.http.HttpAuthSchemeProviderGenerator; +import software.amazon.smithy.typescript.codegen.auth.AuthUtils; import software.amazon.smithy.typescript.codegen.auth.http.integration.HttpAuthTypeScriptIntegration; import software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin; import software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration; @@ -313,10 +313,7 @@ private void generateClientDefaults() { writer.write("httpAuthSchemes?: HttpAuthScheme[];\n"); String httpAuthSchemeProviderName = service.toShapeId().getName() + "HttpAuthSchemeProvider"; - writer.addRelativeImport(httpAuthSchemeProviderName, null, Paths.get(".", - CodegenUtils.SOURCE_FOLDER, - HttpAuthSchemeProviderGenerator.HTTP_AUTH_FOLDER, - HttpAuthSchemeProviderGenerator.HTTP_AUTH_SCHEME_RESOLVER_MODULE)); + writer.addImport(httpAuthSchemeProviderName, null, AuthUtils.AUTH_HTTP_PROVIDER_DEPENDENCY); writer.writeDocs(""" experimentalIdentityAndAuth: Configuration of an HttpAuthSchemeProvider for a client which \ resolves which HttpAuthScheme to use. diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/AuthUtils.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/AuthUtils.java index fef0aa77311..a37149d52e2 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/AuthUtils.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/AuthUtils.java @@ -5,12 +5,18 @@ package software.amazon.smithy.typescript.codegen.auth; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.TreeMap; +import software.amazon.smithy.codegen.core.SymbolDependency; import software.amazon.smithy.model.knowledge.ServiceIndex; import software.amazon.smithy.model.knowledge.ServiceIndex.AuthSchemeMode; import software.amazon.smithy.model.shapes.ServiceShape; import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.typescript.codegen.CodegenUtils; +import software.amazon.smithy.typescript.codegen.Dependency; import software.amazon.smithy.typescript.codegen.auth.http.HttpAuthScheme; import software.amazon.smithy.typescript.codegen.auth.http.SupportedHttpAuthSchemesIndex; import software.amazon.smithy.utils.SmithyInternalApi; @@ -20,6 +26,54 @@ */ @SmithyInternalApi public final class AuthUtils { + public static final String HTTP_AUTH_FOLDER = "auth"; + + public static final String HTTP_AUTH_SCHEME_PROVIDER_MODULE = "httpAuthSchemeProvider"; + + public static final String HTTP_AUTH_SCHEME_PROVIDER_FILE = + HTTP_AUTH_SCHEME_PROVIDER_MODULE + ".ts"; + + public static final String HTTP_AUTH_SCHEME_PROVIDER_PATH = + Paths.get(".", CodegenUtils.SOURCE_FOLDER, HTTP_AUTH_FOLDER, HTTP_AUTH_SCHEME_PROVIDER_FILE).toString(); + + public static final Dependency AUTH_HTTP_PROVIDER_DEPENDENCY = new Dependency() { + @Override + public String getPackageName() { + return Paths.get( + ".", CodegenUtils.SOURCE_FOLDER, + HTTP_AUTH_FOLDER, HTTP_AUTH_SCHEME_PROVIDER_MODULE + ).toString(); + } + + @Override + public List getDependencies() { + return Collections.emptyList(); + } + }; + + public static final String HTTP_AUTH_SCHEME_EXTENSION_MODULE = "httpAuthExtensionConfiguration"; + + public static final String HTTP_AUTH_SCHEME_EXTENSION_FILE = + HTTP_AUTH_SCHEME_EXTENSION_MODULE + ".ts"; + + public static final String HTTP_AUTH_SCHEME_EXTENSION_PATH = + Paths.get(".", CodegenUtils.SOURCE_FOLDER, HTTP_AUTH_FOLDER, HTTP_AUTH_SCHEME_EXTENSION_FILE).toString(); + + public static final Dependency AUTH_HTTP_EXTENSION_DEPENDENCY = new Dependency() { + @Override + public String getPackageName() { + return Paths.get( + ".", CodegenUtils.SOURCE_FOLDER, + HTTP_AUTH_FOLDER, HTTP_AUTH_SCHEME_EXTENSION_MODULE + ).toString(); + } + + @Override + public List getDependencies() { + return Collections.emptyList(); + } + }; + private AuthUtils() {} public static Map getAllEffectiveNoAuthAwareAuthSchemes( diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/HttpAuthSchemeProviderGenerator.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/HttpAuthSchemeProviderGenerator.java index 10d9916477c..eb2157ee638 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/HttpAuthSchemeProviderGenerator.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/HttpAuthSchemeProviderGenerator.java @@ -41,20 +41,6 @@ */ @SmithyInternalApi public class HttpAuthSchemeProviderGenerator implements Runnable { - /** - * Directory segment for {@code auth/}. - */ - public static final String HTTP_AUTH_FOLDER = "auth"; - /** - * File name segment for {@code httpAuthSchemeProvder}. - */ - public static final String HTTP_AUTH_SCHEME_RESOLVER_MODULE = "httpAuthSchemeProvider"; - - private static final String HTTP_AUTH_SCHEME_RESOLVER_FILE = - HTTP_AUTH_SCHEME_RESOLVER_MODULE + ".ts"; - private static final String HTTP_AUTH_SCHEME_RESOLVER_PATH = - Paths.get(CodegenUtils.SOURCE_FOLDER, HTTP_AUTH_FOLDER, HTTP_AUTH_SCHEME_RESOLVER_FILE).toString(); - private final TypeScriptDelegator delegator; private final Model model; @@ -104,7 +90,7 @@ export interface WeatherHttpAuthSchemeParameters { } */ private void generateHttpAuthSchemeParametersInterface() { - delegator.useFileWriter(HTTP_AUTH_SCHEME_RESOLVER_PATH.toString(), w -> { + delegator.useFileWriter(AuthUtils.HTTP_AUTH_SCHEME_PROVIDER_PATH, w -> { w.openBlock(""" /** * @internal @@ -138,7 +124,7 @@ export async function defaultWeatherHttpAuthSchemeParametersProvider( }; */ private void generateDefaultHttpAuthSchemeParametersProviderFunction() { - delegator.useFileWriter(HTTP_AUTH_SCHEME_RESOLVER_PATH.toString(), w -> { + delegator.useFileWriter(AuthUtils.HTTP_AUTH_SCHEME_PROVIDER_PATH, w -> { w.addRelativeImport(serviceSymbol.getName() + "ResolvedConfig", null, Paths.get(".", serviceSymbol.getNamespace())); w.addImport("HandlerExecutionContext", null, TypeScriptDependency.SMITHY_TYPES); @@ -190,7 +176,7 @@ function createSmithyApiHttpApiKeyAuthHttpAuthOption(authParameters: WeatherHttp }; */ private void generateHttpAuthOptionFunction(ShapeId shapeId, HttpAuthScheme authScheme) { - delegator.useFileWriter(HTTP_AUTH_SCHEME_RESOLVER_PATH.toString(), w -> { + delegator.useFileWriter(AuthUtils.HTTP_AUTH_SCHEME_PROVIDER_PATH, w -> { String normalizedAuthSchemeName = normalizeAuthSchemeName(shapeId); w.addDependency(TypeScriptDependency.EXPERIMENTAL_IDENTITY_AND_AUTH); w.addImport("HttpAuthOption", null, TypeScriptDependency.EXPERIMENTAL_IDENTITY_AND_AUTH); @@ -242,7 +228,7 @@ export interface WeatherHttpAuthSchemeProvider { } */ private void generateHttpAuthSchemeProviderInterface() { - delegator.useFileWriter(HTTP_AUTH_SCHEME_RESOLVER_PATH.toString(), w -> { + delegator.useFileWriter(AuthUtils.HTTP_AUTH_SCHEME_PROVIDER_PATH, w -> { w.write(""" /** * @internal @@ -267,7 +253,7 @@ export function defaultWeatherHttpAuthSchemeProvider(authParameters: WeatherHttp }; */ private void generateHttpAuthSchemeProviderDefaultFunction() { - delegator.useFileWriter(HTTP_AUTH_SCHEME_RESOLVER_PATH.toString(), w -> { + delegator.useFileWriter(AuthUtils.HTTP_AUTH_SCHEME_PROVIDER_PATH, w -> { w.openBlock(""" /** * @internal diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/AddHttpApiKeyAuthPlugin.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/AddHttpApiKeyAuthPlugin.java index 4fbb6ed743d..6dce868e563 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/AddHttpApiKeyAuthPlugin.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/AddHttpApiKeyAuthPlugin.java @@ -17,7 +17,6 @@ import software.amazon.smithy.typescript.codegen.TypeScriptSettings; import software.amazon.smithy.typescript.codegen.TypeScriptWriter; import software.amazon.smithy.typescript.codegen.auth.http.HttpAuthOptionProperty; -import software.amazon.smithy.typescript.codegen.auth.http.HttpAuthOptionProperty.Type; import software.amazon.smithy.typescript.codegen.auth.http.HttpAuthScheme; import software.amazon.smithy.utils.SmithyInternalApi; @@ -47,7 +46,7 @@ public Optional getHttpAuthScheme() { return Optional.of(HttpAuthScheme.builder() .schemeId(HttpApiKeyAuthTrait.ID) .applicationProtocol(ApplicationProtocol.createDefaultHttpApplicationProtocol()) - .addConfigField(new ConfigField("apiKey", w -> { + .addConfigField(new ConfigField("apiKey", ConfigField.Type.MAIN, w -> { w.addImport("ApiKeyIdentity", null, TypeScriptDependency.EXPERIMENTAL_IDENTITY_AND_AUTH); w.addImport("ApiKeyIdentityProvider", null, @@ -55,12 +54,12 @@ public Optional getHttpAuthScheme() { w.write("ApiKeyIdentity | ApiKeyIdentityProvider"); }, w -> w.write("The API key to use when making requests."))) .addHttpAuthOptionProperty(new HttpAuthOptionProperty( - "name", Type.SIGNING, t -> w -> { + "name", HttpAuthOptionProperty.Type.SIGNING, t -> w -> { HttpApiKeyAuthTrait httpApiKeyAuthTrait = (HttpApiKeyAuthTrait) t; w.write("$S", httpApiKeyAuthTrait.getName()); })) .addHttpAuthOptionProperty(new HttpAuthOptionProperty( - "in", Type.SIGNING, t -> w -> { + "in", HttpAuthOptionProperty.Type.SIGNING, t -> w -> { w.addImport("HttpApiKeyAuthLocation", null, TypeScriptDependency.EXPERIMENTAL_IDENTITY_AND_AUTH); HttpApiKeyAuthTrait httpApiKeyAuthTrait = (HttpApiKeyAuthTrait) t; @@ -74,7 +73,7 @@ public Optional getHttpAuthScheme() { } })) .addHttpAuthOptionProperty(new HttpAuthOptionProperty( - "scheme", Type.SIGNING, t -> w -> { + "scheme", HttpAuthOptionProperty.Type.SIGNING, t -> w -> { HttpApiKeyAuthTrait httpApiKeyAuthTrait = (HttpApiKeyAuthTrait) t; httpApiKeyAuthTrait.getScheme().ifPresentOrElse( s -> w.write(s), diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/AddHttpBearerAuthPlugin.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/AddHttpBearerAuthPlugin.java index 0fcd37737ba..6caa58706f0 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/AddHttpBearerAuthPlugin.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/AddHttpBearerAuthPlugin.java @@ -10,6 +10,7 @@ import software.amazon.smithy.model.traits.HttpBearerAuthTrait; import software.amazon.smithy.typescript.codegen.ApplicationProtocol; import software.amazon.smithy.typescript.codegen.ConfigField; +import software.amazon.smithy.typescript.codegen.ConfigField.Type; import software.amazon.smithy.typescript.codegen.LanguageTarget; import software.amazon.smithy.typescript.codegen.TypeScriptDependency; import software.amazon.smithy.typescript.codegen.TypeScriptSettings; @@ -43,7 +44,7 @@ public Optional getHttpAuthScheme() { return Optional.of(HttpAuthScheme.builder() .schemeId(HttpBearerAuthTrait.ID) .applicationProtocol(ApplicationProtocol.createDefaultHttpApplicationProtocol()) - .addConfigField(new ConfigField("token", w -> { + .addConfigField(new ConfigField("token", Type.MAIN, w -> { w.addDependency(TypeScriptDependency.EXPERIMENTAL_IDENTITY_AND_AUTH); w.addImport("TokenIdentity", null, TypeScriptDependency.EXPERIMENTAL_IDENTITY_AND_AUTH); diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/AddSigV4AuthPlugin.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/AddSigV4AuthPlugin.java index 23a9d42ca63..f38131f1e33 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/AddSigV4AuthPlugin.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/AddSigV4AuthPlugin.java @@ -15,7 +15,6 @@ import software.amazon.smithy.typescript.codegen.TypeScriptSettings; import software.amazon.smithy.typescript.codegen.TypeScriptWriter; import software.amazon.smithy.typescript.codegen.auth.http.HttpAuthOptionProperty; -import software.amazon.smithy.typescript.codegen.auth.http.HttpAuthOptionProperty.Type; import software.amazon.smithy.typescript.codegen.auth.http.HttpAuthScheme; import software.amazon.smithy.typescript.codegen.auth.http.HttpAuthSchemeParameter; import software.amazon.smithy.utils.SmithyInternalApi; @@ -47,12 +46,12 @@ public Optional getHttpAuthScheme() { .schemeId(ShapeId.from("aws.auth#sigv4")) .applicationProtocol(ApplicationProtocol.createDefaultHttpApplicationProtocol()) .putDefaultSigner(LanguageTarget.SHARED, AWS_SIGV4_AUTH_SIGNER) - .addConfigField(new ConfigField("region", w -> { + .addConfigField(new ConfigField("region", ConfigField.Type.AUXILIARY, w -> { w.addDependency(TypeScriptDependency.SMITHY_TYPES); w.addImport("Provider", "__Provider", TypeScriptDependency.SMITHY_TYPES); w.write("string | __Provider"); }, w -> w.write("The AWS region to which this client will send requests."))) - .addConfigField(new ConfigField("credentials", w -> { + .addConfigField(new ConfigField("credentials", ConfigField.Type.MAIN, w -> { w.addDependency(TypeScriptDependency.SMITHY_TYPES); w.addImport("AwsCredentialIdentity", null, TypeScriptDependency.SMITHY_TYPES); w.addImport("AwsCredentialIdentityProvider", null, TypeScriptDependency.SMITHY_TYPES); @@ -67,11 +66,11 @@ public Optional getHttpAuthScheme() { }); })) .addHttpAuthOptionProperty(new HttpAuthOptionProperty( - "name", Type.SIGNING, t -> w -> { + "name", HttpAuthOptionProperty.Type.SIGNING, t -> w -> { w.write("$S", t.toNode().expectObjectNode().getMember("name")); })) .addHttpAuthOptionProperty(new HttpAuthOptionProperty( - "region", Type.SIGNING, t -> w -> { + "region", HttpAuthOptionProperty.Type.SIGNING, t -> w -> { w.write("authParameters.region"); })) .build()); diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/HttpAuthExtensionConfigurationInterface.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/HttpAuthExtensionConfigurationInterface.java new file mode 100644 index 00000000000..db90da40c42 --- /dev/null +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/HttpAuthExtensionConfigurationInterface.java @@ -0,0 +1,35 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.typescript.codegen.auth.http.integration; + +import software.amazon.smithy.typescript.codegen.Dependency; +import software.amazon.smithy.typescript.codegen.auth.AuthUtils; +import software.amazon.smithy.typescript.codegen.extensions.ExtensionConfigurationInterface; +import software.amazon.smithy.utils.Pair; +import software.amazon.smithy.utils.SmithyInternalApi; + +/** + * Adds the corresponding interface and functions for {@code HttpAuthExtensionConfiguration}. + * + * This is experimental for `experimentalIdentityAndAuth`. + */ +@SmithyInternalApi +public class HttpAuthExtensionConfigurationInterface implements ExtensionConfigurationInterface { + @Override + public Pair name() { + return Pair.of("HttpAuthExtensionConfiguration", AuthUtils.AUTH_HTTP_EXTENSION_DEPENDENCY); + } + + @Override + public Pair getExtensionConfigurationFn() { + return Pair.of("getHttpAuthExtensionConfiguration", AuthUtils.AUTH_HTTP_EXTENSION_DEPENDENCY); + } + + @Override + public Pair resolveRuntimeConfigFn() { + return Pair.of("resolveHttpAuthRuntimeConfig", AuthUtils.AUTH_HTTP_EXTENSION_DEPENDENCY); + } +} diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/HttpAuthRuntimeExtensionIntegration.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/HttpAuthRuntimeExtensionIntegration.java new file mode 100644 index 00000000000..7cc8ee9189a --- /dev/null +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/HttpAuthRuntimeExtensionIntegration.java @@ -0,0 +1,388 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.typescript.codegen.auth.http.integration; + +import java.util.List; +import java.util.Map; +import software.amazon.smithy.model.knowledge.ServiceIndex; +import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.typescript.codegen.CodegenUtils; +import software.amazon.smithy.typescript.codegen.ConfigField; +import software.amazon.smithy.typescript.codegen.TypeScriptCodegenContext; +import software.amazon.smithy.typescript.codegen.TypeScriptDelegator; +import software.amazon.smithy.typescript.codegen.TypeScriptDependency; +import software.amazon.smithy.typescript.codegen.TypeScriptSettings; +import software.amazon.smithy.typescript.codegen.auth.AuthUtils; +import software.amazon.smithy.typescript.codegen.auth.http.HttpAuthScheme; +import software.amazon.smithy.typescript.codegen.auth.http.SupportedHttpAuthSchemesIndex; +import software.amazon.smithy.typescript.codegen.extensions.ExtensionConfigurationInterface; +import software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration; +import software.amazon.smithy.utils.SmithyInternalApi; +import software.amazon.smithy.utils.StringUtils; + +/** + * Adds {@link HttpAuthExtensionConfigurationInterface} to a client. + * + * This is the experimental behavior for `experimentalIdentityAndAuth`. + */ +@SmithyInternalApi +public class HttpAuthRuntimeExtensionIntegration implements TypeScriptIntegration { + + /** + * Integration should only be used if `experimentalIdentityAndAuth` flag is true. + */ + @Override + public boolean matchesSettings(TypeScriptSettings settings) { + return settings.getExperimentalIdentityAndAuth(); + } + + @Override + public List getExtensionConfigurationInterfaces() { + return List.of(new HttpAuthExtensionConfigurationInterface()); + } + + @Override + public void customize(TypeScriptCodegenContext codegenContext) { + if (!codegenContext.settings().generateClient()) { + return; + } + TypeScriptDelegator delegator = codegenContext.writerDelegator(); + SupportedHttpAuthSchemesIndex authIndex = + new SupportedHttpAuthSchemesIndex(codegenContext.integrations()); + ServiceIndex serviceIndex = ServiceIndex.of(codegenContext.model()); + String serviceName = CodegenUtils.getServiceName( + codegenContext.settings(), codegenContext.model(), codegenContext.symbolProvider()); + ServiceShape serviceShape = codegenContext.settings().getService(codegenContext.model()); + Map effectiveAuthSchemes = + AuthUtils.getAllEffectiveNoAuthAwareAuthSchemes(serviceShape, serviceIndex, authIndex); + + generateHttpAuthExtensionConfigurationInterface( + delegator, effectiveAuthSchemes, serviceName); + generateHttpAuthExtensionRuntimeConfigType( + delegator, effectiveAuthSchemes, serviceName); + generateGetHttpAuthExtensionConfigurationFunction( + delegator, effectiveAuthSchemes, serviceName); + generateResolveHttpAuthRuntimeConfigFunction( + delegator, effectiveAuthSchemes, serviceName); + } + + /* + import { + ApiKeyIdentity, + ApiKeyIdentityProvider, + AwsCredentialsIdentity, + AwsCredentialIdentityProvider, + HttpAuthScheme, + TokenIdentity, + TokenIdentityProvider + } from "@smithy/types"; + + import { + WeatherHttpAuthSchemeProvider + } from "./httpAuthSchemeProvider"; + + // ... + + export interface HttpAuthExtensionConfiguration { + setHttpAuthScheme(httpAuthScheme: HttpAuthScheme): void; + httpAuthSchemes(): HttpAuthScheme[]; + + setHttpAuthSchemeProvider(httpAuthSchemeProvider: WeatherHttpAuthSchemeProvider): void; + httpAuthSchemeProvider(): WeatherHttpAuthSchemeProvider; + + // @aws.auth#sigv4 + setCredentials(credentials: AwsCredentialsIdentity | AwsCredentialIdentityProvider): void; + credentials(): AwsCredentialsIdentity | AwsCredentialIdentityProvider | undefined; + + // @httpApiKeyAuth + setApiKey(apiKey: ApiKeyIdentity | ApiKeyIdentityProvider): void; + apiKey(): ApiKeyIdentity | ApiKeyIdentityProvider | undefined; + + // @httpBearerAuth + setToken(token: TokenIdentity| TokenIdentityProvider): void; + token(): TokenIdentity | TokenIdentityProvider | undefined; + } + */ + private void generateHttpAuthExtensionConfigurationInterface( + TypeScriptDelegator delegator, + Map effectiveAuthSchemes, + String serviceName + ) { + delegator.useFileWriter(AuthUtils.HTTP_AUTH_SCHEME_EXTENSION_PATH, w -> { + w.addDependency(TypeScriptDependency.EXPERIMENTAL_IDENTITY_AND_AUTH); + w.addDependency(TypeScriptDependency.SMITHY_TYPES); + w.openBlock(""" + /** + * @internal + */ + export interface HttpAuthExtensionConfiguration {""", "}", + () -> { + w.addImport("HttpAuthScheme", null, TypeScriptDependency.EXPERIMENTAL_IDENTITY_AND_AUTH); + w.write("setHttpAuthScheme(httpAuthScheme: HttpAuthScheme): void;"); + w.write("httpAuthSchemes(): HttpAuthScheme[];"); + + w.addImport(serviceName + "HttpAuthSchemeProvider", null, AuthUtils.AUTH_HTTP_PROVIDER_DEPENDENCY); + w.write("setHttpAuthSchemeProvider(httpAuthSchemeProvider: $LHttpAuthSchemeProvider): void;", + serviceName); + w.write("httpAuthSchemeProvider(): $LHttpAuthSchemeProvider;", serviceName); + + for (HttpAuthScheme authScheme : effectiveAuthSchemes.values()) { + if (authScheme == null) { + continue; + } + for (ConfigField configField : authScheme.getConfigFields()) { + if (!configField.type().equals(ConfigField.Type.MAIN)) { + continue; + } + String capitalizedName = StringUtils.capitalize(configField.name()); + w.write("set$L($L: $C): void;", capitalizedName, configField.name(), configField.source()); + w.write("$L(): $C | undefined;", configField.name(), configField.source()); + } + } + }); + }); + } + + /* + import { + ApiKeyIdentity, + ApiKeyIdentityProvider, + AwsCredentialsIdentity, + AwsCredentialIdentityProvider, + HttpAuthScheme, + TokenIdentity, + TokenIdentityProvider + } from "@smithy/types"; + + import { + WeatherHttpAuthSchemeProvider + } from "./httpAuthSchemeProvider"; + + // ... + + export type HttpAuthRuntimeConfig = Partial<{ + httpAuthSchemes: HttpAuthScheme[]; + httpAuthSchemeProvider: WeatherHttpAuthSchemeProvider; + // @aws.auth#sigv4 + credentials: AwsCredentialsIdentity | AwsCredentialIdentityProvider; + // @httpApiKeyAuth + apiKey: ApiKeyIdentity | ApiKeyIdentityProvider; + // @httpBearerAuth + token: TokenIdentity | TokenIdentityProvider; + }>; + */ + private void generateHttpAuthExtensionRuntimeConfigType( + TypeScriptDelegator delegator, + Map effectiveAuthSchemes, + String serviceName + ) { + delegator.useFileWriter(AuthUtils.HTTP_AUTH_SCHEME_EXTENSION_PATH, w -> { + w.addDependency(TypeScriptDependency.EXPERIMENTAL_IDENTITY_AND_AUTH); + w.addDependency(TypeScriptDependency.SMITHY_TYPES); + w.openBlock(""" + /** + * @internal + */ + export type HttpAuthRuntimeConfig = Partial<{""", "}>;", + () -> { + w.addImport("HttpAuthScheme", null, TypeScriptDependency.EXPERIMENTAL_IDENTITY_AND_AUTH); + w.write("httpAuthSchemes: HttpAuthScheme[];"); + w.addImport(serviceName + "HttpAuthSchemeProvider", null, AuthUtils.AUTH_HTTP_PROVIDER_DEPENDENCY); + w.write("httpAuthSchemeProvider: $LHttpAuthSchemeProvider;", serviceName); + for (HttpAuthScheme authScheme : effectiveAuthSchemes.values()) { + if (authScheme == null) { + continue; + } + for (ConfigField configField : authScheme.getConfigFields()) { + if (!configField.type().equals(ConfigField.Type.MAIN)) { + continue; + } + w.write("$L: $C;", configField.name(), configField.source()); + } + } + }); + }); + } + + /* + import { + ApiKeyIdentity, + ApiKeyIdentityProvider, + AwsCredentialsIdentity, + AwsCredentialIdentityProvider, + HttpAuthScheme, + TokenIdentity, + TokenIdentityProvider + } from "@smithy/types"; + + import { + WeatherHttpAuthSchemeProvider + } from "./httpAuthSchemeProvider"; + + // ... + + export const getHttpAuthExtensionConfiguration = + (runtimeConfig: HttpAuthRuntimeConfig): HttpAuthExtensionConfiguration => { + let _httpAuthSchemes = runtimeConfig.httpAuthSchemes!; + let _httpAuthSchemeProvider = runtimeConfig.httpAuthSchemeProvider!; + let _credentials = runtimeConfig.credentials; + let _apiKey = runtimeConfig.apiKey; + let _token = runtimeConfig.token; + return { + setHttpAuthScheme(httpAuthScheme: HttpAuthScheme): void { + _httpAuthSchemes.push(httpAuthScheme); + }, + httpAuthSchemes(): HttpAuthScheme[] { + return _httpAuthSchemes; + }, + setHttpAuthSchemeProvider(httpAuthSchemeProvider: WeatherHttpAuthSchemeProvider): void { + _httpAuthSchemeProvider = httpAuthSchemeProvider; + }, + httpAuthSchemeProvider(): WeatherHttpAuthSchemeProvider { + return _httpAuthSchemeProvider; + }, + // Dependent on auth traits + setCredentials(credentials: AwsCredentialsIdentity | AwsCredentialIdentityProvider): void { + _credentials = credentials; + }, + credentials(): AwsCredentialsIdentity | AwsCredentialIdentityProvider | undefined { + return _credentials; + }, + setApiKey(apiKey: ApiKeyIdentity | ApiKeyIdentityProvider): void { + _apiKey = apiKey; + }, + apiKey(): ApiKeyIdentity | ApiKeyIdentityProvider | undefined { + return _apiKey; + }, + setToken(token: TokenIdentity | TokenIdentityProvider): void { + _token = token; + }, + token(): TokenIdentity | TokenIdentityProvider | undefined { + return _token; + }, + }; + }; + */ + private void generateGetHttpAuthExtensionConfigurationFunction( + TypeScriptDelegator delegator, + Map effectiveAuthSchemes, + String serviceName + ) { + delegator.useFileWriter(AuthUtils.HTTP_AUTH_SCHEME_EXTENSION_PATH, w -> { + w.addDependency(TypeScriptDependency.EXPERIMENTAL_IDENTITY_AND_AUTH); + w.addDependency(TypeScriptDependency.SMITHY_TYPES); + w.openBlock(""" + /** + * @internal + */ + export const getHttpAuthExtensionConfiguration = (runtimeConfig: HttpAuthRuntimeConfig): \ + HttpAuthExtensionConfiguration => {""", "};", + () -> { + w.addImport("HttpAuthScheme", null, TypeScriptDependency.EXPERIMENTAL_IDENTITY_AND_AUTH); + w.write("let _httpAuthSchemes = runtimeConfig.httpAuthSchemes!;"); + w.addImport(serviceName + "HttpAuthSchemeProvider", null, AuthUtils.AUTH_HTTP_PROVIDER_DEPENDENCY); + w.write("let _httpAuthSchemeProvider = runtimeConfig.httpAuthSchemeProvider!;"); + for (HttpAuthScheme authScheme : effectiveAuthSchemes.values()) { + if (authScheme == null) { + continue; + } + for (ConfigField configField : authScheme.getConfigFields()) { + if (!configField.type().equals(ConfigField.Type.MAIN)) { + continue; + } + w.write("let _$L = runtimeConfig.$L;", configField.name(), configField.name()); + } + } + + w.openBlock("return {", "}", + () -> { + w.write(""" + setHttpAuthScheme(httpAuthScheme: HttpAuthScheme): void { + _httpAuthSchemes.push(httpAuthScheme); + }, + httpAuthSchemes(): HttpAuthScheme[] { + return _httpAuthSchemes; + },"""); + w.write(""" + setHttpAuthSchemeProvider(httpAuthSchemeProvider: $LHttpAuthSchemeProvider): void { + _httpAuthSchemeProvider = httpAuthSchemeProvider; + }, + httpAuthSchemeProvider(): $LHttpAuthSchemeProvider { + return _httpAuthSchemeProvider; + },""", serviceName, serviceName); + for (HttpAuthScheme authScheme : effectiveAuthSchemes.values()) { + if (authScheme == null) { + continue; + } + for (ConfigField configField : authScheme.getConfigFields()) { + if (!configField.type().equals(ConfigField.Type.MAIN)) { + continue; + } + String capitalizedName = StringUtils.capitalize(configField.name()); + w.write(""" + set$L($L: $C): void { + _$L = $L; + }, + $L(): $C | undefined { + return _$L; + },""", + capitalizedName, configField.name(), configField.source(), + configField.name(), configField.name(), + configField.name(), configField.source(), + configField.name()); + } + } + }); + }); + }); + } + + /* + export const resolveHttpAuthRuntimeConfig = + (config: HttpAuthExtensionConfiguration): HttpAuthRuntimeConfig => { + return { + httpAuthSchemes: config.httpAuthSchemes(), + httpAuthSchemeProvider: config.httpAuthSchemeProvider(), + // Dependent on auth traits + credentials: config.credentials(), + apiKey: config.apiKey(), + token: config.token(), + }; + }; + */ + private void generateResolveHttpAuthRuntimeConfigFunction( + TypeScriptDelegator delegator, + Map effectiveAuthSchemes, + String serviceName + ) { + delegator.useFileWriter(AuthUtils.HTTP_AUTH_SCHEME_EXTENSION_PATH, w -> { + w.openBlock(""" + /** + * @internal + */ + export const resolveHttpAuthRuntimeConfig = (config: HttpAuthExtensionConfiguration): \ + HttpAuthRuntimeConfig => {""", "};", + () -> { + w.openBlock("return {", "};", () -> { + w.write("httpAuthSchemes: config.httpAuthSchemes(),"); + w.write("httpAuthSchemeProvider: config.httpAuthSchemeProvider(),"); + for (HttpAuthScheme authScheme : effectiveAuthSchemes.values()) { + if (authScheme == null) { + continue; + } + for (ConfigField configField : authScheme.getConfigFields()) { + if (!configField.type().equals(ConfigField.Type.MAIN)) { + continue; + } + w.write("$L: config.$L(),", configField.name(), configField.name()); + } + } + }); + }); + }); + } +} diff --git a/smithy-typescript-codegen/src/main/resources/META-INF/services/software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration b/smithy-typescript-codegen/src/main/resources/META-INF/services/software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration index 9feb26cb925..684e1cdf01e 100644 --- a/smithy-typescript-codegen/src/main/resources/META-INF/services/software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration +++ b/smithy-typescript-codegen/src/main/resources/META-INF/services/software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration @@ -2,6 +2,7 @@ software.amazon.smithy.typescript.codegen.integration.AddClientRuntimeConfig software.amazon.smithy.typescript.codegen.integration.AddEventStreamDependency software.amazon.smithy.typescript.codegen.integration.AddChecksumRequiredDependency software.amazon.smithy.typescript.codegen.integration.AddDefaultsModeDependency +software.amazon.smithy.typescript.codegen.auth.http.integration.HttpAuthRuntimeExtensionIntegration software.amazon.smithy.typescript.codegen.auth.http.integration.AddNoAuthPlugin software.amazon.smithy.typescript.codegen.auth.http.integration.AddHttpApiKeyAuthPlugin software.amazon.smithy.typescript.codegen.auth.http.integration.AddHttpBearerAuthPlugin