Skip to content

Commit 176dcd6

Browse files
authored
example doc generation (#1555)
1 parent 3beeac6 commit 176dcd6

File tree

1 file changed

+71
-23
lines changed

1 file changed

+71
-23
lines changed

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/CommandGenerator.java

+71-23
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import software.amazon.smithy.model.traits.ErrorTrait;
5252
import software.amazon.smithy.model.traits.ExamplesTrait;
5353
import software.amazon.smithy.model.traits.InternalTrait;
54+
import software.amazon.smithy.model.traits.StreamingTrait;
5455
import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait;
5556
import software.amazon.smithy.typescript.codegen.documentation.DocumentationExampleGenerator;
5657
import software.amazon.smithy.typescript.codegen.documentation.StructureExampleGenerator;
@@ -63,6 +64,7 @@
6364
import software.amazon.smithy.typescript.codegen.sections.PreCommandClassCodeSection;
6465
import software.amazon.smithy.typescript.codegen.sections.SmithyContextCodeSection;
6566
import software.amazon.smithy.typescript.codegen.util.CommandWriterConsumer;
67+
import software.amazon.smithy.typescript.codegen.util.PropertyAccessor;
6668
import software.amazon.smithy.typescript.codegen.validation.SensitiveDataFinder;
6769
import software.amazon.smithy.utils.SmithyInternalApi;
6870

@@ -255,28 +257,30 @@ private String getCommandExample(
255257
) {
256258
String packageName = settings.getPackageName();
257259
String exampleDoc = "@example\n"
258-
+ "Use a bare-bones client and the command you need to make an API call.\n"
259-
+ "```javascript\n"
260-
+ String.format("import { %s, %s } from \"%s\"; // ES Modules import%n", serviceName, commandName,
261-
packageName)
262-
+ String.format("// const { %s, %s } = require(\"%s\"); // CommonJS import%n", serviceName, commandName,
263-
packageName)
264-
+ String.format("const client = new %s(config);%n", serviceName)
265-
+ String.format("const input = %s%n",
266-
StructureExampleGenerator.generateStructuralHintDocumentation(
267-
model.getShape(operation.getInputShape()).get(), model, false, true))
268-
+ String.format("const command = new %s(input);%n", commandName)
269-
+ "const response = await client.send(command);\n"
270-
+ String.format("%s%n",
271-
StructureExampleGenerator.generateStructuralHintDocumentation(
272-
model.getShape(operation.getOutputShape()).get(), model, true, false))
273-
+ "\n```\n"
274-
+ "\n"
275-
+ String.format("@param %s - {@link %s}%n", commandInput, commandInput)
276-
+ String.format("@returns {@link %s}%n", commandOutput)
277-
+ String.format("@see {@link %s} for command's `input` shape.%n", commandInput)
278-
+ String.format("@see {@link %s} for command's `response` shape.%n", commandOutput)
279-
+ String.format("@see {@link %s | config} for %s's `config` shape.%n", configName, serviceName);
260+
+ "Use a bare-bones client and the command you need to make an API call.\n"
261+
+ "```javascript\n"
262+
+ String.format("import { %s, %s } from \"%s\"; // ES Modules import%n", serviceName, commandName,
263+
packageName)
264+
+ String.format("// const { %s, %s } = require(\"%s\"); // CommonJS import%n", serviceName, commandName,
265+
packageName)
266+
+ String.format("const client = new %s(config);%n", serviceName)
267+
+ String.format("const input = %s%n",
268+
StructureExampleGenerator.generateStructuralHintDocumentation(
269+
model.getShape(operation.getInputShape()).get(), model, false, true))
270+
+ String.format("const command = new %s(input);%n", commandName)
271+
+ "const response = await client.send(command);"
272+
+ getStreamingBlobOutputAddendum()
273+
+ "\n"
274+
+ String.format("%s%n",
275+
StructureExampleGenerator.generateStructuralHintDocumentation(
276+
model.getShape(operation.getOutputShape()).get(), model, true, false))
277+
+ "\n```\n"
278+
+ "\n"
279+
+ String.format("@param %s - {@link %s}%n", commandInput, commandInput)
280+
+ String.format("@returns {@link %s}%n", commandOutput)
281+
+ String.format("@see {@link %s} for command's `input` shape.%n", commandInput)
282+
+ String.format("@see {@link %s} for command's `response` shape.%n", commandOutput)
283+
+ String.format("@see {@link %s | config} for %s's `config` shape.%n", configName, serviceName);
280284

281285
return exampleDoc;
282286
}
@@ -301,13 +305,14 @@ private String getCuratedExamples(String commandName) {
301305
.append("""
302306
const input = %s;
303307
const command = new %s(input);
304-
const response = await client.send(command);
308+
const response = await client.send(command);%s
305309
/* response is
306310
%s
307311
*/
308312
""".formatted(
309313
DocumentationExampleGenerator.inputToJavaScriptObject(input),
310314
commandName,
315+
getStreamingBlobOutputAddendum(),
311316
DocumentationExampleGenerator.outputToJavaScriptObject(output.orElse(null))
312317
))
313318
.append("```")
@@ -319,6 +324,49 @@ private String getCuratedExamples(String commandName) {
319324
return exampleDoc;
320325
}
321326

327+
/**
328+
* @param operation - to query.
329+
* @return member name of the streaming blob http payload, or empty string.
330+
*/
331+
private String getStreamingBlobOutputMember(OperationShape operation) {
332+
return (model.expectShape(operation.getOutputShape()))
333+
.getAllMembers()
334+
.values()
335+
.stream()
336+
.filter(memberShape -> {
337+
Shape target = model.expectShape(memberShape.getTarget());
338+
return target.isBlobShape() && (
339+
target.hasTrait(StreamingTrait.class)
340+
|| memberShape.hasTrait(StreamingTrait.class)
341+
);
342+
})
343+
.map(MemberShape::getMemberName)
344+
.findFirst()
345+
.orElse("");
346+
}
347+
348+
/**
349+
* @return e.g. appendable "const bytes = await response.Body.transformToByteArray();".
350+
*/
351+
private String getStreamingBlobOutputAddendum() {
352+
String streamingBlobAddendum = "";
353+
String streamingBlobMemberName = getStreamingBlobOutputMember(operation);
354+
if (!streamingBlobMemberName.isEmpty()) {
355+
String propAccess = PropertyAccessor.getFrom("response", streamingBlobMemberName);
356+
streamingBlobAddendum = """
357+
\n// consume or destroy the stream to free the socket.
358+
const bytes = await %s.transformToByteArray();
359+
// const str = await %s.transformToString();
360+
// %s.destroy(); // only applicable to Node.js Readable streams.
361+
""".formatted(
362+
propAccess,
363+
propAccess,
364+
propAccess
365+
);
366+
}
367+
return streamingBlobAddendum;
368+
}
369+
322370
private String getThrownExceptions() {
323371
List<ShapeId> errors = operation.getErrors();
324372
StringBuilder buffer = new StringBuilder();

0 commit comments

Comments
 (0)