From 7d82b5a94ed4cbe00d9cafefdbcdcd8fa9d36e90 Mon Sep 17 00:00:00 2001 From: Steven Yuan Date: Mon, 29 Jan 2024 12:15:03 -0800 Subject: [PATCH] temp --- .../smithy-go-codegen-test/build.gradle.kts | 1 + .../smithy-go-codegen-test/model/main.smithy | 1 + .../smithy-go-codegen-test/smithy-build.json | 7 + .../smithy/go/codegen/CodegenVisitor.java | 7 +- .../smithy/go/codegen/EnumGenerator.java | 6 +- .../amazon/smithy/go/codegen/GoDelegator.java | 4 +- .../smithy/go/codegen/GoModGenerator.java | 6 +- .../amazon/smithy/go/codegen/GoSettings.java | 25 +- .../amazon/smithy/go/codegen/GoWriter.java | 2 +- .../smithy/go/codegen/IntEnumGenerator.java | 6 +- .../smithy/go/codegen/ManifestWriter.java | 2 + .../smithy/go/codegen/StructureGenerator.java | 6 +- .../smithy/go/codegen/SymbolVisitor.java | 6 +- .../smithy/go/codegen/UnionGenerator.java | 4 +- .../go/codegen/integration/GoIntegration.java | 5 + .../service/GoServerCodegenPlugin.java | 71 ++++ .../codegen/service/GoServerIntegration.java | 34 ++ .../codegen/service/ServerCodegenVisitor.java | 352 ++++++++++++++++++ ...ator.java => ServerProtocolGenerator.java} | 12 +- .../service/TmpCodegenIntegration.java | 44 ++- ...ware.amazon.smithy.build.SmithyBuildPlugin | 1 + ...mithy.go.codegen.integration.GoIntegration | 2 +- 22 files changed, 582 insertions(+), 22 deletions(-) create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/GoServerCodegenPlugin.java create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/GoServerIntegration.java create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/ServerCodegenVisitor.java rename codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/{ProtocolGenerator.java => ServerProtocolGenerator.java} (74%) diff --git a/codegen/smithy-go-codegen-test/build.gradle.kts b/codegen/smithy-go-codegen-test/build.gradle.kts index 5b8a4f29b..319499c67 100644 --- a/codegen/smithy-go-codegen-test/build.gradle.kts +++ b/codegen/smithy-go-codegen-test/build.gradle.kts @@ -43,5 +43,6 @@ repositories { dependencies { implementation("software.amazon.smithy:smithy-protocol-test-traits:$smithyVersion") + implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion") implementation(project(":smithy-go-codegen")) } diff --git a/codegen/smithy-go-codegen-test/model/main.smithy b/codegen/smithy-go-codegen-test/model/main.smithy index d41e76f8b..5bfeb1c7a 100644 --- a/codegen/smithy-go-codegen-test/model/main.smithy +++ b/codegen/smithy-go-codegen-test/model/main.smithy @@ -8,6 +8,7 @@ use smithy.waiters#waitable /// Provides weather forecasts. @httpBearerAuth @fakeProtocol +@aws.protocols#awsJson1_0 @paginated(inputToken: "nextToken", outputToken: "nextToken", pageSize: "pageSize") service Weather { version: "2006-03-01", diff --git a/codegen/smithy-go-codegen-test/smithy-build.json b/codegen/smithy-go-codegen-test/smithy-build.json index b495c9292..9f7bd5308 100644 --- a/codegen/smithy-go-codegen-test/smithy-build.json +++ b/codegen/smithy-go-codegen-test/smithy-build.json @@ -7,6 +7,13 @@ "moduleVersion": "0.0.1", "generateGoMod": true, "goDirective": "1.18" + }, + "go-server-codegen": { + "service": "example.weather#Weather", + "module": "github.com/aws/smithy-go/internal/tests/service/weather", + "moduleVersion": "0.0.1", + "generateGoMod": true, + "goDirective": "1.18" } } } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java index ee26839c4..858060caa 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java @@ -32,6 +32,7 @@ import software.amazon.smithy.codegen.core.Symbol; import software.amazon.smithy.codegen.core.SymbolDependency; import software.amazon.smithy.codegen.core.SymbolProvider; +import software.amazon.smithy.go.codegen.GoSettings.ArtifactType; import software.amazon.smithy.go.codegen.integration.GoIntegration; import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; import software.amazon.smithy.go.codegen.integration.RuntimeClientPlugin; @@ -79,8 +80,10 @@ final class CodegenVisitor extends ShapeVisitor.Default { LOGGER.info("Attempting to discover GoIntegration from the classpath..."); ServiceLoader.load(GoIntegration.class, loader) .forEach(integration -> { - LOGGER.info(() -> "Adding GoIntegration: " + integration.getClass().getName()); - integrations.add(integration); + if (integration.getArtifactType().equals(ArtifactType.CLIENT)) { + LOGGER.info(() -> "Adding GoIntegration: " + integration.getClass().getName()); + integrations.add(integration); + } }); integrations.sort(Comparator.comparingInt(GoIntegration::getOrder)); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/EnumGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/EnumGenerator.java index e689d0aac..496ffffce 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/EnumGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/EnumGenerator.java @@ -24,19 +24,21 @@ import software.amazon.smithy.model.shapes.StringShape; import software.amazon.smithy.model.traits.EnumDefinition; import software.amazon.smithy.model.traits.EnumTrait; +import software.amazon.smithy.utils.SmithyInternalApi; import software.amazon.smithy.utils.StringUtils; /** * Renders enums and their constants. */ -final class EnumGenerator implements Runnable { +@SmithyInternalApi +public final class EnumGenerator implements Runnable { private static final Logger LOGGER = Logger.getLogger(EnumGenerator.class.getName()); private final SymbolProvider symbolProvider; private final GoWriter writer; private final StringShape shape; - EnumGenerator(SymbolProvider symbolProvider, GoWriter writer, StringShape shape) { + public EnumGenerator(SymbolProvider symbolProvider, GoWriter writer, StringShape shape) { this.symbolProvider = symbolProvider; this.writer = writer; this.shape = shape; diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoDelegator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoDelegator.java index 10494d411..d6270c2ee 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoDelegator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoDelegator.java @@ -21,15 +21,17 @@ import software.amazon.smithy.codegen.core.SymbolProvider; import software.amazon.smithy.codegen.core.SymbolReference; import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.utils.SmithyUnstableApi; /** * Manages writers for Go files.Based off of GoWriterDelegator adding support * for getting shape specific GoWriters. */ +@SmithyUnstableApi public final class GoDelegator extends GoWriterDelegator { private final SymbolProvider symbolProvider; - GoDelegator(FileManifest fileManifest, SymbolProvider symbolProvider) { + public GoDelegator(FileManifest fileManifest, SymbolProvider symbolProvider) { super(fileManifest); this.symbolProvider = symbolProvider; diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoModGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoModGenerator.java index 93208adf5..a4b6f81e2 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoModGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoModGenerator.java @@ -22,19 +22,21 @@ import java.util.logging.Logger; import software.amazon.smithy.build.FileManifest; import software.amazon.smithy.codegen.core.CodegenException; +import software.amazon.smithy.utils.SmithyInternalApi; /** * Generates a go.mod file for the project. * *

See here for more information on the format: https://github.com/golang/go/wiki/Modules#gomod */ -final class GoModGenerator { +@SmithyInternalApi +public final class GoModGenerator { private static final Logger LOGGER = Logger.getLogger(GoModGenerator.class.getName()); private GoModGenerator() {} - static void writeGoMod( + public static void writeGoMod( GoSettings settings, FileManifest manifest, GoModuleInfo goModuleInfo diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoSettings.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoSettings.java index c9f44ac48..8de2d4866 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoSettings.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoSettings.java @@ -25,6 +25,7 @@ import software.amazon.smithy.model.node.ObjectNode; import software.amazon.smithy.model.shapes.ServiceShape; import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.utils.SmithyInternalApi; /** * Settings used by {@link GoCodegenPlugin}. @@ -45,6 +46,13 @@ public final class GoSettings { private Boolean generateGoMod = false; private String goDirective = GoModuleInfo.DEFAULT_GO_DIRECTIVE; private ShapeId protocol; + private ArtifactType artifactType; + + @SmithyInternalApi + public enum ArtifactType { + CLIENT, + SERVER; + } /** * Create a settings object from a configuration object node. @@ -53,10 +61,15 @@ public final class GoSettings { * @return Returns the extracted settings. */ public static GoSettings from(ObjectNode config) { + return from(config, ArtifactType.CLIENT); + } + + @SmithyInternalApi + public static GoSettings from(ObjectNode config, ArtifactType artifactType) { GoSettings settings = new GoSettings(); config.warnIfAdditionalProperties( Arrays.asList(SERVICE, MODULE_NAME, MODULE_DESCRIPTION, MODULE_VERSION, GENERATE_GO_MOD, GO_DIRECTIVE)); - + settings.setArtifactType(artifactType); settings.setService(config.expectStringMember(SERVICE).expectShapeId()); settings.setModuleName(config.expectStringMember(MODULE_NAME).getValue()); settings.setModuleDescription(config.getStringMemberOrDefault( @@ -241,4 +254,14 @@ public ShapeId resolveServiceProtocol( public void setProtocol(ShapeId protocol) { this.protocol = Objects.requireNonNull(protocol); } + + @SmithyInternalApi + public ArtifactType getArtifactType() { + return artifactType; + } + + @SmithyInternalApi + public void setArtifactType(ArtifactType artifactType) { + this.artifactType = artifactType; + } } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoWriter.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoWriter.java index e5cd6b83f..d0e5a297f 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoWriter.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoWriter.java @@ -788,7 +788,7 @@ boolean writeShapeDocs(Shape shape) { * @param shape Shape to write the documentation of. * @return Returns true if docs were written. */ - boolean writePackageShapeDocs(Shape shape) { + public boolean writePackageShapeDocs(Shape shape) { return shape.getTrait(DocumentationTrait.class) .map(DocumentationTrait::getValue) .map(docs -> { diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/IntEnumGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/IntEnumGenerator.java index cf4059183..e211ba74a 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/IntEnumGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/IntEnumGenerator.java @@ -26,19 +26,21 @@ import software.amazon.smithy.model.shapes.MemberShape; import software.amazon.smithy.model.traits.DocumentationTrait; import software.amazon.smithy.model.traits.EnumValueTrait; +import software.amazon.smithy.utils.SmithyInternalApi; import software.amazon.smithy.utils.StringUtils; /** * Renders intEnums and their constants. */ -final class IntEnumGenerator implements Runnable { +@SmithyInternalApi +public final class IntEnumGenerator implements Runnable { private static final Logger LOGGER = Logger.getLogger(IntEnumGenerator.class.getName()); private final SymbolProvider symbolProvider; private final GoWriter writer; private final IntEnumShape shape; - IntEnumGenerator(SymbolProvider symbolProvider, GoWriter writer, IntEnumShape shape) { + public IntEnumGenerator(SymbolProvider symbolProvider, GoWriter writer, IntEnumShape shape) { this.symbolProvider = symbolProvider; this.writer = writer; this.shape = shape; diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ManifestWriter.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ManifestWriter.java index 0cbeb50a0..38c09e2f3 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ManifestWriter.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ManifestWriter.java @@ -34,11 +34,13 @@ import software.amazon.smithy.model.node.StringNode; import software.amazon.smithy.model.traits.UnstableTrait; import software.amazon.smithy.utils.SmithyBuilder; +import software.amazon.smithy.utils.SmithyInternalApi; /** * Generates a manifest description of the generated code, minimum go version, * and minimum dependencies required. */ +@SmithyInternalApi public final class ManifestWriter { private static final Logger LOGGER = Logger.getLogger(ManifestWriter.class.getName()); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java index e8c838772..cb2a12ca2 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java @@ -28,11 +28,13 @@ import software.amazon.smithy.model.traits.StreamingTrait; import software.amazon.smithy.utils.MapUtils; import software.amazon.smithy.utils.SetUtils; +import software.amazon.smithy.utils.SmithyInternalApi; /** * Renders structures. */ -final class StructureGenerator implements Runnable { +@SmithyInternalApi +public final class StructureGenerator implements Runnable { private static final Map STANDARD_ERROR_MEMBERS = MapUtils.of( "ErrorCode", "string", "ErrorMessage", "string", @@ -48,7 +50,7 @@ final class StructureGenerator implements Runnable { private final ServiceShape service; private final ProtocolGenerator protocolGenerator; - StructureGenerator( + public StructureGenerator( Model model, SymbolProvider symbolProvider, GoWriter writer, diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SymbolVisitor.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SymbolVisitor.java index e60fd0267..342dd473b 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SymbolVisitor.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SymbolVisitor.java @@ -62,6 +62,7 @@ import software.amazon.smithy.model.traits.EnumTrait; import software.amazon.smithy.model.traits.ErrorTrait; import software.amazon.smithy.model.traits.StreamingTrait; +import software.amazon.smithy.utils.SmithyInternalApi; import software.amazon.smithy.utils.StringUtils; /** @@ -70,7 +71,8 @@ *

Reserved words for Go are automatically escaped so that they are * suffixed with "_". See "reserved-words.txt" for the list of words. */ -final class SymbolVisitor implements SymbolProvider, ShapeVisitor { +@SmithyInternalApi +public final class SymbolVisitor implements SymbolProvider, ShapeVisitor { private static final Logger LOGGER = Logger.getLogger(SymbolVisitor.class.getName()); private final Model model; @@ -82,7 +84,7 @@ final class SymbolVisitor implements SymbolProvider, ShapeVisitor { private final GoPointableIndex pointableIndex; private final GoSettings settings; - SymbolVisitor(Model model, GoSettings settings) { + public SymbolVisitor(Model model, GoSettings settings) { this.model = model; this.settings = settings; this.rootModuleName = settings.getModuleName(); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java index 0e4b87227..53aa06264 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java @@ -29,10 +29,12 @@ import software.amazon.smithy.model.shapes.UnionShape; import software.amazon.smithy.model.traits.ErrorTrait; import software.amazon.smithy.model.traits.StreamingTrait; +import software.amazon.smithy.utils.SmithyInternalApi; /** * Renders unions and type aliases for all their members. */ +@SmithyInternalApi public class UnionGenerator { public static final String UNKNOWN_MEMBER_NAME = "UnknownUnionMember"; @@ -41,7 +43,7 @@ public class UnionGenerator { private final UnionShape shape; private final boolean isEventStream; - UnionGenerator(Model model, SymbolProvider symbolProvider, UnionShape shape) { + public UnionGenerator(Model model, SymbolProvider symbolProvider, UnionShape shape) { this.model = model; this.symbolProvider = symbolProvider; this.shape = shape; 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 077c43ff9..cfae677e0 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 @@ -21,6 +21,7 @@ import software.amazon.smithy.codegen.core.SymbolProvider; import software.amazon.smithy.go.codegen.GoDelegator; import software.amazon.smithy.go.codegen.GoSettings; +import software.amazon.smithy.go.codegen.GoSettings.ArtifactType; import software.amazon.smithy.go.codegen.GoWriter; import software.amazon.smithy.go.codegen.TriConsumer; import software.amazon.smithy.model.Model; @@ -49,6 +50,10 @@ default byte getOrder() { return 0; } + default ArtifactType getArtifactType() { + return ArtifactType.CLIENT; + } + /** * Preprocess the model before code generation. * diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/GoServerCodegenPlugin.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/GoServerCodegenPlugin.java new file mode 100644 index 000000000..ce6008f9a --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/GoServerCodegenPlugin.java @@ -0,0 +1,71 @@ +/* + * Copyright 2024 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.service; + +import java.util.logging.Logger; +import software.amazon.smithy.build.PluginContext; +import software.amazon.smithy.build.SmithyBuildPlugin; +import software.amazon.smithy.codegen.core.SymbolProvider; +import software.amazon.smithy.go.codegen.GoSettings; +import software.amazon.smithy.go.codegen.GoSettings.ArtifactType; +import software.amazon.smithy.go.codegen.SymbolVisitor; +import software.amazon.smithy.model.Model; + +/** + * Plugin to trigger Go code generation. + */ +public final class GoServerCodegenPlugin implements SmithyBuildPlugin { + private static final Logger LOGGER = Logger.getLogger(GoServerCodegenPlugin.class.getName()); + + @Override + public String getName() { + return "go-server-codegen"; + } + + @Override + public void execute(PluginContext context) { + String onlyBuild = System.getenv("SMITHY_GO_BUILD_API"); + if (onlyBuild != null && !onlyBuild.isEmpty()) { + String targetServiceId = + GoSettings.from(context.getSettings(), ArtifactType.SERVER).getService().toString(); + + boolean found = false; + for (String includeServiceId : onlyBuild.split(",")) { + if (targetServiceId.startsWith(includeServiceId)) { + found = true; + break; + } + } + if (!found) { + LOGGER.info("skipping " + targetServiceId); + return; + } + } + + new ServerCodegenVisitor(context).execute(); + } + + /** + * Creates a Go symbol provider. + * + * @param model The model to generate symbols for. + * @param settings The Gosettings to use to create symbol provider + * @return Returns the created provider. + */ + public static SymbolProvider createSymbolProvider(Model model, GoSettings settings) { + return new SymbolVisitor(model, settings); + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/GoServerIntegration.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/GoServerIntegration.java new file mode 100644 index 000000000..c61dfdb4f --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/GoServerIntegration.java @@ -0,0 +1,34 @@ +/* + * Copyright 2024 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.service; + +import java.util.Collections; +import java.util.List; +import software.amazon.smithy.go.codegen.GoSettings.ArtifactType; +import software.amazon.smithy.go.codegen.integration.GoIntegration; +import software.amazon.smithy.utils.SmithyInternalApi; + +@SmithyInternalApi +public interface GoServerIntegration extends GoIntegration { + @Override + default ArtifactType getArtifactType() { + return ArtifactType.SERVER; + } + + default List getServerProtocolGenerators() { + return Collections.emptyList(); + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/ServerCodegenVisitor.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/ServerCodegenVisitor.java new file mode 100644 index 000000000..36419ebd4 --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/ServerCodegenVisitor.java @@ -0,0 +1,352 @@ +/* + * Copyright 2024 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.service; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.TreeSet; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import software.amazon.smithy.build.FileManifest; +import software.amazon.smithy.build.PluginContext; +import software.amazon.smithy.codegen.core.Symbol; +import software.amazon.smithy.codegen.core.SymbolDependency; +import software.amazon.smithy.codegen.core.SymbolProvider; +import software.amazon.smithy.go.codegen.AddOperationShapes; +import software.amazon.smithy.go.codegen.ApplicationProtocol; +import software.amazon.smithy.go.codegen.CodegenUtils; +import software.amazon.smithy.go.codegen.EnumGenerator; +import software.amazon.smithy.go.codegen.EventStreamGenerator; +import software.amazon.smithy.go.codegen.GoDelegator; +import software.amazon.smithy.go.codegen.GoModGenerator; +import software.amazon.smithy.go.codegen.GoModuleInfo; +import software.amazon.smithy.go.codegen.GoSettings; +import software.amazon.smithy.go.codegen.GoSettings.ArtifactType; +import software.amazon.smithy.go.codegen.IntEnumGenerator; +import software.amazon.smithy.go.codegen.ManifestWriter; +import software.amazon.smithy.go.codegen.ProtocolDocumentGenerator; +import software.amazon.smithy.go.codegen.StructureGenerator; +import software.amazon.smithy.go.codegen.UnionGenerator; +import software.amazon.smithy.go.codegen.integration.GoIntegration; +import software.amazon.smithy.go.codegen.integration.RuntimeClientPlugin; +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.neighbor.Walker; +import software.amazon.smithy.model.shapes.IntEnumShape; +import software.amazon.smithy.model.shapes.OperationShape; +import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.shapes.ShapeVisitor; +import software.amazon.smithy.model.shapes.StringShape; +import software.amazon.smithy.model.shapes.StructureShape; +import software.amazon.smithy.model.shapes.UnionShape; +import software.amazon.smithy.model.traits.EnumTrait; +import software.amazon.smithy.model.transform.ModelTransformer; +import software.amazon.smithy.utils.OptionalUtils; + +/** + * Orchestrates Go client generation. + */ +final class ServerCodegenVisitor extends ShapeVisitor.Default { + + private static final Logger LOGGER = Logger.getLogger(ServerCodegenVisitor.class.getName()); + + private final GoSettings settings; + private final Model model; + private final Model modelWithoutTraitShapes; + private final ServiceShape service; + private final FileManifest fileManifest; + private final SymbolProvider symbolProvider; + private final GoDelegator writers; + private final List integrations = new ArrayList<>(); + private final ServerProtocolGenerator protocolGenerator; + private final ApplicationProtocol applicationProtocol; + private final List runtimePlugins = new ArrayList<>(); + private final ProtocolDocumentGenerator protocolDocumentGenerator; + private final EventStreamGenerator eventStreamGenerator; + + ServerCodegenVisitor(PluginContext context) { + // Load all integrations. + ClassLoader loader = context.getPluginClassLoader().orElse(getClass().getClassLoader()); + LOGGER.info("Attempting to discover GoServerIntegration from the classpath..."); + ServiceLoader.load(GoIntegration.class, loader) + .forEach(integration -> { + if (integration.getArtifactType().equals(ArtifactType.SERVER)) { + LOGGER.info(() -> "Adding GoIntegration: " + integration.getClass().getName()); + integrations.add((GoServerIntegration) integration); + } + }); + integrations.sort(Comparator.comparingInt(GoServerIntegration::getOrder)); + + settings = GoSettings.from(context.getSettings()); + fileManifest = context.getFileManifest(); + + Model resolvedModel = context.getModel(); + + var modelTransformer = ModelTransformer.create(); + + /* + * smithy 1.23.0 added support for mixins. This transform flattens and applies + * the mixins + * and remove them from the model + */ + resolvedModel = modelTransformer.flattenAndRemoveMixins(resolvedModel); + + // Add unique operation input/output shapes + resolvedModel = AddOperationShapes.execute(resolvedModel, settings.getService()); + + /* + * smithy 1.12.0 added support for binding common errors to the service shape + * this transform copies these common errors to the operations + */ + resolvedModel = modelTransformer.copyServiceErrorsToOperations(resolvedModel, + settings.getService(resolvedModel)); + + LOGGER.info(() -> "Preprocessing smithy model"); + for (GoServerIntegration goIntegration : integrations) { + resolvedModel = goIntegration.preprocessModel(resolvedModel, settings); + } + + model = resolvedModel; + + // process final model + integrations.forEach(integration -> { + integration.processFinalizedModel(settings, model); + }); + + // fetch runtime plugins + integrations.forEach(integration -> { + integration.getClientPlugins().forEach(runtimePlugin -> { + LOGGER.info(() -> "Adding Go runtime plugin: " + runtimePlugin); + runtimePlugins.add(runtimePlugin); + }); + }); + + modelWithoutTraitShapes = modelTransformer.getModelWithoutTraitShapes(model); + + service = settings.getService(model); + LOGGER.info(() -> "Generating Go server for service " + service.getId()); + + SymbolProvider resolvedProvider = GoServerCodegenPlugin.createSymbolProvider(model, settings); + for (GoServerIntegration integration : integrations) { + resolvedProvider = integration.decorateSymbolProvider(settings, model, resolvedProvider); + } + symbolProvider = resolvedProvider; + + protocolGenerator = resolveProtocolGenerator(integrations, model, service, settings); + applicationProtocol = protocolGenerator.getApplicationProtocol(); + + writers = new GoDelegator(fileManifest, symbolProvider); + + // TODO(SSDK): do we need this? + protocolDocumentGenerator = new ProtocolDocumentGenerator(settings, model, writers); + + // TODO(SSDK): do we need this? + this.eventStreamGenerator = new EventStreamGenerator(settings, model, writers, symbolProvider, service); + } + + private static ServerProtocolGenerator resolveProtocolGenerator( + Collection integrations, + Model model, + ServiceShape service, + GoSettings settings + ) { + // Collect all the supported protocol generators. + Map generators = new HashMap<>(); + for (GoServerIntegration integration : integrations) { + for (ServerProtocolGenerator generator : integration.getServerProtocolGenerators()) { + generators.put(generator.getProtocol(), generator); + } + } + + ServiceIndex serviceIndex = ServiceIndex.of(model); + + ShapeId protocolTrait = settings.resolveServiceProtocol(serviceIndex, service, generators.keySet()); + settings.setProtocol(protocolTrait); + return generators.get(protocolTrait); + } + + void execute() { + // Generate models that are connected to the service being generated. + LOGGER.fine("Walking shapes from " + service.getId() + " to find shapes to generate"); + Set serviceShapes = new TreeSet<>(new Walker(modelWithoutTraitShapes).walkShapes(service)); + + for (Shape shape : serviceShapes) { + shape.accept(this); + } + + // Generate any required types and functions need to support protocol documents. + protocolDocumentGenerator.generateDocumentSupport(); + + // Generate a struct to handle unknown tags in unions + List unions = serviceShapes.stream() + .map(Shape::asUnionShape) + .flatMap(OptionalUtils::stream) + .collect(Collectors.toList()); + if (!unions.isEmpty()) { + writers.useShapeWriter(unions.get(0), writer -> { + UnionGenerator.generateUnknownUnion(writer, unions, symbolProvider); + }); + } + + for (GoServerIntegration integration : integrations) { + integration.writeAdditionalFiles(settings, model, symbolProvider, writers::useFileWriter); + integration.writeAdditionalFiles(settings, model, symbolProvider, writers); + } + + eventStreamGenerator.generateEventStreamInterfaces(); + TopDownIndex.of(model).getContainedOperations(service) + .forEach(eventStreamGenerator::generateOperationEventStreamStructure); + + if (protocolGenerator != null) { + LOGGER.info("Generating serde for protocol " + protocolGenerator.getProtocol() + " on " + service.getId()); + // ProtocolGenerator.GenerationContext.Builder contextBuilder = + // ProtocolGenerator.GenerationContext.builder() + // .protocolName(protocolGenerator.getProtocolName()) + // .integrations(integrations) + // .model(model) + // .service(service) + // .settings(settings) + // .symbolProvider(symbolProvider) + // .delegator(writers); + + LOGGER.info("Generating serde for protocol " + protocolGenerator.getProtocol() + + " on " + service.getId()); + // TODO(SSDK): serialize output -> http.Response + // writers.useFileWriter("serializers.go", settings.getModuleName(), writer -> { + // ProtocolGenerator.GenerationContext context = contextBuilder.writer(writer).build(); + // protocolGenerator.generateRequestSerializers(context); + // protocolGenerator.generateSharedSerializerComponents(context); + // }); + + // TODO(SSDK): serialize http.Request -> input + // writers.useFileWriter("deserializers.go", settings.getModuleName(), writer -> { + // ProtocolGenerator.GenerationContext context = contextBuilder.writer(writer).build(); + // protocolGenerator.generateResponseDeserializers(context); + // protocolGenerator.generateSharedDeserializerComponents(context); + // }); + + // TODO(SSDK): need to implement generateEventStreamComponents in the ProtocolGenerator? + // if (eventStreamGenerator.hasEventStreamOperations()) { + // eventStreamGenerator.writeEventStreamImplementation(writer -> { + // ProtocolGenerator.GenerationContext context = contextBuilder.writer(writer).build(); + // protocolGenerator.generateEventStreamComponents(context); + // }); + // } + + // TODO(SSDK): removed the following, but need to review: + // - endpoints.go + // - auth.go + // - endpoints_test.go + // - protocol_test.go + + // TODO(SSDK): see if we need this + // protocolDocumentGenerator.generateInternalDocumentTypes(protocolGenerator, contextBuilder.build()); + } + + LOGGER.fine("Flushing go writers"); + List dependencies = writers.getDependencies(); + writers.flushWriters(); + + GoModuleInfo goModuleInfo = new GoModuleInfo.Builder() + .goDirective(settings.getGoDirective()) + .dependencies(dependencies) + .build(); + + GoModGenerator.writeGoMod(settings, fileManifest, goModuleInfo); + + LOGGER.fine("Generating build manifest file"); + ManifestWriter.writeManifest(settings, model, fileManifest, goModuleInfo); + } + + @Override + protected Void getDefault(Shape shape) { + return null; + } + + @Override + public Void structureShape(StructureShape shape) { + if (shape.getId().getNamespace().equals(CodegenUtils.getSyntheticTypeNamespace())) { + return null; + } + Symbol symbol = symbolProvider.toSymbol(shape); + writers.useShapeWriter(shape, writer -> new StructureGenerator( + model, symbolProvider, writer, service, shape, symbol, null).run()); + return null; + } + + @Override + public Void stringShape(StringShape shape) { + if (shape.hasTrait(EnumTrait.class)) { + writers.useShapeWriter(shape, writer -> new EnumGenerator(symbolProvider, writer, shape).run()); + } + return null; + } + + @Override + public Void unionShape(UnionShape shape) { + UnionGenerator generator = new UnionGenerator(model, symbolProvider, shape); + writers.useShapeWriter(shape, generator::generateUnion); + writers.useShapeExportedTestWriter(shape, generator::generateUnionExamples); + return null; + } + + @Override + public Void serviceShape(ServiceShape shape) { + if (!Objects.equals(service, shape)) { + LOGGER.fine(() -> "Skipping `" + shape.getId() + "` because it is not `" + service.getId() + "`"); + return null; + } + + // Write API server's package doc for the service. + writers.useFileWriter("doc.go", settings.getModuleName(), (writer) -> { + writer.writePackageDocs(String.format( + "Package %s provides the API server, operations, and parameter types for %s.", + CodegenUtils.getDefaultPackageImportName(settings.getModuleName()), + CodegenUtils.getServiceTitle(shape, "the API"))); + writer.writePackageDocs(""); + writer.writePackageShapeDocs(shape); + }); + + // Write API client type and utilities. + writers.useShapeWriter(shape, serviceWriter -> { + // TODO(SSDK): generate server stuff, like Client ServiceGenerator + + TopDownIndex topDownIndex = TopDownIndex.of(model); + Set containedOperations = new TreeSet<>(topDownIndex.getContainedOperations(service)); + for (OperationShape operation : containedOperations) { + Symbol operationSymbol = symbolProvider.toSymbol(operation); + // TODO(SSDK): generate operation stuff, like Client OpeartionGenerator + } + }); + return null; + } + + @Override + public Void intEnumShape(IntEnumShape shape) { + writers.useShapeWriter(shape, writer -> new IntEnumGenerator(symbolProvider, writer, shape).run()); + return null; + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/ProtocolGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/ServerProtocolGenerator.java similarity index 74% rename from codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/ProtocolGenerator.java rename to codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/ServerProtocolGenerator.java index 5858affeb..6949b97a2 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/ProtocolGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/ServerProtocolGenerator.java @@ -15,9 +15,19 @@ package software.amazon.smithy.go.codegen.service; +import software.amazon.smithy.go.codegen.ApplicationProtocol; import software.amazon.smithy.go.codegen.GoWriter; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.utils.SmithyInternalApi; -public interface ProtocolGenerator { +@SmithyInternalApi +public interface ServerProtocolGenerator { + // Smithy + ApplicationProtocol getApplicationProtocol(); + + ShapeId getProtocol(); + + // Go /** * Generate the operation routing logic for this protocol. */ diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/TmpCodegenIntegration.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/TmpCodegenIntegration.java index 76bb6ffce..773f8f12e 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/TmpCodegenIntegration.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/service/TmpCodegenIntegration.java @@ -15,18 +15,27 @@ package software.amazon.smithy.go.codegen.service; +import java.util.List; +import software.amazon.smithy.aws.traits.protocols.AwsJson1_0Trait; import software.amazon.smithy.codegen.core.SymbolProvider; +import software.amazon.smithy.go.codegen.ApplicationProtocol; import software.amazon.smithy.go.codegen.GoDelegator; import software.amazon.smithy.go.codegen.GoSettings; import software.amazon.smithy.go.codegen.GoWriter; -import software.amazon.smithy.go.codegen.integration.GoIntegration; +import software.amazon.smithy.go.codegen.GoWriter.Writable; import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.utils.SmithyInternalApi; -// TODO: setup invocation of codegen via cli, remove this -public class TmpCodegenIntegration implements GoIntegration { +// TODO(SSDK): setup invocation of codegen via cli, remove this +@SmithyInternalApi +public class TmpCodegenIntegration implements GoServerIntegration { @Override public void writeAdditionalFiles( - GoSettings settings, Model model, SymbolProvider symbolProvider, GoDelegator goDelegator + GoSettings settings, + Model model, + SymbolProvider symbolProvider, + GoDelegator goDelegator ) { final var service = settings.getService(model); goDelegator.useFileWriter("feat_svcgen.go", settings.getModuleName(), GoWriter.ChainWritable.of( @@ -35,4 +44,31 @@ public void writeAdditionalFiles( new NotImplementedError() ).compose()); } + + public static class AwsJson1ServerProtocolGenerator implements ServerProtocolGenerator { + @Override + public ApplicationProtocol getApplicationProtocol() { + return ApplicationProtocol.createDefaultHttpApplicationProtocol(); + } + + @Override + public ShapeId getProtocol() { + return AwsJson1_0Trait.ID; + } + + @Override + public Writable generateHandler() { + throw new UnsupportedOperationException("Unimplemented method 'generateHandler'"); + } + + @Override + public Writable generateSerializeNotImplemented() { + throw new UnsupportedOperationException("Unimplemented method 'generateSerializeNotImplemented'"); + } + } + + @Override + public List getServerProtocolGenerators() { + return List.of(new AwsJson1ServerProtocolGenerator()); + } } diff --git a/codegen/smithy-go-codegen/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin b/codegen/smithy-go-codegen/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin index 9a66c8f58..e7c8bad78 100644 --- a/codegen/smithy-go-codegen/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin +++ b/codegen/smithy-go-codegen/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin @@ -1 +1,2 @@ software.amazon.smithy.go.codegen.GoCodegenPlugin +software.amazon.smithy.go.codegen.service.GoServerCodegenPlugin diff --git a/codegen/smithy-go-codegen/src/main/resources/META-INF/services/software.amazon.smithy.go.codegen.integration.GoIntegration b/codegen/smithy-go-codegen/src/main/resources/META-INF/services/software.amazon.smithy.go.codegen.integration.GoIntegration index 81a2895ce..acc809201 100644 --- a/codegen/smithy-go-codegen/src/main/resources/META-INF/services/software.amazon.smithy.go.codegen.integration.GoIntegration +++ b/codegen/smithy-go-codegen/src/main/resources/META-INF/services/software.amazon.smithy.go.codegen.integration.GoIntegration @@ -16,4 +16,4 @@ software.amazon.smithy.go.codegen.integration.auth.AnonymousAuthScheme software.amazon.smithy.go.codegen.requestcompression.RequestCompression # TODO: remove this when we can call into service codegen directly -software.amazon.smithy.go.codegen.service.TmpCodegenIntegration \ No newline at end of file +software.amazon.smithy.go.codegen.service.TmpCodegenIntegration