diff --git a/Packages/ClientRuntime/Sources/Networking/Http/UnknownHttpServiceError.swift b/Packages/ClientRuntime/Sources/Networking/Http/UnknownHttpServiceError.swift index b4f21d81c..69b436105 100644 --- a/Packages/ClientRuntime/Sources/Networking/Http/UnknownHttpServiceError.swift +++ b/Packages/ClientRuntime/Sources/Networking/Http/UnknownHttpServiceError.swift @@ -4,7 +4,7 @@ */ /// General Service Error structure used when exact error could not be deduced from the `HttpResponse` -public struct UnknownHttpServiceError: HttpServiceError { +public struct UnknownHttpServiceError: HttpServiceError, Swift.Equatable { public var _isThrottling: Bool = false public var _statusCode: HttpStatusCode? diff --git a/Packages/ClientRuntime/Sources/Serialization/SerializationUtils/HeaderUtils.swift b/Packages/ClientRuntime/Sources/Serialization/SerializationUtils/HeaderUtils.swift index 967704692..1408f2da9 100644 --- a/Packages/ClientRuntime/Sources/Serialization/SerializationUtils/HeaderUtils.swift +++ b/Packages/ClientRuntime/Sources/Serialization/SerializationUtils/HeaderUtils.swift @@ -5,49 +5,153 @@ import Foundation +fileprivate extension String { + func readNextQuoted(startIdx: String.Index, delim: Character = ",") throws -> (String.Index, String) { + // startIdx is start of the quoted value, there must be at least an ending quotation mark + if !(self.index(after: startIdx) < self.endIndex) { + throw HeaderDeserializationError.invalidStringHeaderList(value: self) + } + + // find first non-escaped quote or end of string + var endIdx = self.index(after: startIdx) + while endIdx < self.endIndex { + let char = self[endIdx] + if char == "\\" { + // skip escaped chars + endIdx = self.index(after: endIdx) + } else if char == "\"" { + break + } + endIdx = self.index(after: endIdx) + } + + let next = self[self.index(after: startIdx)..= self.endIndex || self[endIdx] != "\"" { + throw HeaderDeserializationError.invalidStringHeaderList(value: self) + } + assert(endIdx < self.endIndex) + assert(self[endIdx] == "\"") + + endIdx = self.index(after: endIdx) + + // consume delim + while endIdx < self.endIndex { + let char = self[endIdx] + if char == " " || char == "\t" { + endIdx = self.index(after: endIdx) + } else if char == delim { + endIdx = self.index(after: endIdx) + break + } else { + throw HeaderDeserializationError.invalidStringHeaderList(value: self) + } + } + + let unescaped = next.replacingOccurrences(of: "\\\"", with: "\"") + .replacingOccurrences(of: "\\\\", with: "\\") + + return (endIdx, unescaped) + } + + func readNextUnquoted(startIdx: String.Index, delim: Character = ",") -> (String.Index, String) { + assert(startIdx < self.endIndex) + + var endIdx = startIdx + + while endIdx < self.endIndex && self[endIdx] != delim { + endIdx = self.index(after: endIdx) + } + + let next = self[startIdx.. String { + if value.trim().count != value.count || value.contains(where: { char1 in + QUOTABLE_HEADER_VALUE_CHARS.contains { char2 in + char1 == char2 + } + }) { + let formatted = value.replacingOccurrences(of: "\\", with: "\\\\") + .replacingOccurrences(of: "\"", with: "\\\"") + return "\"\(formatted)\"" + } else { + return value + } +} + /// Expands the compact Header Representation of List of any type except Dates -public func splitHeaderListValues(_ value: String?) -> [String]? { - guard let value = value else { return nil} - return value.components(separatedBy: ",").map { $0.trim() } +public func splitHeaderListValues(_ value: String?) throws -> [String]? { + guard let value = value else { + return nil + } + var results: [String] = [] + var currIdx = value.startIndex + + while currIdx < value.endIndex { + let next: (idx: String.Index, str: String) + + switch value[currIdx] { + case " ", "\t": + currIdx = value.index(after: currIdx) + continue + case "\"": + next = try value.readNextQuoted(startIdx: currIdx) + default: + next = value.readNextUnquoted(startIdx: currIdx) + } + + currIdx = next.idx + results.append(next.str) + } + + return results + } /// Expands the compact HTTP Header Representation of List of Dates public func splitHttpDateHeaderListValues(_ value: String?) throws -> [String]? { guard let value = value else { return nil} - let separator = "," - let totalSeparators = value.components(separatedBy: separator).count - 1 - if totalSeparators <= 1 { + let n = value.filter({$0 == ","}).count + + if n <= 1 { return [value] - } else if totalSeparators % 2 == 0 { - return splitHeaderListValues(value) + } else if n % 2 == 0 { + throw HeaderDeserializationError.invalidTimestampHeaderList(value: value) } var cnt = 0 var splits: [String] = [] - var start = 0 - var startIdx = value.index(value.startIndex, offsetBy: start) - - for i in 1...value.count { - let currIdx = value.index(value.startIndex, offsetBy: i-1) - if value[currIdx] == "," { + var startIdx = value.startIndex + + for i in value.indices[value.startIndex.. 1 { - startIdx = value.index(value.startIndex, offsetBy: start) - splits.append(String(value[startIdx..() { override fun structureShape(shape: StructureShape): Void? { writers.useShapeWriter(shape) { writer: SwiftWriter -> StructureGenerator(model, symbolProvider, writer, shape, settings, protocolGenerator?.serviceErrorProtocolSymbol).render() } - if (shape.hasTrait() || shape.members().any { it.hasTrait() }) { + if (shape.hasTrait() || shape.members().any { it.hasTrait() || model.expectShape(it.target).hasTrait() }) { writers.useShapeExtensionWriter(shape, "CustomDebugStringConvertible") { writer: SwiftWriter -> - CustomDebugStringConvertibleGenerator(symbolProvider, writer, shape).render() + CustomDebugStringConvertibleGenerator(symbolProvider, writer, shape, model).render() } } return null diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/CustomDebugStringConvertibleGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/CustomDebugStringConvertibleGenerator.kt index 1b301a977..90ab94009 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/CustomDebugStringConvertibleGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/CustomDebugStringConvertibleGenerator.kt @@ -7,6 +7,7 @@ package software.amazon.smithy.swift.codegen.integration import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.codegen.core.SymbolProvider +import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.SensitiveTrait @@ -18,7 +19,8 @@ import software.amazon.smithy.swift.codegen.model.toMemberNames class CustomDebugStringConvertibleGenerator( private val symbolProvider: SymbolProvider, private val writer: SwiftWriter, - private val shape: StructureShape + private val shape: StructureShape, + private val model: Model ) { companion object { const val REDACT_STRING = "CONTENT_REDACTED" @@ -46,11 +48,11 @@ class CustomDebugStringConvertibleGenerator( val symbolName = structSymbol.name writer.writeInline("\"$symbolName(") val membersWithoutSensitiveTrait = membersSortedByName - .filterNot { it.hasTrait(SensitiveTrait::class.java) } + .filterNot { it.hasTrait() || model.expectShape(it.target).hasTrait() } .sortedBy { it.memberName } .toList() val membersWithSensitiveTrait = membersSortedByName - .filter { it.hasTrait(SensitiveTrait::class.java) } + .filter { it.hasTrait() || model.expectShape(it.target).hasTrait() } .sortedBy { it.memberName } .toList() for (member in membersWithoutSensitiveTrait) { diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolUnitTestResponseGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolUnitTestResponseGenerator.kt index 3b3837b22..9d0b3ff08 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolUnitTestResponseGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolUnitTestResponseGenerator.kt @@ -58,25 +58,36 @@ open class HttpProtocolUnitTestResponseGenerator protected constructor(builder: } private fun renderBuildHttpResponseParams(test: HttpResponseTestCase) { - val params = mutableListOf() - params.add("code: ${test.code}") - if (test.headers.isNotEmpty()) { - params.add(renderBuildHttpResponseHeaderParams(test)) - } - test.body.ifPresent { body -> - if (body.isNotBlank() && body.isNotEmpty()) { - params.add("content: HttpBody.stream(ByteStream.from(data: \"\"\"\n${body.replace(".000", "")}\n\"\"\".data(using: .utf8)!))") + writer.write("code: \$L,", test.code) + renderExpectedHeaders(test) + test.body.ifPresentOrElse( + { + body -> + if (body.isNotBlank() && body.isNotEmpty()) { + writer.write("content: HttpBody.stream(ByteStream.from(data: \"\"\"\n${body.replace(".000", "")}\n\"\"\".data(using: .utf8)!))") + } else { + writer.write("content: HttpBody.empty") + } + }, + { + writer.write("content: HttpBody.empty") } - } - writer.write(params.joinToString(",\n")) + ) } - private fun renderBuildHttpResponseHeaderParams(test: HttpResponseTestCase): String { - var headers = mutableListOf() - for (hdr in test.headers.entries) { - headers.add(" \"${hdr.key}\": \"${hdr.value}\"") + private fun renderExpectedHeaders(test: HttpResponseTestCase) { + if (test.headers.isNotEmpty()) { + writer.openBlock("headers: [") + .call { + for ((idx, hdr) in test.headers.entries.withIndex()) { + val suffix = if (idx < test.headers.size - 1) "," else "" + writer.write("\$S: \$S$suffix", hdr.key, hdr.value) + } + } + .closeBlock("],") + } else { + writer.write("headers: nil,") } - return "headers: [\n${headers.joinToString(",\n")}\n]" } protected fun needsResponseDecoder(test: HttpResponseTestCase): Boolean { diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/DefaultCodingKeysCustomizable.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/DefaultCodingKeysCustomizable.kt new file mode 100644 index 000000000..55c16f864 --- /dev/null +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/DefaultCodingKeysCustomizable.kt @@ -0,0 +1,14 @@ +package software.amazon.smithy.swift.codegen.integration.codingKeys + +import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.swift.codegen.SwiftWriter +import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator + +class DefaultCodingKeysCustomizable : CodingKeysCustomizable { + override fun shouldHandleMember(member: MemberShape): Boolean { + return false + } + + override fun handleMember(ctx: ProtocolGenerator.GenerationContext, writer: SwiftWriter, member: MemberShape) { + } +} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseHeaders.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseHeaders.kt index cca5adf4b..3f3c24e90 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseHeaders.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseHeaders.kt @@ -141,7 +141,7 @@ class HttpResponseHeaders( if (memberTarget.isSetShape) { memberValue = "${SwiftTypes.Set}(${memberName}HeaderValues)" } - writer.write("if let ${memberName}HeaderValues = $splitFnPrefix$splitFn(${memberName}HeaderValue) {") + writer.write("if let ${memberName}HeaderValues = try $splitFnPrefix$splitFn(${memberName}HeaderValue) {") writer.indent() // render map function val collectionMemberTargetShape = ctx.model.expectShape(memberTarget.member.target) diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/middlewares/providers/HttpHeaderProvider.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/middlewares/providers/HttpHeaderProvider.kt index 279fa581f..0fec462d2 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/middlewares/providers/HttpHeaderProvider.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/middlewares/providers/HttpHeaderProvider.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.model.knowledge.HttpBindingIndex import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.TimestampShape import software.amazon.smithy.model.traits.TimestampFormatTrait import software.amazon.smithy.swift.codegen.ClientRuntimeTypes import software.amazon.smithy.swift.codegen.SwiftDependency @@ -114,6 +115,8 @@ class HttpHeaderProvider( writer.openBlock("if $memberName != ${member.defaultValue(ctx.symbolProvider)} {", "}") { writer.write("items.add(Header(name: \"$paramName\", value: \$N($memberNameWithExtension)))", SwiftTypes.String) } + } else if (inCollection && ctx.model.expectShape(member.target) !is TimestampShape) { + writer.write("items.add(Header(name: \"$paramName\", value: quoteHeaderValue(\$N($memberNameWithExtension))))", SwiftTypes.String) } else { writer.write("items.add(Header(name: \"$paramName\", value: \$N($memberNameWithExtension)))", SwiftTypes.String) } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/MemberShapeDecodeGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/MemberShapeDecodeGenerator.kt index 13e79da03..2cbf7e380 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/MemberShapeDecodeGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/MemberShapeDecodeGenerator.kt @@ -123,9 +123,9 @@ abstract class MemberShapeDecodeGenerator( memberName: String, containerName: String, topLevelMember: MemberShape, + parentMember: Shape, level: Int = 0 ) { - val isSparse = ctx.model.expectShape(topLevelMember.target).hasTrait() val symbolName = determineSymbolForShape(shape, true) val originalSymbol = ctx.symbolProvider.toSymbol(shape) val decodedMemberName = "${memberName.removeSurroundingBackticks()}Decoded$level" @@ -153,15 +153,15 @@ abstract class MemberShapeDecodeGenerator( writer.write("var \$L:\$T = nil", decodedMemberName, originalSymbol) writer.openBlock("if let \$L = \$L {", "}", listContainerName, listContainerName) { writer.write("\$L = \$N()", decodedMemberName, originalSymbol) - renderDecodeListTarget(nestedTarget, decodedMemberName, listContainerName, insertMethod, topLevelMember, shape.isSetShape, level) + renderDecodeListTarget(nestedTarget, decodedMemberName, listContainerName, insertMethod, topLevelMember, shape, level) } renderAssigningDecodedMember(topLevelMember, decodedMemberName) } else { writer.openBlock("if let \$L = \$L {", "}", memberName, memberName) { val previousDecodedMemberName = "${memberName.removeSurroundingBackticks()}Decoded${level - 1}" - val symbolName = determineSymbolForShape(shape, isSparse) + val symbolName = determineSymbolForShape(shape, false) writer.write("\$L = \$L()", previousDecodedMemberName, symbolName) - renderDecodeListTarget(nestedTarget, containerName, memberName, insertMethod, topLevelMember, shape.isSetShape, level) + renderDecodeListTarget(nestedTarget, containerName, memberName, insertMethod, topLevelMember, shape, level) } } } @@ -175,9 +175,8 @@ abstract class MemberShapeDecodeGenerator( writer.write("\$L = \$L", topLevelMemberName, decodedMemberName) } - private fun renderDecodeListTarget(shape: Shape, decodedMemberName: String, collectionName: String, insertMethod: String, topLevelMember: MemberShape, isSetShape: Boolean, level: Int = 0) { - val topLevelShape = ctx.model.expectShape(topLevelMember.target) - val isSparse = topLevelShape.hasTrait() + private fun renderDecodeListTarget(shape: Shape, decodedMemberName: String, collectionName: String, insertMethod: String, topLevelMember: MemberShape, parentMember: Shape, level: Int = 0) { + val isSparse = parentMember.hasTrait() val iteratorName = "${shape.type.name.lowercase()}$level" val symbolName = determineSymbolForShape(shape, false) val terminator = "?" @@ -190,7 +189,7 @@ abstract class MemberShapeDecodeGenerator( .orElse(defaultTimestampFormat) if (tsFormat == TimestampFormatTrait.Format.EPOCH_SECONDS) { // if decoding a double decode as normal from [[Date]].self - if (!isSparse && !isSetShape) { + if (!isSparse) { writer.openBlock("if let $iteratorName = $iteratorName {", "}") { writer.write("${decodedMemberName}$terminator.$insertMethod($iteratorName)") } @@ -210,7 +209,7 @@ abstract class MemberShapeDecodeGenerator( is CollectionShape -> { val nestedDecodedMemberName = "${iteratorName}Decoded$level" writer.write("var \$L: \$L? = nil", nestedDecodedMemberName, symbolName) - renderDecodeListMember(shape, iteratorName, nestedDecodedMemberName, topLevelMember, level + 1) + renderDecodeListMember(shape, iteratorName, nestedDecodedMemberName, topLevelMember, parentMember, level + 1) writer.openBlock("if let $nestedDecodedMemberName = $nestedDecodedMemberName {", "}") { writer.write("$decodedMemberName$terminator.$insertMethod($nestedDecodedMemberName)") } @@ -224,7 +223,7 @@ abstract class MemberShapeDecodeGenerator( } } else -> { - if (!isSparse && !isSetShape) { + if (!isSparse) { writer.openBlock("if let $iteratorName = $iteratorName {", "}") { writer.write("${decodedMemberName}$terminator.$insertMethod($iteratorName)") } @@ -289,7 +288,7 @@ abstract class MemberShapeDecodeGenerator( is CollectionShape -> { val nestedDecodedMemberName = "${valueIterator}Decoded$level" writer.write("var \$L: \$L? = nil", nestedDecodedMemberName, symbolName) - renderDecodeListMember(valueTargetShape, valueIterator, nestedDecodedMemberName, topLevelMember, level + 1) + renderDecodeListMember(valueTargetShape, valueIterator, nestedDecodedMemberName, topLevelMember, valueTargetShape.member, level + 1) writer.write("$decodedMemberName?[key$level] = $nestedDecodedMemberName") } is MapShape -> { diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/StructDecodeGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/StructDecodeGenerator.kt index 75119efed..b5cc06a22 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/StructDecodeGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/StructDecodeGenerator.kt @@ -57,7 +57,7 @@ class StructDecodeGenerator( val target = ctx.model.expectShape(member.target) val memberNames = ctx.symbolProvider.toMemberNames(member) when (target) { - is CollectionShape -> renderDecodeListMember(target, memberNames.second, containerName, member) + is CollectionShape -> renderDecodeListMember(target, memberNames.second, containerName, member, parentMember = member) is MapShape -> renderDecodeMapMember(target, memberNames.second, containerName, member) is TimestampShape -> renderDecodeForTimestamp(ctx, target, member, containerName) else -> writeDecodeForPrimitive(target, member, containerName) diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/UnionDecodeGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/UnionDecodeGenerator.kt index 07be84ce6..7216840a0 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/UnionDecodeGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/UnionDecodeGenerator.kt @@ -27,7 +27,7 @@ class UnionDecodeGenerator( val target = ctx.model.expectShape(member.target) val memberName = ctx.symbolProvider.toMemberName(member) when (target) { - is CollectionShape -> renderDecodeListMember(target, memberName, containerName, member) + is CollectionShape -> renderDecodeListMember(target, memberName, containerName, member, member) is MapShape -> renderDecodeMapMember(target, memberName, containerName, member) is TimestampShape -> renderDecodeForTimestamp(ctx, target, member, containerName) else -> writeDecodeForPrimitive(target, member, containerName) diff --git a/smithy-swift-codegen/src/test/kotlin/HttpProtocolUnitTestResponseGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/HttpProtocolUnitTestResponseGeneratorTests.kt index 3e5fa2fd4..212932c39 100644 --- a/smithy-swift-codegen/src/test/kotlin/HttpProtocolUnitTestResponseGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/HttpProtocolUnitTestResponseGeneratorTests.kt @@ -95,7 +95,8 @@ open class HttpProtocolUnitTestResponseGeneratorTests { "X-Foo": "Foo", "X-Foo-abc": "ABC", "X-Foo-xyz": "XYZ" - ] + ], + content: HttpBody.empty ) else { XCTFail("Something is wrong with the created http response") return @@ -130,7 +131,8 @@ open class HttpProtocolUnitTestResponseGeneratorTests { code: 200, headers: [ "X-Foo": "Foo" - ] + ], + content: HttpBody.empty ) else { XCTFail("Something is wrong with the created http response") return diff --git a/smithy-swift-codegen/src/test/kotlin/IsolatedHttpProtocolUnitTestRequestGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/IsolatedHttpProtocolUnitTestRequestGeneratorTests.kt index a6a868d8d..5bd06edb5 100644 --- a/smithy-swift-codegen/src/test/kotlin/IsolatedHttpProtocolUnitTestRequestGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/IsolatedHttpProtocolUnitTestRequestGeneratorTests.kt @@ -114,7 +114,8 @@ class InputAndOutputWithHeadersResponseTest: HttpResponseTestBase { headers: [ "X-Double": "NaN", "X-Float": "NaN" - ] + ], + content: HttpBody.empty ) else { XCTFail("Something is wrong with the created http response") return diff --git a/smithy-swift-codegen/src/test/resources/sensitive-trait-test.smithy b/smithy-swift-codegen/src/test/resources/sensitive-trait-test.smithy index 7b2d81b16..54a98384e 100644 --- a/smithy-swift-codegen/src/test/resources/sensitive-trait-test.smithy +++ b/smithy-swift-codegen/src/test/resources/sensitive-trait-test.smithy @@ -29,8 +29,7 @@ operation SensitiveTraitTestRequest { } structure SensitiveTraitInRequestInput { - @sensitive - baz: String, + baz: SensitiveString, foo: String } @@ -46,10 +45,10 @@ structure SensitiveTraitTestRequestInput { } structure SensitiveTraitTestRequestOutput { - @sensitive - foo: String, - @sensitive - bar: String, - @sensitive - baz: String -} \ No newline at end of file + foo: SensitiveString, + bar: SensitiveString, + baz: SensitiveString +} + +@sensitive +string SensitiveString \ No newline at end of file