diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/StructureGenerator.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/StructureGenerator.java index 1f226c252e2..35f6e556e9c 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/StructureGenerator.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/StructureGenerator.java @@ -158,9 +158,9 @@ private void renderErrorStructure() { writer.openBlock("export interface $L extends $L {", symbol.getName(), extendsFrom); writer.write("name: $S;", shape.getId().getName()); writer.write("$$fault: $S;", errorTrait.getValue()); - StructuredMemberWriter config = new StructuredMemberWriter( + StructuredMemberWriter structuredMemberWriter = new StructuredMemberWriter( model, symbolProvider, shape.getAllMembers().values()); - config.writeMembers(writer, shape); + structuredMemberWriter.writeMembers(writer, shape); writer.closeBlock("}"); // interface writer.write(""); renderStructureNamespace(); @@ -168,8 +168,18 @@ private void renderErrorStructure() { private void renderStructureNamespace() { writer.addImport("isa", "__isa", "@aws-sdk/smithy-client"); + writer.addImport("SENSITIVE_STRING", "SENSITIVE_STRING", "@aws-sdk/smithy-client"); Symbol symbol = symbolProvider.toSymbol(shape); writer.openBlock("export namespace $L {", "}", symbol.getName(), () -> { + String objectParam = "obj"; + writer.openBlock("export const filterSensitiveLog = ($L: $L): any => ({", "})", + objectParam, symbol.getName(), + () -> { + StructuredMemberWriter structuredMemberWriter = new StructuredMemberWriter( + model, symbolProvider, shape.getAllMembers().values()); + structuredMemberWriter.writeFilterSensitiveLog(writer, objectParam); + } + ); writer.write("export const isa = (o: any): o is $L => __isa(o, $S);", symbol.getName(), shape.getId().getName() ); diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/StructuredMemberWriter.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/StructuredMemberWriter.java index f3fe419b91e..7d81260cf8a 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/StructuredMemberWriter.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/StructuredMemberWriter.java @@ -18,11 +18,16 @@ import java.util.Collection; import java.util.HashSet; import java.util.Set; +import software.amazon.smithy.codegen.core.CodegenException; import software.amazon.smithy.codegen.core.SymbolProvider; import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.CollectionShape; +import software.amazon.smithy.model.shapes.MapShape; import software.amazon.smithy.model.shapes.MemberShape; import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.StructureShape; import software.amazon.smithy.model.traits.IdempotencyTokenTrait; +import software.amazon.smithy.model.traits.SensitiveTrait; /** * Generates objects, interfaces, enums, etc. @@ -53,7 +58,7 @@ void writeMembers(TypeScriptWriter writer, Shape shape) { position++; boolean wroteDocs = !noDocs && writer.writeMemberDocs(model, member); - String memberName = TypeScriptUtils.sanitizePropertyName(symbolProvider.toMemberName(member)); + String memberName = getSanitizedMemberName(member); String optionalSuffix = shape.isUnionShape() || !isRequiredMember(member) ? "?" : ""; String typeSuffix = isRequiredMember(member) ? " | undefined" : ""; writer.write("${L}${L}${L}: ${T}${L};", memberPrefix, memberName, optionalSuffix, @@ -65,6 +70,173 @@ void writeMembers(TypeScriptWriter writer, Shape shape) { } } + void writeFilterSensitiveLog(TypeScriptWriter writer, String objectParam) { + writer.write("...$L,", objectParam); + for (MemberShape member : members) { + if (isMemberOverwriteRequired(member)) { + Shape memberTarget = model.expectShape(member.getTarget()); + String memberName = getSanitizedMemberName(member); + String memberParam = String.format("%s.%s", objectParam, memberName); + writer.openBlock("...($1L.$2L && { $2L: ", "}),", objectParam, memberName, () -> { + if (member.getMemberTrait(model, SensitiveTrait.class).isPresent()) { + // member is Sensitive, hide the value. + writer.write("SENSITIVE_STRING"); + } else if (memberTarget instanceof StructureShape) { + writeStructureFilterSensitiveLog(writer, memberTarget, memberParam); + } else if (memberTarget instanceof CollectionShape) { + MemberShape collectionMember = ((CollectionShape) memberTarget).getMember(); + writeCollectionFilterSensitiveLog(writer, collectionMember, memberParam); + } else if (memberTarget instanceof MapShape) { + MemberShape mapMember = ((MapShape) memberTarget).getValue(); + writeMapFilterSensitiveLog(writer, mapMember, memberParam); + } + }); + } + } + } + + /** + * Recursively writes filterSensitiveLog for StructureShape. + */ + private void writeStructureFilterSensitiveLog( + TypeScriptWriter writer, + Shape structureTarget, + String structureParam + ) { + if (structureTarget.hasTrait(SensitiveTrait.class)) { + // member is Sensitive, hide the value. + writer.write("SENSITIVE_STRING"); + return; + } + // Call filterSensitiveLog on Structure. + writer.write("$T.filterSensitiveLog($L)", symbolProvider.toSymbol(structureTarget), structureParam); + } + + /** + * Recursively writes filterSensitiveLog for CollectionShape. + */ + private void writeCollectionFilterSensitiveLog( + TypeScriptWriter writer, + MemberShape collectionMember, + String collectionParam + ) { + if (collectionMember.getMemberTrait(model, SensitiveTrait.class).isPresent()) { + // member is Sensitive, hide the value. + writer.write("SENSITIVE_STRING"); + return; + } + + writer.openBlock("$L.map(", ")", collectionParam, () -> { + String itemParam = "item"; + Shape collectionMemberTarget = model.expectShape(collectionMember.getTarget()); + writer.write("$L => ", itemParam); + if (collectionMemberTarget instanceof StructureShape) { + writeStructureFilterSensitiveLog(writer, collectionMemberTarget, itemParam); + } else if (collectionMemberTarget instanceof CollectionShape) { + MemberShape nestedCollectionMember = ((CollectionShape) collectionMemberTarget).getMember(); + writeCollectionFilterSensitiveLog(writer, nestedCollectionMember, itemParam); + } else if (collectionMemberTarget instanceof MapShape) { + MemberShape mapMember = ((MapShape) collectionMemberTarget).getValue(); + writeMapFilterSensitiveLog(writer, mapMember, itemParam); + } else { + // This path should not reach because of recursive isIterationRequired. + throw new CodegenException(String.format( + "CollectionFilterSensitiveLog attempted for %s while it was not required", + collectionMemberTarget.getType() + )); + // For quick-fix in case of high severity issue: + // comment out the exception above and uncomment the line below. + // writer.write("$1L => $1L", itemParam); + } + }); + } + + /** + * Recursively writes filterSensitiveLog for MapShape. + */ + private void writeMapFilterSensitiveLog(TypeScriptWriter writer, MemberShape mapMember, String mapParam) { + if (mapMember.getMemberTrait(model, SensitiveTrait.class).isPresent()) { + // member is Sensitive, hide the value. + writer.write("SENSITIVE_STRING"); + return; + } + + String accParam = "acc"; // accumulator for the reducer + String keyParam = "key"; // key of the Object.entries() key-value pair + String valueParam = "value"; // value of the Object.entries() key-value pair + + // Reducer is common to all shapes. + writer.openBlock("Object.entries($L).reduce(($L: any, [$L, $L]: [string, $T]) => ({", "}), {})", + mapParam, accParam, keyParam, valueParam, symbolProvider.toSymbol(mapMember), () -> { + writer.write("...$L,", accParam); + Shape mapMemberTarget = model.expectShape(mapMember.getTarget()); + writer.openBlock("[$L]: ", ",", keyParam, () -> { + if (mapMemberTarget instanceof StructureShape) { + writeStructureFilterSensitiveLog(writer, mapMemberTarget, valueParam); + } else if (mapMemberTarget instanceof CollectionShape) { + MemberShape collectionMember = ((CollectionShape) mapMemberTarget).getMember(); + writeCollectionFilterSensitiveLog(writer, collectionMember, valueParam); + } else if (mapMemberTarget instanceof MapShape) { + MemberShape nestedMapMember = ((MapShape) mapMemberTarget).getValue(); + writeMapFilterSensitiveLog(writer, nestedMapMember, valueParam); + } else { + // This path should not reach because of recursive isIterationRequired. + throw new CodegenException(String.format( + "MapFilterSensitiveLog attempted for %s while it was not required", + mapMemberTarget.getType() + )); + // For quick-fix in case of high severity issue: + // comment out the exception above and uncomment the line below. + // writer.write("$L", valueParam); + } + + }); + } + ); + } + + /** + * Identifies if iteration is required on member. + * + * @param member a {@link MemberShape} to check for iteration required. + * @return Returns true if the iteration is required on member. + */ + private boolean isIterationRequired(MemberShape member) { + Shape memberTarget = model.expectShape(member.getTarget()); + if (memberTarget instanceof StructureShape) { + return true; + } else if (memberTarget instanceof CollectionShape) { + MemberShape collectionMember = ((CollectionShape) memberTarget).getMember(); + return isIterationRequired(collectionMember); + } else if (memberTarget instanceof MapShape) { + MemberShape mapMember = ((MapShape) memberTarget).getValue(); + return isIterationRequired(mapMember); + } + return false; + } + + /** + * Identifies if member needs to be overwritten in filterSensitiveLog. + * + * @param member a {@link MemberShape} to check if overwrite is required. + * @return Returns true if the overwrite is required on member. + */ + private boolean isMemberOverwriteRequired(MemberShape member) { + return ( + member.getMemberTrait(model, SensitiveTrait.class).isPresent() || isIterationRequired(member) + ); + } + + /** + * Returns the member name to be used in generation. + * + * @param member a {@link MemberShape} to be sanitized. + * @return Returns the member name to be used in generation. + */ + private String getSanitizedMemberName(MemberShape member) { + return TypeScriptUtils.sanitizePropertyName(symbolProvider.toMemberName(member)); + } + /** * Identifies if a member should be required on the generated interface. * diff --git a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/StructureGeneratorTest.java b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/StructureGeneratorTest.java index 36b7d845b75..d15c1179d0d 100644 --- a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/StructureGeneratorTest.java +++ b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/StructureGeneratorTest.java @@ -43,7 +43,158 @@ public void properlyGeneratesRequiredMessageMemberOfException() { + "}"); } - public void testErrorStructureCodegen(String file, String expectedType) { + @Test + public void filtersSensitiveSimpleShape() { + testStructureCodegen("test-sensitive-simple-shape.smithy", + " export const filterSensitiveLog = (obj: GetFooInput): any => ({\n" + + " ...obj,\n" + + " ...(obj.password && { password:\n" + + " SENSITIVE_STRING\n" + + " }),\n" + + " })\n"); + } + + @Test + public void callsFilterForStructureWithSensitiveData() { + testStructureCodegen("test-structure-with-sensitive-data.smithy", + " export const filterSensitiveLog = (obj: GetFooInput): any => ({\n" + + " ...obj,\n" + + " ...(obj.foo && { foo:\n" + + " User.filterSensitiveLog(obj.foo)\n" + + " }),\n" + + " })\n"); + } + + @Test + public void callsFilterInStructureWithSensitiveData() { + testStructureCodegen("test-structure-with-sensitive-data.smithy", + " export const filterSensitiveLog = (obj: User): any => ({\n" + + " ...obj,\n" + + " ...(obj.password && { password:\n" + + " SENSITIVE_STRING\n" + + " }),\n" + + " })\n"); + } + + @Test + public void filtersSensitiveStructure() { + testStructureCodegen("test-sensitive-structure.smithy", + " export const filterSensitiveLog = (obj: GetFooInput): any => ({\n" + + " ...obj,\n" + + " ...(obj.foo && { foo:\n" + + " SENSITIVE_STRING\n" + + " }),\n" + + " })\n"); + } + + @Test + public void filtersSensitiveMemberPointingToStructure() { + testStructureCodegen("test-sensitive-member-pointing-to-structure.smithy", + " export const filterSensitiveLog = (obj: GetFooInput): any => ({\n" + + " ...obj,\n" + + " ...(obj.sensitiveFoo && { sensitiveFoo:\n" + + " SENSITIVE_STRING\n" + + " }),\n" + + " })\n"); + } + + @Test + public void callsFilterForListWithSensitiveData() { + testStructureCodegen("test-list-with-sensitive-data.smithy", + " export const filterSensitiveLog = (obj: GetFooInput): any => ({\n" + + " ...obj,\n" + + " ...(obj.foo && { foo:\n" + + " obj.foo.map(\n" + + " item =>\n" + + " User.filterSensitiveLog(item)\n" + + " )\n" + + " }),\n" + + " })\n"); + } + + @Test + public void callsFilterInListWithSensitiveData() { + testStructureCodegen("test-list-with-sensitive-data.smithy", + " export const filterSensitiveLog = (obj: User): any => ({\n" + + " ...obj,\n" + + " ...(obj.password && { password:\n" + + " SENSITIVE_STRING\n" + + " }),\n" + + " })\n"); + } + + @Test + public void filtersSensitiveList() { + testStructureCodegen("test-sensitive-list.smithy", + " export const filterSensitiveLog = (obj: GetFooInput): any => ({\n" + + " ...obj,\n" + + " ...(obj.foo && { foo:\n" + + " SENSITIVE_STRING\n" + + " }),\n" + + " })\n"); + } + + @Test + public void filtersSensitiveMemberPointingToList() { + testStructureCodegen("test-sensitive-member-pointing-to-list.smithy", + " export const filterSensitiveLog = (obj: GetFooInput): any => ({\n" + + " ...obj,\n" + + " ...(obj.sensitiveFoo && { sensitiveFoo:\n" + + " SENSITIVE_STRING\n" + + " }),\n" + + " })\n"); + } + + @Test + public void callsFilterForMapWithSensitiveData() { + testStructureCodegen("test-map-with-sensitive-data.smithy", + " export const filterSensitiveLog = (obj: User): any => ({\n" + + " ...obj,\n" + + " ...(obj.password && { password:\n" + + " SENSITIVE_STRING\n" + + " }),\n" + + " })\n"); + } + + @Test + public void callsFilterInMapWithSensitiveData() { + testStructureCodegen("test-map-with-sensitive-data.smithy", + " export const filterSensitiveLog = (obj: GetFooInput): any => ({\n" + + " ...obj,\n" + + " ...(obj.foo && { foo:\n" + + " Object.entries(obj.foo).reduce((acc: any, [key, value]: [string, User]) => ({\n" + + " ...acc,\n" + + " [key]:\n" + + " User.filterSensitiveLog(value)\n" + + " ,\n" + + " }), {})\n" + + " }),\n" + + " })\n"); + } + + @Test + public void filtersSensitiveMap() { + testStructureCodegen("test-sensitive-map.smithy", + " export const filterSensitiveLog = (obj: GetFooInput): any => ({\n" + + " ...obj,\n" + + " ...(obj.foo && { foo:\n" + + " SENSITIVE_STRING\n" + + " }),\n" + + " })\n"); + } + + @Test + public void filtersSensitiveMemberPointingToMap() { + testStructureCodegen("test-sensitive-member-pointing-to-map.smithy", + " export const filterSensitiveLog = (obj: GetFooInput): any => ({\n" + + " ...obj,\n" + + " ...(obj.sensitiveFoo && { sensitiveFoo:\n" + + " SENSITIVE_STRING\n" + + " }),\n" + + " })\n"); + } + + private String testStructureCodegen(String file, String expectedType) { Model model = Model.assembler() .addImport(getClass().getResource(file)) .assemble() @@ -53,22 +204,27 @@ public void testErrorStructureCodegen(String file, String expectedType) { .model(model) .fileManifest(manifest) .settings(Node.objectNodeBuilder() - .withMember("service", Node.from("smithy.example#Example")) - .withMember("package", Node.from("example")) - .withMember("packageVersion", Node.from("1.0.0")) - .build()) + .withMember("service", Node.from("smithy.example#Example")) + .withMember("package", Node.from("example")) + .withMember("packageVersion", Node.from("1.0.0")) + .build()) .build(); new TypeScriptCodegenPlugin().execute(context); String contents = manifest.getFileString("/models/index.ts").get(); + assertThat(contents, containsString(expectedType)); + return contents; + } + + private void testErrorStructureCodegen(String file, String expectedType) { + String contents = testStructureCodegen(file, expectedType); + assertThat(contents, containsString("as __isa")); assertThat(contents, containsString("as __SmithyException")); - assertThat(contents, containsString(expectedType)); - assertThat(contents, containsString("namespace Err {\n" - + " export const isa = (o: any): o is Err => " - + "__isa(o, \"Err\");\n" - + "}")); + assertThat(contents, containsString("namespace Err {")); + assertThat(contents, containsString(" export const isa = (o: any): o is Err => " + + "__isa(o, \"Err\");\n")); } @Test diff --git a/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-list-with-sensitive-data.smithy b/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-list-with-sensitive-data.smithy new file mode 100644 index 00000000000..3b9507d796e --- /dev/null +++ b/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-list-with-sensitive-data.smithy @@ -0,0 +1,24 @@ +namespace smithy.example + +@protocols([{name: "aws.rest-json-1.1"}]) +service Example { + version: "1.0.0", + operations: [GetFoo] +} + +operation GetFoo(GetFooInput) + +structure GetFooInput { + foo: UserList +} + +list UserList { + member: User +} + +structure User { + username: String, + + @sensitive + password: String +} \ No newline at end of file diff --git a/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-map-with-sensitive-data.smithy b/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-map-with-sensitive-data.smithy new file mode 100644 index 00000000000..e678ed6cceb --- /dev/null +++ b/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-map-with-sensitive-data.smithy @@ -0,0 +1,25 @@ +namespace smithy.example + +@protocols([{name: "aws.rest-json-1.1"}]) +service Example { + version: "1.0.0", + operations: [GetFoo] +} + +operation GetFoo(GetFooInput) + +structure GetFooInput { + foo: UserMap +} + +map UserMap { + key: String, + value: User +} + +structure User { + username: String, + + @sensitive + password: String +} \ No newline at end of file diff --git a/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-list.smithy b/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-list.smithy new file mode 100644 index 00000000000..5dd5b82655d --- /dev/null +++ b/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-list.smithy @@ -0,0 +1,18 @@ +namespace smithy.example + +@protocols([{name: "aws.rest-json-1.1"}]) +service Example { + version: "1.0.0", + operations: [GetFoo] +} + +operation GetFoo(GetFooInput) + +structure GetFooInput { + foo: SecretNamesList +} + +@sensitive +list SecretNamesList { + member: String +} \ No newline at end of file diff --git a/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-map.smithy b/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-map.smithy new file mode 100644 index 00000000000..1f555c7745a --- /dev/null +++ b/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-map.smithy @@ -0,0 +1,19 @@ +namespace smithy.example + +@protocols([{name: "aws.rest-json-1.1"}]) +service Example { + version: "1.0.0", + operations: [GetFoo] +} + +operation GetFoo(GetFooInput) + +structure GetFooInput { + foo: SecretNamesMap +} + +@sensitive +map SecretNamesMap { + key: String, + value: String +} \ No newline at end of file diff --git a/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-member-pointing-to-list.smithy b/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-member-pointing-to-list.smithy new file mode 100644 index 00000000000..35e4ca49ab8 --- /dev/null +++ b/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-member-pointing-to-list.smithy @@ -0,0 +1,18 @@ +namespace smithy.example + +@protocols([{name: "aws.rest-json-1.1"}]) +service Example { + version: "1.0.0", + operations: [GetFoo] +} + +operation GetFoo(GetFooInput) + +structure GetFooInput { + @sensitive + sensitiveFoo: NamesList +} + +list NamesList { + member: String +} \ No newline at end of file diff --git a/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-member-pointing-to-map.smithy b/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-member-pointing-to-map.smithy new file mode 100644 index 00000000000..621c7e62302 --- /dev/null +++ b/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-member-pointing-to-map.smithy @@ -0,0 +1,19 @@ +namespace smithy.example + +@protocols([{name: "aws.rest-json-1.1"}]) +service Example { + version: "1.0.0", + operations: [GetFoo] +} + +operation GetFoo(GetFooInput) + +structure GetFooInput { + @sensitive + sensitiveFoo: NamesMap +} + +map NamesMap { + key: String, + value: String +} \ No newline at end of file diff --git a/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-member-pointing-to-structure.smithy b/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-member-pointing-to-structure.smithy new file mode 100644 index 00000000000..6cdce95a54c --- /dev/null +++ b/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-member-pointing-to-structure.smithy @@ -0,0 +1,19 @@ +namespace smithy.example + +@protocols([{name: "aws.rest-json-1.1"}]) +service Example { + version: "1.0.0", + operations: [GetFoo] +} + +operation GetFoo(GetFooInput) + +structure GetFooInput { + @sensitive + sensitiveFoo: User +} + +structure User { + firstname: String, + lastname: String +} \ No newline at end of file diff --git a/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-simple-shape.smithy b/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-simple-shape.smithy new file mode 100644 index 00000000000..dd7072376a6 --- /dev/null +++ b/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-simple-shape.smithy @@ -0,0 +1,16 @@ +namespace smithy.example + +@protocols([{name: "aws.rest-json-1.1"}]) +service Example { + version: "1.0.0", + operations: [GetFoo] +} + +operation GetFoo(GetFooInput) + +structure GetFooInput { + username: String, + + @sensitive + password: String +} \ No newline at end of file diff --git a/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-structure.smithy b/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-structure.smithy new file mode 100644 index 00000000000..16109040b66 --- /dev/null +++ b/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-sensitive-structure.smithy @@ -0,0 +1,19 @@ +namespace smithy.example + +@protocols([{name: "aws.rest-json-1.1"}]) +service Example { + version: "1.0.0", + operations: [GetFoo] +} + +operation GetFoo(GetFooInput) + +structure GetFooInput { + foo: SecretUser +} + +@sensitive +structure SecretUser { + firstname: String, + lastname: String +} \ No newline at end of file diff --git a/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-structure-with-sensitive-data.smithy b/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-structure-with-sensitive-data.smithy new file mode 100644 index 00000000000..a5f2f1501d5 --- /dev/null +++ b/smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/test-structure-with-sensitive-data.smithy @@ -0,0 +1,20 @@ +namespace smithy.example + +@protocols([{name: "aws.rest-json-1.1"}]) +service Example { + version: "1.0.0", + operations: [GetFoo] +} + +operation GetFoo(GetFooInput) + +structure GetFooInput { + foo: User +} + +structure User { + username: String, + + @sensitive + password: String +} \ No newline at end of file