From 4e642fac419879365b01eae6c10563eb4b1ea8c5 Mon Sep 17 00:00:00 2001 From: skotambkar Date: Tue, 25 May 2021 17:20:45 -0700 Subject: [PATCH 1/2] add support to integrate and resolve members on the client struct via runtime plugins. Also update middleware add helper functions to be methods on client. --- .../smithy/go/codegen/OperationGenerator.java | 4 +- .../smithy/go/codegen/ServiceGenerator.java | 44 +++++- .../go/codegen/integration/ClientMember.java | 133 ++++++++++++++++++ .../integration/ClientMemberResolver.java | 79 +++++++++++ .../go/codegen/integration/GoIntegration.java | 16 --- .../integration/RuntimeClientPlugin.java | 102 ++++++++++++++ 6 files changed, 358 insertions(+), 20 deletions(-) create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/ClientMember.java create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/ClientMemberResolver.java diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java index ead1dbd46..02bfe34c2 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java @@ -112,7 +112,7 @@ public void run() { writer.write("if params == nil { params = &$T{} }", inputSymbol); writer.write(""); - writer.write("result, metadata, err := c.invokeOperation(ctx, $S, params, optFns, $L)", + writer.write("result, metadata, err := c.invokeOperation(ctx, $S, params, optFns, c.$L)", operationSymbol.getName(), getAddOperationMiddlewareFuncName(operationSymbol)); writer.write("if err != nil { return nil, err }"); writer.write(""); @@ -151,7 +151,7 @@ private void generateAddOperationMiddleware() { Symbol stackSymbol = SymbolUtils.createPointableSymbolBuilder("Stack", SmithyGoDependency.SMITHY_MIDDLEWARE) .build(); - writer.openBlock("func $L(stack $P, options Options) (err error) {", "}", + writer.openBlock("func (c *Client) $L(stack $P, options Options) (err error) {", "}", getAddOperationMiddlewareFuncName(operationSymbol), stackSymbol, () -> { generateOperationProtocolMiddlewareAdders(); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java index ec95a1400..8e68c727c 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java @@ -22,6 +22,8 @@ import java.util.stream.Collectors; import software.amazon.smithy.codegen.core.Symbol; import software.amazon.smithy.codegen.core.SymbolProvider; +import software.amazon.smithy.go.codegen.integration.ClientMember; +import software.amazon.smithy.go.codegen.integration.ClientMemberResolver; import software.amazon.smithy.go.codegen.integration.ConfigField; import software.amazon.smithy.go.codegen.integration.ConfigFieldResolver; import software.amazon.smithy.go.codegen.integration.GoIntegration; @@ -82,6 +84,13 @@ public void run() { CodegenUtils.getServiceTitle(service, "the API"))); writer.openBlock("type $T struct {", "}", serviceSymbol, () -> { writer.write("options $L", CONFIG_NAME); + + // Add client members resolved from runtime plugins to the client struct. + for (ClientMember clientMember : getAllClientMembers()) { + writer.write(""); + clientMember.getDocumentation().ifPresent(writer::writeDocs); + writer.write("$L $P", clientMember.getName(), clientMember.getType()); + } }); generateConstructor(serviceSymbol); @@ -89,6 +98,18 @@ public void run() { generateClientInvokeOperation(); } + private void writeClientMemberResolvers( + GoWriter writer, + RuntimeClientPlugin plugin, + Predicate predicate + ) { + plugin.getClientMemberResolvers().stream().filter(predicate) + .forEach(resolver -> { + writer.write("$T(client)", resolver.getResolver()); + writer.write(""); + }); + } + private void writeConfigFieldResolvers( GoWriter writer, RuntimeClientPlugin plugin, @@ -136,6 +157,11 @@ private void generateConstructor(Symbol serviceSymbol) { writer.write("options: options,"); }).write(""); + // Run any client member resolver functions registered by runtime plugins. + for (RuntimeClientPlugin plugin : plugins) { + writeClientMemberResolvers(writer, plugin, resolver -> true); + } + writer.write("return client"); }); } @@ -150,7 +176,7 @@ private void generateConfig() { .build(); writer.write("APIOptions []func($P) error", stackSymbol).write(""); - // Add config fields to the options struct. + // Add config fields to the options struct. - getAllClientMembers for (ConfigField configField : getAllConfigFields()) { configField.getDocumentation().ifPresent(writer::writeDocs); writer.write("$L $P", configField.getName(), configField.getType()); @@ -179,7 +205,6 @@ private void generateConfig() { writer.write("o.$L = v", configField.getName()); }); }).write(""); - }); generateApplicationProtocolTypes(); @@ -209,6 +234,21 @@ private List getAllConfigFields() { .collect(Collectors.toList()); } + private List getAllClientMembers() { + List clientMembers = new ArrayList<>(); + for (RuntimeClientPlugin runtimeClientPlugin : runtimePlugins) { + if (!runtimeClientPlugin.matchesService(model, service)) { + continue; + } + + clientMembers.addAll(runtimeClientPlugin.getClientMembers()); + } + return clientMembers.stream() + .distinct() + .sorted(Comparator.comparing(ClientMember::getName)) + .collect(Collectors.toList()); + } + private void generateApplicationProtocolConfig() { ensureSupportedProtocol(); writer.writeDocs( diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/ClientMember.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/ClientMember.java new file mode 100644 index 000000000..b2501c073 --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/ClientMember.java @@ -0,0 +1,133 @@ +/* + * 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.go.codegen.integration; + +import java.util.Objects; +import java.util.Optional; +import software.amazon.smithy.codegen.core.Symbol; +import software.amazon.smithy.utils.SmithyBuilder; +import software.amazon.smithy.utils.ToSmithyBuilder; + +/** + * Represents a member field on a client struct. + */ +public class ClientMember implements ToSmithyBuilder { + private final String name; + private final Symbol type; + private final String documentation; + + public ClientMember(Builder builder) { + this.name = Objects.requireNonNull(builder.name); + this.type = Objects.requireNonNull(builder.type); + this.documentation = builder.documentation; + } + + /** + * @return Returns the name of the client member field. + */ + public String getName() { + return name; + } + + /** + * @return Returns the type Symbol for the member field. + */ + public Symbol getType() { + return type; + } + + /** + * @return Gets the optional documentation for the member field. + */ + public Optional getDocumentation() { + return Optional.ofNullable(documentation); + } + + @Override + public SmithyBuilder toBuilder() { + return builder().type(type).name(name).documentation(documentation); + } + + public static Builder builder() { + return new Builder(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ClientMember that = (ClientMember) o; + return Objects.equals(getName(), that.getName()) + && Objects.equals(getType(), that.getType()) + && Objects.equals(getDocumentation(), that.getDocumentation()); + } + + @Override + public int hashCode() { + return Objects.hash(getName(), getType(), getDocumentation()); + } + + /** + * Builds a ClientMember. + */ + public static class Builder implements SmithyBuilder { + private String name; + private Symbol type; + private String documentation; + + @Override + public ClientMember build() { + return new ClientMember(this); + } + + /** + * Set the name of the member field on client. + * + * @param name is the name of the field on the client. + * @return Returns the builder. + */ + public Builder name(String name) { + this.name = name; + return this; + } + + /** + * Sets the type of the client field. + * + * @param type A Symbol representing the type of the client field. + * @return Returns the builder. + */ + public Builder type(Symbol type) { + this.type = type; + return this; + } + + /** + * Sets the documentation for the client field. + * + * @param documentation The documentation for the client field. + * @return Returns the builder. + */ + public Builder documentation(String documentation) { + this.documentation = documentation; + return this; + } + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/ClientMemberResolver.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/ClientMemberResolver.java new file mode 100644 index 000000000..fe81b3b99 --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/ClientMemberResolver.java @@ -0,0 +1,79 @@ +/* + * Copyright 2020 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.go.codegen.integration; + +import java.util.Objects; +import software.amazon.smithy.codegen.core.Symbol; +import software.amazon.smithy.utils.SmithyBuilder; + +/** + * Represent symbol that points to a function that operates + * on the client member fields during client construction. + * + * Any configuration that a plugin requires in order to function should be + * checked in this function, either setting a default value if possible or + * returning an error if not. + */ +public final class ClientMemberResolver { + private final Symbol resolver; + + private ClientMemberResolver(Builder builder) { + resolver = SmithyBuilder.requiredState("resolver", builder.resolver); + } + + public Symbol getResolver() { + return resolver; + } + + public static Builder builder() { + return new Builder(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ClientMemberResolver that = (ClientMemberResolver) o; + return resolver.equals(that.resolver); + } + + /** + * Returns a hash code value for the object. + * @return the hash code. + */ + @Override + public int hashCode() { + return Objects.hash(resolver); + } + + public static class Builder implements SmithyBuilder { + private Symbol resolver; + + public Builder resolver(Symbol resolver) { + this.resolver = resolver; + return this; + } + + @Override + public ClientMemberResolver build() { + return new ClientMemberResolver(this); + } + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/GoIntegration.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/GoIntegration.java index 5790d82aa..d08d36572 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/GoIntegration.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/GoIntegration.java @@ -153,22 +153,6 @@ default List getProtocolGenerators() { return Collections.emptyList(); } - /** - * Adds additional client config interface fields. - * - * @param settings Settings used to generate. - * @param model Model to generate from. - * @param symbolProvider Symbol provider used for codegen. - * @param writer TypeScript writer to write to. - */ - default void addConfigInterfaceFields( - GoSettings settings, - Model model, - SymbolProvider symbolProvider, - GoWriter writer - ) { - // pass - } /** * Processes the finalized model before runtime plugins are consumed and diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/RuntimeClientPlugin.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/RuntimeClientPlugin.java index 0c581fa3a..8b3b7075e 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/RuntimeClientPlugin.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/RuntimeClientPlugin.java @@ -41,6 +41,8 @@ public final class RuntimeClientPlugin implements ToSmithyBuilder configFields; private final Set configFieldResolvers; + private final Set clientMembers; + private final Set clientMemberResolvers; private final MiddlewareRegistrar registerMiddleware; private RuntimeClientPlugin(Builder builder) { @@ -48,6 +50,8 @@ private RuntimeClientPlugin(Builder builder) { servicePredicate = builder.servicePredicate; configFields = builder.configFields; registerMiddleware = builder.registerMiddleware; + clientMembers = builder.clientMembers; + clientMemberResolvers = builder.clientMemberResolvers; configFieldResolvers = builder.configFieldResolvers; } @@ -73,6 +77,14 @@ public Set getConfigFieldResolvers() { return configFieldResolvers; } + /** + * Gets the client members that will be added to the client structure by this plugin. + * @return the client member resolvers. + */ + public Set getClientMemberResolvers() { + return clientMemberResolvers; + } + /** * Gets the optionally present middleware registrar object that resolves to middleware registering function. * @@ -135,6 +147,27 @@ public Set getConfigFields() { return configFields; } + /** + * Gets the client member fields that will be added to the client structure by this plugin. + * + *

Each client member field will be added to the client's structure. + * E.g.: + *

+ * type Client struct { + * + * options Options + * + * // My cache. + * cache map[string]string + * } + *

+ * + * @return Returns the client members to add to the client structure. + */ + public Set getClientMembers() { + return clientMembers; + } + public static Builder builder() { return new Builder(); } @@ -142,6 +175,7 @@ public static Builder builder() { @Override public SmithyBuilder toBuilder() { return builder() + .clientMemberResolvers(clientMemberResolvers) .configFieldResolvers(configFieldResolvers) .servicePredicate(servicePredicate) .operationPredicate(operationPredicate) @@ -156,6 +190,8 @@ public static final class Builder implements SmithyBuilder private OperationPredicate operationPredicate = (model, service, operation) -> false; private Set configFields = new HashSet<>(); private Set configFieldResolvers = new HashSet<>(); + private Set clientMembers = new HashSet<>(); + private Set clientMemberResolvers = new HashSet<>(); private MiddlewareRegistrar registerMiddleware; @Override @@ -301,5 +337,71 @@ public Builder addConfigFieldResolver(ConfigFieldResolver configFieldResolver) { this.configFieldResolvers.add(configFieldResolver); return this; } + + /** + * Sets the client member fields that will be added to the client struct + * by this plugin. + * + *

Each client member field will be added to the client's struct. + * E.g.: + *

+ * type Client struct { + * option Options + * + * // My cache added using plugin + * cache map[string]string + * } + *

+ * + * @param clientMembers The client members to add on the client. + * @return Returns the builder. + */ + public Builder clientMembers(Collection clientMembers) { + this.clientMembers = new HashSet<>(clientMembers); + return this; + } + + /** + * Adds a client member that will be added to the client structure by this plugin. + * + *

Each client member field will be added to the client's structure. + * E.g.: + *

+ * type Client struct { + * option Options + * + * // my cache added using plugin + * cache map[string]string + * } + * + * @param clientMember The clientMember to add to the client structure. + * @return Returns the builder. + */ + public Builder addClientMember(ClientMember clientMember) { + this.clientMembers.add(clientMember); + return this; + } + + /** + * Sets the client member resolvers that will be added to the client by this plugin. + * + * @param clientMemberResolvers The client member resolvers. + * @return Returns the builder. + */ + public Builder clientMemberResolvers(Collection clientMemberResolvers) { + this.clientMemberResolvers = new HashSet<>(clientMemberResolvers); + return this; + } + + /** + * Adds a client member resolver that will be added to the client by this plugin. + * + * @param clientMemberResolver The client member resolver. + * @return Returns the builder. + */ + public Builder addClientMemberResolver(ClientMemberResolver clientMemberResolver) { + this.clientMemberResolvers.add(clientMemberResolver); + return this; + } } } From d1e0d12ffecf4311568b7ad9272dd38e91ef723e Mon Sep 17 00:00:00 2001 From: skotambkar Date: Wed, 26 May 2021 10:41:08 -0700 Subject: [PATCH 2/2] minor comment fix --- .../software/amazon/smithy/go/codegen/ServiceGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java index 8e68c727c..321bed5a9 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java @@ -176,7 +176,7 @@ private void generateConfig() { .build(); writer.write("APIOptions []func($P) error", stackSymbol).write(""); - // Add config fields to the options struct. - getAllClientMembers + // Add config fields to the options struct. for (ConfigField configField : getAllConfigFields()) { configField.getDocumentation().ifPresent(writer::writeDocs); writer.write("$L $P", configField.getName(), configField.getType());