From 47291dfe0a155c35d6b26e3e36d77c96557c1252 Mon Sep 17 00:00:00 2001 From: George Fu Date: Tue, 13 Jun 2023 19:58:15 +0000 Subject: [PATCH] widen blob payload input types, add blob payload output converter --- .../typescript/codegen/CodegenUtils.java | 93 ++++++++++++++++--- .../typescript/codegen/CommandGenerator.java | 43 +++++++-- .../codegen/TypeScriptDependency.java | 1 + .../HttpProtocolGeneratorUtils.java | 15 +-- 4 files changed, 117 insertions(+), 35 deletions(-) diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/CodegenUtils.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/CodegenUtils.java index 6e6a68d2b18..4c231047e1a 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/CodegenUtils.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/CodegenUtils.java @@ -29,6 +29,7 @@ import software.amazon.smithy.model.shapes.ServiceShape; import software.amazon.smithy.model.shapes.Shape; import software.amazon.smithy.model.shapes.StructureShape; +import software.amazon.smithy.model.traits.HttpPayloadTrait; import software.amazon.smithy.model.traits.StreamingTrait; import software.amazon.smithy.typescript.codegen.integration.AddSdkStreamMixinDependency; import software.amazon.smithy.utils.SmithyUnstableApi; @@ -135,10 +136,11 @@ static List getBlobStreamingMembers(Model model, StructureShape sha * Refer here for more rationales: https://github.com/aws/aws-sdk-js-v3/issues/843 */ static void writeClientCommandStreamingInputType( - TypeScriptWriter writer, - Symbol containerSymbol, - String typeName, - MemberShape streamingMember + TypeScriptWriter writer, + Symbol containerSymbol, + String typeName, + MemberShape streamingMember, + String commandName ) { String memberName = streamingMember.getMemberName(); String optionalSuffix = streamingMember.isRequired() ? "" : "?"; @@ -149,8 +151,8 @@ static void writeClientCommandStreamingInputType( writer.write("$1L$2L: $3T[$1S]|string|Uint8Array|Buffer;", memberName, optionalSuffix, containerSymbol); }); - writer.writeDocs(String.format("This interface extends from `%1$s` interface. There are more parameters than" - + " `%2$s` defined in {@link %1$s}", containerSymbol.getName(), memberName)); + + writer.writeDocs("@public\n\nThe input for {@link " + commandName + "}."); writer.write("export interface $1L extends $1LType {}", typeName); } @@ -160,18 +162,18 @@ static void writeClientCommandStreamingInputType( * stream to string, buffer or WHATWG stream API. */ static void writeClientCommandStreamingOutputType( - TypeScriptWriter writer, - Symbol containerSymbol, - String typeName, - MemberShape streamingMember + TypeScriptWriter writer, + Symbol containerSymbol, + String typeName, + MemberShape streamingMember, + String commandName ) { String memberName = streamingMember.getMemberName(); - String optionalSuffix = streamingMember.isRequired() ? "" : "?"; writer.addImport("MetadataBearer", "__MetadataBearer", TypeScriptDependency.AWS_SDK_TYPES.packageName); writer.addImport("SdkStream", "__SdkStream", TypeScriptDependency.AWS_SDK_TYPES.packageName); writer.addImport("WithSdkStreamMixin", "__WithSdkStreamMixin", TypeScriptDependency.AWS_SDK_TYPES.packageName); - + writer.writeDocs("@public\n\nThe output of {@link " + commandName + "}."); writer.write( "export interface $L extends __WithSdkStreamMixin<$T, $S>, __MetadataBearer {}", typeName, @@ -180,6 +182,73 @@ static void writeClientCommandStreamingOutputType( ); } + static List getBlobPayloadMembers(Model model, StructureShape shape) { + return shape.getAllMembers().values().stream() + .filter(memberShape -> { + Shape target = model.expectShape(memberShape.getTarget()); + return target.isBlobShape() + && memberShape.hasTrait(HttpPayloadTrait.class) + && !target.hasTrait(StreamingTrait.class); + }) + .collect(Collectors.toList()); + } + + static void writeClientCommandBlobPayloadInputType( + TypeScriptWriter writer, + Symbol containerSymbol, + String typeName, + MemberShape payloadMember, + String commandName + ) { + String memberName = payloadMember.getMemberName(); + String optionalSuffix = payloadMember.isRequired() ? "" : "?"; + + writer.addImport("BlobTypes", null, TypeScriptDependency.AWS_SDK_TYPES); + + writer.writeDocs("@public"); + writer.openBlock("export type $LType = Omit<$T, $S> & {", "};", + typeName, + containerSymbol, + memberName, + () -> { + writer.write("$1L$2L: BlobTypes;", memberName, optionalSuffix); + } + ); + + writer.writeDocs("@public\n\nThe input for {@link " + commandName + "}."); + writer.write("export interface $1L extends $1LType {}", typeName); + } + + static void writeClientCommandBlobPayloadOutputType( + TypeScriptWriter writer, + Symbol containerSymbol, + String typeName, + MemberShape payloadMember, + String commandName + ) { + String memberName = payloadMember.getMemberName(); + String optionalSuffix = payloadMember.isRequired() ? "" : "?"; + + writer.addImport("Uint8ArrayBlobAdapter", null, TypeScriptDependency.UTIL_STREAM); + writer.addDependency(TypeScriptDependency.UTIL_STREAM); + + writer.writeDocs("@public"); + writer.openBlock("export type $LType = Omit<$T, $S> & {", "};", + typeName, + containerSymbol, + memberName, + () -> { + writer.write("$1L$2L: Uint8ArrayBlobAdapter;", memberName, optionalSuffix); + } + ); + + writer.writeDocs("@public\n\nThe output of {@link " + commandName + "}."); + writer.write( + "export interface $1L extends $1LType, __MetadataBearer {}", + typeName + ); + } + /** * Returns the list of function parameter key-value pairs to be written for * provided parameters map. diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/CommandGenerator.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/CommandGenerator.java index 82093ac4125..5622730e0f1 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/CommandGenerator.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/CommandGenerator.java @@ -15,7 +15,10 @@ package software.amazon.smithy.typescript.codegen; +import static software.amazon.smithy.typescript.codegen.CodegenUtils.getBlobPayloadMembers; import static software.amazon.smithy.typescript.codegen.CodegenUtils.getBlobStreamingMembers; +import static software.amazon.smithy.typescript.codegen.CodegenUtils.writeClientCommandBlobPayloadInputType; +import static software.amazon.smithy.typescript.codegen.CodegenUtils.writeClientCommandBlobPayloadOutputType; import static software.amazon.smithy.typescript.codegen.CodegenUtils.writeClientCommandStreamingInputType; import static software.amazon.smithy.typescript.codegen.CodegenUtils.writeClientCommandStreamingOutputType; @@ -375,38 +378,58 @@ private void addInputAndOutputTypes() { } private void writeInputType(String typeName, Optional inputShape, String commandName) { - writer.writeDocs("@public\n\nThe input for {@link " + commandName + "}."); if (inputShape.isPresent()) { StructureShape input = inputShape.get(); List blobStreamingMembers = getBlobStreamingMembers(model, input); - if (blobStreamingMembers.isEmpty()) { - writer.write("export interface $L extends $T {}", typeName, symbolProvider.toSymbol(input)); + List blobPayloadMembers = getBlobPayloadMembers(model, input); + + if (!blobStreamingMembers.isEmpty()) { + writeClientCommandStreamingInputType( + writer, symbolProvider.toSymbol(input), typeName, + blobStreamingMembers.get(0), commandName + ); + } else if (!blobPayloadMembers.isEmpty()) { + writeClientCommandBlobPayloadInputType( + writer, symbolProvider.toSymbol(input), typeName, + blobPayloadMembers.get(0), commandName + ); } else { - writeClientCommandStreamingInputType(writer, symbolProvider.toSymbol(input), typeName, - blobStreamingMembers.get(0)); + writer.writeDocs("@public\n\nThe input for {@link " + commandName + "}."); + writer.write("export interface $L extends $T {}", typeName, symbolProvider.toSymbol(input)); } } else { // If the input is non-existent, then use an empty object. + writer.writeDocs("@public\n\nThe input for {@link " + commandName + "}."); writer.write("export interface $L {}", typeName); } } private void writeOutputType(String typeName, Optional outputShape, String commandName) { - writer.writeDocs("@public\n\nThe output of {@link " + commandName + "}."); // Output types should always be MetadataBearers, possibly in addition // to a defined output shape. writer.addImport("MetadataBearer", "__MetadataBearer", TypeScriptDependency.AWS_SDK_TYPES.packageName); if (outputShape.isPresent()) { StructureShape output = outputShape.get(); List blobStreamingMembers = getBlobStreamingMembers(model, output); - if (blobStreamingMembers.isEmpty()) { + List blobPayloadMembers = getBlobPayloadMembers(model, output); + + if (!blobStreamingMembers.isEmpty()) { + writeClientCommandStreamingOutputType( + writer, symbolProvider.toSymbol(output), typeName, + blobStreamingMembers.get(0), commandName + ); + } else if (!blobPayloadMembers.isEmpty()) { + writeClientCommandBlobPayloadOutputType( + writer, symbolProvider.toSymbol(output), typeName, + blobPayloadMembers.get(0), commandName + ); + } else { + writer.writeDocs("@public\n\nThe output of {@link " + commandName + "}."); writer.write("export interface $L extends $T, __MetadataBearer {}", typeName, symbolProvider.toSymbol(outputShape.get())); - } else { - writeClientCommandStreamingOutputType(writer, symbolProvider.toSymbol(output), typeName, - blobStreamingMembers.get(0)); } } else { + writer.writeDocs("@public\n\nThe output of {@link " + commandName + "}."); writer.write("export interface $L extends __MetadataBearer {}", typeName); } } diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptDependency.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptDependency.java index 0ad6c6a2e68..7c918705b9b 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptDependency.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptDependency.java @@ -51,6 +51,7 @@ public enum TypeScriptDependency implements SymbolDependencyContainer { MIDDLEWARE_SERDE("dependencies", "@aws-sdk/middleware-serde", true), MIDDLEWARE_RETRY("dependencies", "@aws-sdk/middleware-retry", true), UTIL_RETRY("dependencies", "@aws-sdk/util-retry", false), + UTIL_STREAM("dependencies", "@aws-sdk/util-stream", false), MIDDLEWARE_STACK("dependencies", "@aws-sdk/middleware-stack", true), MIDDLEWARE_ENDPOINTS_V2("dependencies", "@aws-sdk/middleware-endpoint", false), AWS_SDK_UTIL_ENDPOINTS("dependencies", "@aws-sdk/util-endpoints", false), diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/integration/HttpProtocolGeneratorUtils.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/integration/HttpProtocolGeneratorUtils.java index e5986c14bc5..5ac122b5997 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/integration/HttpProtocolGeneratorUtils.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/integration/HttpProtocolGeneratorUtils.java @@ -248,25 +248,14 @@ static void generateMetadataDeserializer(GenerationContext context, SymbolRefere } /** - * Writes a response body stream collector. This function converts the low-level response body stream to + * Imports a response body stream collector. This function converts the low-level response body stream to * Uint8Array binary data. * * @param context The generation context. */ static void generateCollectBody(GenerationContext context) { TypeScriptWriter writer = context.getWriter(); - - writer.addImport("SerdeContext", "__SerdeContext", TypeScriptDependency.SMITHY_TYPES); - writer.write("// Collect low-level response body stream to Uint8Array."); - writer.openBlock("const collectBody = (streamBody: any = new Uint8Array(), context: __SerdeContext): " - + "Promise => {", "};", () -> { - writer.openBlock("if (streamBody instanceof Uint8Array) {", "}", () -> { - writer.write("return Promise.resolve(streamBody);"); - }); - writer.write("return context.streamCollector(streamBody) || Promise.resolve(new Uint8Array());"); - }); - - writer.write(""); + writer.addImport("collectBody", null, TypeScriptDependency.AWS_SMITHY_CLIENT); } /**