Skip to content

Commit

Permalink
docs: create doc examples from ExamplesTrait in Commands
Browse files Browse the repository at this point in the history
  • Loading branch information
kuhe committed Jun 12, 2024
1 parent 38da900 commit 0aef2bf
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.knowledge.OperationIndex;
import software.amazon.smithy.model.knowledge.TopDownIndex;
import software.amazon.smithy.model.node.ObjectNode;
import software.amazon.smithy.model.shapes.MemberShape;
import software.amazon.smithy.model.shapes.OperationShape;
import software.amazon.smithy.model.shapes.ServiceShape;
Expand All @@ -47,8 +48,10 @@
import software.amazon.smithy.model.traits.DeprecatedTrait;
import software.amazon.smithy.model.traits.DocumentationTrait;
import software.amazon.smithy.model.traits.ErrorTrait;
import software.amazon.smithy.model.traits.ExamplesTrait;
import software.amazon.smithy.model.traits.InternalTrait;
import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait;
import software.amazon.smithy.typescript.codegen.documentation.DocumentationExampleGenerator;
import software.amazon.smithy.typescript.codegen.documentation.StructureExampleGenerator;
import software.amazon.smithy.typescript.codegen.endpointsV2.RuleSetParameterFinder;
import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator;
Expand Down Expand Up @@ -130,11 +133,13 @@ private void generateClientCommand() {
String name = symbol.getName();

StringBuilder additionalDocs = new StringBuilder()
.append("\n")
.append(getCommandExample(
serviceSymbol.getName(), configType, name, inputType.getName(), outputType.getName()))
.append("\n")
.append(getThrownExceptions());
.append("\n")
.append(getCommandExample(
serviceSymbol.getName(), configType, name, inputType.getName(), outputType.getName()))
.append("\n")
.append(getThrownExceptions())
.append("\n")
.append(getCuratedExamples(name));

boolean operationHasDocumentation = operation.hasTrait(DocumentationTrait.class);

Expand Down Expand Up @@ -199,10 +204,12 @@ private void generateClientCommand() {
writer.write("}"); // class close bracket.
}

private String getCommandExample(String serviceName, String configName, String commandName, String commandInput,
String commandOutput) {
private String getCommandExample(
String serviceName, String configName, String commandName,
String commandInput, String commandOutput
) {
String packageName = settings.getPackageName();
return "@example\n"
String exampleDoc = "@example\n"
+ "Use a bare-bones client and the command you need to make an API call.\n"
+ "```javascript\n"
+ String.format("import { %s, %s } from \"%s\"; // ES Modules import%n", serviceName, commandName,
Expand All @@ -225,6 +232,46 @@ private String getCommandExample(String serviceName, String configName, String c
+ String.format("@see {@link %s} for command's `input` shape.%n", commandInput)
+ String.format("@see {@link %s} for command's `response` shape.%n", commandOutput)
+ String.format("@see {@link %s | config} for %s's `config` shape.%n", configName, serviceName);

return exampleDoc;
}

/**
* Handwritten examples from the operation ExamplesTrait.
*/
private String getCuratedExamples(String commandName) {
String exampleDoc = "";
if (operation.getTrait(ExamplesTrait.class).isPresent()) {
List<ExamplesTrait.Example> examples = operation.getTrait(ExamplesTrait.class).get().getExamples();
StringBuilder buffer = new StringBuilder();

for (ExamplesTrait.Example example : examples) {
ObjectNode input = example.getInput();
Optional<ObjectNode> output = example.getOutput();
buffer
.append("\n")
.append(String.format("@example %s%n", example.getTitle()))
.append("```javascript\n")
.append(String.format("/* %s */%n", example.getDocumentation().orElse("")))
.append("""
const input = %s;
const command = new %s(input);
const response = await client.send(command);
/* response is
%s
*/
""".formatted(
DocumentationExampleGenerator.inputToJavaScriptObject(input),
commandName,
DocumentationExampleGenerator.outputToJavaScriptObject(output.orElse(null))
))
.append("```")
.append("\n");
}

exampleDoc += buffer.toString();
}
return exampleDoc;
}

private String getThrownExceptions() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.typescript.codegen.documentation;

import java.util.stream.Collectors;
import software.amazon.smithy.model.node.ArrayNode;
import software.amazon.smithy.model.node.BooleanNode;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.NullNode;
import software.amazon.smithy.model.node.NumberNode;
import software.amazon.smithy.model.node.ObjectNode;
import software.amazon.smithy.model.node.StringNode;
import software.amazon.smithy.utils.SmithyInternalApi;

@SmithyInternalApi
public final class DocumentationExampleGenerator {
private DocumentationExampleGenerator() {}

/**
* @return the ObjectNode from the curated example written as a JavaScript object literal.
*/
public static String inputToJavaScriptObject(ObjectNode node) {
if (node == null) {
return "{ /* empty */ }";
}
return write(node, 0);
}

public static String outputToJavaScriptObject(ObjectNode node) {
if (node == null) {
return "{ /* metadata only */ }";
}
return write(node, 0);
}

private static String write(Node node, int indent) {
StringBuilder buffer = new StringBuilder();
String indentation = " ".repeat(indent);

switch (node.getType()) {
case OBJECT -> {
ObjectNode objectNode = node.expectObjectNode();
if (objectNode.getMembers().isEmpty()) {
return indentation + "{ /* empty */ }";
}
String membersJoined = objectNode.getMembers()
.entrySet()
.stream()
.map(entry -> indentation
+ " "
+ entry.getKey().getValue()
+ ": "
+ write(entry.getValue(), indent + 2))
.collect(Collectors.joining(",\n"));

return buffer
.append("{\n")
.append(membersJoined).append("\n")
.append(indentation).append("}")
.toString();
}
case ARRAY -> {
ArrayNode arrayNode = node.expectArrayNode();
if (arrayNode.getElements().isEmpty()) {
return indentation + "[]";
}
String membersJoined = arrayNode.getElements()
.stream()
.map(elementNode -> indentation
+ " "
+ write(elementNode, indent + 2))
.collect(Collectors.joining(",\n"));

return buffer
.append("[\n")
.append(membersJoined).append("\n")
.append(indentation).append("]")
.toString();
}
case STRING -> {
StringNode stringNode = node.expectStringNode();
return "\"" + stringNode.getValue() + "\"";
}
case NUMBER -> {
NumberNode numberNode = node.expectNumberNode();
return numberNode.getValue().toString();
}
case BOOLEAN -> {
BooleanNode booleanNode = node.expectBooleanNode();
return booleanNode.toString();
}
case NULL -> {
NullNode nullNode = node.expectNullNode();
return nullNode.toString();
}
default -> throw new IllegalStateException("Unexpected value: " + node.getType());
}
}
}

0 comments on commit 0aef2bf

Please sign in to comment.