From a7f65e56eacf5a983aee0e3b1d8facf73b7965bd Mon Sep 17 00:00:00 2001 From: Jordon Phillips Date: Mon, 27 Nov 2023 10:34:33 +0100 Subject: [PATCH 1/3] Add conditional protocol trait documentation This adds a section to write protocol traits to. Each protocol will have its own tab so services with multiple protocols can clearly document them separately in one doc site. --- .../core/generators/GeneratorUtils.java | 194 ++++++++++++++++++ .../core/generators/MemberGenerator.java | 12 +- .../core/generators/OperationGenerator.java | 9 + .../core/generators/ResourceGenerator.java | 5 +- .../core/generators/ServiceGenerator.java | 4 +- .../ServiceShapeGeneratorUtils.java | 96 --------- .../generators/StructuredShapeGenerator.java | 10 + .../docgen/core/sections/ProtocolSection.java | 31 +++ .../core/sections/ProtocolsSection.java | 31 +++ smithy-docgen-test/build.gradle | 1 + smithy-docgen-test/model/main.smithy | 7 + 11 files changed, 299 insertions(+), 101 deletions(-) create mode 100644 smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/GeneratorUtils.java delete mode 100644 smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/ServiceShapeGeneratorUtils.java create mode 100644 smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/sections/ProtocolSection.java create mode 100644 smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/sections/ProtocolsSection.java diff --git a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/GeneratorUtils.java b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/GeneratorUtils.java new file mode 100644 index 0000000..b1b29ce --- /dev/null +++ b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/GeneratorUtils.java @@ -0,0 +1,194 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.docgen.core.generators; + +import java.util.List; +import java.util.Locale; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import software.amazon.smithy.docgen.core.DocGenerationContext; +import software.amazon.smithy.docgen.core.DocSymbolProvider; +import software.amazon.smithy.docgen.core.sections.BoundOperationSection; +import software.amazon.smithy.docgen.core.sections.BoundOperationsSection; +import software.amazon.smithy.docgen.core.sections.BoundResourceSection; +import software.amazon.smithy.docgen.core.sections.BoundResourcesSection; +import software.amazon.smithy.docgen.core.sections.ProtocolSection; +import software.amazon.smithy.docgen.core.sections.ProtocolsSection; +import software.amazon.smithy.docgen.core.writers.DocWriter; +import software.amazon.smithy.docgen.core.writers.DocWriter.ListType; +import software.amazon.smithy.model.knowledge.ServiceIndex; +import software.amazon.smithy.model.shapes.EntityShape; +import software.amazon.smithy.model.shapes.OperationShape; +import software.amazon.smithy.model.shapes.ResourceShape; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.utils.CodeInterceptor; +import software.amazon.smithy.utils.CodeSection; +import software.amazon.smithy.utils.SmithyInternalApi; +import software.amazon.smithy.utils.StringUtils; + +/** + * Provides common generation methods for services and resources. + */ +@SmithyInternalApi +final class GeneratorUtils { + private GeneratorUtils() {} + + static void generateOperationListing( + DocGenerationContext context, + DocWriter writer, + EntityShape shape, + List operations + ) { + writer.pushState(new BoundOperationsSection(context, shape, operations)); + + if (operations.isEmpty()) { + writer.popState(); + return; + } + + var parentLinkId = context.symbolProvider().toSymbol(shape) + .expectProperty(DocSymbolProvider.LINK_ID_PROPERTY, String.class); + writer.openHeading("Operations", parentLinkId + "-operations"); + writer.openList(ListType.UNORDERED); + + for (var operation : operations) { + writer.pushState(new BoundOperationSection(context, shape, operation)); + writeListingElement(context, writer, operation); + writer.popState(); + } + + writer.closeList(ListType.UNORDERED); + writer.closeHeading(); + writer.popState(); + } + + static void generateResourceListing( + DocGenerationContext context, + DocWriter writer, + EntityShape shape, + List resources + ) { + writer.pushState(new BoundResourcesSection(context, shape, resources)); + + if (resources.isEmpty()) { + writer.popState(); + return; + } + + var parentLinkId = context.symbolProvider().toSymbol(shape) + .expectProperty(DocSymbolProvider.LINK_ID_PROPERTY, String.class); + var heading = shape.isServiceShape() ? "Resources" : "Sub-Resources"; + writer.openHeading(heading, parentLinkId + "-" + heading.toLowerCase(Locale.ENGLISH)); + writer.openList(ListType.UNORDERED); + + for (var resource : resources) { + writer.pushState(new BoundResourceSection(context, shape, resource)); + writeListingElement(context, writer, resource); + writer.popState(); + } + + writer.closeList(ListType.UNORDERED); + writer.closeHeading(); + writer.popState(); + } + + private static void writeListingElement(DocGenerationContext context, DocWriter writer, Shape shape) { + writer.openListItem(ListType.UNORDERED); + var symbol = context.symbolProvider().toSymbol(shape); + writer.writeInline("$R: ", symbol).writeShapeDocs(shape, context.model()); + writer.closeListItem(ListType.UNORDERED); + } + + static void writeProtocolsSection(DocGenerationContext context, DocWriter writer, Shape shape) { + var protocols = ServiceIndex.of(context.model()).getProtocols(context.settings().service()).keySet(); + if (protocols.isEmpty()) { + return; + } + writer.pushState(new ProtocolsSection(context, shape)); + + AtomicReference tabGroupContents = new AtomicReference<>(); + var tabGroup = capture(writer, tabGroupWriter -> { + tabGroupWriter.openTabGroup(); + tabGroupContents.set(capture(tabGroupWriter, w -> { + for (var protocol : protocols) { + writeProtocolSection(context, w, shape, protocol); + } + })); + tabGroupWriter.closeTabGroup(); + }); + + if (StringUtils.isBlank(tabGroupContents.get())) { + // The extra newline is needed because the section intercepting logic actually adds one + // by virtue of calling write instead of writeInline + writer.unwrite("$L\n", tabGroup); + } + + writer.popState(); + } + + private static void writeProtocolSection( + DocGenerationContext context, + DocWriter writer, + Shape shape, + ShapeId protocol + ) { + var protocolSymbol = context.symbolProvider().toSymbol(context.model().expectShape(protocol)); + + AtomicReference tabContents = new AtomicReference<>(); + var tab = capture(writer, tabWriter -> { + tabWriter.openTab(protocolSymbol.getName()); + tabContents.set(capture(tabWriter, w2 -> tabWriter.injectSection( + new ProtocolSection(context, shape, protocol)))); + tabWriter.closeTab(); + }); + + if (StringUtils.isBlank(tabContents.get())) { + // The extra newline is needed because the section intercepting logic actually adds one + // by virtue of calling write instead of writeInline + writer.unwrite("$L\n", tab); + } + } + + /** + * Captures and returns what is written by the given consumer. + * + * @param writer The writer to capture from. + * @param consumer A consumer that writes text to be captured. + * @return Returns what was written by the consumer. + */ + private static String capture(DocWriter writer, Consumer consumer) { + var recorder = new RecordingInterceptor(); + writer.pushState(new CapturingSection()).onSection(recorder); + consumer.accept(writer); + writer.popState(); + return recorder.getContents(); + } + + private record CapturingSection() implements CodeSection {} + + /** + * Records what was written to the section previously and writes it back. + */ + private static final class RecordingInterceptor implements CodeInterceptor { + private String contents = null; + + public String getContents() { + return contents; + } + + @Override + public Class sectionType() { + return CapturingSection.class; + } + + @Override + public void write(DocWriter writer, String previousText, CapturingSection section) { + contents = previousText; + writer.writeWithNoFormatting(previousText); + } + } +} diff --git a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/MemberGenerator.java b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/MemberGenerator.java index 4ff7e03..eb4759e 100644 --- a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/MemberGenerator.java +++ b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/MemberGenerator.java @@ -51,12 +51,21 @@ *

The output of this can be customized in a number of ways. To add details to * or re-write particular sections, register an interceptor with * {@link software.amazon.smithy.docgen.core.DocIntegration#interceptors}. The following - * sections are guaranteed to be present: + * sections will be present: * *

    *
  • {@link MemberSection}: Enables re-writing the documentation for specific members. + * *
  • {@link ShapeMembersSection}: Enables re-writing or overwriting the entire list * of members, including changes made in other sections. + * + *
  • {@link software.amazon.smithy.docgen.core.sections.ProtocolSection} Enables adding + * traits that are specific to a particular protocol. This section will only be present if + * there are protocol traits applied to the service. If there are multiple protocol traits, + * this section will appear once per protocol. + * + *
  • {@link software.amazon.smithy.docgen.core.sections.ProtocolsSection} Enables + * modifying the tab group containing all the protocol traits for all the protocols. *
* *

To change the intermediate format (e.g. from markdown to restructured text), @@ -114,6 +123,7 @@ public void run() { writer.injectSection(new ShapeSubheadingSection(context, member)); writer.writeShapeDocs(member, context.model()); writer.injectSection(new ShapeDetailsSection(context, member)); + GeneratorUtils.writeProtocolsSection(context, writer, member); writer.closeDefinitionListItem(); writer.popState(); } diff --git a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/OperationGenerator.java b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/OperationGenerator.java index d616f14..edf1e27 100644 --- a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/OperationGenerator.java +++ b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/OperationGenerator.java @@ -52,6 +52,14 @@ * the operation might return. If a synthetic error needs to be applied to an * operation, it is better to simply add it to the shape with * {@link software.amazon.smithy.docgen.core.DocIntegration#preprocessModel}. + * + *

  • {@link software.amazon.smithy.docgen.core.sections.ProtocolSection} Enables adding + * traits that are specific to a particular protocol. This section will only be present if + * there are protocol traits applied to the service. If there are multiple protocol traits, + * this section will appear once per protocol. + * + *
  • {@link software.amazon.smithy.docgen.core.sections.ProtocolsSection} Enables + * modifying the tab group containing all the protocol traits for all the protocols. * * * Additionally, if the operation's input or output shapes have members the following @@ -98,6 +106,7 @@ public void accept(GenerateOperationDirective writer.injectSection(new ShapeSubheadingSection(context, operation)); writer.writeShapeDocs(operation, directive.model()); writer.injectSection(new ShapeDetailsSection(context, operation)); + GeneratorUtils.writeProtocolsSection(context, writer, operation); new MemberGenerator(context, writer, operation, MemberListingType.INPUT).run(); new MemberGenerator(context, writer, operation, MemberListingType.OUTPUT).run(); diff --git a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/ResourceGenerator.java b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/ResourceGenerator.java index 9c5258c..66c669a 100644 --- a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/ResourceGenerator.java +++ b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/ResourceGenerator.java @@ -103,6 +103,7 @@ public void accept(DocGenerationContext context, ResourceShape resource) { writer.injectSection(new ShapeSubheadingSection(context, resource)); writer.writeShapeDocs(resource, context.model()); writer.injectSection(new ShapeDetailsSection(context, resource)); + GeneratorUtils.writeProtocolsSection(context, writer, resource); new MemberGenerator(context, writer, resource, MemberListingType.RESOURCE_IDENTIFIERS).run(); new MemberGenerator(context, writer, resource, MemberListingType.RESOURCE_PROPERTIES).run(); @@ -110,7 +111,7 @@ public void accept(DocGenerationContext context, ResourceShape resource) { var subResources = resource.getResources().stream().sorted() .map(id -> context.model().expectShape(id, ResourceShape.class)) .toList(); - ServiceShapeGeneratorUtils.generateResourceListing(context, writer, resource, subResources); + GeneratorUtils.generateResourceListing(context, writer, resource, subResources); generateLifecycleDocs(context, writer, resource); @@ -119,7 +120,7 @@ public void accept(DocGenerationContext context, ResourceShape resource) { var operations = operationIds.stream().sorted() .map(id -> context.model().expectShape(id, OperationShape.class)) .toList(); - ServiceShapeGeneratorUtils.generateOperationListing(context, writer, resource, operations); + GeneratorUtils.generateOperationListing(context, writer, resource, operations); writer.closeHeading(); writer.popState(); diff --git a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/ServiceGenerator.java b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/ServiceGenerator.java index c6a6561..4df6cf9 100644 --- a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/ServiceGenerator.java +++ b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/ServiceGenerator.java @@ -93,10 +93,10 @@ public void accept(GenerateServiceDirective d // TODO: topographically sort resources var resources = topDownIndex.getContainedResources(service).stream().sorted().toList(); - ServiceShapeGeneratorUtils.generateResourceListing(context, writer, service, resources); + GeneratorUtils.generateResourceListing(context, writer, service, resources); var operations = topDownIndex.getContainedOperations(service).stream().sorted().toList(); - ServiceShapeGeneratorUtils.generateOperationListing(context, writer, service, operations); + GeneratorUtils.generateOperationListing(context, writer, service, operations); writeAuthSection(context, writer, service); diff --git a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/ServiceShapeGeneratorUtils.java b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/ServiceShapeGeneratorUtils.java deleted file mode 100644 index b945621..0000000 --- a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/ServiceShapeGeneratorUtils.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.docgen.core.generators; - -import java.util.List; -import java.util.Locale; -import software.amazon.smithy.docgen.core.DocGenerationContext; -import software.amazon.smithy.docgen.core.DocSymbolProvider; -import software.amazon.smithy.docgen.core.sections.BoundOperationSection; -import software.amazon.smithy.docgen.core.sections.BoundOperationsSection; -import software.amazon.smithy.docgen.core.sections.BoundResourceSection; -import software.amazon.smithy.docgen.core.sections.BoundResourcesSection; -import software.amazon.smithy.docgen.core.writers.DocWriter; -import software.amazon.smithy.docgen.core.writers.DocWriter.ListType; -import software.amazon.smithy.model.shapes.EntityShape; -import software.amazon.smithy.model.shapes.OperationShape; -import software.amazon.smithy.model.shapes.ResourceShape; -import software.amazon.smithy.model.shapes.Shape; -import software.amazon.smithy.utils.SmithyInternalApi; - -/** - * Provides common generation methods for services and resources. - */ -@SmithyInternalApi -final class ServiceShapeGeneratorUtils { - private ServiceShapeGeneratorUtils() {} - - static void generateOperationListing( - DocGenerationContext context, - DocWriter writer, - EntityShape shape, - List operations - ) { - writer.pushState(new BoundOperationsSection(context, shape, operations)); - - if (operations.isEmpty()) { - writer.popState(); - return; - } - - var parentLinkId = context.symbolProvider().toSymbol(shape) - .expectProperty(DocSymbolProvider.LINK_ID_PROPERTY, String.class); - writer.openHeading("Operations", parentLinkId + "-operations"); - writer.openList(ListType.UNORDERED); - - for (var operation : operations) { - writer.pushState(new BoundOperationSection(context, shape, operation)); - writeListingElement(context, writer, operation); - writer.popState(); - } - - writer.closeList(ListType.UNORDERED); - writer.closeHeading(); - writer.popState(); - } - - static void generateResourceListing( - DocGenerationContext context, - DocWriter writer, - EntityShape shape, - List resources - ) { - writer.pushState(new BoundResourcesSection(context, shape, resources)); - - if (resources.isEmpty()) { - writer.popState(); - return; - } - - var parentLinkId = context.symbolProvider().toSymbol(shape) - .expectProperty(DocSymbolProvider.LINK_ID_PROPERTY, String.class); - var heading = shape.isServiceShape() ? "Resources" : "Sub-Resources"; - writer.openHeading(heading, parentLinkId + "-" + heading.toLowerCase(Locale.ENGLISH)); - writer.openList(ListType.UNORDERED); - - for (var resource : resources) { - writer.pushState(new BoundResourceSection(context, shape, resource)); - writeListingElement(context, writer, resource); - writer.popState(); - } - - writer.closeList(ListType.UNORDERED); - writer.closeHeading(); - writer.popState(); - } - - private static void writeListingElement(DocGenerationContext context, DocWriter writer, Shape shape) { - writer.openListItem(ListType.UNORDERED); - var symbol = context.symbolProvider().toSymbol(shape); - writer.writeInline("$R: ", symbol).writeShapeDocs(shape, context.model()); - writer.closeListItem(ListType.UNORDERED); - } -} diff --git a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/StructuredShapeGenerator.java b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/StructuredShapeGenerator.java index a1f771b..5af63c2 100644 --- a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/StructuredShapeGenerator.java +++ b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/StructuredShapeGenerator.java @@ -32,6 +32,15 @@ * *
  • {@link ShapeSection}: Enables re-writing or overwriting the entire page, * including changes made in other sections. + * + *
  • {@link software.amazon.smithy.docgen.core.sections.ProtocolSection} Enables adding + * traits that are specific to a particular protocol. This section will only be present if + * there are protocol traits applied to the service. If there are multiple protocol traits, + * this section will appear once per protocol. This section will also appear for each member. + * + *
  • {@link software.amazon.smithy.docgen.core.sections.ProtocolsSection} Enables + * modifying the tab group containing all the protocol traits for all the protocols. This + * section will also appear for each member. * * * Additionally, if the shape has members the following sections will also be present: @@ -75,6 +84,7 @@ public void accept(Shape shape, MemberListingType listingType) { writer.injectSection(new ShapeSubheadingSection(context, shape)); writer.writeShapeDocs(shape, context.model()); writer.injectSection(new ShapeDetailsSection(context, shape)); + GeneratorUtils.writeProtocolsSection(context, writer, shape); new MemberGenerator(context, writer, shape, listingType).run(); diff --git a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/sections/ProtocolSection.java b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/sections/ProtocolSection.java new file mode 100644 index 0000000..20fe054 --- /dev/null +++ b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/sections/ProtocolSection.java @@ -0,0 +1,31 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.docgen.core.sections; + +import software.amazon.smithy.docgen.core.DocGenerationContext; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.utils.CodeSection; +import software.amazon.smithy.utils.SmithyUnstableApi; + +/** + * A section that contains protocol-specific information for a specific protocol + * for a given shape. + * + * @param context The context used to generate documentation. + * @param shape The shape to add protocol information to. + * @param protocol The shape id of the protocol being documented. + * + * @see ProtocolsSection to make changes to all protocols and how they're displayed. + * @see ShapeSection to make non-protocol-specific changes to a shape's docs. + */ +@SmithyUnstableApi +public record ProtocolSection( + DocGenerationContext context, + Shape shape, + ShapeId protocol +) implements CodeSection { +} diff --git a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/sections/ProtocolsSection.java b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/sections/ProtocolsSection.java new file mode 100644 index 0000000..b79858a --- /dev/null +++ b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/sections/ProtocolsSection.java @@ -0,0 +1,31 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.docgen.core.sections; + +import software.amazon.smithy.docgen.core.DocGenerationContext; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.utils.CodeSection; +import software.amazon.smithy.utils.SmithyUnstableApi; + +/** + * A section that contains all protocol-specific information for the given shape for + * all protocols. + * + *

    Each individual protocol has its own {@link ProtocolSection}. If the service has + * more than one protocol, these sections will be in tabs. + * + * @param context The context used to generate documentation. + * @param shape The shape to add protocol information to. + * + * @see ProtocolSection to make additions to a particular protocol's section. + * @see ShapeSection to make non-protocol-specific changes to a shape's docs. + */ +@SmithyUnstableApi +public record ProtocolsSection( + DocGenerationContext context, + Shape shape +) implements CodeSection { +} diff --git a/smithy-docgen-test/build.gradle b/smithy-docgen-test/build.gradle index 57616ab..751e427 100644 --- a/smithy-docgen-test/build.gradle +++ b/smithy-docgen-test/build.gradle @@ -30,4 +30,5 @@ repositories { dependencies { implementation(project(":smithy-docgen-core")) + implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion") } diff --git a/smithy-docgen-test/model/main.smithy b/smithy-docgen-test/model/main.smithy index 7cb4c97..e934c32 100644 --- a/smithy-docgen-test/model/main.smithy +++ b/smithy-docgen-test/model/main.smithy @@ -2,6 +2,10 @@ $version: "2.0" namespace com.example +use aws.protocols#awsJson1_0 +use aws.protocols#restJson1 +use aws.protocols#restXml + /// This service is not intended to be representative of a real service. Rather, it is /// meant to exercise different kinds of behavior that the documentation generator /// should handle. For example, the implementation must be able to handle HTML @@ -12,6 +16,9 @@ namespace com.example @httpBearerAuth @httpApiKeyAuth(name: "auth-bearing-header", in: "header", scheme: "Bearer") @auth([httpApiKeyAuth, httpBearerAuth, httpDigestAuth]) +@awsJson1_0 +@restJson1 +@restXml service DocumentedService { version: "2023-10-13" operations: [ From c643685936c21c78d26af93f380fa7c1b17998a9 Mon Sep 17 00:00:00 2001 From: Jordon Phillips Date: Mon, 27 Nov 2023 11:16:10 +0100 Subject: [PATCH 2/3] Add jsonName trait docs --- .../integrations/BuiltinsIntegration.java | 2 + .../interceptors/JsonNameInterceptor.java | 39 ++++++++++++++ .../ProtocolTraitInterceptor.java | 54 +++++++++++++++++++ smithy-docgen-test/model/main.smithy | 2 + 4 files changed, 97 insertions(+) create mode 100644 smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/interceptors/JsonNameInterceptor.java create mode 100644 smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/interceptors/ProtocolTraitInterceptor.java diff --git a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/integrations/BuiltinsIntegration.java b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/integrations/BuiltinsIntegration.java index 3884df7..cc03b96 100644 --- a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/integrations/BuiltinsIntegration.java +++ b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/integrations/BuiltinsIntegration.java @@ -17,6 +17,7 @@ import software.amazon.smithy.docgen.core.interceptors.ExternalDocsInterceptor; import software.amazon.smithy.docgen.core.interceptors.IdempotencyInterceptor; import software.amazon.smithy.docgen.core.interceptors.InternalInterceptor; +import software.amazon.smithy.docgen.core.interceptors.JsonNameInterceptor; import software.amazon.smithy.docgen.core.interceptors.LengthInterceptor; import software.amazon.smithy.docgen.core.interceptors.NoReplaceBindingInterceptor; import software.amazon.smithy.docgen.core.interceptors.NoReplaceOperationInterceptor; @@ -75,6 +76,7 @@ public List> interce return List.of( new OperationAuthInterceptor(), new ApiKeyAuthInterceptor(), + new JsonNameInterceptor(), new PaginationInterceptor(), new RequestCompressionInterceptor(), new NoReplaceBindingInterceptor(), diff --git a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/interceptors/JsonNameInterceptor.java b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/interceptors/JsonNameInterceptor.java new file mode 100644 index 0000000..34111c0 --- /dev/null +++ b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/interceptors/JsonNameInterceptor.java @@ -0,0 +1,39 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.docgen.core.interceptors; + +import software.amazon.smithy.docgen.core.sections.ProtocolSection; +import software.amazon.smithy.docgen.core.writers.DocWriter; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.JsonNameTrait; +import software.amazon.smithy.utils.SmithyInternalApi; + +/** + * Adds a member's + * jsonName to the {@link ProtocolSection} if the protocol supports it. + */ +@SmithyInternalApi +public final class JsonNameInterceptor extends ProtocolTraitInterceptor { + + @Override + protected Class getTraitClass() { + return JsonNameTrait.class; + } + + @Override + protected ShapeId getTraitId() { + return JsonNameTrait.ID; + } + + @Override + public void write(DocWriter writer, String previousText, ProtocolSection section, JsonNameTrait trait) { + writer.putContext("jsonKeyName", "JSON key name:"); + writer.write(""" + ${jsonKeyName:B} $` + + $L""", trait.getValue(), previousText); + } +} diff --git a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/interceptors/ProtocolTraitInterceptor.java b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/interceptors/ProtocolTraitInterceptor.java new file mode 100644 index 0000000..a2a10a9 --- /dev/null +++ b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/interceptors/ProtocolTraitInterceptor.java @@ -0,0 +1,54 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.docgen.core.interceptors; + +import software.amazon.smithy.docgen.core.sections.ProtocolSection; +import software.amazon.smithy.docgen.core.writers.DocWriter; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.ProtocolDefinitionTrait; +import software.amazon.smithy.model.traits.Trait; +import software.amazon.smithy.utils.CodeInterceptor; + +/** + * Implements an interceptor that adds protocol trait documentation. + * + * @param The class of the protocol trait. + */ +abstract class ProtocolTraitInterceptor implements CodeInterceptor { + + /** + * @return returns the class of the protocol trait. + */ + protected abstract Class getTraitClass(); + + /** + * @return returns the shape id of the protocol trait. + */ + protected abstract ShapeId getTraitId(); + + @Override + public boolean isIntercepted(ProtocolSection section) { + if (section.shape().getMemberTrait(section.context().model(), getTraitClass()).isEmpty()) { + return false; + } + var protocolShape = section.context().model().expectShape(section.protocol()); + var protocolDefinition = protocolShape.expectTrait(ProtocolDefinitionTrait.class); + return protocolDefinition.getTraits().contains(getTraitId()); + } + + @Override + public Class sectionType() { + return ProtocolSection.class; + } + + @Override + public void write(DocWriter writer, String previousText, ProtocolSection section) { + var trait = section.shape().getMemberTrait(section.context().model(), getTraitClass()).get(); + write(writer, previousText, section, trait); + } + + abstract void write(DocWriter writer, String previousText, ProtocolSection section, T trait); +} diff --git a/smithy-docgen-test/model/main.smithy b/smithy-docgen-test/model/main.smithy index e934c32..07f5c65 100644 --- a/smithy-docgen-test/model/main.smithy +++ b/smithy-docgen-test/model/main.smithy @@ -116,6 +116,8 @@ structure DocumentedStructure { /// This is a simple string member. /// It has documentation that can span multiple lines. @since("2023-11-16") + @jsonName("foo") + @xmlName("bar") string: String /// This member has a pattern trait on it. From 306d1cabcb20333a384d0bc6521a20abc8d616c3 Mon Sep 17 00:00:00 2001 From: Jordon Phillips Date: Mon, 27 Nov 2023 11:22:47 +0100 Subject: [PATCH 3/3] Add xmlName trait docs --- .../integrations/BuiltinsIntegration.java | 2 + .../core/interceptors/XmlNameInterceptor.java | 44 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/interceptors/XmlNameInterceptor.java diff --git a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/integrations/BuiltinsIntegration.java b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/integrations/BuiltinsIntegration.java index cc03b96..b6e07d1 100644 --- a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/integrations/BuiltinsIntegration.java +++ b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/integrations/BuiltinsIntegration.java @@ -33,6 +33,7 @@ import software.amazon.smithy.docgen.core.interceptors.SparseInterceptor; import software.amazon.smithy.docgen.core.interceptors.UniqueItemsInterceptor; import software.amazon.smithy.docgen.core.interceptors.UnstableInterceptor; +import software.amazon.smithy.docgen.core.interceptors.XmlNameInterceptor; import software.amazon.smithy.docgen.core.writers.DocWriter; import software.amazon.smithy.docgen.core.writers.MarkdownWriter; import software.amazon.smithy.utils.CodeInterceptor; @@ -77,6 +78,7 @@ public List> interce new OperationAuthInterceptor(), new ApiKeyAuthInterceptor(), new JsonNameInterceptor(), + new XmlNameInterceptor(), new PaginationInterceptor(), new RequestCompressionInterceptor(), new NoReplaceBindingInterceptor(), diff --git a/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/interceptors/XmlNameInterceptor.java b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/interceptors/XmlNameInterceptor.java new file mode 100644 index 0000000..6be7dbe --- /dev/null +++ b/smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/interceptors/XmlNameInterceptor.java @@ -0,0 +1,44 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.docgen.core.interceptors; + +import software.amazon.smithy.docgen.core.sections.ProtocolSection; +import software.amazon.smithy.docgen.core.writers.DocWriter; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.XmlNameTrait; +import software.amazon.smithy.utils.SmithyInternalApi; + +/** + * Adds a member's + * xmlName to the {@link ProtocolSection} if the protocol supports it. + */ +@SmithyInternalApi +public final class XmlNameInterceptor extends ProtocolTraitInterceptor { + @Override + protected Class getTraitClass() { + return XmlNameTrait.class; + } + + @Override + protected ShapeId getTraitId() { + return XmlNameTrait.ID; + } + + @Override + public boolean isIntercepted(ProtocolSection section) { + // The xmlName trait uniquely doesn't inherit values from the target as a member. + return super.isIntercepted(section) && section.shape().hasTrait(XmlNameTrait.class); + } + + @Override + void write(DocWriter writer, String previousText, ProtocolSection section, XmlNameTrait trait) { + writer.putContext("xmlTagName", "XML tag name:"); + writer.write(""" + ${xmlTagName:B} $` + + $L""", trait.getValue(), previousText); + } +}