From bc80da44a3b9ac0c25662ee69c86eba0de62a39f Mon Sep 17 00:00:00 2001 From: Steven Yuan Date: Wed, 20 Sep 2023 10:50:32 -0700 Subject: [PATCH] feat(experimentalIdentityAndAuth): add `collect*()` methods to dedupe `ConfigField`s and `HttpAuthSchemeParameter` (#948) --- .../ServiceBareBonesClientGenerator.java | 31 ++++----- .../typescript/codegen/auth/AuthUtils.java | 56 ++++++++++++++++ .../http/HttpAuthSchemeProviderGenerator.java | 15 ++--- .../HttpAuthRuntimeExtensionIntegration.java | 67 ++++++------------- 4 files changed, 98 insertions(+), 71 deletions(-) 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 f0fae0ea084..933ba83ad82 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 @@ -28,12 +28,15 @@ import software.amazon.smithy.codegen.core.SymbolProvider; import software.amazon.smithy.codegen.core.SymbolReference; import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.knowledge.ServiceIndex; import software.amazon.smithy.model.knowledge.TopDownIndex; import software.amazon.smithy.model.shapes.OperationShape; import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.model.shapes.ShapeId; import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait; import software.amazon.smithy.typescript.codegen.auth.AuthUtils; -import software.amazon.smithy.typescript.codegen.auth.http.integration.HttpAuthTypeScriptIntegration; +import software.amazon.smithy.typescript.codegen.auth.http.HttpAuthScheme; +import software.amazon.smithy.typescript.codegen.auth.http.SupportedHttpAuthSchemesIndex; import software.amazon.smithy.typescript.codegen.endpointsV2.EndpointsV2Generator; import software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin; import software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration; @@ -329,20 +332,18 @@ private void generateClientDefaults() { // Write custom configuration dependencies. for (TypeScriptIntegration integration : integrations) { integration.addConfigInterfaceFields(settings, model, symbolProvider, writer); - // feat(experimentalIdentityAndAuth): write any HttpAuthScheme config fields into ClientDefaults - // WARNING: may be changed later in lieu of {@link TypeScriptIntegration#addConfigInterfaceFields()}, - // but will depend after HttpAuthScheme integration implementations. - if (settings.getExperimentalIdentityAndAuth()) { - if (!(integration instanceof HttpAuthTypeScriptIntegration)) { - continue; - } - HttpAuthTypeScriptIntegration httpAuthIntegration = (HttpAuthTypeScriptIntegration) integration; - httpAuthIntegration.getHttpAuthScheme().ifPresent(authScheme -> { - for (ConfigField configField : authScheme.getConfigFields()) { - writer.writeDocs(() -> writer.write("$C", configField.docs())); - writer.write("$L?: $C;\n", configField.name(), configField.inputType()); - } - }); + } + + // feat(experimentalIdentityAndAuth): write any HttpAuthScheme config fields into ClientDefaults + // WARNING: may be changed later in lieu of {@link TypeScriptIntegration#addConfigInterfaceFields()}, + // but will depend after HttpAuthScheme integration implementations. + if (settings.getExperimentalIdentityAndAuth()) { + Map httpAuthSchemes = AuthUtils.getAllEffectiveNoAuthAwareAuthSchemes( + service, ServiceIndex.of(model), new SupportedHttpAuthSchemesIndex(integrations)); + Map configFields = AuthUtils.collectConfigFields(httpAuthSchemes.values()); + for (ConfigField configField : configFields.values()) { + writer.writeDocs(() -> writer.write("$C", configField.docs())); + writer.write("$L?: $C;\n", configField.name(), configField.inputType()); } } }).write(""); 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 0770e13c4b7..d147c307a8b 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 @@ -6,18 +6,23 @@ package software.amazon.smithy.typescript.codegen.auth; import java.nio.file.Paths; +import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; +import software.amazon.smithy.codegen.core.CodegenException; 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.ConfigField; import software.amazon.smithy.typescript.codegen.Dependency; import software.amazon.smithy.typescript.codegen.auth.http.HttpAuthScheme; +import software.amazon.smithy.typescript.codegen.auth.http.HttpAuthSchemeParameter; import software.amazon.smithy.typescript.codegen.auth.http.SupportedHttpAuthSchemesIndex; import software.amazon.smithy.utils.SmithyInternalApi; @@ -93,4 +98,55 @@ public static Map getAllEffectiveNoAuthAwareAuthSchemes // END return effectiveAuthSchemes; } + + public static Map collectConfigFields(Collection httpAuthSchemes) { + Map configFields = new HashMap<>(); + for (HttpAuthScheme authScheme : httpAuthSchemes) { + if (authScheme == null) { + continue; + } + for (ConfigField configField : authScheme.getConfigFields()) { + if (configFields.containsKey(configField.name())) { + ConfigField existingConfigField = configFields.get(configField.name()); + if (!configField.equals(existingConfigField)) { + throw new CodegenException("Contradicting `ConfigField` defintions for `" + + configField.name() + + "`; existing: " + + existingConfigField + + ", conflict: " + + configField); + } + } else { + configFields.put(configField.name(), configField); + } + } + } + return configFields; + } + + public static Map collectHttpAuthSchemeParameters( + Collection httpAuthSchemes) { + Map httpAuthSchemeParameters = new HashMap<>(); + for (HttpAuthScheme authScheme : httpAuthSchemes) { + if (authScheme == null) { + continue; + } + for (HttpAuthSchemeParameter param : authScheme.getHttpAuthSchemeParameters()) { + if (httpAuthSchemeParameters.containsKey(param.name())) { + HttpAuthSchemeParameter existingParam = httpAuthSchemeParameters.get(param.name()); + if (!param.equals(existingParam)) { + throw new CodegenException("Contradicting `HttpAuthSchemeParameter` defintions for `" + + param.name() + + "`; existing: " + + existingParam + + ", conflict: " + + param); + } + } else { + httpAuthSchemeParameters.put(param.name(), param); + } + } + } + return httpAuthSchemeParameters; + } } 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 c7065fa02e6..7e53bc97340 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 @@ -49,6 +49,7 @@ public class HttpAuthSchemeProviderGenerator implements Runnable { private final ServiceShape serviceShape; private final Symbol serviceSymbol; private final String serviceName; + private final Map httpAuthSchemeParameters; /** * Create an HttpAuthSchemeProviderGenerator. @@ -73,6 +74,8 @@ public HttpAuthSchemeProviderGenerator( this.serviceShape = settings.getService(model); this.serviceSymbol = symbolProvider.toSymbol(serviceShape); this.serviceName = CodegenUtils.getServiceName(settings, model, symbolProvider); + this.httpAuthSchemeParameters = + AuthUtils.collectHttpAuthSchemeParameters(authIndex.getSupportedHttpAuthSchemes().values()); } @Override @@ -103,10 +106,8 @@ private void generateHttpAuthSchemeParametersInterface() { export interface $LHttpAuthSchemeParameters extends HttpAuthSchemeParameters {""", "}", serviceName, () -> { - for (HttpAuthScheme authScheme : authIndex.getSupportedHttpAuthSchemes().values()) { - for (HttpAuthSchemeParameter parameter : authScheme.getHttpAuthSchemeParameters()) { - w.write("$L?: $C;", parameter.name(), parameter.type()); - } + for (HttpAuthSchemeParameter parameter : httpAuthSchemeParameters.values()) { + w.write("$L?: $C;", parameter.name(), parameter.type()); } }); }); @@ -145,10 +146,8 @@ private void generateDefaultHttpAuthSchemeParametersProviderFunction() { () -> { w.openBlock("return {", "};", () -> { w.write("operation: getSmithyContext(context).operation as string,"); - for (HttpAuthScheme authScheme : authIndex.getSupportedHttpAuthSchemes().values()) { - for (HttpAuthSchemeParameter parameter : authScheme.getHttpAuthSchemeParameters()) { - w.write("$L: $C,", parameter.name(), parameter.source()); - } + for (HttpAuthSchemeParameter parameter : httpAuthSchemeParameters.values()) { + w.write("$L: $C,", parameter.name(), parameter.source()); } }); }); 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 index 47d46c69393..929894e43d6 100644 --- 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 @@ -59,15 +59,16 @@ public void customize(TypeScriptCodegenContext codegenContext) { ServiceShape serviceShape = codegenContext.settings().getService(codegenContext.model()); Map effectiveAuthSchemes = AuthUtils.getAllEffectiveNoAuthAwareAuthSchemes(serviceShape, serviceIndex, authIndex); + Map configFields = AuthUtils.collectConfigFields(effectiveAuthSchemes.values()); generateHttpAuthExtensionConfigurationInterface( - delegator, effectiveAuthSchemes, serviceName); + delegator, configFields, serviceName); generateHttpAuthExtensionRuntimeConfigType( - delegator, effectiveAuthSchemes, serviceName); + delegator, configFields, serviceName); generateGetHttpAuthExtensionConfigurationFunction( - delegator, effectiveAuthSchemes, serviceName); + delegator, configFields, serviceName); generateResolveHttpAuthRuntimeConfigFunction( - delegator, effectiveAuthSchemes, serviceName); + delegator, configFields, serviceName); } /* @@ -109,7 +110,7 @@ export interface HttpAuthExtensionConfiguration { */ private void generateHttpAuthExtensionConfigurationInterface( TypeScriptDelegator delegator, - Map effectiveAuthSchemes, + Map configFields, String serviceName ) { delegator.useFileWriter(AuthUtils.HTTP_AUTH_SCHEME_EXTENSION_PATH, w -> { @@ -130,14 +131,8 @@ export interface HttpAuthExtensionConfiguration {""", "}", 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; - } + for (ConfigField configField : configFields.values()) { + if (configField.type().equals(ConfigField.Type.MAIN)) { String capitalizedName = StringUtils.capitalize(configField.name()); w.write("set$L($L: $C): void;", capitalizedName, configField.name(), configField.inputType()); w.write("$L(): $C | undefined;", configField.name(), configField.inputType()); @@ -177,7 +172,7 @@ export interface HttpAuthExtensionConfiguration {""", "}", */ private void generateHttpAuthExtensionRuntimeConfigType( TypeScriptDelegator delegator, - Map effectiveAuthSchemes, + Map configFields, String serviceName ) { delegator.useFileWriter(AuthUtils.HTTP_AUTH_SCHEME_EXTENSION_PATH, w -> { @@ -193,14 +188,8 @@ private void generateHttpAuthExtensionRuntimeConfigType( 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; - } + for (ConfigField configField : configFields.values()) { + if (configField.type().equals(ConfigField.Type.MAIN)) { w.write("$L: $C;", configField.name(), configField.inputType()); } } @@ -269,7 +258,7 @@ private void generateHttpAuthExtensionRuntimeConfigType( */ private void generateGetHttpAuthExtensionConfigurationFunction( TypeScriptDelegator delegator, - Map effectiveAuthSchemes, + Map configFields, String serviceName ) { delegator.useFileWriter(AuthUtils.HTTP_AUTH_SCHEME_EXTENSION_PATH, w -> { @@ -286,14 +275,8 @@ private void generateGetHttpAuthExtensionConfigurationFunction( 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; - } + for (ConfigField configField : configFields.values()) { + if (configField.type().equals(ConfigField.Type.MAIN)) { w.write("let _$L = runtimeConfig.$L;", configField.name(), configField.name()); } } @@ -314,14 +297,8 @@ private void generateGetHttpAuthExtensionConfigurationFunction( 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; - } + for (ConfigField configField : configFields.values()) { + if (configField.type().equals(ConfigField.Type.MAIN)) { String capitalizedName = StringUtils.capitalize(configField.name()); w.write(""" set$L($L: $C): void { @@ -356,7 +333,7 @@ private void generateGetHttpAuthExtensionConfigurationFunction( */ private void generateResolveHttpAuthRuntimeConfigFunction( TypeScriptDelegator delegator, - Map effectiveAuthSchemes, + Map configFields, String serviceName ) { delegator.useFileWriter(AuthUtils.HTTP_AUTH_SCHEME_EXTENSION_PATH, w -> { @@ -370,14 +347,8 @@ private void generateResolveHttpAuthRuntimeConfigFunction( 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; - } + for (ConfigField configField : configFields.values()) { + if (configField.type().equals(ConfigField.Type.MAIN)) { w.write("$L: config.$L(),", configField.name(), configField.name()); } }