Skip to content

Commit

Permalink
widen blob payload input types, add blob payload output converter
Browse files Browse the repository at this point in the history
  • Loading branch information
kuhe committed Jun 16, 2023
1 parent ae9cb41 commit 47291df
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -135,10 +136,11 @@ static List<MemberShape> 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() ? "" : "?";
Expand All @@ -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);
}

Expand All @@ -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,
Expand All @@ -180,6 +182,73 @@ static void writeClientCommandStreamingOutputType(
);
}

static List<MemberShape> 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -375,38 +378,58 @@ private void addInputAndOutputTypes() {
}

private void writeInputType(String typeName, Optional<StructureShape> inputShape, String commandName) {
writer.writeDocs("@public\n\nThe input for {@link " + commandName + "}.");
if (inputShape.isPresent()) {
StructureShape input = inputShape.get();
List<MemberShape> blobStreamingMembers = getBlobStreamingMembers(model, input);
if (blobStreamingMembers.isEmpty()) {
writer.write("export interface $L extends $T {}", typeName, symbolProvider.toSymbol(input));
List<MemberShape> 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<StructureShape> 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<MemberShape> blobStreamingMembers = getBlobStreamingMembers(model, output);
if (blobStreamingMembers.isEmpty()) {
List<MemberShape> 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);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Uint8Array> => {", "};", () -> {
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);
}

/**
Expand Down

0 comments on commit 47291df

Please sign in to comment.