diff --git a/Package.swift b/Package.swift index 419fd359d..6c8ee4f34 100644 --- a/Package.swift +++ b/Package.swift @@ -33,6 +33,7 @@ let package = Package( .library(name: "SmithySerialization", targets: ["SmithySerialization"]), .library(name: "SmithyAWSJSON", targets: ["SmithyAWSJSON"]), .library(name: "SmithyRPCv2CBOR", targets: ["SmithyRPCv2CBOR"]), + .library(name: "SmithyRestXML", targets: ["SmithyRestXML"]), .library(name: "ClientRuntime", targets: ["ClientRuntime"]), .library(name: "SmithyRetriesAPI", targets: ["SmithyRetriesAPI"]), .library(name: "SmithyRetries", targets: ["SmithyRetries"]), @@ -315,6 +316,17 @@ let package = Package( "SmithyCBOR", ] ), + .target( + name: "SmithyRestXML", + dependencies: [ + "ClientRuntime", + "Smithy", + "SmithyEventStreams", + "SmithySerialization", + "SmithyXML", + "SmithyTimestamps", + ] + ), .testTarget( name: "ClientRuntimeTests", dependencies: [ diff --git a/Sources/Smithy/TraitLibrary/AllSupportedTraits.swift b/Sources/Smithy/TraitLibrary/AllSupportedTraits.swift index d369e7894..c88208937 100644 --- a/Sources/Smithy/TraitLibrary/AllSupportedTraits.swift +++ b/Sources/Smithy/TraitLibrary/AllSupportedTraits.swift @@ -28,6 +28,13 @@ private let allSupportedTraitTypes: [ShapeID: any Trait.Type] = [ ErrorTrait.id: ErrorTrait.self, EventHeaderTrait.id: EventHeaderTrait.self, EventPayloadTrait.id: EventPayloadTrait.self, + HttpHeaderTrait.id: HttpHeaderTrait.self, + HttpLabelTrait.id: HttpLabelTrait.self, + HttpPayloadTrait.id: HttpPayloadTrait.self, + HttpPrefixHeadersTrait.id: HttpPrefixHeadersTrait.self, + HttpQueryTrait.id: HttpQueryTrait.self, + HttpQueryParamsTrait.id: HttpQueryParamsTrait.self, + HttpResponseCodeTrait.id: HttpResponseCodeTrait.self, InputTrait.id: InputTrait.self, OutputTrait.id: OutputTrait.self, RequiredTrait.id: RequiredTrait.self, @@ -36,6 +43,10 @@ private let allSupportedTraitTypes: [ShapeID: any Trait.Type] = [ StreamingTrait.id: StreamingTrait.self, TimestampFormatTrait.id: TimestampFormatTrait.self, UnitTypeTrait.id: UnitTypeTrait.self, // UnitTypeTrait will only ever appear in Prelude.unitSchema + XmlAttributeTrait.id: XmlAttributeTrait.self, + XmlFlattenedTrait.id: XmlFlattenedTrait.self, + XmlNameTrait.id: XmlNameTrait.self, + XmlNamespaceTrait.id: XmlNamespaceTrait.self, // Synthetic traits TargetsUnitTrait.id: TargetsUnitTrait.self, diff --git a/Sources/Smithy/TraitLibrary/HttpHeaderTrait.swift b/Sources/Smithy/TraitLibrary/HttpHeaderTrait.swift new file mode 100644 index 000000000..28a350b94 --- /dev/null +++ b/Sources/Smithy/TraitLibrary/HttpHeaderTrait.swift @@ -0,0 +1,20 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +@_spi(SchemaBasedSerde) +public struct HttpHeaderTrait: Trait { + public static var id: ShapeID { .init("smithy.api", "httpHeader") } + public let value: String + public var node: Node { .string(value) } + + public init(node: Node) throws { + guard case .string(let value) = node else { + throw TraitError("httpHeader trait requires a string value") + } + self.value = value + } +} diff --git a/Sources/Smithy/TraitLibrary/HttpLabelTrait.swift b/Sources/Smithy/TraitLibrary/HttpLabelTrait.swift new file mode 100644 index 000000000..e77b0b517 --- /dev/null +++ b/Sources/Smithy/TraitLibrary/HttpLabelTrait.swift @@ -0,0 +1,13 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +@_spi(SchemaBasedSerde) +public struct HttpLabelTrait: Trait { + public static var id: ShapeID { .init("smithy.api", "httpLabel") } + public var node: Node { [:] } + public init(node: Node) throws {} +} diff --git a/Sources/Smithy/TraitLibrary/HttpPayloadTrait.swift b/Sources/Smithy/TraitLibrary/HttpPayloadTrait.swift new file mode 100644 index 000000000..7e46001cd --- /dev/null +++ b/Sources/Smithy/TraitLibrary/HttpPayloadTrait.swift @@ -0,0 +1,13 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +@_spi(SchemaBasedSerde) +public struct HttpPayloadTrait: Trait { + public static var id: ShapeID { .init("smithy.api", "httpPayload") } + public var node: Node { [:] } + public init(node: Node) throws {} +} diff --git a/Sources/Smithy/TraitLibrary/HttpPrefixHeadersTrait.swift b/Sources/Smithy/TraitLibrary/HttpPrefixHeadersTrait.swift new file mode 100644 index 000000000..777e133a4 --- /dev/null +++ b/Sources/Smithy/TraitLibrary/HttpPrefixHeadersTrait.swift @@ -0,0 +1,20 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +@_spi(SchemaBasedSerde) +public struct HttpPrefixHeadersTrait: Trait { + public static var id: ShapeID { .init("smithy.api", "httpPrefixHeaders") } + public let value: String + public var node: Node { .string(value) } + + public init(node: Node) throws { + guard case .string(let value) = node else { + throw TraitError("httpPrefixHeaders trait requires a string value") + } + self.value = value + } +} diff --git a/Sources/Smithy/TraitLibrary/HttpQueryParamsTrait.swift b/Sources/Smithy/TraitLibrary/HttpQueryParamsTrait.swift new file mode 100644 index 000000000..1cf38847c --- /dev/null +++ b/Sources/Smithy/TraitLibrary/HttpQueryParamsTrait.swift @@ -0,0 +1,13 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +@_spi(SchemaBasedSerde) +public struct HttpQueryParamsTrait: Trait { + public static var id: ShapeID { .init("smithy.api", "httpQueryParams") } + public var node: Node { [:] } + public init(node: Node) throws {} +} diff --git a/Sources/Smithy/TraitLibrary/HttpQueryTrait.swift b/Sources/Smithy/TraitLibrary/HttpQueryTrait.swift new file mode 100644 index 000000000..58cac6413 --- /dev/null +++ b/Sources/Smithy/TraitLibrary/HttpQueryTrait.swift @@ -0,0 +1,20 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +@_spi(SchemaBasedSerde) +public struct HttpQueryTrait: Trait { + public static var id: ShapeID { .init("smithy.api", "httpQuery") } + public let value: String + public var node: Node { .string(value) } + + public init(node: Node) throws { + guard case .string(let value) = node else { + throw TraitError("httpQuery trait requires a string value") + } + self.value = value + } +} diff --git a/Sources/Smithy/TraitLibrary/HttpResponseCodeTrait.swift b/Sources/Smithy/TraitLibrary/HttpResponseCodeTrait.swift new file mode 100644 index 000000000..edaa81a90 --- /dev/null +++ b/Sources/Smithy/TraitLibrary/HttpResponseCodeTrait.swift @@ -0,0 +1,13 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +@_spi(SchemaBasedSerde) +public struct HttpResponseCodeTrait: Trait { + public static var id: ShapeID { .init("smithy.api", "httpResponseCode") } + public var node: Node { [:] } + public init(node: Node) throws {} +} diff --git a/Sources/Smithy/TraitLibrary/XmlAttributeTrait.swift b/Sources/Smithy/TraitLibrary/XmlAttributeTrait.swift new file mode 100644 index 000000000..d392c2807 --- /dev/null +++ b/Sources/Smithy/TraitLibrary/XmlAttributeTrait.swift @@ -0,0 +1,13 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +@_spi(SchemaBasedSerde) +public struct XmlAttributeTrait: Trait { + public static var id: ShapeID { .init("smithy.api", "xmlAttribute") } + public var node: Node { [:] } + public init(node: Node) throws {} +} diff --git a/Sources/Smithy/TraitLibrary/XmlFlattenedTrait.swift b/Sources/Smithy/TraitLibrary/XmlFlattenedTrait.swift new file mode 100644 index 000000000..425df4dfc --- /dev/null +++ b/Sources/Smithy/TraitLibrary/XmlFlattenedTrait.swift @@ -0,0 +1,13 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +@_spi(SchemaBasedSerde) +public struct XmlFlattenedTrait: Trait { + public static var id: ShapeID { .init("smithy.api", "xmlFlattened") } + public var node: Node { [:] } + public init(node: Node) throws {} +} diff --git a/Sources/Smithy/TraitLibrary/XmlNameTrait.swift b/Sources/Smithy/TraitLibrary/XmlNameTrait.swift new file mode 100644 index 000000000..4efce68f7 --- /dev/null +++ b/Sources/Smithy/TraitLibrary/XmlNameTrait.swift @@ -0,0 +1,20 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +@_spi(SchemaBasedSerde) +public struct XmlNameTrait: Trait { + public static var id: ShapeID { .init("smithy.api", "xmlName") } + public let value: String + public var node: Node { .string(value) } + + public init(node: Node) throws { + guard case .string(let value) = node else { + throw TraitError("xmlName trait requires a string value") + } + self.value = value + } +} diff --git a/Sources/Smithy/TraitLibrary/XmlNamespaceTrait.swift b/Sources/Smithy/TraitLibrary/XmlNamespaceTrait.swift new file mode 100644 index 000000000..32153e098 --- /dev/null +++ b/Sources/Smithy/TraitLibrary/XmlNamespaceTrait.swift @@ -0,0 +1,33 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +@_spi(SchemaBasedSerde) +public struct XmlNamespaceTrait: Trait { + public static var id: ShapeID { .init("smithy.api", "xmlNamespace") } + public let uri: String + public let prefix: String? + public var node: Node { + var dict: [String: Node] = ["uri": .string(uri)] + if let prefix { dict["prefix"] = .string(prefix) } + return .object(dict) + } + + public init(node: Node) throws { + guard case .object(let dict) = node else { + throw TraitError("xmlNamespace trait requires an object value") + } + guard case .string(let uri) = dict["uri"] else { + throw TraitError("xmlNamespace trait requires a 'uri' string") + } + self.uri = uri + if case .string(let prefix) = dict["prefix"] { + self.prefix = prefix + } else { + self.prefix = nil + } + } +} diff --git a/Sources/SmithyCodegenCore/ModelTransformer/Model+InputOutput.swift b/Sources/SmithyCodegenCore/ModelTransformer/Model+InputOutput.swift index 217be24e3..023d255bd 100644 --- a/Sources/SmithyCodegenCore/ModelTransformer/Model+InputOutput.swift +++ b/Sources/SmithyCodegenCore/ModelTransformer/Model+InputOutput.swift @@ -14,6 +14,8 @@ import struct Smithy.ShapeID import struct Smithy.TargetsUnitTrait @_spi(SchemaBasedSerde) import struct Smithy.TraitCollection +import struct Smithy.XmlNamespaceTrait +import struct Smithy.XmlNameTrait extension Model { @@ -27,6 +29,11 @@ extension Model { .filter { $0.type == .operation } .compactMap { $0 as? OperationShape } + // Get service-level @xmlNamespace to propagate to synthetic input shapes (for RestXML root element) + let serviceXmlNamespace = shapes.values + .first { $0.type == .service } + .flatMap { try? $0.traits.getTrait(XmlNamespaceTrait.self) } + // Make a copy of this model's shapes to modify var newShapes = shapes @@ -52,9 +59,24 @@ extension Model { // Add UsedAsInput and UsedAsOutput traits to the input/output structures // These traits allow us to identify inputs/outputs by trait, but allow us to // leave the Smithy input & output traits as set on the original model. - let newInput = newStruct(newID: newInputShapeID, newTraits: [UsedAsInputTrait()], original: inputShape) + // Also add XmlNameTrait with the original shape name so XML serialization uses + // the correct root element name (e.g. "SimpleScalarPropertiesRequest" not "SimpleScalarPropertiesInput"). + var inputExtraTraits = TraitCollection() + inputExtraTraits.add(UsedAsInputTrait()) + if !inputShape.hasTrait(XmlNameTrait.self) && inputShape.id != Prelude.unitSchema.id { + inputExtraTraits.add(try XmlNameTrait(node: .string(inputShape.id.name))) + } + if let ns = serviceXmlNamespace, !inputShape.hasTrait(XmlNamespaceTrait.self) { + inputExtraTraits.add(ns) + } + var outputExtraTraits = TraitCollection() + outputExtraTraits.add(UsedAsOutputTrait()) + if !outputShape.hasTrait(XmlNameTrait.self) && outputShape.id != Prelude.unitSchema.id { + outputExtraTraits.add(try XmlNameTrait(node: .string(outputShape.id.name))) + } + let newInput = newStruct(newID: newInputShapeID, newTraits: inputExtraTraits, original: inputShape) let newInputShapeMembers = try renamedMembers(newID: newInputShapeID, original: inputShape) - let newOutput = newStruct(newID: newOutputShapeID, newTraits: [UsedAsOutputTrait()], original: outputShape) + let newOutput = newStruct(newID: newOutputShapeID, newTraits: outputExtraTraits, original: outputShape) let newOutputShapeMembers = try renamedMembers(newID: newOutputShapeID, original: outputShape) // Add the new input & output and their members to the new shape dictionary. diff --git a/Sources/SmithyCodegenCore/Schemas/SchemasCodegen.swift b/Sources/SmithyCodegenCore/Schemas/SchemasCodegen.swift index bb88c9671..081671c5c 100644 --- a/Sources/SmithyCodegenCore/Schemas/SchemasCodegen.swift +++ b/Sources/SmithyCodegenCore/Schemas/SchemasCodegen.swift @@ -13,6 +13,7 @@ import struct Smithy.ShapeID import enum Smithy.ShapeType @_spi(SchemaBasedSerde) import func Smithy.traitType +import struct Smithy.XmlNameTrait /// A generator for the `Schemas.swift` package struct SchemasCodegen { @@ -139,7 +140,9 @@ package struct SchemasCodegen { // Get all the trait IDs that apply to this member & sort let memberTraitIDs = Set(memberShape.traits.traitDict.keys) let targetTraitIDs = Set(try memberShape.target.traits.traitDict.keys) - let allTraitIDs = Array(memberTraitIDs.union(targetTraitIDs)).smithySorted() + // @xmlName on the target renames the target shape, not the member; don't inherit. + let inheritableTargetTraitIDs = targetTraitIDs.filter { $0 != XmlNameTrait.id } + let allTraitIDs = Array(memberTraitIDs.union(inheritableTargetTraitIDs)).smithySorted() var pairs = [(ShapeID, Node)]() for traitID in allTraitIDs { diff --git a/Sources/SmithyEventStreams/Deserializers/EventContentDeserializer.swift b/Sources/SmithyEventStreams/Deserializers/EventContentDeserializer.swift index 11a2824e3..7206e5e35 100644 --- a/Sources/SmithyEventStreams/Deserializers/EventContentDeserializer.swift +++ b/Sources/SmithyEventStreams/Deserializers/EventContentDeserializer.swift @@ -32,11 +32,17 @@ struct EventContentDeserializer: ShapeDeserializer { // Deserialize the event payload, to the member marked with @eventPayload if it exists, // to the structure's members otherwise. - // Use a deserializer for the protocol in use, by making it from the codec. - let payloadDeserializer = try codec.makeDeserializer(data: message.payload) if let payloadMember = schema.members.first(where: { $0.hasTrait(EventPayloadTrait.self) }) { - try T.readConsumer(payloadMember, &value, payloadDeserializer) + // Skip codec for blob payloads — codec would parse as XML/JSON. + if (payloadMember.target ?? payloadMember).type == .blob { + let blobDeserializer = RawBlobDeserializer(data: message.payload) + try T.readConsumer(payloadMember, &value, blobDeserializer) + } else { + let payloadDeserializer = try codec.makeDeserializer(data: message.payload) + try T.readConsumer(payloadMember, &value, payloadDeserializer) + } } else { + let payloadDeserializer = try codec.makeDeserializer(data: message.payload) try payloadDeserializer.readStruct(schema, &value) } diff --git a/Sources/SmithyEventStreams/Deserializers/RawBlobDeserializer.swift b/Sources/SmithyEventStreams/Deserializers/RawBlobDeserializer.swift new file mode 100644 index 000000000..eab8e5da2 --- /dev/null +++ b/Sources/SmithyEventStreams/Deserializers/RawBlobDeserializer.swift @@ -0,0 +1,98 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import struct Foundation.Data +import struct Foundation.Date +import struct Smithy.Document +@_spi(SchemaBasedSerde) +import struct Smithy.Schema +@_spi(SchemaBasedSerde) +import protocol SmithySerialization.DeserializableStruct +import struct SmithySerialization.SerializerError +@_spi(SchemaBasedSerde) +import protocol SmithySerialization.ShapeDeserializer + +/// Returns raw bytes for blob reads; used for blob @eventPayload members. +struct RawBlobDeserializer: ShapeDeserializer { + let data: Data + + func readBlob(_ schema: Schema) throws -> Data { + data + } + + func readStruct(_ schema: Schema, _ value: inout T) throws where T: DeserializableStruct { + throw notImplemented + } + + func readList(_ schema: Schema, _ consumer: (any ShapeDeserializer) throws -> E) throws -> [E] { + throw notImplemented + } + + func readMap(_ schema: Schema, _ consumer: (any ShapeDeserializer) throws -> V) throws -> [String: V] { + throw notImplemented + } + + func readBoolean(_ schema: Schema) throws -> Bool { + throw notImplemented + } + + func readByte(_ schema: Schema) throws -> Int8 { + throw notImplemented + } + + func readShort(_ schema: Schema) throws -> Int16 { + throw notImplemented + } + + func readInteger(_ schema: Schema) throws -> Int { + throw notImplemented + } + + func readLong(_ schema: Schema) throws -> Int { + throw notImplemented + } + + func readFloat(_ schema: Schema) throws -> Float { + throw notImplemented + } + + func readDouble(_ schema: Schema) throws -> Double { + throw notImplemented + } + + func readBigInteger(_ schema: Schema) throws -> Int64 { + throw notImplemented + } + + func readBigDecimal(_ schema: Schema) throws -> Double { + throw notImplemented + } + + func readString(_ schema: Schema) throws -> String { + throw notImplemented + } + + func readDocument(_ schema: Schema) throws -> Document { + throw notImplemented + } + + func readTimestamp(_ schema: Schema) throws -> Date { + throw notImplemented + } + + func readNull(_ schema: Schema) throws -> T? { + throw notImplemented + } + + func isNull() throws -> Bool { + throw notImplemented + } + + var containerSize: Int { -1 } + + private var notImplemented: SerializerError { .init("Not implemented") } +} diff --git a/Sources/SmithyRestXML/BaseError.swift b/Sources/SmithyRestXML/BaseError.swift new file mode 100644 index 000000000..6f55c58cb --- /dev/null +++ b/Sources/SmithyRestXML/BaseError.swift @@ -0,0 +1,64 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import enum Smithy.Prelude +@_spi(SchemaBasedSerde) +import struct Smithy.Schema +@_spi(SchemaBasedSerde) +import protocol SmithySerialization.DeserializableStruct +@_spi(SchemaBasedSerde) +import typealias SmithySerialization.ReadStructConsumer +@_spi(SchemaBasedSerde) +import protocol SmithySerialization.ShapeDeserializer + +struct BaseError { + var code: String? + var message: String? +} + +extension BaseError: DeserializableStruct { + + private static var schema: Smithy.Schema { + .init( + id: .init("swift.synthetic", "BaseError"), + type: .structure, + members: [ + .init( + id: .init("swift.synthetic", "BaseError", "Code"), + type: .member, + target: Prelude.stringSchema, + index: 0 + ), + .init( + id: .init("swift.synthetic", "BaseError", "Message"), + type: .member, + target: Prelude.stringSchema, + index: 1 + ), + ] + ) + } + + static var readConsumer: SmithySerialization.ReadStructConsumer { + { memberSchema, value, deserializer in + switch memberSchema.index { + case 0: + value.code = try deserializer.readString(memberSchema) + case 1: + value.message = try deserializer.readString(memberSchema) + default: + break + } + } + } + + static func deserialize(_ deserializer: any ShapeDeserializer) throws -> Self { + var value = Self() + try deserializer.readStruct(Self.schema, &value) + return value + } +} diff --git a/Sources/SmithyRestXML/Codec.swift b/Sources/SmithyRestXML/Codec.swift new file mode 100644 index 000000000..8940817e2 --- /dev/null +++ b/Sources/SmithyRestXML/Codec.swift @@ -0,0 +1,28 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import struct Foundation.Data +@_spi(SchemaBasedSerde) +import protocol SmithySerialization.Codec +@_spi(SchemaBasedSerde) +import protocol SmithySerialization.ShapeDeserializer +@_spi(SchemaBasedSerde) +import protocol SmithySerialization.ShapeSerializer + +@_spi(SchemaBasedSerde) +public struct Codec: SmithySerialization.Codec { + + public init() {} + + public func makeSerializer() throws -> any ShapeSerializer { + Serializer() + } + + public func makeDeserializer(data: Data) throws -> any ShapeDeserializer { + try Deserializer(data: data) + } +} diff --git a/Sources/SmithyRestXML/Deserializer.swift b/Sources/SmithyRestXML/Deserializer.swift new file mode 100644 index 000000000..7f604ea3d --- /dev/null +++ b/Sources/SmithyRestXML/Deserializer.swift @@ -0,0 +1,411 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import struct Foundation.Data +import struct Foundation.Date +import enum Smithy.ByteStream +import struct Smithy.Document +@_spi(SchemaBasedSerde) +import struct Smithy.HttpHeaderTrait +@_spi(SchemaBasedSerde) +import struct Smithy.HttpPayloadTrait +@_spi(SchemaBasedSerde) +import struct Smithy.HttpPrefixHeadersTrait +@_spi(SchemaBasedSerde) +import struct Smithy.HttpResponseCodeTrait +@_spi(SchemaBasedSerde) +import struct Smithy.Schema +@_spi(SchemaBasedSerde) +import struct Smithy.TimestampFormatTrait +@_spi(SchemaBasedSerde) +import struct Smithy.XmlAttributeTrait +@_spi(SchemaBasedSerde) +import struct Smithy.XmlFlattenedTrait +@_spi(SchemaBasedSerde) +import struct Smithy.XmlNameTrait +import class SmithyHTTPAPI.HTTPResponse +@_spi(SchemaBasedSerde) +import protocol SmithySerialization.DeserializableStruct +@_spi(SchemaBasedSerde) +import typealias SmithySerialization.ReadStructConsumer +@_spi(SchemaBasedSerde) +import typealias SmithySerialization.ReadValueConsumer +@_spi(SchemaBasedSerde) +import struct SmithySerialization.SerializerError +@_spi(SchemaBasedSerde) +import protocol SmithySerialization.ShapeDeserializer +@_spi(SmithyTimestamps) import enum SmithyTimestamps.TimestampFormat +@_spi(SmithyTimestamps) import struct SmithyTimestamps.TimestampFormatter +@_spi(SmithyReadWrite) import struct SmithyXML.NodeInfo +@_spi(SmithyReadWrite) import class SmithyXML.Reader + +@_spi(SchemaBasedSerde) +public struct Deserializer: ShapeDeserializer { + let reader: Reader + let httpResponse: HTTPResponse? + let rawBodyData: Data? + let bodyStream: ByteStream? + /// Reader holds comma-split header list children rather than XML element children. + let isHeaderList: Bool + /// Switches the default timestamp format from `.dateTime` (XML) to `.httpDate` (HTTP spec). + let isFromHttpHeader: Bool + + public init(data: Data) throws { + self.httpResponse = nil + self.rawBodyData = nil + self.bodyStream = nil + self.isHeaderList = false + self.isFromHttpHeader = false + if data.isEmpty { + self.reader = Reader() + } else { + self.reader = try Reader.from(data: data) + } + } + + init( + reader: Reader, + httpResponse: HTTPResponse? = nil, + rawBodyData: Data? = nil, + bodyStream: ByteStream? = nil, + isHeaderList: Bool = false, + isFromHttpHeader: Bool = false + ) { + self.reader = reader + self.httpResponse = httpResponse + self.rawBodyData = rawBodyData + self.bodyStream = bodyStream + self.isHeaderList = isHeaderList + self.isFromHttpHeader = isFromHttpHeader + } + + init(httpResponse: HTTPResponse, bodyData: Data) throws { + self.httpResponse = httpResponse + self.rawBodyData = bodyData + self.bodyStream = nil + self.isHeaderList = false + self.isFromHttpHeader = false + if bodyData.isEmpty { + self.reader = Reader() + } else { + self.reader = (try? Reader.from(data: bodyData)) ?? Reader() + } + } + + init(httpResponse: HTTPResponse, bodyStream: ByteStream) { + self.httpResponse = httpResponse + self.rawBodyData = nil + self.bodyStream = bodyStream + self.isHeaderList = false + self.isFromHttpHeader = false + self.reader = Reader() + } + + private func targetSchema(_ schema: Schema) -> Schema { + schema.target ?? schema + } + + public func readStruct(_ schema: Schema, _ value: inout T) throws { + let structSchema: Schema + switch schema.type { + case .structure, .union: + structSchema = schema + case .member: + guard let target = schema.target else { + throw XMLDeserializerError("Expected non-nil target on \(schema)") + } + structSchema = target + default: + throw XMLDeserializerError("unexpected schema type \(schema.type) used with readStruct") + } + + for member in structSchema.members { + if let memberDeserializer = try httpBindingDeserializer(for: member) { + do { + try T.readConsumer(member, &value, memberDeserializer) + } catch is DecodedNull {} + continue + } + let elementName = xmlElementName(for: member) + let isAttribute = member.hasTrait(XmlAttributeTrait.self) + let childReader: Reader + if isAttribute { + childReader = reader[NodeInfo(elementName, location: .attribute)] + } else if reader.nodeInfo.name == elementName && reader.hasContent && reader.children.isEmpty { + // @s3UnwrappedXmlOutput: leaf reader is itself the member value. + childReader = reader + } else { + childReader = reader[NodeInfo(elementName)] + } + guard childReader.hasContent || !childReader.children.isEmpty else { continue } + do { + let memberDeserializer = Deserializer(reader: childReader) + try T.readConsumer(member, &value, memberDeserializer) + } catch is DecodedNull {} + } + } + + /// Returns a Deserializer for HTTP-bound members; nil for body-bound. + private func httpBindingDeserializer(for member: Schema) throws -> Deserializer? { + guard let httpResponse else { return nil } + + if let headerTrait = try member.getTrait(HttpHeaderTrait.self) { + guard let headerValue = httpResponse.headers.value(for: headerTrait.value) else { return nil } + if member.target?.type == .list || member.type == .list { + let memberTarget = member.target?.member.target ?? member.target?.member + let isTimestamp = memberTarget?.type == .timestamp + let listReader = Reader() + for part in splitHeaderList(headerValue, isTimestamp: isTimestamp) { + listReader.addChild(Reader(content: part)) + } + return Deserializer(reader: listReader, isHeaderList: true, isFromHttpHeader: true) + } + return Deserializer(reader: Reader(content: headerValue), isFromHttpHeader: true) + } + + if let prefixTrait = try member.getTrait(HttpPrefixHeadersTrait.self) { + let prefix = prefixTrait.value + let mapReader = Reader() + let lowerPrefix = prefix.lowercased() + for (name, values) in httpResponse.headers.dictionary where name.lowercased().hasPrefix(lowerPrefix) { + let key = String(name.dropFirst(prefix.count)) + let entry = Reader(nodeInfo: "entry", content: nil) + entry.addChild(Reader(nodeInfo: "key", content: key)) + entry.addChild(Reader(nodeInfo: "value", content: values.joined(separator: ", "))) + mapReader.addChild(entry) + } + return Deserializer(reader: mapReader, isFromHttpHeader: true) + } + + if member.hasTrait(HttpResponseCodeTrait.self) { + return Deserializer(reader: Reader(content: String(httpResponse.statusCode.rawValue))) + } + + if member.hasTrait(HttpPayloadTrait.self) { + let targetType = member.target?.type ?? member.type + switch targetType { + case .structure, .union: + guard reader.hasContent || !reader.children.isEmpty else { return nil } + return Deserializer(reader: reader, httpResponse: httpResponse, rawBodyData: rawBodyData) + case .blob: + if let bodyStream { + return Deserializer(reader: Reader(), httpResponse: httpResponse, bodyStream: bodyStream) + } + guard let rawBodyData, !rawBodyData.isEmpty else { return nil } + // Reader.readIfPresent(Data) decodes base64 — for a raw blob, stash and return as-is. + return Deserializer(reader: Reader(), httpResponse: httpResponse, rawBodyData: rawBodyData) + case .string, .enum: + guard let rawBodyData, !rawBodyData.isEmpty, + let str = String(data: rawBodyData, encoding: .utf8) else { return nil } + return Deserializer(reader: Reader(content: str)) + default: + return nil + } + } + + return nil + } + + /// Splits a comma-separated HTTP header list value per RFC 7230, respecting quoted strings + /// and HTTP date values (which contain a comma, e.g. "Mon, 16 Dec 2019 23:48:18 GMT"). + private func splitHeaderList(_ value: String, isTimestamp: Bool = false) -> [String] { + var result: [String] = [] + var current = "" + var inQuotes = false + for ch in value { + if ch == "\"" { inQuotes.toggle(); current.append(ch); continue } + if ch == "," && !inQuotes { + let trimmed = current.trimmingCharacters(in: .whitespaces) + if isTimestamp && trimmed.count == 3 && trimmed.allSatisfy(\.isLetter) { + current.append(ch) + continue + } + result.append(trimmed) + current = "" + continue + } + current.append(ch) + } + if !current.isEmpty { + result.append(current.trimmingCharacters(in: .whitespaces)) + } + return result + } + + public func readList(_ schema: Schema, _ consumer: ReadValueConsumer) throws -> [E] { + if isHeaderList { + return try reader.children.map { try consumer(Deserializer(reader: $0, isFromHttpHeader: true)) } + } + let isFlattened = schema.hasTrait(XmlFlattenedTrait.self) + var list = [E]() + + if isFlattened { + let elementName = xmlElementName(for: schema) + let siblings = (reader.parent?.children ?? [reader]).filter { + $0.nodeInfo.name == elementName + } + guard !siblings.isEmpty else { return list } + for sibling in siblings { + let element = try consumer(Deserializer(reader: sibling)) + list.append(element) + } + } else { + let memberSchema = targetSchema(schema).member + let memberName = xmlElementName(for: memberSchema) + let members = reader.children.filter { $0.nodeInfo.name == memberName } + for member in members { + let element = try consumer(Deserializer(reader: member)) + list.append(element) + } + } + return list + } + + public func readMap(_ schema: Schema, _ consumer: ReadValueConsumer) throws -> [String: V] { + let isFlattened = schema.hasTrait(XmlFlattenedTrait.self) + let mapSchema = targetSchema(schema) + let keyName = xmlElementName(for: mapSchema.key) + let valueName = xmlElementName(for: mapSchema.value) + var map = [String: V]() + + let entries: [Reader] + if isFlattened { + let elementName = xmlElementName(for: schema) + entries = (reader.parent?.children ?? []).filter { $0.nodeInfo.name == elementName } + } else { + entries = reader.children.filter { $0.nodeInfo.name == "entry" } + } + + for entry in entries { + let keyReader = entry[NodeInfo(keyName)] + guard let key: String = try keyReader.readIfPresent() else { continue } + let valueReader = entry[NodeInfo(valueName)] + let value = try consumer(Deserializer(reader: valueReader)) + map[key] = value + } + return map + } + + public func readBoolean(_ schema: Schema) throws -> Bool { + guard let value: Bool = try reader.readIfPresent() else { + throw XMLDeserializerError("Expected boolean for \(schema.id)") + } + return value + } + + public func readByte(_ schema: Schema) throws -> Int8 { + guard let value: Int8 = try reader.readIfPresent() else { + throw XMLDeserializerError("Expected Int8 for \(schema.id)") + } + return value + } + + public func readShort(_ schema: Schema) throws -> Int16 { + guard let value: Int16 = try reader.readIfPresent() else { + throw XMLDeserializerError("Expected Int16 for \(schema.id)") + } + return value + } + + public func readInteger(_ schema: Schema) throws -> Int { + guard let value: Int = try reader.readIfPresent() else { + throw XMLDeserializerError("Expected Int for \(schema.id)") + } + return value + } + + public func readLong(_ schema: Schema) throws -> Int { + guard let value: Int = try reader.readIfPresent() else { + throw XMLDeserializerError("Expected Int (long) for \(schema.id)") + } + return value + } + + public func readFloat(_ schema: Schema) throws -> Float { + guard let value: Float = try reader.readIfPresent() else { + throw XMLDeserializerError("Expected Float for \(schema.id)") + } + return value + } + + public func readDouble(_ schema: Schema) throws -> Double { + guard let value: Double = try reader.readIfPresent() else { + throw XMLDeserializerError("Expected Double for \(schema.id)") + } + return value + } + + public func readBigInteger(_ schema: Schema) throws -> Int64 { + guard let str: String = try reader.readIfPresent(), let value = Int64(str) else { + throw XMLDeserializerError("Expected Int64 for \(schema.id)") + } + return value + } + + public func readBigDecimal(_ schema: Schema) throws -> Double { + try readDouble(schema) + } + + public func readString(_ schema: Schema) throws -> String { + guard let value: String = try reader.readIfPresent() else { + throw XMLDeserializerError("Expected String for \(schema.id)") + } + return value + } + + public func readBlob(_ schema: Schema) throws -> Data { + if let rawBodyData { return rawBodyData } + guard let value: Data = try reader.readIfPresent() else { + throw XMLDeserializerError("Expected blob for \(schema.id)") + } + return value + } + + public func readDataStream(_ schema: Schema) throws -> ByteStream { + return bodyStream ?? .data(nil) + } + + public func readTimestamp(_ schema: Schema) throws -> Date { + let format: TimestampFormat + if (try? schema.getTrait(TimestampFormatTrait.self)) != nil { + format = resolveTimestampFormat(schema) + } else { + format = isFromHttpHeader ? .httpDate : .dateTime + } + guard let value: Date = try reader.readTimestampIfPresent(format: format) else { + throw XMLDeserializerError("Expected timestamp for \(schema.id)") + } + return value + } + + public func readDocument(_ schema: Schema) throws -> Document { + throw SerializerError("Document type not supported in XML") + } + + public func readNull(_ schema: Schema) throws -> T? { + return nil + } + + public func isNull() throws -> Bool { + !reader.hasContent && reader.children.isEmpty + } + + public var containerSize: Int { reader.children.count } + + private func xmlElementName(for schema: Schema) -> String { + (try? schema.getTrait(XmlNameTrait.self))?.value ?? schema.memberName ?? schema.id.member ?? schema.id.name + } +} + +struct XMLDeserializerError: Error { + let localizedDescription: String + init(_ localizedDescription: String) { + self.localizedDescription = localizedDescription + } +} + +struct DecodedNull: Error {} diff --git a/Sources/SmithyRestXML/HTTPClientProtocol.swift b/Sources/SmithyRestXML/HTTPClientProtocol.swift new file mode 100644 index 000000000..dfca66296 --- /dev/null +++ b/Sources/SmithyRestXML/HTTPClientProtocol.swift @@ -0,0 +1,234 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import protocol ClientRuntime.HTTPError +import protocol ClientRuntime.ServiceError +import struct ClientRuntime.UnknownHTTPServiceError +import struct Foundation.Data +import enum Smithy.ByteStream +import enum Smithy.ClientError +import class Smithy.Context +@_spi(SchemaBasedSerde) +import struct Smithy.HttpPayloadTrait +@_spi(SchemaBasedSerde) +import struct Smithy.Schema +@_spi(SchemaBasedSerde) +import struct Smithy.ShapeID +@_spi(SchemaBasedSerde) +import struct Smithy.StreamingTrait +@_spi(SchemaBasedSerde) +import struct Smithy.TargetsUnitTrait +@_spi(SchemaBasedSerde) +import struct SmithyEventStreams.EventStreamDeserializer +import class SmithyHTTPAPI.HTTPRequest +import class SmithyHTTPAPI.HTTPRequestBuilder +import class SmithyHTTPAPI.HTTPResponse +@_spi(SchemaBasedSerde) +import protocol SmithySerialization.ClientProtocol +@_spi(SchemaBasedSerde) +import protocol SmithySerialization.Codec +@_spi(SchemaBasedSerde) +import protocol SmithySerialization.DeserializableStruct +@_spi(SchemaBasedSerde) +import struct SmithySerialization.Operation +@_spi(SchemaBasedSerde) +import struct SmithySerialization.TypeRegistry +@_spi(SmithyReadWrite) import struct SmithyXML.NodeInfo +@_spi(SmithyReadWrite) import class SmithyXML.Reader + +@_spi(SchemaBasedSerde) +public struct HTTPClientProtocol: SmithySerialization.ClientProtocol, Sendable { + public typealias RequestType = HTTPRequest + public typealias ResponseType = HTTPResponse + + public let id = ShapeID("aws.protocols", "restXml") + public let codec: SmithySerialization.Codec = Codec() + public let noErrorWrapping: Bool + public let handleEmpty404: Bool + + private let customErrorResolver: ( + @Sendable (HTTPResponse, Data, TypeRegistry, Bool) async throws -> (any Error)? + )? + + private let errorPostProcessor: ( + @Sendable (inout any (ServiceError & HTTPError & Error), HTTPResponse) -> Void + )? + + public init(noErrorWrapping: Bool = false, handleEmpty404: Bool = false) { + self.init( + noErrorWrapping: noErrorWrapping, + handleEmpty404: handleEmpty404, + customErrorResolver: nil, + errorPostProcessor: nil + ) + } + + private init( + noErrorWrapping: Bool, + handleEmpty404: Bool, + customErrorResolver: (@Sendable (HTTPResponse, Data, TypeRegistry, Bool) async throws -> (any Error)?)?, + errorPostProcessor: (@Sendable (inout any (ServiceError & HTTPError & Error), HTTPResponse) -> Void)? + ) { + self.noErrorWrapping = noErrorWrapping + self.handleEmpty404 = handleEmpty404 + self.customErrorResolver = customErrorResolver + self.errorPostProcessor = errorPostProcessor + } + + public func withCustomErrorResolver( + _ resolver: @escaping @Sendable (HTTPResponse, Data, TypeRegistry, Bool) async throws -> (any Error)? + ) -> HTTPClientProtocol { + HTTPClientProtocol( + noErrorWrapping: noErrorWrapping, + handleEmpty404: handleEmpty404, + customErrorResolver: resolver, + errorPostProcessor: errorPostProcessor + ) + } + + public func withErrorPostProcessor( + _ postProcessor: @escaping @Sendable (inout any (ServiceError & HTTPError & Error), HTTPResponse) -> Void + ) -> HTTPClientProtocol { + HTTPClientProtocol( + noErrorWrapping: noErrorWrapping, + handleEmpty404: handleEmpty404, + customErrorResolver: customErrorResolver, + errorPostProcessor: postProcessor + ) + } + + public func serializeRequest( + operation: Operation, + input: Input, + requestBuilder: HTTPRequestBuilder, + context: Context + ) throws { + // If the operation input targets smithy.api#Unit, don't serialize a body. + guard !operation.inputSchema.hasTrait(TargetsUnitTrait.self) else { + requestBuilder.withBody(.data(nil)) + return + } + + let serializer = Serializer() + try input.serialize(serializer) + if let streamingBody = serializer.streamingBody { + requestBuilder.withBody(streamingBody) + } else { + let data = try serializer.data + requestBuilder.withBody(.data(data)) + } + } + + public func deserializeResponse( + operation: Operation, + context: Context, + response: HTTPResponse + ) async throws -> Output { + if (200..<300).contains(response.statusCode.rawValue) { + let hasEventStreamPayload = operation.outputSchema.members.contains { member in + guard member.hasTrait(HttpPayloadTrait.self) else { return false } + guard member.hasTrait(StreamingTrait.self) + || (member.target?.hasTrait(StreamingTrait.self) ?? false) + else { return false } + return (member.target ?? member).type == .union + } + if hasEventStreamPayload { + let eventStreamDeserializer = EventStreamDeserializer(codec: codec, response: response) + return try Output.deserialize(eventStreamDeserializer) + } + let hasStreamingPayload = operation.outputSchema.members.contains { member in + guard member.hasTrait(HttpPayloadTrait.self) else { return false } + return member.hasTrait(StreamingTrait.self) + || (member.target?.hasTrait(StreamingTrait.self) ?? false) + } + if hasStreamingPayload { + let deserializer = Deserializer(httpResponse: response, bodyStream: response.body) + return try Output.deserialize(deserializer) + } + let bodyData = try await response.body.readData() ?? Data() + let deserializer = try Deserializer(httpResponse: response, bodyData: bodyData) + return try Output.deserialize(deserializer) + } else { + let bodyData = try await response.body.readData() ?? Data() + let errorTypeRegistry = operation.errorTypeRegistry + + if let customErrorResolver, + let resolvedError = try await customErrorResolver( + response, bodyData, errorTypeRegistry, noErrorWrapping + ) { + throw resolvedError + } + + // Tolerate non-XML error bodies (HTML 5xx pages, truncated responses) + // by surfacing UnknownHTTPServiceError instead of leaking a parse error. + guard let errorDeserializer = try? Deserializer(data: bodyData) else { + throw UnknownHTTPServiceError( + httpResponse: response, + message: nil, + typeName: nil + ) + } + let errorReader = errorDeserializer.reader + + let baseErrorDeserializer: Deserializer + if noErrorWrapping { + baseErrorDeserializer = errorDeserializer + } else { + let errorElement = errorReader.children.first { + $0.nodeInfo.name == "Error" + } ?? errorReader + baseErrorDeserializer = Deserializer(reader: errorElement) + } + + let baseError = try BaseError.deserialize(baseErrorDeserializer) + let code = baseError.code + + let registryEntry: TypeRegistry.Entry? + if let code { + registryEntry = errorTypeRegistry.find { entry in + entry.schema.id.name == code + } + } else if handleEmpty404 && bodyData.isEmpty && response.statusCode == .notFound { + registryEntry = errorTypeRegistry.find { entry in + entry.schema.id.name == "NotFound" + } + } else { + registryEntry = nil + } + + if let registryEntry { + let specificDeserializer: Deserializer + if noErrorWrapping { + specificDeserializer = errorDeserializer + } else { + let errorElement = errorReader.children.first { + $0.nodeInfo.name == "Error" + } ?? errorReader + specificDeserializer = Deserializer(reader: errorElement, httpResponse: response) + } + let error = try registryEntry.swiftType.deserialize(specificDeserializer) + + guard var modeledError = error as? ServiceError & HTTPError & Error else { + throw ClientError.invalidValue( + "Modeled error does not conform to ServiceError & HTTPError & Error." + + " This should never happen, please file a bug on aws-sdk-swift." + ) + } + modeledError.message = baseError.message + modeledError.httpResponse = response + errorPostProcessor?(&modeledError, response) + throw modeledError + } else { + throw UnknownHTTPServiceError( + httpResponse: response, + message: baseError.message, + typeName: code + ) + } + } + } +} diff --git a/Sources/SmithyRestXML/Plugin.swift b/Sources/SmithyRestXML/Plugin.swift new file mode 100644 index 000000000..e38b144e4 --- /dev/null +++ b/Sources/SmithyRestXML/Plugin.swift @@ -0,0 +1,19 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import ClientRuntime + +// RestXML URL paths come from per-operation @http traits, so no protocol-level +// interceptor wiring is needed here. +@_spi(SchemaBasedSerde) +public struct Plugin: ClientRuntime.Plugin { + + public init() {} + + public func configureClient(clientConfiguration: inout Config) async throws { + } +} diff --git a/Sources/SmithyRestXML/Serializer.swift b/Sources/SmithyRestXML/Serializer.swift new file mode 100644 index 000000000..0df02ed50 --- /dev/null +++ b/Sources/SmithyRestXML/Serializer.swift @@ -0,0 +1,342 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import struct Foundation.Data +import struct Foundation.Date +import enum Smithy.ByteStream +@_spi(SchemaBasedSerde) +import struct Smithy.HttpHeaderTrait +@_spi(SchemaBasedSerde) +import struct Smithy.HttpLabelTrait +@_spi(SchemaBasedSerde) +import struct Smithy.HttpPayloadTrait +@_spi(SchemaBasedSerde) +import struct Smithy.HttpPrefixHeadersTrait +@_spi(SchemaBasedSerde) +import struct Smithy.HttpQueryParamsTrait +@_spi(SchemaBasedSerde) +import struct Smithy.HttpQueryTrait +@_spi(SchemaBasedSerde) +import struct Smithy.HttpResponseCodeTrait +@_spi(SchemaBasedSerde) +import struct Smithy.Schema +@_spi(SchemaBasedSerde) +import protocol Smithy.SmithyDocument +@_spi(SchemaBasedSerde) +import struct Smithy.TimestampFormatTrait +@_spi(SchemaBasedSerde) +import struct Smithy.XmlAttributeTrait +@_spi(SchemaBasedSerde) +import struct Smithy.XmlFlattenedTrait +@_spi(SchemaBasedSerde) +import struct Smithy.XmlNamespaceTrait +@_spi(SchemaBasedSerde) +import struct Smithy.XmlNameTrait +@_spi(SchemaBasedSerde) +import protocol SmithySerialization.SerializableStruct +@_spi(SchemaBasedSerde) +import struct SmithySerialization.SerializerError +@_spi(SchemaBasedSerde) +import protocol SmithySerialization.ShapeSerializer +@_spi(SchemaBasedSerde) +import typealias SmithySerialization.WriteValueConsumer +@_spi(SmithyTimestamps) import enum SmithyTimestamps.TimestampFormat +@_spi(SmithyTimestamps) import struct SmithyTimestamps.TimestampFormatter +@_spi(SmithyReadWrite) import struct SmithyXML.NodeInfo +@_spi(SmithyReadWrite) import class SmithyXML.Writer + +@_spi(SchemaBasedSerde) +public final class Serializer: ShapeSerializer { + fileprivate var rootWriter: Writer? + fileprivate var rawBlobData: Data? + var streamingBody: ByteStream? + + public init() {} + + public func writeStruct(_ schema: Schema, _ value: S) throws { + if let payloadMember = schema.members.first(where: { $0.hasTrait(HttpPayloadTrait.self) }) { + let payloadSerializer = PayloadMemberSerializer(parent: self) + try S.writeConsumer(payloadMember, value, payloadSerializer) + return + } + let nodeInfo = xmlNodeInfo(for: schema) + let writer: Writer + if let rootWriter { + writer = rootWriter + } else { + writer = Writer(nodeInfo: nodeInfo) + rootWriter = writer + } + let memberSerializer = MemberSerializer(parent: writer) + for member in schema.members { + try S.writeConsumer(member, value, memberSerializer) + } + } + + public func writeList(_ schema: Schema, _ value: [E], _ consumer: WriteValueConsumer) throws {} + public func writeMap(_ schema: Schema, _ value: [String: V], _ consumer: WriteValueConsumer) throws {} + public func writeBoolean(_ schema: Schema, _ value: Bool) throws {} + public func writeByte(_ schema: Schema, _ value: Int8) throws {} + public func writeShort(_ schema: Schema, _ value: Int16) throws {} + public func writeInteger(_ schema: Schema, _ value: Int) throws {} + public func writeLong(_ schema: Schema, _ value: Int) throws {} + public func writeFloat(_ schema: Schema, _ value: Float) throws {} + public func writeDouble(_ schema: Schema, _ value: Double) throws {} + public func writeBigInteger(_ schema: Schema, _ value: Int64) throws {} + public func writeBigDecimal(_ schema: Schema, _ value: Double) throws {} + public func writeString(_ schema: Schema, _ value: String) throws {} + public func writeBlob(_ schema: Schema, _ value: Data) throws {} + public func writeTimestamp(_ schema: Schema, _ value: Date) throws {} + public func writeDocument(_ schema: Schema, _ value: any SmithyDocument) throws { + throw SerializerError("Document type not supported in XML") + } + public func writeNull(_ schema: Schema) throws {} + + public var data: Data { + get throws { + if let rawBlobData { return rawBlobData } + guard let rootWriter else { return Data() } + return try rootWriter.data() + } + } +} + +private final class MemberSerializer: ShapeSerializer { + let parent: Writer + + init(parent: Writer) { + self.parent = parent + } + + func writeStruct(_ schema: Schema, _ value: S) throws { + guard !isHttpBound(schema) else { return } + let child = parent[xmlNodeInfo(for: schema)] + child.isCollection = true + let memberSerializer = MemberSerializer(parent: child) + for member in schema.members { + try S.writeConsumer(member, value, memberSerializer) + } + } + + func writeList(_ schema: Schema, _ value: [E], _ consumer: WriteValueConsumer) throws { + guard !isHttpBound(schema) else { return } + let isFlattened = schema.hasTrait(XmlFlattenedTrait.self) + if isFlattened { + let nodeInfo = xmlNodeInfo(for: schema) + for element in value { + try consumer(element, ValueSerializer(writer: parent[nodeInfo])) + } + } else { + let listWriter = parent[xmlNodeInfo(for: schema)] + listWriter.isCollection = true + let memberSchema = (schema.target ?? schema).member + let memberNodeInfo = xmlNodeInfo(for: memberSchema) + for element in value { + try consumer(element, ValueSerializer(writer: listWriter[memberNodeInfo])) + } + } + } + + func writeMap(_ schema: Schema, _ value: [String: V], _ consumer: WriteValueConsumer) throws { + guard !isHttpBound(schema) else { return } + let isFlattened = schema.hasTrait(XmlFlattenedTrait.self) + let mapSchema = schema.target ?? schema + let keyNodeInfo = xmlNodeInfo(for: mapSchema.key) + let valueNodeInfo = xmlNodeInfo(for: mapSchema.value) + if isFlattened { + let nodeInfo = xmlNodeInfo(for: schema) + for (k, v) in value { + let entry = parent[nodeInfo] + try entry[keyNodeInfo].write(k) + try consumer(v, ValueSerializer(writer: entry[valueNodeInfo])) + } + } else { + let mapWriter = parent[xmlNodeInfo(for: schema)] + mapWriter.isCollection = true + for (k, v) in value { + let entry = mapWriter[NodeInfo("entry")] + try entry[keyNodeInfo].write(k) + try consumer(v, ValueSerializer(writer: entry[valueNodeInfo])) + } + } + } + + func writeBoolean(_ schema: Schema, _ value: Bool) throws { + guard !isHttpBound(schema) else { return } + try parent[xmlNodeInfo(for: schema)].write(value) + } + func writeByte(_ schema: Schema, _ value: Int8) throws { + guard !isHttpBound(schema) else { return } + try parent[xmlNodeInfo(for: schema)].write(value) + } + func writeShort(_ schema: Schema, _ value: Int16) throws { + guard !isHttpBound(schema) else { return } + try parent[xmlNodeInfo(for: schema)].write(value) + } + func writeInteger(_ schema: Schema, _ value: Int) throws { + guard !isHttpBound(schema) else { return } + try parent[xmlNodeInfo(for: schema)].write(value) + } + func writeLong(_ schema: Schema, _ value: Int) throws { + guard !isHttpBound(schema) else { return } + try parent[xmlNodeInfo(for: schema)].write(value) + } + func writeFloat(_ schema: Schema, _ value: Float) throws { + guard !isHttpBound(schema) else { return } + try parent[xmlNodeInfo(for: schema)].write(value) + } + func writeDouble(_ schema: Schema, _ value: Double) throws { + guard !isHttpBound(schema) else { return } + try parent[xmlNodeInfo(for: schema)].write(value) + } + func writeBigInteger(_ schema: Schema, _ value: Int64) throws { + guard !isHttpBound(schema) else { return } + try parent[xmlNodeInfo(for: schema)].write(String(value)) + } + func writeBigDecimal(_ schema: Schema, _ value: Double) throws { + guard !isHttpBound(schema) else { return } + try parent[xmlNodeInfo(for: schema)].write(value) + } + func writeString(_ schema: Schema, _ value: String) throws { + guard !isHttpBound(schema) else { return } + try parent[xmlNodeInfo(for: schema)].write(value) + } + func writeBlob(_ schema: Schema, _ value: Data) throws { + guard !isHttpBound(schema) else { return } + try parent[xmlNodeInfo(for: schema)].write(value) + } + func writeTimestamp(_ schema: Schema, _ value: Date) throws { + guard !isHttpBound(schema) else { return } + try parent[xmlNodeInfo(for: schema)].writeTimestamp(value, format: resolveTimestampFormat(schema)) + } + func writeDocument(_ schema: Schema, _ value: any SmithyDocument) throws { + throw SerializerError("Document type not supported in XML") + } + func writeNull(_ schema: Schema) throws {} + var data: Data { get throws { Data() } } +} + +private struct ValueSerializer: ShapeSerializer { + let writer: Writer + + func writeStruct(_ schema: Schema, _ value: S) throws { + let memberSerializer = MemberSerializer(parent: writer) + for member in schema.members { + try S.writeConsumer(member, value, memberSerializer) + } + } + + func writeList(_ schema: Schema, _ value: [E], _ consumer: WriteValueConsumer) throws { + let memberSchema = (schema.target ?? schema).member + let memberNodeInfo = xmlNodeInfo(for: memberSchema) + for element in value { + try consumer(element, ValueSerializer(writer: writer[memberNodeInfo])) + } + } + + func writeMap(_ schema: Schema, _ value: [String: V], _ consumer: WriteValueConsumer) throws { + let mapSchema = schema.target ?? schema + let keyNodeInfo = xmlNodeInfo(for: mapSchema.key) + let valueNodeInfo = xmlNodeInfo(for: mapSchema.value) + for (k, v) in value { + let entry = writer[NodeInfo("entry")] + try entry[keyNodeInfo].write(k) + try consumer(v, ValueSerializer(writer: entry[valueNodeInfo])) + } + } + + func writeBoolean(_ schema: Schema, _ value: Bool) throws { try writer.write(value) } + func writeByte(_ schema: Schema, _ value: Int8) throws { try writer.write(value) } + func writeShort(_ schema: Schema, _ value: Int16) throws { try writer.write(value) } + func writeInteger(_ schema: Schema, _ value: Int) throws { try writer.write(value) } + func writeLong(_ schema: Schema, _ value: Int) throws { try writer.write(value) } + func writeFloat(_ schema: Schema, _ value: Float) throws { try writer.write(value) } + func writeDouble(_ schema: Schema, _ value: Double) throws { try writer.write(value) } + func writeBigInteger(_ schema: Schema, _ value: Int64) throws { try writer.write(String(value)) } + func writeBigDecimal(_ schema: Schema, _ value: Double) throws { try writer.write(value) } + func writeString(_ schema: Schema, _ value: String) throws { try writer.write(value) } + func writeBlob(_ schema: Schema, _ value: Data) throws { try writer.write(value) } + func writeTimestamp(_ schema: Schema, _ value: Date) throws { + try writer.writeTimestamp(value, format: resolveTimestampFormat(schema)) + } + func writeDocument(_ schema: Schema, _ value: any SmithyDocument) throws { + throw SerializerError("Document type not supported in XML") + } + func writeNull(_ schema: Schema) throws {} + var data: Data { get throws { Data() } } +} + +private func xmlNodeInfo(for schema: Schema) -> NodeInfo { + let name = (try? schema.getTrait(XmlNameTrait.self))?.value + ?? schema.memberName ?? schema.id.member ?? schema.id.name + let location: NodeInfo.Location = schema.hasTrait(XmlAttributeTrait.self) ? .attribute : .element + let namespaceDef: NodeInfo.Namespace? + if let ns = try? schema.getTrait(XmlNamespaceTrait.self) { + namespaceDef = NodeInfo.Namespace(prefix: ns.prefix ?? "", uri: ns.uri) + } else { + namespaceDef = nil + } + return NodeInfo(name, location: location, namespaceDef: namespaceDef) +} + +private func isHttpBound(_ schema: Schema) -> Bool { + schema.hasTrait(HttpHeaderTrait.self) || + schema.hasTrait(HttpLabelTrait.self) || + schema.hasTrait(HttpQueryTrait.self) || + schema.hasTrait(HttpQueryParamsTrait.self) || + schema.hasTrait(HttpPrefixHeadersTrait.self) || + schema.hasTrait(HttpResponseCodeTrait.self) +} + +private final class PayloadMemberSerializer: ShapeSerializer { + let outer: Serializer + + init(parent: Serializer) { + self.outer = parent + } + + func writeStruct(_ schema: Schema, _ value: S) throws { + let nodeInfo = schema.hasTrait(XmlNameTrait.self) + ? xmlNodeInfo(for: schema) + : xmlNodeInfo(for: schema.target ?? schema) + let writer = Writer(nodeInfo: nodeInfo) + outer.rootWriter = writer + let structSchema = schema.target ?? schema + let memberSerializer = MemberSerializer(parent: writer) + for member in structSchema.members { + try S.writeConsumer(member, value, memberSerializer) + } + } + + func writeBlob(_ schema: Schema, _ value: Data) throws { + outer.rawBlobData = value + } + + func writeDataStream(_ schema: Schema, _ value: ByteStream) throws { + outer.streamingBody = value + } + + func writeString(_ schema: Schema, _ value: String) throws { + outer.rawBlobData = Data(value.utf8) + } + + func writeList(_ schema: Schema, _ value: [E], _ consumer: WriteValueConsumer) throws {} + func writeMap(_ schema: Schema, _ value: [String: V], _ consumer: WriteValueConsumer) throws {} + func writeBoolean(_ schema: Schema, _ value: Bool) throws {} + func writeByte(_ schema: Schema, _ value: Int8) throws {} + func writeShort(_ schema: Schema, _ value: Int16) throws {} + func writeInteger(_ schema: Schema, _ value: Int) throws {} + func writeLong(_ schema: Schema, _ value: Int) throws {} + func writeFloat(_ schema: Schema, _ value: Float) throws {} + func writeDouble(_ schema: Schema, _ value: Double) throws {} + func writeBigInteger(_ schema: Schema, _ value: Int64) throws {} + func writeBigDecimal(_ schema: Schema, _ value: Double) throws {} + func writeTimestamp(_ schema: Schema, _ value: Date) throws {} + func writeDocument(_ schema: Schema, _ value: any SmithyDocument) throws {} + func writeNull(_ schema: Schema) throws {} + var data: Data { get throws { Data() } } +} diff --git a/Sources/SmithyRestXML/TimestampUtils.swift b/Sources/SmithyRestXML/TimestampUtils.swift new file mode 100644 index 000000000..1620b73b2 --- /dev/null +++ b/Sources/SmithyRestXML/TimestampUtils.swift @@ -0,0 +1,23 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +@_spi(SchemaBasedSerde) +import struct Smithy.Schema +@_spi(SchemaBasedSerde) +import struct Smithy.TimestampFormatTrait +@_spi(SmithyTimestamps) import enum SmithyTimestamps.TimestampFormat + +func resolveTimestampFormat(_ schema: Schema) -> TimestampFormat { + guard let traitFormat = try? schema.getTrait(TimestampFormatTrait.self)?.format else { + return .dateTime + } + switch traitFormat { + case .dateTime: return .dateTime + case .httpDate: return .httpDate + case .epochSeconds: return .epochSeconds + } +} diff --git a/Sources/SmithyXML/Reader/Reader.swift b/Sources/SmithyXML/Reader/Reader.swift index 88189cf8a..bd2d335ab 100644 --- a/Sources/SmithyXML/Reader/Reader.swift +++ b/Sources/SmithyXML/Reader/Reader.swift @@ -23,11 +23,21 @@ public final class Reader: SmithyReader { public var hasContent: Bool { content != nil } var content: String? - // MARK: - init & deinit + /// An empty reader, returned when data cannot be parsed. + public init() { + self.nodeInfo = "" + } - /// Creates an "empty" reader. This Reader will be returned when the data cannot be parsed. - init() { + /// A Reader holding only literal text content (no element name). + public init(content: String) { self.nodeInfo = "" + self.content = content + } + + /// A Reader with a node name and optional text content. + public init(nodeInfo: NodeInfo, content: String?) { + self.nodeInfo = nodeInfo + self.content = content } /// Used to create a new XML node during reading from XML. @@ -54,7 +64,7 @@ public final class Reader: SmithyReader { } } - func addChild(_ child: Reader) { + public func addChild(_ child: Reader) { children.append(child) child.parent = self } diff --git a/Sources/SmithyXML/Writer/Writer.swift b/Sources/SmithyXML/Writer/Writer.swift index ee58bb391..2a69e7d2f 100644 --- a/Sources/SmithyXML/Writer/Writer.swift +++ b/Sources/SmithyXML/Writer/Writer.swift @@ -27,11 +27,9 @@ public final class Writer: SmithyWriter { var children: [Writer] = [] weak var parent: Writer? let nodeInfo: NodeInfo - var isCollection = false + public var isCollection = false public var nodeInfoPath: [NodeInfo] { (parent?.nodeInfoPath ?? []) + [nodeInfo] } - // MARK: - init & deinit - /// Used by the `DocumentWriter` to begin serialization of a model to XML. /// - Parameter nodeInfo: The node info for the XML node. public init(nodeInfo: NodeInfo) { diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/PackageManifestGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/PackageManifestGenerator.kt index 393141586..86af01966 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/PackageManifestGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/PackageManifestGenerator.kt @@ -6,11 +6,13 @@ package software.amazon.smithy.swift.codegen import software.amazon.smithy.codegen.core.SymbolDependency import software.amazon.smithy.swift.codegen.core.GenerationContext +import software.amazon.smithy.swift.codegen.integration.serde.SerdeUtils class PackageManifestGenerator( val ctx: GenerationContext, ) { fun writePackageManifest(dependencies: List) { + val usesSchemaBased = SerdeUtils.useSchemaBased(ctx.settings, ctx.model) ctx.writerDelegator().useFileWriter("Package.swift") { writer -> writer.write("// swift-tools-version: \$L", ctx.settings.swiftVersion) writer.write("") @@ -48,8 +50,20 @@ class PackageManifestGenerator( writer.openBlock("targets: [", "]") { writer.openBlock(".target(", "),") { writer.write("name: \$S,", ctx.settings.moduleName) - writer.openBlock("dependencies: [", "]") { - dependenciesByTarget.forEach { writeTargetDependency(writer, it) } + if (usesSchemaBased) { + writer.openBlock("dependencies: [", "],") { + dependenciesByTarget.forEach { writeTargetDependency(writer, it) } + } + writer.openBlock("plugins: [", "]") { + writer.openBlock(".plugin(", ")") { + writer.write("name: \"SmithyCodeGeneratorPlugin\",") + writer.write("package: \"smithy-swift\"") + } + } + } else { + writer.openBlock("dependencies: [", "]") { + dependenciesByTarget.forEach { writeTargetDependency(writer, it) } + } } } writer.openBlock(".testTarget(", ")") { diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftDependency.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftDependency.kt index dc4fbb109..bcc02db88 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftDependency.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftDependency.kt @@ -64,6 +64,7 @@ class SwiftDependency( val SMITHY_WAITERS_API = smithySwiftDependency("SmithyWaitersAPI") val SMITHY_AWSJSON = smithySwiftDependency("SmithyAWSJSON") val SMITHY_RPCV2CBOR = smithySwiftDependency("SmithyRPCv2CBOR") + val SMITHY_REST_XML = smithySwiftDependency("SmithyRestXML") fun smithySwiftDependency(name: String): SwiftDependency = SwiftDependency( diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/aws/protocols/restxml/RestXMLPlugin.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/aws/protocols/restxml/RestXMLPlugin.kt new file mode 100644 index 000000000..1ed332c9d --- /dev/null +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/aws/protocols/restxml/RestXMLPlugin.kt @@ -0,0 +1,9 @@ +package software.amazon.smithy.swift.codegen.aws.protocols.restxml + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.swift.codegen.integration.Plugin +import software.amazon.smithy.swift.codegen.swiftmodules.SmithyRestXMLTypes + +class RestXMLPlugin : Plugin { + override val className: Symbol = SmithyRestXMLTypes.Plugin +} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/aws/protocols/restxml/RestXmlCustomizations.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/aws/protocols/restxml/RestXmlCustomizations.kt index 7765c89c4..dc73cf8a6 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/aws/protocols/restxml/RestXmlCustomizations.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/aws/protocols/restxml/RestXmlCustomizations.kt @@ -6,9 +6,16 @@ package software.amazon.smithy.swift.codegen.aws.protocols.restxml import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.swift.codegen.SwiftWriter import software.amazon.smithy.swift.codegen.integration.DefaultHTTPProtocolCustomizations +import software.amazon.smithy.swift.codegen.integration.Plugin import software.amazon.smithy.swift.codegen.swiftmodules.ClientRuntimeTypes +import software.amazon.smithy.swift.codegen.swiftmodules.SmithyRestXMLTypes open class RestXmlCustomizations : DefaultHTTPProtocolCustomizations() { override val baseErrorSymbol: Symbol = ClientRuntimeTypes.RestXML.RestXMLError + + override fun renderClientProtocol(writer: SwiftWriter): String = writer.format("\$N()", SmithyRestXMLTypes.HTTPClientProtocol) + + override val plugins: List = listOf(RestXMLPlugin()) } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/aws/protocols/restxml/RestXmlProtocolGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/aws/protocols/restxml/RestXmlProtocolGenerator.kt index a556f5f28..ffe246fc5 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/aws/protocols/restxml/RestXmlProtocolGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/aws/protocols/restxml/RestXmlProtocolGenerator.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.swift.codegen.aws.protocols.restxml import software.amazon.smithy.aws.traits.protocols.RestXmlTrait import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.swift.codegen.integration.DefaultHTTPProtocolCustomizations @@ -49,6 +50,17 @@ open class RestXmlProtocolGenerator( "S3PreservesLeadingDotSegmentInUriLabel", ) + override fun addProtocolSpecificMiddleware( + ctx: ProtocolGenerator.GenerationContext, + operation: OperationShape, + ) { + super.addProtocolSpecificMiddleware(ctx, operation) + + // Remove these middlewares as they are handled by the schema-based ClientProtocol & Operation + operationMiddleware.removeMiddleware(operation, "OperationInputBodyMiddleware") + operationMiddleware.removeMiddleware(operation, "DeserializeMiddleware") + } + override fun generateDeserializers(ctx: ProtocolGenerator.GenerationContext) { super.generateDeserializers(ctx) val errorShapes = resolveErrorShapes(ctx) diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HTTPBindingProtocolGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HTTPBindingProtocolGenerator.kt index 83bc9383c..0e5e9b07c 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HTTPBindingProtocolGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HTTPBindingProtocolGenerator.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.swift.codegen.integration import software.amazon.smithy.aws.traits.auth.UnsignedPayloadTrait +import software.amazon.smithy.aws.traits.protocols.RestXmlTrait import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.knowledge.HttpBinding import software.amazon.smithy.model.knowledge.HttpBindingIndex @@ -149,7 +150,7 @@ abstract class HTTPBindingProtocolGenerator( continue } val httpBindingResolver = getProtocolHttpBindingResolver(ctx, defaultContentType) - if (!usesSchemaBased) { + if (!usesSchemaBased || ctx.service.hasTrait()) { HttpUrlPathProvider.renderUrlPathMiddleware(ctx, operation, httpBindingResolver) HttpHeaderProvider.renderHeaderMiddleware(ctx, operation, httpBindingResolver, customizations.defaultTimestampFormat) HttpQueryItemProvider.renderQueryMiddleware(ctx, operation, httpBindingResolver, customizations.defaultTimestampFormat) diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/SerdeUtils.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/SerdeUtils.kt index 14e004540..e3737f2aa 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/SerdeUtils.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/SerdeUtils.kt @@ -2,6 +2,7 @@ package software.amazon.smithy.swift.codegen.integration.serde import software.amazon.smithy.aws.traits.protocols.AwsJson1_0Trait import software.amazon.smithy.aws.traits.protocols.AwsJson1_1Trait +import software.amazon.smithy.aws.traits.protocols.RestXmlTrait import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.protocol.traits.Rpcv2CborTrait @@ -21,7 +22,9 @@ class SerdeUtils { private fun useSchemaBased(service: ServiceShape) = // This fun is temporary; it will be eliminated when all services/protocols are moved to schema-based - // Right now this function only returns true for rpcv2Cbor or AwsJson 1.0 / 1.1 based services - service.hasTrait() || service.hasTrait() || service.hasTrait() + service.hasTrait() || + service.hasTrait() || + service.hasTrait() || + service.hasTrait() } } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/swiftmodules/SmithyRestXMLTypes.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/swiftmodules/SmithyRestXMLTypes.kt new file mode 100644 index 000000000..6d580cef5 --- /dev/null +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/swiftmodules/SmithyRestXMLTypes.kt @@ -0,0 +1,24 @@ +package software.amazon.smithy.swift.codegen.swiftmodules + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.swift.codegen.SwiftDeclaration +import software.amazon.smithy.swift.codegen.SwiftDependency + +object SmithyRestXMLTypes { + val HTTPClientProtocol = runtimeSymbol("HTTPClientProtocol", SwiftDeclaration.STRUCT) + val Plugin = runtimeSymbol("Plugin", SwiftDeclaration.STRUCT) +} + +private fun runtimeSymbol( + name: String, + declaration: SwiftDeclaration?, + additionalImports: List = emptyList(), + spiName: List = listOf("SchemaBasedSerde"), +): Symbol = + SwiftSymbol.make( + name, + declaration, + SwiftDependency.SMITHY_REST_XML, + additionalImports, + spiName, + ) diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/manifestanddocs/PackageManifestGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/manifestanddocs/PackageManifestGeneratorTests.kt index 48ce8d8bc..85bfa098a 100644 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/manifestanddocs/PackageManifestGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/manifestanddocs/PackageManifestGeneratorTests.kt @@ -77,6 +77,12 @@ class PackageManifestGeneratorTests { name: "ClientRuntime", package: "smithy-swift" ), + ], + plugins: [ + .plugin( + name: "SmithyCodeGeneratorPlugin", + package: "smithy-swift" + ) ] ), .testTarget( diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolgeneratormocks/MockHTTPRestXMLProtocolGenerator.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolgeneratormocks/MockHTTPRestXMLProtocolGenerator.kt index 2f34ec274..ec3b7ce31 100644 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolgeneratormocks/MockHTTPRestXMLProtocolGenerator.kt +++ b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolgeneratormocks/MockHTTPRestXMLProtocolGenerator.kt @@ -10,17 +10,24 @@ import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.swift.codegen.SwiftWriter import software.amazon.smithy.swift.codegen.integration.DefaultHTTPProtocolCustomizations import software.amazon.smithy.swift.codegen.integration.HTTPBindingProtocolGenerator import software.amazon.smithy.swift.codegen.integration.HttpProtocolTestGenerator import software.amazon.smithy.swift.codegen.integration.HttpProtocolUnitTestErrorGenerator import software.amazon.smithy.swift.codegen.integration.HttpProtocolUnitTestRequestGenerator import software.amazon.smithy.swift.codegen.integration.HttpProtocolUnitTestResponseGenerator +import software.amazon.smithy.swift.codegen.integration.Plugin import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.isInHttpBody import software.amazon.smithy.swift.codegen.requestandresponse.TestHttpProtocolClientGeneratorFactory +import software.amazon.smithy.swift.codegen.swiftmodules.SmithyRestXMLTypes -class MockRestXMLHTTPProtocolCustomizations : DefaultHTTPProtocolCustomizations() +class MockRestXMLHTTPProtocolCustomizations : DefaultHTTPProtocolCustomizations() { + override fun renderClientProtocol(writer: SwiftWriter): String = writer.format("\$N()", SmithyRestXMLTypes.HTTPClientProtocol) + + override val plugins: List = listOf() +} class MockHTTPRestXMLProtocolGenerator : HTTPBindingProtocolGenerator(MockRestXMLHTTPProtocolCustomizations()) { override val defaultContentType: String = "application/xml" @@ -32,7 +39,9 @@ class MockHTTPRestXMLProtocolGenerator : HTTPBindingProtocolGenerator(MockRestXM ctx: ProtocolGenerator.GenerationContext, operation: OperationShape, ) { - // Intentionally empty + // Remove middlewares handled by schema-based ClientProtocol + operationMiddleware.removeMiddleware(operation, "OperationInputBodyMiddleware") + operationMiddleware.removeMiddleware(operation, "DeserializeMiddleware") } override fun addUserAgentMiddleware( diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/BlobDecodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/BlobDecodeXMLGenerationTests.kt deleted file mode 100644 index 1051c8737..000000000 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/BlobDecodeXMLGenerationTests.kt +++ /dev/null @@ -1,52 +0,0 @@ -package software.amazon.smithy.swift.codegen.protocolspecificserde.xml - -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import io.kotest.matchers.string.shouldContainOnlyOnce -import org.junit.jupiter.api.Test -import software.amazon.smithy.swift.codegen.getFileContents - -class BlobDecodeXMLGenerationTests { - @Test - fun `decode blob`() { - val context = setupTests("Isolated/Restxml/xml-blobs.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlBlobsOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlBlobsOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlBlobsOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlBlobsOutput() - value.data = try reader["data"].readIfPresent() - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `decode blob nested`() { - val context = setupTests("Isolated/Restxml/xml-blobs.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlBlobsNestedOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlBlobsNestedOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlBlobsNestedOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlBlobsNestedOutput() - value.nestedBlobList = try reader["nestedBlobList"].readListIfPresent(memberReadingClosure: SmithyReadWrite.listReadingClosure(memberReadingClosure: SmithyReadWrite.ReadingClosures.readData(from:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } -} diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/BlobEncodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/BlobEncodeXMLGenerationTests.kt deleted file mode 100644 index f1ab57a54..000000000 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/BlobEncodeXMLGenerationTests.kt +++ /dev/null @@ -1,44 +0,0 @@ -package software.amazon.smithy.swift.codegen.protocolspecificserde.xml - -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import io.kotest.matchers.string.shouldContainOnlyOnce -import org.junit.jupiter.api.Test -import software.amazon.smithy.swift.codegen.getFileContents - -class BlobEncodeXMLGenerationTests { - @Test - fun `encode blob`() { - val context = setupTests("Isolated/Restxml/xml-blobs.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlBlobsInput+Write.swift") - val expectedContents = """ -extension XmlBlobsInput { - - static func write(value: XmlBlobsInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["data"].write(value.data) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `encode nested blob`() { - val context = setupTests("Isolated/Restxml/xml-blobs.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlBlobsNestedInput+Write.swift") - val expectedContents = """ -extension XmlBlobsNestedInput { - - static func write(value: XmlBlobsNestedInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["nestedBlobList"].writeList(value.nestedBlobList, memberWritingClosure: SmithyReadWrite.listWritingClosure(memberWritingClosure: SmithyReadWrite.WritingClosures.writeData(value:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } -} diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/EnumDecodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/EnumDecodeXMLGenerationTests.kt deleted file mode 100644 index ed921c148..000000000 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/EnumDecodeXMLGenerationTests.kt +++ /dev/null @@ -1,55 +0,0 @@ -package software.amazon.smithy.swift.codegen.protocolspecificserde.xml - -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import io.kotest.matchers.string.shouldContainOnlyOnce -import org.junit.jupiter.api.Test -import software.amazon.smithy.swift.codegen.getFileContents - -class EnumDecodeXMLGenerationTests { - @Test - fun `decode enum`() { - val context = setupTests("Isolated/Restxml/xml-enums.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlEnumsOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlEnumsOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlEnumsOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlEnumsOutput() - value.fooEnum1 = try reader["fooEnum1"].readIfPresent() - value.fooEnum2 = try reader["fooEnum2"].readIfPresent() - value.fooEnum3 = try reader["fooEnum3"].readIfPresent() - value.fooEnumList = try reader["fooEnumList"].readListIfPresent(memberReadingClosure: SmithyReadWrite.ReadingClosureBox().read(from:), memberNodeInfo: "member", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `decode enum nested`() { - val context = setupTests("Isolated/Restxml/xml-enums.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlEnumsNestedOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlEnumsNestedOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlEnumsNestedOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlEnumsNestedOutput() - value.nestedEnumsList = try reader["nestedEnumsList"].readListIfPresent(memberReadingClosure: SmithyReadWrite.listReadingClosure(memberReadingClosure: SmithyReadWrite.ReadingClosureBox().read(from:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } -} diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/EnumEncodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/EnumEncodeXMLGenerationTests.kt deleted file mode 100644 index 30efa67f8..000000000 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/EnumEncodeXMLGenerationTests.kt +++ /dev/null @@ -1,47 +0,0 @@ -package software.amazon.smithy.swift.codegen.protocolspecificserde.xml - -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import io.kotest.matchers.string.shouldContainOnlyOnce -import org.junit.jupiter.api.Test -import software.amazon.smithy.swift.codegen.getFileContents - -class EnumEncodeXMLGenerationTests { - @Test - fun `001 encode enum`() { - val context = setupTests("Isolated/Restxml/xml-enums.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlEnumsInput+Write.swift") - val expectedContents = """ -extension XmlEnumsInput { - - static func write(value: XmlEnumsInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["fooEnum1"].write(value.fooEnum1) - try writer["fooEnum2"].write(value.fooEnum2) - try writer["fooEnum3"].write(value.fooEnum3) - try writer["fooEnumList"].writeList(value.fooEnumList, memberWritingClosure: SmithyReadWrite.WritingClosureBox().write(value:to:), memberNodeInfo: "member", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `002 encode nested enum`() { - val context = setupTests("Isolated/Restxml/xml-enums.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlEnumsNestedInput+Write.swift") - val expectedContents = """ -extension XmlEnumsNestedInput { - - static func write(value: XmlEnumsNestedInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["nestedEnumsList"].writeList(value.nestedEnumsList, memberWritingClosure: SmithyReadWrite.listWritingClosure(memberWritingClosure: SmithyReadWrite.WritingClosureBox().write(value:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } -} diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/ListDecodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/ListDecodeXMLGenerationTests.kt deleted file mode 100644 index 05ff0862a..000000000 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/ListDecodeXMLGenerationTests.kt +++ /dev/null @@ -1,158 +0,0 @@ -package software.amazon.smithy.swift.codegen.protocolspecificserde.xml - -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import io.kotest.matchers.string.shouldContainOnlyOnce -import org.junit.jupiter.api.Test -import software.amazon.smithy.swift.codegen.getFileContents - -class ListDecodeXMLGenerationTests { - @Test - fun `001 wrapped list with xmlName`() { - val context = setupTests("Isolated/Restxml/xml-lists-xmlname.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlListXmlNameOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlListXmlNameOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlListXmlNameOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlListXmlNameOutput() - value.renamedListMembers = try reader["renamed"].readListIfPresent(memberReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), memberNodeInfo: "item", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `002 wrapped nested list with xmlname`() { - val context = setupTests("Isolated/Restxml/xml-lists-xmlname-nested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlListXmlNameNestedOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlListXmlNameNestedOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlListXmlNameNestedOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlListXmlNameNestedOutput() - value.renamedListMembers = try reader["renamed"].readListIfPresent(memberReadingClosure: SmithyReadWrite.listReadingClosure(memberReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), memberNodeInfo: "subItem", isFlattened: false), memberNodeInfo: "item", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `003 decode flattened list`() { - val context = setupTests("Isolated/Restxml/xml-lists-flattened.smithy", "aws.protocoltests.restxml#RestXml") - - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlFlattenedListOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlFlattenedListOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlFlattenedListOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlFlattenedListOutput() - value.myGroceryList = try reader["myGroceryList"].readListIfPresent(memberReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), memberNodeInfo: "member", isFlattened: true) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `004 decode flattened empty list`() { - val context = setupTests("Isolated/Restxml/xml-lists-emptyFlattened.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlEmptyFlattenedListsOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlEmptyFlattenedListsOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlEmptyFlattenedListsOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlEmptyFlattenedListsOutput() - value.booleanList = try reader["booleanList"].readListIfPresent(memberReadingClosure: SmithyReadWrite.ReadingClosures.readBool(from:), memberNodeInfo: "member", isFlattened: false) - value.integerList = try reader["integerList"].readListIfPresent(memberReadingClosure: SmithyReadWrite.ReadingClosures.readInt(from:), memberNodeInfo: "member", isFlattened: false) - value.stringList = try reader["stringList"].readListIfPresent(memberReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), memberNodeInfo: "member", isFlattened: true) - value.stringSet = try reader["stringSet"].readListIfPresent(memberReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), memberNodeInfo: "member", isFlattened: true) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `005 decode nestednested flattened list serialization`() { - val context = setupTests("Isolated/Restxml/xml-lists-nestednested-flattened.smithy", "aws.protocoltests.restxml#RestXml") - val contents = - getFileContents(context.manifest, "Sources/RestXml/models/XmlNestedNestedFlattenedListOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlNestedNestedFlattenedListOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlNestedNestedFlattenedListOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlNestedNestedFlattenedListOutput() - value.nestedNestedStringList = try reader["nestedNestedStringList"].readListIfPresent(memberReadingClosure: SmithyReadWrite.listReadingClosure(memberReadingClosure: SmithyReadWrite.listReadingClosure(memberReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: true) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `012 decode list containing map`() { - val context = setupTests("Isolated/Restxml/xml-lists-contain-map.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlListContainMapOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlListContainMapOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlListContainMapOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlListContainMapOutput() - value.myList = try reader["myList"].readListIfPresent(memberReadingClosure: SmithyReadWrite.mapReadingClosure(valueReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `013 decode flattened list containing map`() { - val context = setupTests("Isolated/Restxml/xml-lists-flattened-contain-map.smithy", "aws.protocoltests.restxml#RestXml") - val contents = - getFileContents(context.manifest, "Sources/RestXml/models/XmlListFlattenedContainMapOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlListFlattenedContainMapOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlListFlattenedContainMapOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlListFlattenedContainMapOutput() - value.myList = try reader["myList"].readListIfPresent(memberReadingClosure: SmithyReadWrite.mapReadingClosure(valueReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), memberNodeInfo: "member", isFlattened: true) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } -} diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/ListEncodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/ListEncodeXMLGenerationTests.kt deleted file mode 100644 index 89c930c6c..000000000 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/ListEncodeXMLGenerationTests.kt +++ /dev/null @@ -1,226 +0,0 @@ -package software.amazon.smithy.swift.codegen.protocolspecificserde.xml - -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import io.kotest.matchers.string.shouldContainOnlyOnce -import org.junit.jupiter.api.Test -import software.amazon.smithy.swift.codegen.getFileContents - -class ListEncodeXMLGenerationTests { - @Test - fun `001 wrapped list with xmlName`() { - val context = setupTests("Isolated/Restxml/xml-lists-xmlname.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlListXmlNameInput+Write.swift") - val expectedContents = """ -extension XmlListXmlNameInput { - - static func write(value: XmlListXmlNameInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["renamed"].writeList(value.renamedListMembers, memberWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), memberNodeInfo: "item", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `002 nested wrapped list with xmlname`() { - val context = setupTests("Isolated/Restxml/xml-lists-xmlname-nested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlListXmlNameNestedInput+Write.swift") - val expectedContents = """ -extension XmlListXmlNameNestedInput { - - static func write(value: XmlListXmlNameNestedInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["renamed"].writeList(value.renamedListMembers, memberWritingClosure: SmithyReadWrite.listWritingClosure(memberWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), memberNodeInfo: "subItem", isFlattened: false), memberNodeInfo: "item", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `003 nested wrapped list serialization`() { - val context = setupTests("Isolated/Restxml/xml-lists-nested-wrapped.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlNestedWrappedListInput+Write.swift") - val expectedContents = """ -extension XmlNestedWrappedListInput { - - static func write(value: XmlNestedWrappedListInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["nestedStringList"].writeList(value.nestedStringList, memberWritingClosure: SmithyReadWrite.listWritingClosure(memberWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `004 nestednested wrapped list serialization`() { - val context = setupTests("Isolated/Restxml/xml-lists-nestednested-wrapped.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlNestedNestedWrappedListInput+Write.swift") - val expectedContents = """ -extension XmlNestedNestedWrappedListInput { - - static func write(value: XmlNestedNestedWrappedListInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["nestedNestedStringList"].writeList(value.nestedNestedStringList, memberWritingClosure: SmithyReadWrite.listWritingClosure(memberWritingClosure: SmithyReadWrite.listWritingClosure(memberWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `005 nestednested flattened list serialization`() { - val context = setupTests("Isolated/Restxml/xml-lists-nestednested-flattened.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlNestedNestedFlattenedListInput+Write.swift") - val expectedContents = """ -extension XmlNestedNestedFlattenedListInput { - - static func write(value: XmlNestedNestedFlattenedListInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["nestedNestedStringList"].writeList(value.nestedNestedStringList, memberWritingClosure: SmithyReadWrite.listWritingClosure(memberWritingClosure: SmithyReadWrite.listWritingClosure(memberWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: true) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `006 empty lists`() { - val context = setupTests("Isolated/Restxml/xml-lists-empty.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlEmptyListsInput+Write.swift") - val expectedContents = """ -extension XmlEmptyListsInput { - - static func write(value: XmlEmptyListsInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["booleanList"].writeList(value.booleanList, memberWritingClosure: SmithyReadWrite.WritingClosures.writeBool(value:to:), memberNodeInfo: "member", isFlattened: false) - try writer["integerList"].writeList(value.integerList, memberWritingClosure: SmithyReadWrite.WritingClosures.writeInt(value:to:), memberNodeInfo: "member", isFlattened: false) - try writer["stringList"].writeList(value.stringList, memberWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), memberNodeInfo: "member", isFlattened: false) - try writer["stringSet"].writeList(value.stringSet, memberWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), memberNodeInfo: "member", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `007 wrapped list serialization`() { - val context = setupTests("Isolated/Restxml/xml-lists-wrapped.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlWrappedListInput+Write.swift") - val expectedContents = """ -extension XmlWrappedListInput { - - static func write(value: XmlWrappedListInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["myGroceryList"].writeList(value.myGroceryList, memberWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), memberNodeInfo: "member", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `008 flattened list serialization`() { - val context = setupTests("Isolated/Restxml/xml-lists-flattened.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlFlattenedListInput+Write.swift") - val expectedContents = """ -extension XmlFlattenedListInput { - - static func write(value: XmlFlattenedListInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["myGroceryList"].writeList(value.myGroceryList, memberWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), memberNodeInfo: "member", isFlattened: true) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `010 encode nested flattened datetime encodable`() { - val context = setupTests("Isolated/Restxml/xml-lists-flattened-nested-datetime.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlTimestampsNestedFlattenedInput+Write.swift") - val expectedContents = """ -extension XmlTimestampsNestedFlattenedInput { - - static func write(value: XmlTimestampsNestedFlattenedInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["nestedTimestampList"].writeList(value.nestedTimestampList, memberWritingClosure: SmithyReadWrite.listWritingClosure(memberWritingClosure: SmithyReadWrite.timestampWritingClosure(format: SmithyTimestamps.TimestampFormat.epochSeconds), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: .init("nestedMember", namespaceDef: .init(prefix: "baz", uri: "http://baz.com")), isFlattened: true) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `011 encode flattened empty list`() { - val context = setupTests("Isolated/Restxml/xml-lists-emptyFlattened.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlEmptyFlattenedListsInput+Write.swift") - val expectedContents = """ -extension XmlEmptyFlattenedListsInput { - - static func write(value: XmlEmptyFlattenedListsInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["booleanList"].writeList(value.booleanList, memberWritingClosure: SmithyReadWrite.WritingClosures.writeBool(value:to:), memberNodeInfo: "member", isFlattened: false) - try writer["integerList"].writeList(value.integerList, memberWritingClosure: SmithyReadWrite.WritingClosures.writeInt(value:to:), memberNodeInfo: "member", isFlattened: false) - try writer["stringList"].writeList(value.stringList, memberWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), memberNodeInfo: "member", isFlattened: true) - try writer["stringSet"].writeList(value.stringSet, memberWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), memberNodeInfo: "member", isFlattened: true) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `011 encode list flattened nested with xmlname`() { - val context = setupTests("Isolated/Restxml/xml-lists-flattened-nested-xmlname.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlListNestedFlattenedXmlNameInput+Write.swift") - val expectedContents = """ -extension XmlListNestedFlattenedXmlNameInput { - - static func write(value: XmlListNestedFlattenedXmlNameInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["listOfNestedStrings"].writeList(value.nestedList, memberWritingClosure: SmithyReadWrite.listWritingClosure(memberWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), memberNodeInfo: "nestedNestedMember", isFlattened: false), memberNodeInfo: "nestedMember", isFlattened: true) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `012 encode list containing map`() { - val context = setupTests("Isolated/Restxml/xml-lists-contain-map.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlListContainMapInput+Write.swift") - val expectedContents = """ -extension XmlListContainMapInput { - - static func write(value: XmlListContainMapInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["myList"].writeList(value.myList, memberWritingClosure: SmithyReadWrite.mapWritingClosure(valueWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `013 encode flattened list containing map`() { - val context = setupTests("Isolated/Restxml/xml-lists-flattened-contain-map.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlListFlattenedContainMapInput+Write.swift") - val expectedContents = """ -extension XmlListFlattenedContainMapInput { - - static func write(value: XmlListFlattenedContainMapInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["myList"].writeList(value.myList, memberWritingClosure: SmithyReadWrite.mapWritingClosure(valueWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), memberNodeInfo: "member", isFlattened: true) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } -} diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/MapDecodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/MapDecodeXMLGenerationTests.kt deleted file mode 100644 index 0fd4d37a4..000000000 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/MapDecodeXMLGenerationTests.kt +++ /dev/null @@ -1,398 +0,0 @@ -package software.amazon.smithy.swift.codegen.protocolspecificserde.xml - -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import io.kotest.matchers.string.shouldContainOnlyOnce -import org.junit.jupiter.api.Test -import software.amazon.smithy.swift.codegen.getFileContents - -class MapDecodeXMLGenerationTests { - @Test - fun `001 decode wrapped map`() { - val context = setupTests("Isolated/Restxml/xml-maps.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlMapsOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlMapsOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlMapsOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.read(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `002 decode wrapped map with name protocol`() { - val context = setupTests("Isolated/Restxml/xml-maps.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsWithNameProtocolOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlMapsWithNameProtocolOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlMapsWithNameProtocolOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlMapsWithNameProtocolOutput() - value.`protocol` = try reader["protocol"].readMapIfPresent(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.read(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `003 decode nested wrapped map`() { - val context = setupTests("Isolated/Restxml/xml-maps-nested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsNestedOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlMapsNestedOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlMapsNestedOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlMapsNestedOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: SmithyReadWrite.mapReadingClosure(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.read(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `004 decode nested nested wrapped map`() { - val context = setupTests("Isolated/Restxml/xml-maps-nestednested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsNestedNestedOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlMapsNestedNestedOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlMapsNestedNestedOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlMapsNestedNestedOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: SmithyReadWrite.mapReadingClosure(valueReadingClosure: SmithyReadWrite.mapReadingClosure(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.read(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `005 decode flattened map`() { - val context = setupTests("Isolated/Restxml/xml-maps-flattened.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlFlattenedMapsOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlFlattenedMapsOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlFlattenedMapsOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlFlattenedMapsOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.read(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `006 decode flattened nested map`() { - val context = setupTests("Isolated/Restxml/xml-maps-flattened-nested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsFlattenedNestedOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlMapsFlattenedNestedOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlMapsFlattenedNestedOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlMapsFlattenedNestedOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: SmithyReadWrite.mapReadingClosure(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.read(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `007 decode map with xmlname`() { - val context = setupTests("Isolated/Restxml/xml-maps-xmlname.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsXmlNameOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlMapsXmlNameOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlMapsXmlNameOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlMapsXmlNameOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.read(from:), keyNodeInfo: "Attribute", valueNodeInfo: "Setting", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `008 decode map with xmlname flattened`() { - val context = setupTests("Isolated/Restxml/xml-maps-xmlname-flattened.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsXmlNameFlattenedOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlMapsXmlNameFlattenedOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlMapsXmlNameFlattenedOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlMapsXmlNameFlattenedOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.read(from:), keyNodeInfo: "SomeCustomKey", valueNodeInfo: "SomeCustomValue", isFlattened: true) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `009 decode map with xmlname nested`() { - val context = setupTests("Isolated/Restxml/xml-maps-xmlname-nested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsXmlNameNestedOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlMapsXmlNameNestedOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlMapsXmlNameNestedOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlMapsXmlNameNestedOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: SmithyReadWrite.mapReadingClosure(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.read(from:), keyNodeInfo: "CustomKey2", valueNodeInfo: "CustomValue2", isFlattened: false), keyNodeInfo: "CustomKey1", valueNodeInfo: "CustomValue1", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `011 decode flattened nested map with xmlname`() { - val context = setupTests("Isolated/Restxml/xml-maps-flattened-nested-xmlname.smithy", "aws.protocoltests.restxml#RestXml") - val contents = - getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsFlattenedNestedXmlNameOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlMapsFlattenedNestedXmlNameOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlMapsFlattenedNestedXmlNameOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlMapsFlattenedNestedXmlNameOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: SmithyReadWrite.mapReadingClosure(valueReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), keyNodeInfo: "K", valueNodeInfo: "V", isFlattened: false), keyNodeInfo: "yek", valueNodeInfo: "eulav", isFlattened: true) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `011 decode map with xmlnamespace`() { - val context = setupTests("Isolated/Restxml/xml-maps-namespace.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsXmlNamespaceOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlMapsXmlNamespaceOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlMapsXmlNamespaceOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlMapsXmlNamespaceOutput() - value.myMap = try reader[.init("myMap", namespaceDef: .init(prefix: "", uri: "http://boo.com"))].readMapIfPresent(valueReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), keyNodeInfo: .init("Quality", namespaceDef: .init(prefix: "", uri: "http://doo.com")), valueNodeInfo: .init("Degree", namespaceDef: .init(prefix: "", uri: "http://eoo.com")), isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `012 decode flattened map with xmlnamespace`() { - val context = setupTests("Isolated/Restxml/xml-maps-flattened-namespace.smithy", "aws.protocoltests.restxml#RestXml") - val contents = - getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsFlattenedXmlNamespaceOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlMapsFlattenedXmlNamespaceOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlMapsFlattenedXmlNamespaceOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlMapsFlattenedXmlNamespaceOutput() - value.myMap = try reader[.init("myMap", namespaceDef: .init(prefix: "", uri: "http://boo.com"))].readMapIfPresent(valueReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), keyNodeInfo: .init("Uid", namespaceDef: .init(prefix: "", uri: "http://doo.com")), valueNodeInfo: .init("Val", namespaceDef: .init(prefix: "", uri: "http://eoo.com")), isFlattened: true) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `013 decode nested map with xmlnamespace`() { - val context = setupTests("Isolated/Restxml/xml-maps-nested-namespace.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsNestedXmlNamespaceOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlMapsNestedXmlNamespaceOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlMapsNestedXmlNamespaceOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlMapsNestedXmlNamespaceOutput() - value.myMap = try reader[.init("myMap", namespaceDef: .init(prefix: "", uri: "http://boo.com"))].readMapIfPresent(valueReadingClosure: SmithyReadWrite.mapReadingClosure(valueReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), keyNodeInfo: .init("K", namespaceDef: .init(prefix: "", uri: "http://goo.com")), valueNodeInfo: .init("V", namespaceDef: .init(prefix: "", uri: "http://hoo.com")), isFlattened: false), keyNodeInfo: .init("yek", namespaceDef: .init(prefix: "", uri: "http://doo.com")), valueNodeInfo: .init("eulav", namespaceDef: .init(prefix: "", uri: "http://eoo.com")), isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `014 decode nested flattened map with xmlnamespace`() { - val context = setupTests("Isolated/Restxml/xml-maps-flattened-nested-namespace.smithy", "aws.protocoltests.restxml#RestXml") - val contents = - getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsFlattenedNestedXmlNamespaceOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlMapsFlattenedNestedXmlNamespaceOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlMapsFlattenedNestedXmlNamespaceOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlMapsFlattenedNestedXmlNamespaceOutput() - value.myMap = try reader[.init("myMap", namespaceDef: .init(prefix: "", uri: "http://boo.com"))].readMapIfPresent(valueReadingClosure: SmithyReadWrite.mapReadingClosure(valueReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), keyNodeInfo: .init("K", namespaceDef: .init(prefix: "", uri: "http://goo.com")), valueNodeInfo: .init("V", namespaceDef: .init(prefix: "", uri: "http://hoo.com")), isFlattened: false), keyNodeInfo: .init("yek", namespaceDef: .init(prefix: "", uri: "http://doo.com")), valueNodeInfo: .init("eulav", namespaceDef: .init(prefix: "", uri: "http://eoo.com")), isFlattened: true) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `015 decode map containing list`() { - val context = setupTests("Isolated/Restxml/xml-maps-contain-list.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsContainListOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlMapsContainListOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlMapsContainListOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlMapsContainListOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: SmithyReadWrite.listReadingClosure(memberReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), memberNodeInfo: "member", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `016 decode flattened map containing list`() { - val context = setupTests("Isolated/Restxml/xml-maps-flattened-contain-list.smithy", "aws.protocoltests.restxml#RestXml") - val contents = - getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsFlattenedContainListOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlMapsFlattenedContainListOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlMapsFlattenedContainListOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlMapsFlattenedContainListOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: SmithyReadWrite.listReadingClosure(memberReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), memberNodeInfo: "member", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `017 decode map containing timestamp`() { - val context = setupTests("Isolated/Restxml/xml-maps-timestamp.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsTimestampsOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlMapsTimestampsOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlMapsTimestampsOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlMapsTimestampsOutput() - value.timestampMap = try reader["timestampMap"].readMapIfPresent(valueReadingClosure: SmithyReadWrite.timestampReadingClosure(format: SmithyTimestamps.TimestampFormat.epochSeconds), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `018 decode flattened map containing timestamp`() { - val context = setupTests("Isolated/Restxml/xml-maps-flattened-timestamp.smithy", "aws.protocoltests.restxml#RestXml") - val contents = - getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsFlattenedTimestampsOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlMapsFlattenedTimestampsOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlMapsFlattenedTimestampsOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlMapsFlattenedTimestampsOutput() - value.timestampMap = try reader["timestampMap"].readMapIfPresent(valueReadingClosure: SmithyReadWrite.timestampReadingClosure(format: SmithyTimestamps.TimestampFormat.epochSeconds), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `019 two maps that may conflict with KeyValue`() { - val context = setupTests("Isolated/Restxml/xml-maps-2x.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsTwoOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlMapsTwoOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlMapsTwoOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlMapsTwoOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - value.mySecondMap = try reader["mySecondMap"].readMapIfPresent(valueReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } -} diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/MapEncodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/MapEncodeXMLGenerationTests.kt deleted file mode 100644 index 14ab914ef..000000000 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/MapEncodeXMLGenerationTests.kt +++ /dev/null @@ -1,317 +0,0 @@ -package software.amazon.smithy.swift.codegen.protocolspecificserde.xml - -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import io.kotest.matchers.string.shouldContainOnlyOnce -import org.junit.jupiter.api.Test -import software.amazon.smithy.swift.codegen.getFileContents - -class MapEncodeXMLGenerationTests { - @Test - fun `001 encode map`() { - val context = setupTests("Isolated/Restxml/xml-maps.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsInput+Write.swift") - val expectedContents = """ -extension XmlMapsInput { - - static func write(value: XmlMapsInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `002 encode map with name protocol`() { - val context = setupTests("Isolated/Restxml/xml-maps.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsWithNameProtocolInput+Write.swift") - val expectedContents = """ -extension XmlMapsWithNameProtocolInput { - - static func write(value: XmlMapsWithNameProtocolInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["protocol"].writeMap(value.`protocol`, valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `003 encode nested wrapped map`() { - val context = setupTests("Isolated/Restxml/xml-maps-nested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsNestedInput+Write.swift") - val expectedContents = """ -extension XmlMapsNestedInput { - - static func write(value: XmlMapsNestedInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: SmithyReadWrite.mapWritingClosure(valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `004 encode nested nested wrapped map`() { - val context = setupTests("Isolated/Restxml/xml-maps-nestednested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsNestedNestedInput+Write.swift") - val expectedContents = """ -extension XmlMapsNestedNestedInput { - - static func write(value: XmlMapsNestedNestedInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: SmithyReadWrite.mapWritingClosure(valueWritingClosure: SmithyReadWrite.mapWritingClosure(valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `005 encode flattened map`() { - val context = setupTests("Isolated/Restxml/xml-maps-flattened.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlFlattenedMapsInput+Write.swift") - val expectedContents = """ -extension XmlFlattenedMapsInput { - - static func write(value: XmlFlattenedMapsInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `006 encode flattened nested map`() { - val context = setupTests("Isolated/Restxml/xml-maps-flattened-nested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsFlattenedNestedInput+Write.swift") - val expectedContents = """ -extension XmlMapsFlattenedNestedInput { - - static func write(value: XmlMapsFlattenedNestedInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: SmithyReadWrite.mapWritingClosure(valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `007 encode map with xmlname`() { - val context = setupTests("Isolated/Restxml/xml-maps-xmlname.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsXmlNameInput+Write.swift") - val expectedContents = """ -extension XmlMapsXmlNameInput { - - static func write(value: XmlMapsXmlNameInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.write(value:to:), keyNodeInfo: "Attribute", valueNodeInfo: "Setting", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `008 encode map with xmlname flattened`() { - val context = setupTests("Isolated/Restxml/xml-maps-xmlname-flattened.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsXmlNameFlattenedInput+Write.swift") - val expectedContents = """ -extension XmlMapsXmlNameFlattenedInput { - - static func write(value: XmlMapsXmlNameFlattenedInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.write(value:to:), keyNodeInfo: "SomeCustomKey", valueNodeInfo: "SomeCustomValue", isFlattened: true) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `009 encode map with xmlname nested`() { - val context = setupTests("Isolated/Restxml/xml-maps-xmlname-nested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsXmlNameNestedInput+Write.swift") - val expectedContents = """ -extension XmlMapsXmlNameNestedInput { - - static func write(value: XmlMapsXmlNameNestedInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: SmithyReadWrite.mapWritingClosure(valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.write(value:to:), keyNodeInfo: "CustomKey2", valueNodeInfo: "CustomValue2", isFlattened: false), keyNodeInfo: "CustomKey1", valueNodeInfo: "CustomValue1", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `010 encode flattened nested map with xmlname`() { - val context = setupTests("Isolated/Restxml/xml-maps-flattened-nested-xmlname.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsFlattenedNestedXmlNameInput+Write.swift") - val expectedContents = """ -extension XmlMapsFlattenedNestedXmlNameInput { - - static func write(value: XmlMapsFlattenedNestedXmlNameInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: SmithyReadWrite.mapWritingClosure(valueWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), keyNodeInfo: "K", valueNodeInfo: "V", isFlattened: false), keyNodeInfo: "yek", valueNodeInfo: "eulav", isFlattened: true) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `011 encode map with xmlnamespace`() { - val context = setupTests("Isolated/Restxml/xml-maps-namespace.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsXmlNamespaceInput+Write.swift") - val expectedContents = """ -extension XmlMapsXmlNamespaceInput { - - static func write(value: XmlMapsXmlNamespaceInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer[.init("myMap", namespaceDef: .init(prefix: "", uri: "http://boo.com"))].writeMap(value.myMap, valueWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), keyNodeInfo: .init("Quality", namespaceDef: .init(prefix: "", uri: "http://doo.com")), valueNodeInfo: .init("Degree", namespaceDef: .init(prefix: "", uri: "http://eoo.com")), isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `012 encode flattened map xmlnamespace`() { - val context = setupTests("Isolated/Restxml/xml-maps-flattened-namespace.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsFlattenedXmlNamespaceInput+Write.swift") - val expectedContents = """ -extension XmlMapsFlattenedXmlNamespaceInput { - - static func write(value: XmlMapsFlattenedXmlNamespaceInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer[.init("myMap", namespaceDef: .init(prefix: "", uri: "http://boo.com"))].writeMap(value.myMap, valueWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), keyNodeInfo: .init("Uid", namespaceDef: .init(prefix: "", uri: "http://doo.com")), valueNodeInfo: .init("Val", namespaceDef: .init(prefix: "", uri: "http://eoo.com")), isFlattened: true) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `013 encode nested map with xmlnamespace`() { - val context = setupTests("Isolated/Restxml/xml-maps-nested-namespace.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsNestedXmlNamespaceInput+Write.swift") - val expectedContents = """ -extension XmlMapsNestedXmlNamespaceInput { - - static func write(value: XmlMapsNestedXmlNamespaceInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer[.init("myMap", namespaceDef: .init(prefix: "", uri: "http://boo.com"))].writeMap(value.myMap, valueWritingClosure: SmithyReadWrite.mapWritingClosure(valueWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), keyNodeInfo: .init("K", namespaceDef: .init(prefix: "", uri: "http://goo.com")), valueNodeInfo: .init("V", namespaceDef: .init(prefix: "", uri: "http://hoo.com")), isFlattened: false), keyNodeInfo: .init("yek", namespaceDef: .init(prefix: "", uri: "http://doo.com")), valueNodeInfo: .init("eulav", namespaceDef: .init(prefix: "", uri: "http://eoo.com")), isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `014 encode nested flattened map with xmlnamespace`() { - val context = setupTests("Isolated/Restxml/xml-maps-flattened-nested-namespace.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsFlattenedNestedXmlNamespaceInput+Write.swift") - val expectedContents = """ -extension XmlMapsFlattenedNestedXmlNamespaceInput { - - static func write(value: XmlMapsFlattenedNestedXmlNamespaceInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer[.init("myMap", namespaceDef: .init(prefix: "", uri: "http://boo.com"))].writeMap(value.myMap, valueWritingClosure: SmithyReadWrite.mapWritingClosure(valueWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), keyNodeInfo: .init("K", namespaceDef: .init(prefix: "", uri: "http://goo.com")), valueNodeInfo: .init("V", namespaceDef: .init(prefix: "", uri: "http://hoo.com")), isFlattened: false), keyNodeInfo: .init("yek", namespaceDef: .init(prefix: "", uri: "http://doo.com")), valueNodeInfo: .init("eulav", namespaceDef: .init(prefix: "", uri: "http://eoo.com")), isFlattened: true) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `015 encode map containing list`() { - val context = setupTests("Isolated/Restxml/xml-maps-contain-list.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsContainListInput+Write.swift") - val expectedContents = """ -extension XmlMapsContainListInput { - - static func write(value: XmlMapsContainListInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: SmithyReadWrite.listWritingClosure(memberWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), memberNodeInfo: "member", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `016 encode flattened map containing list`() { - val context = setupTests("Isolated/Restxml/xml-maps-flattened-contain-list.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsFlattenedContainListInput+Write.swift") - val expectedContents = """ -extension XmlMapsFlattenedContainListInput { - - static func write(value: XmlMapsFlattenedContainListInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: SmithyReadWrite.listWritingClosure(memberWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), memberNodeInfo: "member", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `017 encode map containing timestamp`() { - val context = setupTests("Isolated/Restxml/xml-maps-timestamp.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsTimestampsInput+Write.swift") - val expectedContents = """ -extension XmlMapsTimestampsInput { - - static func write(value: XmlMapsTimestampsInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["timestampMap"].writeMap(value.timestampMap, valueWritingClosure: SmithyReadWrite.timestampWritingClosure(format: SmithyTimestamps.TimestampFormat.epochSeconds), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `017 encode flattened map containing timestamp`() { - val context = setupTests("Isolated/Restxml/xml-maps-flattened-timestamp.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlMapsFlattenedTimestampsInput+Write.swift") - val expectedContents = """ -extension XmlMapsFlattenedTimestampsInput { - - static func write(value: XmlMapsFlattenedTimestampsInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["timestampMap"].writeMap(value.timestampMap, valueWritingClosure: SmithyReadWrite.timestampWritingClosure(format: SmithyTimestamps.TimestampFormat.epochSeconds), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `018 encode fooenumMap`() { - val context = setupTests("Isolated/Restxml/xml-maps-nested-fooenum.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/NestedXmlMapsInput+Write.swift") - val expectedContents = """ -extension NestedXmlMapsInput { - - static func write(value: NestedXmlMapsInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["flatNestedMap"].writeMap(value.flatNestedMap, valueWritingClosure: SmithyReadWrite.mapWritingClosure(valueWritingClosure: SmithyReadWrite.WritingClosureBox().write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) - try writer["nestedMap"].writeMap(value.nestedMap, valueWritingClosure: SmithyReadWrite.mapWritingClosure(valueWritingClosure: SmithyReadWrite.WritingClosureBox().write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } -} diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/NamespaceEncodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/NamespaceEncodeXMLGenerationTests.kt deleted file mode 100644 index 5f2449737..000000000 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/NamespaceEncodeXMLGenerationTests.kt +++ /dev/null @@ -1,119 +0,0 @@ -package software.amazon.smithy.swift.codegen.protocolspecificserde.xml - -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import io.kotest.matchers.string.shouldContainOnlyOnce -import org.junit.jupiter.api.Test -import software.amazon.smithy.swift.codegen.getFileContents - -class NamespaceEncodeXMLGenerationTests { - @Test - fun `001 xmlnamespace, XmlNamespacesInput, Encodable`() { - val context = setupTests("Isolated/Restxml/xml-namespace.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlNamespacesInput+Write.swift") - val expectedContents = """ -extension XmlNamespacesInput { - - static func write(value: XmlNamespacesInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer[.init("nested", namespaceDef: .init(prefix: "", uri: "http://boo.com"))].write(value.nested, with: RestXmlProtocolClientTypes.XmlNamespaceNested.write(value:to:)) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `003 xmlnamespace, XmlNamespaceNested`() { - val context = setupTests("Isolated/Restxml/xml-namespace.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlNamespaceNested+ReadWrite.swift") - val expectedContents = """ -extension RestXmlProtocolClientTypes.XmlNamespaceNested { - - static func write(value: RestXmlProtocolClientTypes.XmlNamespaceNested?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer[.init("foo", namespaceDef: .init(prefix: "baz", uri: "http://baz.com"))].write(value.foo) - try writer[.init("values", namespaceDef: .init(prefix: "", uri: "http://qux.com"))].writeList(value.values, memberWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), memberNodeInfo: .init("member", namespaceDef: .init(prefix: "", uri: "http://bux.com")), isFlattened: false) - } - - static func read(from reader: SmithyXML.Reader) throws -> RestXmlProtocolClientTypes.XmlNamespaceNested { - guard reader.hasContent else { throw SmithyReadWrite.ReaderError.requiredValueNotPresent } - var value = RestXmlProtocolClientTypes.XmlNamespaceNested() - value.foo = try reader[.init("foo", namespaceDef: .init(prefix: "baz", uri: "http://baz.com"))].readIfPresent() - value.values = try reader[.init("values", namespaceDef: .init(prefix: "", uri: "http://qux.com"))].readListIfPresent(memberReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), memberNodeInfo: .init("member", namespaceDef: .init(prefix: "", uri: "http://bux.com")), isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `005 xmlnamespace nested list, Encodable`() { - val context = setupTests("Isolated/Restxml/xml-namespace-nestedlist.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlNamespaceNestedListInput+Write.swift") - val expectedContents = """ -extension XmlNamespaceNestedListInput { - - static func write(value: XmlNamespaceNestedListInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer[.init("nested", namespaceDef: .init(prefix: "", uri: "http://aux.com"))].writeList(value.nested, memberWritingClosure: SmithyReadWrite.listWritingClosure(memberWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), memberNodeInfo: .init("member", namespaceDef: .init(prefix: "bzzzz", uri: "http://bar.com")), isFlattened: false), memberNodeInfo: .init("member", namespaceDef: .init(prefix: "baz", uri: "http://bux.com")), isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `007 xmlnamespace nested flattened list, encodable`() { - val context = setupTests("Isolated/Restxml/xml-namespace-flattenedlist.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlNamespaceFlattenedListInput+Write.swift") - val expectedContents = """ -extension XmlNamespaceFlattenedListInput { - - static func write(value: XmlNamespaceFlattenedListInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer[.init("nested", namespaceDef: .init(prefix: "baz", uri: "http://aux.com"))].writeList(value.nested, memberWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), memberNodeInfo: "member", isFlattened: true) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `010 xmlnamespace on service, encodable`() { - val context = setupTests("Isolated/Restxml/xml-namespace-onservice.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlNamespacesOnServiceInput+Write.swift") - val expectedContents = """ -extension XmlNamespacesOnServiceInput { - - static func write(value: XmlNamespacesOnServiceInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["foo"].write(value.foo) - try writer[.init("nested", namespaceDef: .init(prefix: "xsi", uri: "https://example.com"))].write(value.nested, with: RestXmlProtocolClientTypes.NestedWithNamespace.write(value:to:)) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `011 xmlnamespace on service, encodable`() { - val context = setupTests("Isolated/Restxml/xml-namespace-onservice-overridable.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlNamespacesOnServiceOverridableInput+Write.swift") - val expectedContents = """ -extension XmlNamespacesOnServiceOverridableInput { - - static func write(value: XmlNamespacesOnServiceOverridableInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["foo"].write(value.foo) - try writer[.init("nested", namespaceDef: .init(prefix: "xsi", uri: "https://example.com"))].write(value.nested, with: RestXmlProtocolClientTypes.NestedWithNamespace.write(value:to:)) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } -} diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/RecursiveShapesDecodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/RecursiveShapesDecodeXMLGenerationTests.kt deleted file mode 100644 index cf9026d9f..000000000 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/RecursiveShapesDecodeXMLGenerationTests.kt +++ /dev/null @@ -1,52 +0,0 @@ -package software.amazon.smithy.swift.codegen.protocolspecificserde.xml - -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import io.kotest.matchers.string.shouldContainOnlyOnce -import org.junit.jupiter.api.Test -import software.amazon.smithy.swift.codegen.getFileContents - -class RecursiveShapesDecodeXMLGenerationTests { - @Test - fun `decode recursive shape`() { - val context = setupTests("Isolated/Restxml/xml-recursive.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlRecursiveShapesOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlRecursiveShapesOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlRecursiveShapesOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlRecursiveShapesOutput() - value.nested = try reader["nested"].readIfPresent(with: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1.read(from:)) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `decode recursive nested shape`() { - val context = setupTests("Isolated/Restxml/xml-recursive-nested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlNestedRecursiveShapesOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlNestedRecursiveShapesOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlNestedRecursiveShapesOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlNestedRecursiveShapesOutput() - value.nestedRecursiveList = try reader["nestedRecursiveList"].readListIfPresent(memberReadingClosure: SmithyReadWrite.listReadingClosure(memberReadingClosure: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1.read(from:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } -} diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/RecursiveShapesEncodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/RecursiveShapesEncodeXMLGenerationTests.kt deleted file mode 100644 index c75daca59..000000000 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/RecursiveShapesEncodeXMLGenerationTests.kt +++ /dev/null @@ -1,78 +0,0 @@ -package software.amazon.smithy.swift.codegen.protocolspecificserde.xml - -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import io.kotest.matchers.string.shouldContainOnlyOnce -import org.junit.jupiter.api.Test -import software.amazon.smithy.swift.codegen.getFileContents - -class RecursiveShapesEncodeXMLGenerationTests { - @Test - fun `001 encode recursive shape Nested1`() { - val context = setupTests("Isolated/Restxml/xml-recursive.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/RecursiveShapesInputOutputNested1+ReadWrite.swift") - val expectedContents = """ -extension RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1 { - - static func write(value: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["foo"].write(value.foo) - try writer["nested"].write(value.nested, with: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested2.write(value:to:)) - } - - static func read(from reader: SmithyXML.Reader) throws -> RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1 { - guard reader.hasContent else { throw SmithyReadWrite.ReaderError.requiredValueNotPresent } - var value = RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1() - value.foo = try reader["foo"].readIfPresent() - value.nested = try reader["nested"].readIfPresent(with: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested2.read(from:)) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `encode recursive shape Nested2`() { - val context = setupTests("Isolated/Restxml/xml-recursive.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/RecursiveShapesInputOutputNested2+ReadWrite.swift") - val expectedContents = """ -extension RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested2 { - - static func write(value: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested2?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["bar"].write(value.bar) - try writer["recursiveMember"].write(value.recursiveMember, with: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1.write(value:to:)) - } - - static func read(from reader: SmithyXML.Reader) throws -> RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested2 { - guard reader.hasContent else { throw SmithyReadWrite.ReaderError.requiredValueNotPresent } - var value = RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested2() - value.bar = try reader["bar"].readIfPresent() - value.recursiveMember = try reader["recursiveMember"].readIfPresent(with: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1.read(from:)) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `encode recursive nested shape`() { - val context = setupTests("Isolated/Restxml/xml-recursive-nested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlNestedRecursiveShapesInput+Write.swift") - val expectedContents = """ -extension XmlNestedRecursiveShapesInput { - - static func write(value: XmlNestedRecursiveShapesInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["nestedRecursiveList"].writeList(value.nestedRecursiveList, memberWritingClosure: SmithyReadWrite.listWritingClosure(memberWritingClosure: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1.write(value:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } -} diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/SetDecodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/SetDecodeXMLGenerationTests.kt deleted file mode 100644 index cbfc702dd..000000000 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/SetDecodeXMLGenerationTests.kt +++ /dev/null @@ -1,52 +0,0 @@ -package software.amazon.smithy.swift.codegen.protocolspecificserde.xml - -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import io.kotest.matchers.string.shouldContainOnlyOnce -import org.junit.jupiter.api.Test -import software.amazon.smithy.swift.codegen.getFileContents - -class SetDecodeXMLGenerationTests { - @Test - fun `XmlEnumSetOutputBody decodable`() { - val context = setupTests("Isolated/Restxml/xml-sets.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlEnumSetOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlEnumSetOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlEnumSetOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlEnumSetOutput() - value.fooEnumSet = try reader["fooEnumSet"].readListIfPresent(memberReadingClosure: SmithyReadWrite.ReadingClosureBox().read(from:), memberNodeInfo: "member", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `XmlEnumNestedSetOutputBody nested decodable`() { - val context = setupTests("Isolated/Restxml/xml-sets-nested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlEnumNestedSetOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlEnumNestedSetOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlEnumNestedSetOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlEnumNestedSetOutput() - value.fooEnumSet = try reader["fooEnumSet"].readListIfPresent(memberReadingClosure: SmithyReadWrite.listReadingClosure(memberReadingClosure: SmithyReadWrite.ReadingClosureBox().read(from:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } -} diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/SetEncodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/SetEncodeXMLGenerationTests.kt deleted file mode 100644 index fb07ddfd1..000000000 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/SetEncodeXMLGenerationTests.kt +++ /dev/null @@ -1,44 +0,0 @@ -package software.amazon.smithy.swift.codegen.protocolspecificserde.xml - -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import io.kotest.matchers.string.shouldContainOnlyOnce -import org.junit.jupiter.api.Test -import software.amazon.smithy.swift.codegen.getFileContents - -class SetEncodeXMLGenerationTests { - @Test - fun `001 wrapped set serialization`() { - val context = setupTests("Isolated/Restxml/xml-sets.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlEnumSetInput+Write.swift") - val expectedContents = """ -extension XmlEnumSetInput { - - static func write(value: XmlEnumSetInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["fooEnumSet"].writeList(value.fooEnumSet, memberWritingClosure: SmithyReadWrite.WritingClosureBox().write(value:to:), memberNodeInfo: "member", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `002 wrapped nested set serialization`() { - val context = setupTests("Isolated/Restxml/xml-sets-nested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlEnumNestedSetInput+Write.swift") - val expectedContents = """ -extension XmlEnumNestedSetInput { - - static func write(value: XmlEnumNestedSetInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["fooEnumSet"].writeList(value.fooEnumSet, memberWritingClosure: SmithyReadWrite.listWritingClosure(memberWritingClosure: SmithyReadWrite.WritingClosureBox().write(value:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } -} diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/StructDecodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/StructDecodeXMLGenerationTests.kt deleted file mode 100644 index ed3d7d42b..000000000 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/StructDecodeXMLGenerationTests.kt +++ /dev/null @@ -1,110 +0,0 @@ -package software.amazon.smithy.swift.codegen.protocolspecificserde.xml - -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import io.kotest.matchers.string.shouldContainOnlyOnce -import org.junit.jupiter.api.Test -import software.amazon.smithy.swift.codegen.getFileContents - -class StructDecodeXMLGenerationTests { - @Test - fun `XmlWrappedListOutputBody decodable`() { - val context = setupTests("Isolated/Restxml/xml-lists-wrapped.smithy", "aws.protocoltests.restxml#RestXml") - - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlWrappedListOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlWrappedListOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlWrappedListOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlWrappedListOutput() - value.myGroceryList = try reader["myGroceryList"].readListIfPresent(memberReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), memberNodeInfo: "member", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `SimpleScalarPropertiesOutputBody decodable`() { - val context = setupTests("Isolated/Restxml/xml-scalar.smithy", "aws.protocoltests.restxml#RestXml") - - val contents = getFileContents(context.manifest, "Sources/RestXml/models/SimpleScalarPropertiesOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension SimpleScalarPropertiesOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> SimpleScalarPropertiesOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = SimpleScalarPropertiesOutput() - if let fooHeaderValue = httpResponse.headers.value(for: "X-Foo") { - value.foo = fooHeaderValue - } - value.byteValue = try reader["byteValue"].readIfPresent() - value.doubleValue = try reader["DoubleDribble"].readIfPresent() - value.falseBooleanValue = try reader["falseBooleanValue"].readIfPresent() - value.floatValue = try reader["floatValue"].readIfPresent() - value.integerValue = try reader["integerValue"].readIfPresent() - value.longValue = try reader["longValue"].readIfPresent() - value.`protocol` = try reader["protocol"].readIfPresent() - value.shortValue = try reader["shortValue"].readIfPresent() - value.stringValue = try reader["stringValue"].readIfPresent() - value.trueBooleanValue = try reader["trueBooleanValue"].readIfPresent() - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `nestednested wrapped list deserialization`() { - val context = setupTests("Isolated/Restxml/xml-lists-nestednested-wrapped.smithy", "aws.protocoltests.restxml#RestXml") - val contents = - getFileContents(context.manifest, "Sources/RestXml/models/XmlNestedNestedWrappedListOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlNestedNestedWrappedListOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlNestedNestedWrappedListOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlNestedNestedWrappedListOutput() - value.nestedNestedStringList = try reader["nestedNestedStringList"].readListIfPresent(memberReadingClosure: SmithyReadWrite.listReadingClosure(memberReadingClosure: SmithyReadWrite.listReadingClosure(memberReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `empty lists decode`() { - val context = setupTests("Isolated/Restxml/xml-lists-empty.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlEmptyListsOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlEmptyListsOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlEmptyListsOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlEmptyListsOutput() - value.booleanList = try reader["booleanList"].readListIfPresent(memberReadingClosure: SmithyReadWrite.ReadingClosures.readBool(from:), memberNodeInfo: "member", isFlattened: false) - value.integerList = try reader["integerList"].readListIfPresent(memberReadingClosure: SmithyReadWrite.ReadingClosures.readInt(from:), memberNodeInfo: "member", isFlattened: false) - value.stringList = try reader["stringList"].readListIfPresent(memberReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), memberNodeInfo: "member", isFlattened: false) - value.stringSet = try reader["stringSet"].readListIfPresent(memberReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), memberNodeInfo: "member", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } -} diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/StructEncodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/StructEncodeXMLGenerationTests.kt deleted file mode 100644 index 940d00b31..000000000 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/StructEncodeXMLGenerationTests.kt +++ /dev/null @@ -1,62 +0,0 @@ -package software.amazon.smithy.swift.codegen.protocolspecificserde.xml - -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import io.kotest.matchers.string.shouldContainOnlyOnce -import org.junit.jupiter.api.Test -import software.amazon.smithy.swift.codegen.getFileContents - -class StructEncodeXMLGenerationTests { - @Test - fun `simpleScalar serialization`() { - val context = setupTests("Isolated/Restxml/xml-scalar.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/SimpleScalarPropertiesInput+Write.swift") - val expectedContents = """ -extension SimpleScalarPropertiesInput { - - static func write(value: SimpleScalarPropertiesInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["byteValue"].write(value.byteValue) - try writer["DoubleDribble"].write(value.doubleValue) - try writer["falseBooleanValue"].write(value.falseBooleanValue) - try writer["floatValue"].write(value.floatValue) - try writer["integerValue"].write(value.integerValue) - try writer["longValue"].write(value.longValue) - try writer["protocol"].write(value.`protocol`) - try writer["shortValue"].write(value.shortValue) - try writer["stringValue"].write(value.stringValue) - try writer["trueBooleanValue"].write(value.trueBooleanValue) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `008 structure xmlName`() { - val context = setupTests("Isolated/Restxml/xml-lists-structure.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/StructureListMember+ReadWrite.swift") - val expectedContents = """ -extension RestXmlProtocolClientTypes.StructureListMember { - - static func write(value: RestXmlProtocolClientTypes.StructureListMember?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["value"].write(value.a) - try writer["other"].write(value.b) - } - - static func read(from reader: SmithyXML.Reader) throws -> RestXmlProtocolClientTypes.StructureListMember { - guard reader.hasContent else { throw SmithyReadWrite.ReaderError.requiredValueNotPresent } - var value = RestXmlProtocolClientTypes.StructureListMember() - value.a = try reader["value"].readIfPresent() - value.b = try reader["other"].readIfPresent() - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } -} diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/TimeStampDecodeGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/TimeStampDecodeGenerationTests.kt deleted file mode 100644 index 3159402b0..000000000 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/TimeStampDecodeGenerationTests.kt +++ /dev/null @@ -1,98 +0,0 @@ -package software.amazon.smithy.swift.codegen.protocolspecificserde.xml - -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import io.kotest.matchers.string.shouldContainOnlyOnce -import org.junit.jupiter.api.Test -import software.amazon.smithy.swift.codegen.getFileContents - -class TimeStampDecodeGenerationTests { - @Test - fun `001 decode all timestamps`() { - val context = setupTests("Isolated/Restxml/xml-timestamp.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlTimestampsOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlTimestampsOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlTimestampsOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlTimestampsOutput() - value.dateTime = try reader["dateTime"].readTimestampIfPresent(format: SmithyTimestamps.TimestampFormat.dateTime) - value.epochSeconds = try reader["epochSeconds"].readTimestampIfPresent(format: SmithyTimestamps.TimestampFormat.epochSeconds) - value.httpDate = try reader["httpDate"].readTimestampIfPresent(format: SmithyTimestamps.TimestampFormat.httpDate) - value.normal = try reader["normal"].readTimestampIfPresent(format: SmithyTimestamps.TimestampFormat.dateTime) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `002 decode nested timestamps`() { - val context = setupTests("Isolated/Restxml/xml-timestamp-nested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlTimestampsNestedOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlTimestampsNestedOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlTimestampsNestedOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlTimestampsNestedOutput() - value.nestedTimestampList = try reader["nestedTimestampList"].readListIfPresent(memberReadingClosure: SmithyReadWrite.listReadingClosure(memberReadingClosure: SmithyReadWrite.timestampReadingClosure(format: SmithyTimestamps.TimestampFormat.epochSeconds), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `003 decode nested timestamps HttpDate`() { - val context = setupTests("Isolated/Restxml/xml-timestamp-nested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = - getFileContents(context.manifest, "Sources/RestXml/models/XmlTimestampsNestedHTTPDateOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlTimestampsNestedHTTPDateOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlTimestampsNestedHTTPDateOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlTimestampsNestedHTTPDateOutput() - value.nestedTimestampList = try reader["nestedTimestampList"].readListIfPresent(memberReadingClosure: SmithyReadWrite.listReadingClosure(memberReadingClosure: SmithyReadWrite.timestampReadingClosure(format: SmithyTimestamps.TimestampFormat.httpDate), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - return value - } -} -""" - - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `004 decode nested timestamps xmlName`() { - val context = setupTests("Isolated/Restxml/xml-timestamp-nested-xmlname.smithy", "aws.protocoltests.restxml#RestXml") - val contents = - getFileContents(context.manifest, "Sources/RestXml/models/XmlTimestampsNestedXmlNameOutput+HttpResponseBinding.swift") - val expectedContents = """ -extension XmlTimestampsNestedXmlNameOutput { - - static func httpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> XmlTimestampsNestedXmlNameOutput { - let data = try await httpResponse.data() - let responseReader = try SmithyXML.Reader.from(data: data) - let reader = responseReader - var value = XmlTimestampsNestedXmlNameOutput() - value.nestedTimestampList = try reader["nestedTimestampList"].readListIfPresent(memberReadingClosure: SmithyReadWrite.listReadingClosure(memberReadingClosure: SmithyReadWrite.timestampReadingClosure(format: SmithyTimestamps.TimestampFormat.epochSeconds), memberNodeInfo: "nestedTag2", isFlattened: false), memberNodeInfo: "nestedTag1", isFlattened: false) - return value - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } -} diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/TimeStampEncodeGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/TimeStampEncodeGenerationTests.kt deleted file mode 100644 index 56814b9b1..000000000 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/TimeStampEncodeGenerationTests.kt +++ /dev/null @@ -1,96 +0,0 @@ -package software.amazon.smithy.swift.codegen.protocolspecificserde.xml - -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import io.kotest.matchers.string.shouldContainOnlyOnce -import org.junit.jupiter.api.Test -import software.amazon.smithy.swift.codegen.getFileContents - -class TimeStampEncodeGenerationTests { - @Test - fun `001 encode all timestamps`() { - val context = setupTests("Isolated/Restxml/xml-timestamp.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlTimestampsInput+Write.swift") - val expectedContents = """ -extension XmlTimestampsInput { - - static func write(value: XmlTimestampsInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["dateTime"].writeTimestamp(value.dateTime, format: SmithyTimestamps.TimestampFormat.dateTime) - try writer["epochSeconds"].writeTimestamp(value.epochSeconds, format: SmithyTimestamps.TimestampFormat.epochSeconds) - try writer["httpDate"].writeTimestamp(value.httpDate, format: SmithyTimestamps.TimestampFormat.httpDate) - try writer["normal"].writeTimestamp(value.normal, format: SmithyTimestamps.TimestampFormat.dateTime) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `002 encode nested list with timestamps`() { - val context = setupTests("Isolated/Restxml/xml-timestamp-nested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlTimestampsNestedInput+Write.swift") - val expectedContents = """ -extension XmlTimestampsNestedInput { - - static func write(value: XmlTimestampsNestedInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["nestedTimestampList"].writeList(value.nestedTimestampList, memberWritingClosure: SmithyReadWrite.listWritingClosure(memberWritingClosure: SmithyReadWrite.timestampWritingClosure(format: SmithyTimestamps.TimestampFormat.epochSeconds), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `003 encode nested list with timestamps httpDate`() { - val context = setupTests("Isolated/Restxml/xml-timestamp-nested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlTimestampsNestedHTTPDateInput+Write.swift") - val expectedContents = """ -extension XmlTimestampsNestedHTTPDateInput { - - static func write(value: XmlTimestampsNestedHTTPDateInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["nestedTimestampList"].writeList(value.nestedTimestampList, memberWritingClosure: SmithyReadWrite.listWritingClosure(memberWritingClosure: SmithyReadWrite.timestampWritingClosure(format: SmithyTimestamps.TimestampFormat.httpDate), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `004 encode nested list with timestamps with xmlname`() { - val context = setupTests("Isolated/Restxml/xml-timestamp-nested-xmlname.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlTimestampsNestedXmlNameInput+Write.swift") - val expectedContents = """ -extension XmlTimestampsNestedXmlNameInput { - - static func write(value: XmlTimestampsNestedXmlNameInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["nestedTimestampList"].writeList(value.nestedTimestampList, memberWritingClosure: SmithyReadWrite.listWritingClosure(memberWritingClosure: SmithyReadWrite.timestampWritingClosure(format: SmithyTimestamps.TimestampFormat.epochSeconds), memberNodeInfo: "nestedTag2", isFlattened: false), memberNodeInfo: "nestedTag1", isFlattened: false) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `005 encode all timestamps, withxmlName`() { - val context = setupTests("Isolated/Restxml/xml-timestamp-xmlname.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlTimestampsXmlNameInput+Write.swift") - val expectedContents = """ -extension XmlTimestampsXmlNameInput { - - static func write(value: XmlTimestampsXmlNameInput?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - try writer["dateTime"].writeTimestamp(value.dateTime, format: SmithyTimestamps.TimestampFormat.dateTime) - try writer["notNormalName"].writeTimestamp(value.normal, format: SmithyTimestamps.TimestampFormat.dateTime) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } -} diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/UnionEncodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/UnionEncodeXMLGenerationTests.kt deleted file mode 100644 index 8f6b551a4..000000000 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/protocolspecificserde/xml/UnionEncodeXMLGenerationTests.kt +++ /dev/null @@ -1,90 +0,0 @@ -package software.amazon.smithy.swift.codegen.protocolspecificserde.xml - -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import io.kotest.matchers.string.shouldContainOnlyOnce -import org.junit.jupiter.api.Test -import software.amazon.smithy.swift.codegen.getFileContents - -class UnionEncodeXMLGenerationTests { - @Test - fun `001 XmlUnionShape+Codable`() { - val context = setupTests("Isolated/Restxml/xml-unions.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlUnionShape+ReadWrite.swift") - val expectedContents = """ -extension RestXmlProtocolClientTypes.XmlUnionShape { - - static func write(value: RestXmlProtocolClientTypes.XmlUnionShape?, to writer: SmithyXML.Writer) throws { - guard let value else { return } - switch value { - case let .datavalue(datavalue): - try writer["dataValue"].write(datavalue) - case let .doublevalue(doublevalue): - try writer["doubleValue"].write(doublevalue) - case let .mapvalue(mapvalue): - try writer["mapValue"].writeMap(mapvalue, valueWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), keyNodeInfo: "K", valueNodeInfo: "V", isFlattened: false) - case let .stringlist(stringlist): - try writer["stringList"].writeList(stringlist, memberWritingClosure: SmithyReadWrite.WritingClosures.writeString(value:to:), memberNodeInfo: "member", isFlattened: false) - case let .structvalue(structvalue): - try writer["structValue"].write(structvalue, with: RestXmlProtocolClientTypes.XmlNestedUnionStruct.write(value:to:)) - case let .timestampvalue(timestampvalue): - try writer["timeStampValue"].writeTimestamp(timestampvalue, format: SmithyTimestamps.TimestampFormat.dateTime) - case let .unionvalue(unionvalue): - try writer["unionValue"].write(unionvalue, with: RestXmlProtocolClientTypes.XmlUnionShape.write(value:to:)) - case let .sdkUnknown(sdkUnknown): - try writer["sdkUnknown"].write(sdkUnknown) - } - } - - static func read(from reader: SmithyXML.Reader) throws -> RestXmlProtocolClientTypes.XmlUnionShape { - guard reader.hasContent else { throw SmithyReadWrite.ReaderError.requiredValueNotPresent } - let name = reader.children.filter { ${'$'}0.hasContent && ${'$'}0.nodeInfo.name != "__type" }.first?.nodeInfo.name - switch name { - case "doubleValue": - return .doublevalue(try reader["doubleValue"].read()) - case "dataValue": - return .datavalue(try reader["dataValue"].read()) - case "unionValue": - return .unionvalue(try reader["unionValue"].read(with: RestXmlProtocolClientTypes.XmlUnionShape.read(from:))) - case "structValue": - return .structvalue(try reader["structValue"].read(with: RestXmlProtocolClientTypes.XmlNestedUnionStruct.read(from:))) - case "mapValue": - return .mapvalue(try reader["mapValue"].readMap(valueReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), keyNodeInfo: "K", valueNodeInfo: "V", isFlattened: false)) - case "stringList": - return .stringlist(try reader["stringList"].readList(memberReadingClosure: SmithyReadWrite.ReadingClosures.readString(from:), memberNodeInfo: "member", isFlattened: false)) - case "timeStampValue": - return .timestampvalue(try reader["timeStampValue"].readTimestamp(format: SmithyTimestamps.TimestampFormat.dateTime)) - default: - return .sdkUnknown(name ?? "") - } - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `002 XmlUnionShape should be marked as indirect`() { - val context = setupTests("Isolated/Restxml/xml-unions.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "Sources/RestXml/models/XmlUnionShape.swift") - val expectedContents = """ -extension ExampleClientTypes { - - public indirect enum XmlUnionShape: Swift.Sendable { - case doublevalue(Swift.Double) - case datavalue(Foundation.Data) - case unionvalue(ExampleClientTypes.XmlUnionShape) - case structvalue(ExampleClientTypes.XmlNestedUnionStruct) - case mapvalue([Swift.String: Swift.String]) - case stringlist([Swift.String]) - case timestampvalue(Foundation.Date) - case sdkUnknown(Swift.String) - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } -} diff --git a/smithy-swift-codegen/src/test/resources/pagination-truncation.smithy b/smithy-swift-codegen/src/test/resources/pagination-truncation.smithy index 6ab35cada..74fdd2b84 100644 --- a/smithy-swift-codegen/src/test/resources/pagination-truncation.smithy +++ b/smithy-swift-codegen/src/test/resources/pagination-truncation.smithy @@ -2,12 +2,12 @@ $version: "1.0" namespace software.amazon.smithy.swift.codegen.synthetic -use aws.protocols#restXml +use aws.protocols#restJson1 @trait(selector: "*") structure paginationTruncationMember { } -@restXml +@restJson1 service Lambda { operations: [ListFunctionsTruncated] } diff --git a/smithy-swift-codegen/src/test/resources/pagination.smithy b/smithy-swift-codegen/src/test/resources/pagination.smithy index f0296e646..978a6153e 100644 --- a/smithy-swift-codegen/src/test/resources/pagination.smithy +++ b/smithy-swift-codegen/src/test/resources/pagination.smithy @@ -1,8 +1,8 @@ namespace com.test -use aws.protocols#restXml +use aws.protocols#restJson1 -@restXml +@restJson1 service Lambda { operations: [ListFunctions, ListFunctions2, ListFunctions3] } diff --git a/smithy-swift-codegen/src/test/resources/waiters-none.smithy b/smithy-swift-codegen/src/test/resources/waiters-none.smithy index e9fb58848..927f009b8 100644 --- a/smithy-swift-codegen/src/test/resources/waiters-none.smithy +++ b/smithy-swift-codegen/src/test/resources/waiters-none.smithy @@ -1,9 +1,9 @@ namespace com.test use smithy.waiters#waitable -use aws.protocols#restXml +use aws.protocols#restJson1 -@restXml +@restJson1 service TestHasNoWaiters { operations: [NoWaiting] } diff --git a/smithy-swift-codegen/src/test/resources/waiters.smithy b/smithy-swift-codegen/src/test/resources/waiters.smithy index 2960c90dd..c865e4f49 100644 --- a/smithy-swift-codegen/src/test/resources/waiters.smithy +++ b/smithy-swift-codegen/src/test/resources/waiters.smithy @@ -1,9 +1,9 @@ namespace com.test use smithy.waiters#waitable -use aws.protocols#restXml +use aws.protocols#restJson1 -@restXml +@restJson1 service TestHasWaiters { operations: [HeadBucket] }