diff --git a/.swiftlint.yml b/.swiftlint.yml index 1811db955..6361f435c 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -5,6 +5,8 @@ excluded: - Tests/ClientRuntimeTests/* - Tests/SmithyTestUtilTests/* - Tests/SmithyXMLTests/* + - Tests/SmithyJSONTests/* + - Tests/SmithyFormURLTests/* - Tests/SmithyTimestampsTests/* - Tests/WeatherSDKTests/* - smithy-swift-codegen-test/build/* diff --git a/Package.swift b/Package.swift index cdc2bab18..3544844d6 100644 --- a/Package.swift +++ b/Package.swift @@ -31,6 +31,8 @@ let package = Package( .library(name: "ClientRuntime", targets: ["ClientRuntime"]), .library(name: "SmithyReadWrite", targets: ["SmithyReadWrite"]), .library(name: "SmithyXML", targets: ["SmithyXML"]), + .library(name: "SmithyJSON", targets: ["SmithyJSON"]), + .library(name: "SmithyFormURL", targets: ["SmithyFormURL"]), .library(name: "SmithyTestUtil", targets: ["SmithyTestUtil"]), ], dependencies: [ @@ -42,6 +44,8 @@ let package = Package( name: "ClientRuntime", dependencies: [ "SmithyXML", + "SmithyJSON", + "SmithyFormURL", .product(name: "AwsCommonRuntimeKit", package: "aws-crt-swift"), .product(name: "Logging", package: "swift-log"), ], @@ -49,7 +53,12 @@ let package = Package( .copy("PrivacyInfo.xcprivacy") ] ), - .target(name: "SmithyReadWrite"), + .target( + name: "SmithyReadWrite", + dependencies: [ + "SmithyTimestamps" + ] + ), .target( name: "SmithyXML", dependencies: [ @@ -58,6 +67,20 @@ let package = Package( libXML2DependencyOrNil ].compactMap { $0 } ), + .target( + name: "SmithyJSON", + dependencies: [ + "SmithyReadWrite", + "SmithyTimestamps" + ] + ), + .target( + name: "SmithyFormURL", + dependencies: [ + "SmithyReadWrite", + "SmithyTimestamps" + ] + ), libXML2TargetOrNil, .target( name: "SmithyTimestamps" @@ -74,6 +97,14 @@ let package = Package( name: "SmithyXMLTests", dependencies: ["SmithyXML", "ClientRuntime"] ), + .testTarget( + name: "SmithyJSONTests", + dependencies: ["SmithyJSON", "ClientRuntime"] + ), + .testTarget( + name: "SmithyFormURLTests", + dependencies: ["SmithyFormURL", "ClientRuntime"] + ), .testTarget( name: "SmithyTimestampsTests", dependencies: ["SmithyTimestamps"] diff --git a/Sources/ClientRuntime/Config/DefaultSDKRuntimeConfiguration.swift b/Sources/ClientRuntime/Config/DefaultSDKRuntimeConfiguration.swift index fb0cb8369..67d32a548 100644 --- a/Sources/ClientRuntime/Config/DefaultSDKRuntimeConfiguration.swift +++ b/Sources/ClientRuntime/Config/DefaultSDKRuntimeConfiguration.swift @@ -15,16 +15,6 @@ public struct DefaultSDKRuntimeConfiguration: MessageEncoderStream, Stream { + public class DefaultMessageEncoderStream: MessageEncoderStream, Stream { let stream: AsyncThrowingStream let messageEncoder: MessageEncoder let messageSigner: MessageSigner diff --git a/Sources/ClientRuntime/EventStream/MessageMarshallable.swift b/Sources/ClientRuntime/EventStream/MessageMarshallable.swift index c8f583a77..81e9c8f4e 100644 --- a/Sources/ClientRuntime/EventStream/MessageMarshallable.swift +++ b/Sources/ClientRuntime/EventStream/MessageMarshallable.swift @@ -5,27 +5,4 @@ // SPDX-License-Identifier: Apache-2.0 // -/// Marshals an event stream event into a `Message`. -/// Codgegen generates a conformance to this protocol for each event stream event input. -public protocol MessageMarshallable { - /// Marshals a event stream event into a `Message`. - /// - Parameters: - /// - encoder: RequestEncoder to use to encode the event stream event. - /// Note: event type may contain nested types that need to be encoded - /// using the same encoder. - /// - Returns: The marshalled `Message`. - func marshall(encoder: RequestEncoder) throws -> EventStream.Message -} - public typealias MarshalClosure = (T) throws -> (EventStream.Message) - -/// Provides a `MarshalClosure` for event payloads that are Swift `Encodable`. -/// - Parameter requestEncoder: The Swift `Encoder` to be used for encoding this event payload. -/// - Returns: A `MarshalClosure` that uses the provided encoder to encode event payloads. -public func jsonMarshalClosure( - requestEncoder: RequestEncoder -) -> MarshalClosure { - return { eventStream in - try eventStream.marshall(encoder: requestEncoder) - } -} diff --git a/Sources/ClientRuntime/EventStream/MessageUnmarshallable.swift b/Sources/ClientRuntime/EventStream/MessageUnmarshallable.swift index 816be6b0a..2d3b5e9ef 100644 --- a/Sources/ClientRuntime/EventStream/MessageUnmarshallable.swift +++ b/Sources/ClientRuntime/EventStream/MessageUnmarshallable.swift @@ -5,25 +5,4 @@ // SPDX-License-Identifier: Apache-2.0 // -/// Unmarshals a `Message` into a event stream event. -/// Codgegen generates a conformance to this protocol for each event stream event output. -public protocol MessageUnmarshallable { - /// Unmarshals a `Message` into a event stream event. - /// - Parameters: - /// - message: The message to unmarshal. - /// - decoder: ResponseDecoder to use to decode the event stream event. - /// Note: event type may contain nested types that need to be decoded - /// using the same decoder. - init(message: EventStream.Message, decoder: ResponseDecoder) throws -} - public typealias UnmarshalClosure = (EventStream.Message) throws -> T - -/// Provides an `UnmarshalClosure` for event payloads that are Swift `Decodable`. -/// - Parameter responseDecoder: The Swift `Decoder` to be used for decoding this event payload. -/// - Returns: An `UnmarshalClosure` that uses the provided decoder to decode event payloads. -public func jsonUnmarshalClosure(responseDecoder: ResponseDecoder) -> UnmarshalClosure { - return { message in - try T(message: message, decoder: responseDecoder) - } -} diff --git a/Sources/ClientRuntime/Networking/BaseError/BaseError.swift b/Sources/ClientRuntime/Networking/BaseError/BaseError.swift new file mode 100644 index 000000000..22ff0805a --- /dev/null +++ b/Sources/ClientRuntime/Networking/BaseError/BaseError.swift @@ -0,0 +1,29 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +public protocol BaseError { + var httpResponse: HttpResponse { get } + var code: String { get } + var message: String? { get } + var requestID: String? { get } + + func customError() -> Error? +} + +public extension BaseError { + + /// Returns a custom error from the error response, if any. + /// + /// By default, a `BaseError` returns no custom error unless + /// the implementation provides its own implementation of this method. + /// - Returns: Some custom `Error` or `nil` if none. + func customError() -> Error? { nil } +} + +public enum BaseErrorDecodeError: Error { + case missingRequiredData +} diff --git a/Sources/ClientRuntime/Networking/Http/HTTPResponseClosure.swift b/Sources/ClientRuntime/Networking/Http/HTTPResponseClosure.swift deleted file mode 100644 index 8bb64869b..000000000 --- a/Sources/ClientRuntime/Networking/Http/HTTPResponseClosure.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -public typealias HTTPResponseOutputClosure = (HttpResponse) async throws -> OperationStackOutput - -public typealias HTTPResponseOutputBinding = - (HttpResponse, HTTPResponseDocumentBinding) async throws -> OperationStackOutput - -/// Provides a response closure for a type that conforms to the `HttpResponseBinding` decoding protocol. -/// -/// This allows for use of JSON and FormURL serialized types with closure-based deserialization. -/// - Parameter decoder: The decoder to be used for decoding the value. -/// - Returns: A `HTTPResponseOutputClosure` that can be used to decode a value of the specified type. -public func responseClosure( - decoder: Decoder -) -> HTTPResponseOutputClosure { - return { response in - try await OperationStackOutput(httpResponse: response, decoder: decoder) - } -} - -/// Provides a response closure for a type that provides closures for its output bindings. -/// - Parameters: -/// - responseOutputBinding: The `HTTPResponseOutputBinding` for the model to be deserialized. -/// - responseDocumentBinding: A `HTTPResponseDocumentBinding` to convert the HTTP response to a `Reader`. -/// - Returns: a `HTTPResponseOutputClosure` that can be used to deserialize a value of the specified type. -public func responseClosure( - _ responseOutputBinding: @escaping HTTPResponseOutputBinding, - _ responseDocumentBinding: @escaping HTTPResponseDocumentBinding -) -> HTTPResponseOutputClosure { - return { response in - try await responseOutputBinding(response, responseDocumentBinding) - } -} diff --git a/Sources/ClientRuntime/Networking/Http/HTTPResponseDocumentBinding.swift b/Sources/ClientRuntime/Networking/Http/HTTPResponseDocumentBinding.swift deleted file mode 100644 index 8749558c2..000000000 --- a/Sources/ClientRuntime/Networking/Http/HTTPResponseDocumentBinding.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -import class SmithyXML.Reader - -public typealias HTTPResponseDocumentBinding = (HttpResponse) async throws -> Reader - -/// Creates a `HTTPResponseDocumentBinding` for converting a HTTP response into a `Reader`. -public var responseDocumentBinding: HTTPResponseDocumentBinding { - return { response in - let data = try await response.body.readData() - response.body = .data(data) - return try Reader.from(data: data ?? Data()) - } -} diff --git a/Sources/ClientRuntime/Networking/Http/HTTPResponseErrorClosure.swift b/Sources/ClientRuntime/Networking/Http/HTTPResponseErrorClosure.swift deleted file mode 100644 index 0157f1227..000000000 --- a/Sources/ClientRuntime/Networking/Http/HTTPResponseErrorClosure.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -import class SmithyXML.Reader - -/// Defines a closure that can be used to convert a HTTP response to a Swift `Error`. -public typealias HTTPResponseErrorClosure = (HttpResponse) async throws -> Error - -/// Defines a closure that can be used to convert a HTTP response and `Reader` for that response to a Swift `Error`. -public typealias HTTPResponseErrorBinding = - (HttpResponse, HTTPResponseDocumentBinding) async throws -> Error - -/// Provides a `HTTPResponseErrorClosure` for types that have Swift Decodable-based deserialization. -/// - Parameters: -/// - errorBinding: The `HttpResponseErrorBinding`-conforming type for this operation. -/// - decoder: The Swift `Decoder` to be used with the response. -/// - Returns: The `HTTPResponseErrorClosure` for deserializing this error. -public func responseErrorClosure( - _ errorBinding: OperationErrorBinding.Type, - decoder: Decoder -) -> HTTPResponseErrorClosure { - return { response in - try await OperationErrorBinding.makeError(httpResponse: response, decoder: decoder) - } -} - -/// Provides a `HTTPResponseErrorClosure` for types that have closure-based deserialization. -/// - Parameters: -/// - responseErrorBinding: The `HTTPResponseErrorBinding` closure for this operation. -/// - responseDocumentBinding: The `HTTPResponseDocumentBinding` closure for converting the HTTP response into a `Reader`. -/// - Returns: The `HTTPResponseErrorClosure` for deserializing this error. -public func responseErrorClosure( - _ responseErrorBinding: @escaping HTTPResponseErrorBinding, - _ responseDocumentBinding: @escaping HTTPResponseDocumentBinding -) -> HTTPResponseErrorClosure { - return { response in - try await responseErrorBinding(response, responseDocumentBinding) - } -} diff --git a/Sources/ClientRuntime/Networking/Http/HttpContext.swift b/Sources/ClientRuntime/Networking/Http/HttpContext.swift index 196cd4f48..139b176e2 100644 --- a/Sources/ClientRuntime/Networking/Http/HttpContext.swift +++ b/Sources/ClientRuntime/Networking/Http/HttpContext.swift @@ -37,14 +37,6 @@ public class HttpContext: MiddlewareContext { return attributes.get(key: AttributeKeys.isChunkedEligibleStream) } - public func getDecoder() -> ResponseDecoder { - return attributes.get(key: AttributeKeys.decoder)! - } - - public func getEncoder() -> RequestEncoder { - return attributes.get(key: AttributeKeys.encoder)! - } - public func getExpiration() -> TimeInterval { return attributes.get(key: AttributeKeys.expiration) ?? 0 } @@ -178,18 +170,6 @@ public class HttpContextBuilder { return self } - @discardableResult - public func withDecoder(value: ResponseDecoder) -> HttpContextBuilder { - self.attributes.set(key: AttributeKeys.decoder, value: value) - return self - } - - @discardableResult - public func withEncoder(value: RequestEncoder) -> HttpContextBuilder { - self.attributes.set(key: AttributeKeys.encoder, value: value) - return self - } - @discardableResult public func withExpiration(value: TimeInterval) -> HttpContextBuilder { self.attributes.set(key: AttributeKeys.expiration, value: value) @@ -329,8 +309,6 @@ public enum AttributeKeys { public static let authSchemeResolver = AttributeKey(name: "AuthSchemeResolver") public static let authSchemes = AttributeKey(name: "AuthSchemes") public static let bidirectionalStreaming = AttributeKey(name: "BidirectionalStreaming") - public static let decoder = AttributeKey(name: "Decoder") - public static let encoder = AttributeKey(name: "Encoder") public static let flowType = AttributeKey(name: "FlowType") public static let host = AttributeKey(name: "Host") public static let hostPrefix = AttributeKey(name: "HostPrefix") diff --git a/Sources/ClientRuntime/Networking/Http/HttpResponse.swift b/Sources/ClientRuntime/Networking/Http/HttpResponse.swift index 4c18ea117..39f598b76 100644 --- a/Sources/ClientRuntime/Networking/Http/HttpResponse.swift +++ b/Sources/ClientRuntime/Networking/Http/HttpResponse.swift @@ -1,7 +1,11 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import protocol SmithyReadWrite.WireDataProviding import AwsCommonRuntimeKit public class HttpResponse: HttpUrlResponse, ResponseMessage { @@ -32,6 +36,15 @@ extension HttpResponse: CustomDebugStringConvertible { } } +extension HttpResponse: WireDataProviding { + + public func data() async throws -> Data { + let data = try await body.readData() + body = .data(data) + return data ?? Data() + } +} + extension ByteStream { // Convert the body stream to a ValidatingFileStream to check checksums diff --git a/Sources/ClientRuntime/Networking/Http/HttpResponseBinding.swift b/Sources/ClientRuntime/Networking/Http/HttpResponseBinding.swift deleted file mode 100644 index a1435cf08..000000000 --- a/Sources/ClientRuntime/Networking/Http/HttpResponseBinding.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -/// The interface for creating the response object that results from a successful HTTP/HTTPS response. -public protocol HttpResponseBinding { - - /// The interface for creating the response object that results from a successful HTTP/HTTPS response. - init(httpResponse: HttpResponse, decoder: ResponseDecoder?) async throws -} diff --git a/Sources/ClientRuntime/Networking/Http/HttpResponseErrorBinding.swift b/Sources/ClientRuntime/Networking/Http/HttpResponseErrorBinding.swift deleted file mode 100644 index 07d317882..000000000 --- a/Sources/ClientRuntime/Networking/Http/HttpResponseErrorBinding.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -/// The interface for creating the response object that results from a HTTP/HTTPS error response. -/// -/// Value returned may be of any type that is a Swift `Error`. -public protocol HttpResponseErrorBinding { - - /// The interface for creating the response object that results from a HTTP/HTTPS error response. - /// - /// Value returned may be of any type that is a Swift `Error`. - static func makeError(httpResponse: HttpResponse, decoder: ResponseDecoder?) async throws -> Error -} diff --git a/Sources/ClientRuntime/Networking/Http/Middlewares/DeserializeMiddleware.swift b/Sources/ClientRuntime/Networking/Http/Middlewares/DeserializeMiddleware.swift index f8925d192..d259664a1 100644 --- a/Sources/ClientRuntime/Networking/Http/Middlewares/DeserializeMiddleware.swift +++ b/Sources/ClientRuntime/Networking/Http/Middlewares/DeserializeMiddleware.swift @@ -5,17 +5,19 @@ // SPDX-License-Identifier: Apache-2.0 // +import SmithyReadWrite + public struct DeserializeMiddleware: Middleware { public var id: String = "Deserialize" - let httpResponseClosure: HTTPResponseOutputClosure - let httpResponseErrorClosure: HTTPResponseErrorClosure + let wireResponseClosure: WireResponseOutputClosure + let wireResponseErrorClosure: WireResponseErrorClosure public init( - _ httpResponseClosure: @escaping HTTPResponseOutputClosure, - _ httpResponseErrorClosure: @escaping HTTPResponseErrorClosure + _ wireResponseClosure: @escaping WireResponseOutputClosure, + _ wireResponseErrorClosure: @escaping WireResponseErrorClosure ) { - self.httpResponseClosure = httpResponseClosure - self.httpResponseErrorClosure = httpResponseErrorClosure + self.wireResponseClosure = wireResponseClosure + self.wireResponseErrorClosure = wireResponseErrorClosure } public func handle(context: HttpContext, input: SdkHttpRequest, @@ -61,7 +63,7 @@ extension DeserializeMiddleware: ResponseMessageDeserializer { let copiedResponse = response if (200..<300).contains(response.statusCode.rawValue) { - return .success(try await httpResponseClosure(copiedResponse)) + return .success(try await wireResponseClosure(copiedResponse)) } else { // if the response is a stream, we need to cache the stream so that it can be read again // error deserialization reads the stream multiple times to first deserialize the protocol error @@ -69,7 +71,7 @@ extension DeserializeMiddleware: ResponseMessageDeserializer { // and then the service error eg. [AccountNotFoundException](https://github.com/awslabs/aws-sdk-swift/blob/d1d18eefb7457ed27d416b372573a1f815004eb1/Sources/Services/AWSCloudTrail/models/Models.swift#L62) let bodyData = try await copiedResponse.body.readData() copiedResponse.body = .data(bodyData) - return .failure(try await httpResponseErrorClosure(copiedResponse)) + return .failure(try await wireResponseErrorClosure(copiedResponse)) } } } diff --git a/Sources/ClientRuntime/Networking/Http/Middlewares/RequestBody/BodyMiddleware.swift b/Sources/ClientRuntime/Networking/Http/Middlewares/RequestBody/BodyMiddleware.swift index da7ddc2aa..1c2e510ba 100644 --- a/Sources/ClientRuntime/Networking/Http/Middlewares/RequestBody/BodyMiddleware.swift +++ b/Sources/ClientRuntime/Networking/Http/Middlewares/RequestBody/BodyMiddleware.swift @@ -6,22 +6,22 @@ // import struct Foundation.Data -import typealias SmithyReadWrite.DocumentWritingClosure +import protocol SmithyReadWrite.SmithyWriter import typealias SmithyReadWrite.WritingClosure public struct BodyMiddleware: Middleware { + Writer: SmithyWriter>: Middleware { public let id: Swift.String = "BodyMiddleware" - let documentWritingClosure: DocumentWritingClosure + let rootNodeInfo: Writer.NodeInfo let inputWritingClosure: WritingClosure public init( - documentWritingClosure: @escaping DocumentWritingClosure, + rootNodeInfo: Writer.NodeInfo, inputWritingClosure: @escaping WritingClosure ) { - self.documentWritingClosure = documentWritingClosure + self.rootNodeInfo = rootNodeInfo self.inputWritingClosure = inputWritingClosure } @@ -48,8 +48,13 @@ extension BodyMiddleware: RequestMessageSerializer { public func apply(input: OperationStackInput, builder: SdkHttpRequestBuilder, attributes: HttpContext) throws { do { - let data = try documentWritingClosure(input, inputWritingClosure) - builder.withBody(.data(data)) + let data = try Writer.write( + input, + rootNodeInfo: rootNodeInfo, + with: inputWritingClosure + ) + let body = ByteStream.data(data) + builder.withBody(body) } catch { throw ClientError.serializationFailed(error.localizedDescription) } diff --git a/Sources/ClientRuntime/Networking/Http/Middlewares/RequestBody/EventStreamBodyMiddleware.swift b/Sources/ClientRuntime/Networking/Http/Middlewares/RequestBody/EventStreamBodyMiddleware.swift index b7ca84a54..46bb5f553 100644 --- a/Sources/ClientRuntime/Networking/Http/Middlewares/RequestBody/EventStreamBodyMiddleware.swift +++ b/Sources/ClientRuntime/Networking/Http/Middlewares/RequestBody/EventStreamBodyMiddleware.swift @@ -6,12 +6,11 @@ // import struct Foundation.Data -import typealias SmithyReadWrite.DocumentWritingClosure import typealias SmithyReadWrite.WritingClosure public struct EventStreamBodyMiddleware: + OperationStackInputPayload>: Middleware { public let id: Swift.String = "EventStreamBodyMiddleware" diff --git a/Sources/ClientRuntime/Networking/Http/Middlewares/RequestBody/PayloadBodyMiddleware.swift b/Sources/ClientRuntime/Networking/Http/Middlewares/RequestBody/PayloadBodyMiddleware.swift index f90e73858..2ddd494e1 100644 --- a/Sources/ClientRuntime/Networking/Http/Middlewares/RequestBody/PayloadBodyMiddleware.swift +++ b/Sources/ClientRuntime/Networking/Http/Middlewares/RequestBody/PayloadBodyMiddleware.swift @@ -6,27 +6,27 @@ // import struct Foundation.Data -import typealias SmithyReadWrite.DocumentWritingClosure +import protocol SmithyReadWrite.SmithyWriter import typealias SmithyReadWrite.WritingClosure public struct PayloadBodyMiddleware: Middleware { + Writer: SmithyWriter>: Middleware { public let id: Swift.String = "PayloadBodyMiddleware" - let documentWritingClosure: DocumentWritingClosure + let rootNodeInfo: Writer.NodeInfo let inputWritingClosure: WritingClosure let keyPath: KeyPath let defaultBody: String? public init( - documentWritingClosure: @escaping DocumentWritingClosure, + rootNodeInfo: Writer.NodeInfo, inputWritingClosure: @escaping WritingClosure, keyPath: KeyPath, defaultBody: String? ) { - self.documentWritingClosure = documentWritingClosure + self.rootNodeInfo = rootNodeInfo self.inputWritingClosure = inputWritingClosure self.keyPath = keyPath self.defaultBody = defaultBody @@ -48,21 +48,6 @@ public struct PayloadBodyMiddleware Error { + public static func makeError( + baseError: Base + ) throws -> Error { UnknownHTTPServiceError( - httpResponse: httpResponse, - message: message, - typeName: typeName + httpResponse: baseError.httpResponse, + message: baseError.message, + typeName: baseError.code ) } } diff --git a/Sources/ClientRuntime/Networking/Streaming/ByteStream.swift b/Sources/ClientRuntime/Networking/Streaming/ByteStream.swift index eac1b0a22..336ea46fc 100644 --- a/Sources/ClientRuntime/Networking/Streaming/ByteStream.swift +++ b/Sources/ClientRuntime/Networking/Streaming/ByteStream.swift @@ -28,36 +28,6 @@ public enum ByteStream { } } -extension ByteStream: Codable { - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - if container.decodeNil() { - self = .data(nil) - } else { - let data = try container.decode(Data.self) - self = .data(data) - } - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - switch self { - case .data(let data): - try container.encode(data) - case .stream: - throw EncodingError.invalidValue( - self, - EncodingError.Context( - codingPath: encoder.codingPath, - debugDescription: "Cannot encode a stream." - ) - ) - case .noStream: - try container.encodeNil() - } - } -} - extension ByteStream { // Static property for an empty ByteStream diff --git a/Sources/ClientRuntime/Serialization/Decoder/JSONDecoder+Extensions.swift b/Sources/ClientRuntime/Serialization/Decoder/JSONDecoder+Extensions.swift deleted file mode 100644 index 1ccbda530..000000000 --- a/Sources/ClientRuntime/Serialization/Decoder/JSONDecoder+Extensions.swift +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import Foundation - -public typealias JSONDecoder = Foundation.JSONDecoder -extension JSONDecoder: ResponseDecoder { - public func decode(responseBody: Data) throws -> T where T: Decodable { - return try decode(T.self, from: responseBody) - } -} diff --git a/Sources/ClientRuntime/Serialization/Decoder/ResponseDecoder.swift b/Sources/ClientRuntime/Serialization/Decoder/ResponseDecoder.swift deleted file mode 100644 index 8ce95db29..000000000 --- a/Sources/ClientRuntime/Serialization/Decoder/ResponseDecoder.swift +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import Foundation - -public protocol ResponseDecoder { - func decode(responseBody: Data) throws -> T -} diff --git a/Sources/ClientRuntime/Serialization/Encoder/JSONEncoder+Extensions.swift b/Sources/ClientRuntime/Serialization/Encoder/JSONEncoder+Extensions.swift deleted file mode 100644 index 82c0137d7..000000000 --- a/Sources/ClientRuntime/Serialization/Encoder/JSONEncoder+Extensions.swift +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import Foundation - -public typealias JSONEncoder = Foundation.JSONEncoder -extension JSONEncoder: RequestEncoder {} diff --git a/Sources/ClientRuntime/Serialization/Encoder/RequestEncoder.swift b/Sources/ClientRuntime/Serialization/Encoder/RequestEncoder.swift deleted file mode 100644 index 5dadf5f49..000000000 --- a/Sources/ClientRuntime/Serialization/Encoder/RequestEncoder.swift +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -public protocol RequestEncoder { - func encode(_ value: T) throws -> Data -} diff --git a/Sources/ClientRuntime/Serialization/FormURL/Encoder/FormURLEncoder.swift b/Sources/ClientRuntime/Serialization/FormURL/Encoder/FormURLEncoder.swift deleted file mode 100644 index 541520042..000000000 --- a/Sources/ClientRuntime/Serialization/FormURL/Encoder/FormURLEncoder.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -// Inspired from: -// https://stackoverflow.com/questions/45169254/custom-swift-encoder-decoder-for-the-strings-resource-format - -public class FormURLEncoder: RequestEncoder { - public init() {} - - public func encode(_ value: T) throws -> Data where T: Encodable { - let formURLEncoding = FormURLEncoding() - try value.encode(to: formURLEncoding) - let sortedKeyValues = formURLEncoding.data.strings.sorted(by: { keyVal1, keyVal2 in - keyVal1.key < keyVal2.key - }) - let formEncodedString = formURLEncodeFormat(from: sortedKeyValues) - return formEncodedString.data(using: .utf8) ?? Data() - } - - private func formURLEncodeFormat(from strings: [(String, String)]) -> String { - let keyValues = strings.map { key, value in - "\(key.urlPercentEncoding())=\(value.urlPercentEncoding())" - } - return keyValues.joined(separator: "&") - } -} diff --git a/Sources/ClientRuntime/Serialization/FormURL/Encoder/FormURLEncoding.swift b/Sources/ClientRuntime/Serialization/FormURL/Encoder/FormURLEncoding.swift deleted file mode 100644 index b5559d3aa..000000000 --- a/Sources/ClientRuntime/Serialization/FormURL/Encoder/FormURLEncoding.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -struct FormURLEncoding: Encoder { - - final class EncodedData { - private(set) var strings: [String: String] = [:] - - func encode(key codingKey: [CodingKey], value: String) { - let key = codingKey.map { - $0.stringValue - }.filter { - !$0.isEmpty - }.joined(separator: ".") - - strings[key] = value - } - } - - var data: EncodedData - - init(to encodedData: EncodedData = EncodedData()) { - self.data = encodedData - } - - // MARK: Encoder conformance - var codingPath: [CodingKey] = [] - let userInfo: [CodingUserInfoKey: Any] = [:] - - func container(keyedBy type: Key.Type) -> KeyedEncodingContainer { - var container = FormURLKeyedEncoding(to: data) - container.codingPath = codingPath - return KeyedEncodingContainer(container) - } - - func unkeyedContainer() -> UnkeyedEncodingContainer { - var container = FormURLUnkeyedEncoding(to: data) - container.codingPath = codingPath - return container - } - - func singleValueContainer() -> SingleValueEncodingContainer { - var container = FormURLSingleValueEncoding(to: data) - container.codingPath = codingPath - return container - } -} diff --git a/Sources/ClientRuntime/Serialization/FormURL/Encoder/FormURLKeyedEncoding.swift b/Sources/ClientRuntime/Serialization/FormURL/Encoder/FormURLKeyedEncoding.swift deleted file mode 100644 index 7eb8fecbc..000000000 --- a/Sources/ClientRuntime/Serialization/FormURL/Encoder/FormURLKeyedEncoding.swift +++ /dev/null @@ -1,129 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -struct FormURLKeyedEncoding: KeyedEncodingContainerProtocol { - - private let data: FormURLEncoding.EncodedData - - init(to data: FormURLEncoding.EncodedData) { - self.data = data - } - - var codingPath: [CodingKey] = [] - - mutating func encodeNil(forKey key: Key) throws { - data.encode(key: codingPath + [key], value: "nil") - } - - mutating func encode(_ value: Bool, forKey key: Key) throws { - data.encode(key: codingPath + [key], value: value.description) - } - - mutating func encode(_ value: String, forKey key: Key) throws { - data.encode(key: codingPath + [key], value: value) - } - - mutating func encode(_ value: Double, forKey key: Key) throws { - if value.isNaN { - data.encode(key: codingPath + [key], value: "NaN") - } else { - switch value { - case .infinity: - data.encode(key: codingPath + [key], value: "Infinity") - case -.infinity: - data.encode(key: codingPath + [key], value: "-Infinity") - default: - data.encode(key: codingPath + [key], value: value.description) - } - } - } - - mutating func encode(_ value: Float, forKey key: Key) throws { - if value.isNaN { - data.encode(key: codingPath + [key], value: "NaN") - } else { - switch value { - case .infinity: - data.encode(key: codingPath + [key], value: "Infinity") - case -.infinity: - data.encode(key: codingPath + [key], value: "-Infinity") - default: - data.encode(key: codingPath + [key], value: value.description) - } - } - } - - mutating func encode(_ value: Int, forKey key: Key) throws { - data.encode(key: codingPath + [key], value: value.description) - } - - mutating func encode(_ value: Int8, forKey key: Key) throws { - data.encode(key: codingPath + [key], value: value.description) - } - - mutating func encode(_ value: Int16, forKey key: Key) throws { - data.encode(key: codingPath + [key], value: value.description) - } - - mutating func encode(_ value: Int32, forKey key: Key) throws { - data.encode(key: codingPath + [key], value: value.description) - } - - mutating func encode(_ value: Int64, forKey key: Key) throws { - data.encode(key: codingPath + [key], value: value.description) - } - - mutating func encode(_ value: UInt, forKey key: Key) throws { - data.encode(key: codingPath + [key], value: value.description) - } - - mutating func encode(_ value: UInt8, forKey key: Key) throws { - data.encode(key: codingPath + [key], value: value.description) - } - - mutating func encode(_ value: UInt16, forKey key: Key) throws { - data.encode(key: codingPath + [key], value: value.description) - } - - mutating func encode(_ value: UInt32, forKey key: Key) throws { - data.encode(key: codingPath + [key], value: value.description) - } - - mutating func encode(_ value: UInt64, forKey key: Key) throws { - data.encode(key: codingPath + [key], value: value.description) - } - - mutating func encode(_ value: T, forKey key: Key) throws { - var stringsEncoding = FormURLEncoding(to: data) - stringsEncoding.codingPath = codingPath + [key] - try value.encode(to: stringsEncoding) - } - - mutating func nestedContainer(keyedBy keyType: NestedKey.Type, - forKey key: Key) -> KeyedEncodingContainer { - var container = FormURLKeyedEncoding(to: data) - container.codingPath = codingPath + [key] - return KeyedEncodingContainer(container) - } - - mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { - var container = FormURLUnkeyedEncoding(to: data) - container.codingPath = codingPath + [key] - return container - } - - mutating func superEncoder() -> Encoder { - let superKey = Key(stringValue: "super")! - return superEncoder(forKey: superKey) - } - - mutating func superEncoder(forKey key: Key) -> Encoder { - var stringsEncoding = FormURLEncoding(to: data) - stringsEncoding.codingPath = codingPath + [key] - return stringsEncoding - } -} diff --git a/Sources/ClientRuntime/Serialization/FormURL/Encoder/FormURLReadWrite.swift b/Sources/ClientRuntime/Serialization/FormURL/Encoder/FormURLReadWrite.swift deleted file mode 100644 index 107fc7912..000000000 --- a/Sources/ClientRuntime/Serialization/FormURL/Encoder/FormURLReadWrite.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -import typealias SmithyReadWrite.DocumentWritingClosure -import typealias SmithyReadWrite.WritingClosure - -public class FormURLWriter { - private let encoder: any RequestEncoder - var data = Data() - - init(encoder: any RequestEncoder) { - self.encoder = encoder - } - - func encode(_ value: T) throws { - self.data = try encoder.encode(value) - } -} - -public enum FormURLReadWrite { - - public static func documentWritingClosure( - encoder: RequestEncoder - ) -> DocumentWritingClosure { - return { value, writingClosure in - let formURLWriter = FormURLWriter(encoder: encoder) - try writingClosure(value, formURLWriter) - return formURLWriter.data - } - } - - public static func writingClosure() -> WritingClosure { - return { value, writer in - try writer.encode(value) - } - } -} diff --git a/Sources/ClientRuntime/Serialization/FormURL/Encoder/FormURLSingleValueEncoding.swift b/Sources/ClientRuntime/Serialization/FormURL/Encoder/FormURLSingleValueEncoding.swift deleted file mode 100644 index 13edc0eb6..000000000 --- a/Sources/ClientRuntime/Serialization/FormURL/Encoder/FormURLSingleValueEncoding.swift +++ /dev/null @@ -1,83 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -struct FormURLSingleValueEncoding: SingleValueEncodingContainer { - - private let data: FormURLEncoding.EncodedData - - init(to data: FormURLEncoding.EncodedData) { - self.data = data - } - - var codingPath: [CodingKey] = [] - - mutating func encodeNil() throws { - data.encode(key: codingPath, value: "nil") - } - - mutating func encode(_ value: Bool) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: String) throws { - data.encode(key: codingPath, value: value) - } - - mutating func encode(_ value: Double) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: Float) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: Int) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: Int8) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: Int16) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: Int32) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: Int64) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: UInt) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: UInt8) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: UInt16) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: UInt32) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: UInt64) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: T) throws { - var stringsEncoding = FormURLEncoding(to: data) - stringsEncoding.codingPath = codingPath - try value.encode(to: stringsEncoding) - } -} diff --git a/Sources/ClientRuntime/Serialization/FormURL/Encoder/FormURLUnkeyedEncoding.swift b/Sources/ClientRuntime/Serialization/FormURL/Encoder/FormURLUnkeyedEncoding.swift deleted file mode 100644 index 50398eb11..000000000 --- a/Sources/ClientRuntime/Serialization/FormURL/Encoder/FormURLUnkeyedEncoding.swift +++ /dev/null @@ -1,103 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -struct FormURLUnkeyedEncoding: UnkeyedEncodingContainer { - - private let data: FormURLEncoding.EncodedData - - init(to data: FormURLEncoding.EncodedData) { - self.data = data - } - - var codingPath: [CodingKey] = [] - - private(set) var count: Int = 1 - - mutating func encodeNil() throws { - data.encode(key: codingPath, value: "nil") - } - - mutating func encode(_ value: Bool) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: String) throws { - data.encode(key: codingPath, value: value) - } - - mutating func encode(_ value: Double) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: Float) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: Int) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: Int8) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: Int16) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: Int32) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: Int64) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: UInt) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: UInt8) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: UInt16) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: UInt32) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: UInt64) throws { - data.encode(key: codingPath, value: value.description) - } - - mutating func encode(_ value: T) throws { - var stringsEncoding = FormURLEncoding(to: data) - stringsEncoding.codingPath = codingPath - try value.encode(to: stringsEncoding) - } - - mutating func nestedContainer(keyedBy keyType: NestedKey.Type) - -> KeyedEncodingContainer { - var container = FormURLKeyedEncoding(to: data) - container.codingPath = codingPath - return KeyedEncodingContainer(container) - } - - mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { - var container = FormURLUnkeyedEncoding(to: data) - container.codingPath = codingPath - return container - } - - mutating func superEncoder() -> Encoder { - let stringsEncoding = FormURLEncoding(to: data) - return stringsEncoding - } -} diff --git a/Sources/ClientRuntime/Serialization/SerializationUtils/JSONReadWrite.swift b/Sources/ClientRuntime/Serialization/SerializationUtils/JSONReadWrite.swift deleted file mode 100644 index 1b1043a59..000000000 --- a/Sources/ClientRuntime/Serialization/SerializationUtils/JSONReadWrite.swift +++ /dev/null @@ -1,73 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -import SmithyReadWrite - -public class JSONWriter { - private let encoder: any RequestEncoder - var data = Data() - - init(encoder: any RequestEncoder) { - self.encoder = encoder - } - - func encode(_ value: T) throws { - self.data = try encoder.encode(value) - } -} - -public class JSONReader { - private let decoder: any ResponseDecoder - private let data: Data - - init(data: Data, decoder: any ResponseDecoder) { - self.data = data - self.decoder = decoder - } - - func decode() throws -> T { - try decoder.decode(responseBody: data) - } -} - -public enum JSONReadWrite { - - public static func documentWritingClosure( - encoder: RequestEncoder - ) -> DocumentWritingClosure { - return { value, writingClosure in - let jsonWriter = JSONWriter(encoder: encoder) - try writingClosure(value, jsonWriter) - return jsonWriter.data - } - } - - public static func writingClosure() -> WritingClosure { - return { value, writer in - try writer.encode(value) - } - } - - public static func documentReadingClosure( - decoder: ResponseDecoder - ) -> DocumentReadingClosure { - return { data, readingClosure in - let jsonReader = JSONReader(data: data, decoder: decoder) - if let value = try readingClosure(jsonReader) { - return value - } else { - throw DocumentError.requiredValueNotPresent - } - } - } - - public static func readingClosure() -> ReadingClosure { - return { reader in - try reader.decode() - } - } -} diff --git a/Sources/ClientRuntime/Serialization/SerializationUtils/Key.swift b/Sources/ClientRuntime/Serialization/SerializationUtils/Key.swift deleted file mode 100644 index 96589a3c7..000000000 --- a/Sources/ClientRuntime/Serialization/SerializationUtils/Key.swift +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import Foundation - -public struct Key: CodingKey { - public let stringValue: String - public init(stringValue: String) { - self.stringValue = stringValue - self.intValue = nil - } - - public init(_ stringValue: String) { - self.stringValue = stringValue - self.intValue = nil - } - - public let intValue: Int? - public init?(intValue: Int) { - return nil - } -} diff --git a/Sources/ClientRuntime/Serialization/SerializationUtils/KeyedDecodingContainer+Extension.swift b/Sources/ClientRuntime/Serialization/SerializationUtils/KeyedDecodingContainer+Extension.swift deleted file mode 100644 index 71f2fcbd1..000000000 --- a/Sources/ClientRuntime/Serialization/SerializationUtils/KeyedDecodingContainer+Extension.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -extension KeyedDecodingContainer where K: CodingKey { - public func nestedContainerNonThrowable(keyedBy type: NestedKey.Type, - forKey key: KeyedDecodingContainer.Key) - -> KeyedDecodingContainer? where NestedKey: CodingKey { - do { - return try nestedContainer(keyedBy: type, forKey: key) - } catch { - return nil - } - } -} diff --git a/Sources/ClientRuntime/Serialization/SerializationUtils/TimestampFormatter.swift b/Sources/ClientRuntime/Serialization/SerializationUtils/TimestampFormatter.swift index c2b3eb587..36e279ca3 100644 --- a/Sources/ClientRuntime/Serialization/SerializationUtils/TimestampFormatter.swift +++ b/Sources/ClientRuntime/Serialization/SerializationUtils/TimestampFormatter.swift @@ -5,7 +5,8 @@ // SPDX-License-Identifier: Apache-2.0 // -import SmithyTimestamps +import struct SmithyTimestamps.TimestampFormatter +import enum SmithyTimestamps.TimestampFormat public typealias TimestampFormatter = SmithyTimestamps.TimestampFormatter public typealias TimestampFormat = SmithyTimestamps.TimestampFormat diff --git a/Sources/ClientRuntime/Serialization/SerializationUtils/XML/CollectionMember.swift b/Sources/ClientRuntime/Serialization/SerializationUtils/XML/CollectionMember.swift deleted file mode 100644 index 481281150..000000000 --- a/Sources/ClientRuntime/Serialization/SerializationUtils/XML/CollectionMember.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -/* - * Used for decoding lists for RestXML - */ -public struct CollectionMember: Codable where M: Codable { - public let member: [M] - public enum CodingKeys: String, CodingKey { - case member - - public var rawValue: String { - switch self { - case .member: return customMemberName() - } - } - - func customMemberName() -> String { - return String(describing: MemberCodingKey.self) - } - } -} diff --git a/Sources/ClientRuntime/Serialization/SerializationUtils/XML/CollectionMemberCodingKey.swift b/Sources/ClientRuntime/Serialization/SerializationUtils/XML/CollectionMemberCodingKey.swift deleted file mode 100644 index c13c993d1..000000000 --- a/Sources/ClientRuntime/Serialization/SerializationUtils/XML/CollectionMemberCodingKey.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -/* - * Used to for wrapped lists that use a custom coding key for `member` for RestXML - */ -public struct CollectionMemberCodingKey { - public enum CodingKeys: String, CodingKey { - case member - - public var rawValue: String { - switch self { - case .member: return customMemberName() - } - } - - func customMemberName() -> String { - return String(describing: MemberCodingKey.self) - } - } -} diff --git a/Sources/ClientRuntime/Serialization/SerializationUtils/XML/MapEntry.swift b/Sources/ClientRuntime/Serialization/SerializationUtils/XML/MapEntry.swift deleted file mode 100644 index 855c93c99..000000000 --- a/Sources/ClientRuntime/Serialization/SerializationUtils/XML/MapEntry.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -public struct MapEntry: Decodable where K: Decodable, V: Decodable { - public let entry: [MapKeyValue]? - public enum CodingKeys: String, CodingKey { - case entry - } -} diff --git a/Sources/ClientRuntime/Serialization/SerializationUtils/XML/MapKeyValue.swift b/Sources/ClientRuntime/Serialization/SerializationUtils/XML/MapKeyValue.swift deleted file mode 100644 index 0b8cf1953..000000000 --- a/Sources/ClientRuntime/Serialization/SerializationUtils/XML/MapKeyValue.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -public struct MapKeyValue: Decodable where K: Decodable, V: Decodable { - public let key: K - public let value: V - - public enum CodingKeys: String, CodingKey { - case key - case value - - public var rawValue: String { - switch self { - case .key: return customKeyName() - case .value: return customValueName() - } - } - func customKeyName() -> String { - return String(describing: CustomKeyName.self) - } - func customValueName() -> String { - return String(describing: CustomValueName.self) - } - } -} diff --git a/Sources/ClientRuntime/Serialization/SerializationUtils/XML/ValueWriters.swift b/Sources/ClientRuntime/Serialization/SerializationUtils/XML/ValueWriters.swift index 42e599a0e..953909dd4 100644 --- a/Sources/ClientRuntime/Serialization/SerializationUtils/XML/ValueWriters.swift +++ b/Sources/ClientRuntime/Serialization/SerializationUtils/XML/ValueWriters.swift @@ -5,9 +5,9 @@ // SPDX-License-Identifier: Apache-2.0 // -import class SmithyXML.Writer +import protocol SmithyReadWrite.SmithyWriter -extension Writer { +extension SmithyWriter { public func write(_ value: ByteStream?) throws { // This serialization will never be performed in practice, since diff --git a/Sources/SmithyFormURL/NodeInfo.swift b/Sources/SmithyFormURL/NodeInfo.swift new file mode 100644 index 000000000..92e9b8bee --- /dev/null +++ b/Sources/SmithyFormURL/NodeInfo.swift @@ -0,0 +1,24 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +public struct NodeInfo: Equatable { + + /// The name for this FormURL node, or an empty string if none. + public let name: String + + public init(_ name: String) { + self.name = name + } +} + +extension NodeInfo: ExpressibleByStringLiteral { + public typealias StringLiteralType = String + + public init(stringLiteral value: String) { + self.init(value) + } +} diff --git a/Sources/SmithyFormURL/Writer.swift b/Sources/SmithyFormURL/Writer.swift new file mode 100644 index 000000000..bedd8ce8a --- /dev/null +++ b/Sources/SmithyFormURL/Writer.swift @@ -0,0 +1,203 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import protocol SmithyReadWrite.SmithyWriter +import enum SmithyReadWrite.Document +import enum SmithyTimestamps.TimestampFormat +import struct SmithyTimestamps.TimestampFormatter +import struct Foundation.Data +import struct Foundation.Date +import struct Foundation.CharacterSet + +public final class Writer: SmithyWriter { + public typealias NodeInfo = SmithyFormURL.NodeInfo + + let nodeInfo: NodeInfo + var content: String? + var children: [Writer] = [] + weak var parent: Writer? + public var nodeInfoPath: [NodeInfo] { (parent?.nodeInfoPath ?? []) + [nodeInfo] } + + public required init(nodeInfo: NodeInfo) { + self.nodeInfo = nodeInfo + } + + init(nodeInfo: NodeInfo, parent: Writer? = nil) { + self.nodeInfo = nodeInfo + self.parent = parent + } +} + +public extension Writer { + + func data() throws -> Data { + return Data((query ?? "").utf8) + } + + private var query: String? { + var subqueries = [String]() + if !nodeInfo.name.isEmpty && !((content ?? "").isEmpty && !children.isEmpty) { + self.subqueryString.map { subqueries.append($0) } + } + children.forEach { child in + if let query = child.query { subqueries.append(query) } + } + guard !subqueries.isEmpty else { return nil } + return subqueries.joined(separator: "&") + } + + private var subqueryString: String? { + guard let content else { return nil } + let queryName = nodeInfoPath.map(\.name).filter { !$0.isEmpty }.joined(separator: ".") + return "\(queryName.urlPercentEncodedForQuery)=\(content.urlPercentEncodedForQuery)" + } + + subscript(nodeInfo: NodeInfo) -> Writer { + if let child = children.first(where: { $0.nodeInfo == nodeInfo }) { + return child + } else { + let newChild = Writer(nodeInfo: nodeInfo, parent: self) + addChild(newChild) + return newChild + } + } + + func write(_ value: Bool?) throws { + record(string: value.map { $0 ? "true" : "false" }) + } + + func write(_ value: String?) throws { + record(string: value) + } + + func write(_ value: Double?) throws { + guard let value else { return } + guard !value.isNaN else { + record(string: "NaN") + return + } + switch value { + case .infinity: + record(string: "Infinity") + case -.infinity: + record(string: "-Infinity") + default: + record(string: "\(value)") + } + } + + func write(_ value: Float?) throws { + guard let value else { return } + guard !value.isNaN else { + record(string: "NaN") + return + } + switch value { + case .infinity: + record(string: "Infinity") + case -.infinity: + record(string: "-Infinity") + default: + record(string: "\(value)") + } + } + + func write(_ value: Int?) throws { + record(string: value.map { "\($0)" }) + } + + func write(_ value: Int8?) throws { + record(string: value.map { "\($0)" }) + } + + func write(_ value: Int16?) throws { + record(string: value.map { "\($0)" }) + } + + func write(_ value: UInt8?) throws { + record(string: value.map { "\($0)" }) + } + + func write(_ value: Data?) throws { + try write(value?.base64EncodedString()) + } + + func write(_ value: Document?) throws { + // No operation. Smithy document not supported in FormURL + } + + func writeTimestamp(_ value: Date?, format: SmithyTimestamps.TimestampFormat) throws { + guard let value else { return } + record(string: TimestampFormatter(format: format).string(from: value)) + } + + func write(_ value: T?) throws where T: RawRepresentable, T.RawValue == Int { + try write(value?.rawValue) + } + + func write(_ value: T?) throws where T: RawRepresentable, T.RawValue == String { + try write(value?.rawValue) + } + + func writeMap( + _ value: [String: T]?, + valueWritingClosure: (T, Writer) throws -> Void, + keyNodeInfo: NodeInfo, + valueNodeInfo: NodeInfo, + isFlattened: Bool + ) throws { + guard let value, !value.isEmpty else { return } + let entryWriter = isFlattened ? self : self[.init("entry")] + let keysAndValues = value.map { (key: $0.key, value: $0.value) }.sorted { $0.key < $1.key } + for (index, (key, value)) in keysAndValues.enumerated() { + let indexedWriter = entryWriter[.init("\(index + 1)")] + try indexedWriter[keyNodeInfo].write(key) + try valueWritingClosure(value, indexedWriter[valueNodeInfo]) + } + } + + func writeList( + _ value: [T]?, + memberWritingClosure: (T, Writer) throws -> Void, + memberNodeInfo: NodeInfo, + isFlattened: Bool + ) throws { + guard let value else { return } + guard !value.isEmpty else { try write(""); return } + let entryWriter = isFlattened ? self : self[memberNodeInfo] + for (index, value) in value.enumerated() { + let indexedWriter = entryWriter[.init("\(index + 1)")] + try memberWritingClosure(value, indexedWriter) + } + } + + func writeNull() throws { + // Null not defined in FormURL. + // No action taken, node remains in 'no content' state. + } + + // MARK: - Private methods + + private func addChild(_ child: Writer) { + children.append(child) + child.parent = self + } + + private func record(string: String?) { + guard let string else { return } + content = string + } +} + +private extension String { + + private static let allowedForQuery = CharacterSet.alphanumerics.union(CharacterSet(charactersIn: "_-.~")) + + var urlPercentEncodedForQuery: String { + addingPercentEncoding(withAllowedCharacters: Self.allowedForQuery) ?? self + } +} diff --git a/Sources/SmithyJSON/JSONNode.swift b/Sources/SmithyJSON/JSONNode.swift new file mode 100644 index 000000000..5a7df34ec --- /dev/null +++ b/Sources/SmithyJSON/JSONNode.swift @@ -0,0 +1,21 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import class Foundation.NSNumber + +enum JSONNode: Equatable { + case bool(Bool) + case number(NSNumber) + case string(String) + case null + case array + case object +} + +enum JSONError: Error { + case unknownJSONContent +} diff --git a/Sources/SmithyJSON/NodeInfo.swift b/Sources/SmithyJSON/NodeInfo.swift new file mode 100644 index 000000000..0ada900e0 --- /dev/null +++ b/Sources/SmithyJSON/NodeInfo.swift @@ -0,0 +1,24 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +public struct NodeInfo: Equatable { + + /// The name for this JSON node, or an empty string if none. + public let name: String + + public init(_ name: String) { + self.name = name + } +} + +extension NodeInfo: ExpressibleByStringLiteral { + public typealias StringLiteralType = String + + public init(stringLiteral value: String) { + self.init(value) + } +} diff --git a/Sources/SmithyJSON/Reader/Reader+JSONDeserialization.swift b/Sources/SmithyJSON/Reader/Reader+JSONDeserialization.swift new file mode 100644 index 000000000..42c660101 --- /dev/null +++ b/Sources/SmithyJSON/Reader/Reader+JSONDeserialization.swift @@ -0,0 +1,24 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import struct Foundation.Data +import class Foundation.NSError +import class Foundation.JSONSerialization +import class Foundation.NSNull + +extension Reader { + + public static func from(data: Data) throws -> Reader { + guard !data.isEmpty else { return Reader(nodeInfo: "", parent: nil) } + do { + let jsonObject = try JSONSerialization.jsonObject(with: data, options: [.fragmentsAllowed]) + return try Reader(nodeInfo: "", jsonObject: jsonObject) + } catch let error as NSError where error.domain == "NSCocoaErrorDomain" && error.code == 3840 { + return try Reader(nodeInfo: "", jsonObject: [:]) + } + } +} diff --git a/Sources/SmithyJSON/Reader/Reader.swift b/Sources/SmithyJSON/Reader/Reader.swift new file mode 100644 index 000000000..517596370 --- /dev/null +++ b/Sources/SmithyJSON/Reader/Reader.swift @@ -0,0 +1,230 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import protocol SmithyReadWrite.SmithyReader +import enum SmithyReadWrite.Document +import typealias SmithyReadWrite.ReadingClosure +import enum SmithyReadWrite.ReaderError +import enum SmithyTimestamps.TimestampFormat +import struct SmithyTimestamps.TimestampFormatter +import struct Foundation.Data +import struct Foundation.Date +import class Foundation.NSNull +import class Foundation.NSNumber +import func CoreFoundation.CFGetTypeID +import func CoreFoundation.CFBooleanGetTypeID + +public final class Reader: SmithyReader { + public typealias NodeInfo = SmithyJSON.NodeInfo + + public let nodeInfo: NodeInfo + let jsonNode: JSONNode? + public internal(set) var children = [Reader]() + public internal(set) weak var parent: Reader? + public var hasContent: Bool { jsonNode != nil && jsonNode != .null } + + init(nodeInfo: NodeInfo, jsonObject: Any?, parent: Reader? = nil) throws { + self.nodeInfo = nodeInfo + self.jsonNode = try Self.jsonNode(for: jsonObject) + self.parent = parent + self.children = try Self.children(from: jsonObject, parent: self) + } + + init(nodeInfo: NodeInfo, parent: Reader?) { + self.nodeInfo = nodeInfo + self.jsonNode = nil + self.parent = parent + } + + private static func jsonNode(for jsonObject: Any?) throws -> JSONNode? { + if jsonObject is [String: Any] { + return .object + } else if jsonObject is [Any] { + return .array + } else if let nsNumber = jsonObject as? NSNumber, CFGetTypeID(nsNumber) == CFBooleanGetTypeID() { + return .bool(nsNumber.boolValue) + } else if let nsNumber = jsonObject as? NSNumber { + return .number(nsNumber) + } else if let string = jsonObject as? String { + return .string(string) + } else if jsonObject is NSNull { + return .null + } else { + throw JSONError.unknownJSONContent + } + } + + private static func children(from jsonObject: Any?, parent: Reader) throws -> [Reader] { + if let object = jsonObject as? [String: Any] { + return try object.map { try Reader(nodeInfo: .init($0.key), jsonObject: $0.value, parent: parent) } + } else if let list = jsonObject as? [Any] { + return try list.map { try Reader(nodeInfo: "", jsonObject: $0, parent: parent) } + } else { + return [] + } + } +} + +public extension Reader { + + subscript(nodeInfo: NodeInfo) -> Reader { + if let match = children.first(where: { nodeInfo.name == $0.nodeInfo.name }) { + return match + } else { + // The queried node doesn't exist. Return one that has nil content. + return Reader(nodeInfo: nodeInfo, parent: self) + } + } + + func readIfPresent() throws -> String? { + switch jsonNode { + case .string(let string): return string + default: return nil + } + } + + func readIfPresent() throws -> Int8? { + switch jsonNode { + case .number(let number): return Int8(truncating: number) + default: return nil + } + } + + func readIfPresent() throws -> Int16? { + switch jsonNode { + case .number(let number): return Int16(truncating: number) + default: return nil + } + } + + func readIfPresent() throws -> Int? { + switch jsonNode { + case .number(let number): return number.intValue + default: return nil + } + } + + func readIfPresent() throws -> Float? { + switch jsonNode { + case .number(let number): return number.floatValue + case .string(let string): + switch string { + case "NaN": return .nan + case "Infinity": return .infinity + case "-Infinity": return -.infinity + default: return nil + } + default: return nil + } + } + + func readIfPresent() throws -> Double? { + switch jsonNode { + case .number(let number): return number.doubleValue + case .string(let string): + switch string { + case "NaN": return .nan + case "Infinity": return .infinity + case "-Infinity": return -.infinity + default: return nil + } + default: return nil + } + } + + func readIfPresent() throws -> Bool? { + switch jsonNode { + case .bool(let bool): return bool + default: return nil + } + } + + func readIfPresent() throws -> Data? { + switch jsonNode { + case .string(let string): return Data(base64Encoded: Data(string.utf8)) + default: return nil + } + } + + func readIfPresent() throws -> Document? { + guard let jsonObject = self.jsonObject else { return nil } + return try Document.make(from: jsonObject) + } + + func readTimestampIfPresent(format: SmithyTimestamps.TimestampFormat) throws -> Date? { + switch jsonNode { + case .string(let string): return TimestampFormatter(format: format).date(from: string) + case .number(let number): return TimestampFormatter(format: format).date(from: "\(number)") + default: return nil + } + } + + func readIfPresent() throws -> T? where T: RawRepresentable, T.RawValue == Int { + guard let rawValue: Int = try readIfPresent() else { return nil } + return T(rawValue: rawValue) + } + + func readIfPresent() throws -> T? where T: RawRepresentable, T.RawValue == String { + guard let rawValue: String = try readIfPresent() else { return nil } + return T(rawValue: rawValue) + } + + func readMapIfPresent( + valueReadingClosure: (Reader) throws -> Value, + keyNodeInfo: NodeInfo, + valueNodeInfo: NodeInfo, + isFlattened: Bool + ) throws -> [String: Value]? { + if jsonNode != .object { return nil } + var dict = [String: Value]() + for mapEntry in children { + do { + let value = try valueReadingClosure(mapEntry) + dict.updateValue(value, forKey: mapEntry.nodeInfo.name) + } catch ReaderError.requiredValueNotPresent { + // This catch will "tolerate" a JSON null value in a map. + // Any other unreadable value is still an error + if !(try mapEntry.readNullIfPresent() ?? false) { throw ReaderError.requiredValueNotPresent } + } + } + return dict + } + + func readListIfPresent( + memberReadingClosure: (Reader) throws -> Member, + memberNodeInfo: NodeInfo, + isFlattened: Bool + ) throws -> [Member]? { + if jsonNode != .array { return nil } + return try children.map { try memberReadingClosure($0) } + } + + func readNullIfPresent() throws -> Bool? { + guard let jsonNode else { return nil } + return jsonNode == .null + } + + // MARK: - Private methods + + private var jsonObject: Any? { + guard let jsonNode else { return nil } + switch jsonNode { + case .bool(let bool): + return NSNumber(booleanLiteral: bool) + case .number(let number): + return number + case .string(let string): + return string + case .null: + return NSNull() + case .array: + return children.compactMap { $0.jsonObject } + case .object: + return Dictionary(uniqueKeysWithValues: children.map { ($0.nodeInfo.name, $0.jsonObject) }) + } + } +} diff --git a/Sources/SmithyJSON/Writer/Writer+JSONSerialization.swift b/Sources/SmithyJSON/Writer/Writer+JSONSerialization.swift new file mode 100644 index 000000000..2a14b5077 --- /dev/null +++ b/Sources/SmithyJSON/Writer/Writer+JSONSerialization.swift @@ -0,0 +1,56 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import struct Foundation.Data +import class Foundation.JSONSerialization +import class Foundation.NSNull +import class Foundation.NSNumber + +extension Writer { + + public func data() throws -> Data { + trimTree() + return try JSONSerialization.data(withJSONObject: jsonObject(), options: [.fragmentsAllowed]) + } + + private func trimTree() { + guard jsonNode != nil else { + parent?.children.removeAll { $0 === self } + parent = nil + return + } + children.forEach { $0.trimTree() } + } + + private func jsonObject() -> Any { + switch jsonNode { + case .bool(let bool): + return bool + case .number(let number): + guard !number.doubleValue.isNaN else { return "NaN" } + switch number.doubleValue { + case .infinity: + return "Infinity" + case -.infinity: + return "-Infinity" + default: + return number + } + case .string(let string): + return string + case .null: + return NSNull() + case .array: + return children.compactMap { $0.jsonObject() } + case .object: + return Dictionary(uniqueKeysWithValues: children.map { ($0.nodeInfo.name, $0.jsonObject()) }) + case nil: + // This case will never happen since tree was trimmed of nils before this method is called + return NSNull() + } + } +} diff --git a/Sources/SmithyJSON/Writer/Writer.swift b/Sources/SmithyJSON/Writer/Writer.swift new file mode 100644 index 000000000..6552af757 --- /dev/null +++ b/Sources/SmithyJSON/Writer/Writer.swift @@ -0,0 +1,168 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import protocol SmithyReadWrite.SmithyWriter +import enum SmithyReadWrite.Document +import enum SmithyTimestamps.TimestampFormat +import struct SmithyTimestamps.TimestampFormatter +import struct Foundation.Data +import struct Foundation.Date +import class Foundation.NSNumber + +public final class Writer: SmithyWriter { + public typealias NodeInfo = SmithyJSON.NodeInfo + + let nodeInfo: NodeInfo + var jsonNode: JSONNode? + var children: [Writer] = [] + weak var parent: Writer? + + public init(nodeInfo: NodeInfo) { + self.nodeInfo = nodeInfo + } + + public required init(nodeInfo: NodeInfo, parent: Writer? = nil) { + self.nodeInfo = nodeInfo + self.parent = parent + } +} + +public extension Writer { + + subscript(nodeInfo: NodeInfo) -> Writer { + self.jsonNode = .object + if let child = children.first(where: { $0.nodeInfo == nodeInfo }) { + return child + } else { + let newChild = Writer(nodeInfo: nodeInfo, parent: self) + children.append(newChild) + return newChild + } + } + + func write(_ value: Bool?) throws { + guard let value else { return } + self.jsonNode = .bool(value) + } + + func write(_ value: String?) throws { + guard let value else { return } + self.jsonNode = .string(value) + } + + func write(_ value: Double?) throws { + guard let value else { return } + self.jsonNode = .number(NSNumber(value: value)) + } + + func write(_ value: Float?) throws { + guard let value else { return } + self.jsonNode = .number(NSNumber(value: value)) + } + + func write(_ value: Int?) throws { + guard let value else { return } + self.jsonNode = .number(NSNumber(value: value)) + } + + func write(_ value: Int8?) throws { + guard let value else { return } + self.jsonNode = .number(NSNumber(value: value)) + } + + func write(_ value: Int16?) throws { + guard let value else { return } + self.jsonNode = .number(NSNumber(value: value)) + } + + func write(_ value: UInt8?) throws { + guard let value else { return } + self.jsonNode = .number(NSNumber(value: value)) + } + + func write(_ value: Data?) throws { + try write(value?.base64EncodedString()) + } + + func write(_ value: Document?) throws { + guard let value else { return } + switch value { + case .object(let object): + try object.forEach { try self[.init($0.key)].write($0.value) } + self.jsonNode = .object + case .array(let array): + try array.enumerated().forEach { try self[.init("\($0.offset)")].write($0.element) } + self.jsonNode = .array + case .boolean(let bool): + try write(bool) + case .number(let number): + try write(number) + case .string(let string): + try write(string) + case .null: + self.jsonNode = .null + } + } + + func writeTimestamp(_ value: Date?, format: SmithyTimestamps.TimestampFormat) throws { + guard let value else { return } + switch format { + case .epochSeconds: + self.jsonNode = .number(NSNumber(value: value.timeIntervalSince1970)) + case .dateTime, .httpDate: + self.jsonNode = .string(TimestampFormatter(format: format).string(from: value)) + } + } + + func write(_ value: T?) throws where T: RawRepresentable, T.RawValue == Int { + try write(value?.rawValue) + } + + func write(_ value: T?) throws where T: RawRepresentable, T.RawValue == String { + try write(value?.rawValue) + } + + func writeMap( + _ value: [String: T]?, + valueWritingClosure: (T, Writer) throws -> Void, + keyNodeInfo: NodeInfo, + valueNodeInfo: NodeInfo, + isFlattened: Bool + ) throws { + guard let value else { return } + self.jsonNode = .object + for (key, value) in value { + try valueWritingClosure(value, self[.init(key)]) + } + } + + func writeList( + _ value: [T]?, + memberWritingClosure: (T, Writer) throws -> Void, + memberNodeInfo: NodeInfo, + isFlattened: Bool + ) throws { + guard let value else { return } + self.jsonNode = .array + for member in value { + let element = Writer(nodeInfo: "") + addChild(element) + try memberWritingClosure(member, element) + } + } + + func writeNull() throws { + jsonNode = .null + } + + // MARK: - Private methods + + private func addChild(_ child: Writer) { + children.append(child) + child.parent = self + } +} diff --git a/Sources/SmithyReadWrite/DocumentWritingClosure.swift b/Sources/SmithyReadWrite/DataProviding.swift similarity index 60% rename from Sources/SmithyReadWrite/DocumentWritingClosure.swift rename to Sources/SmithyReadWrite/DataProviding.swift index d5645afc1..b43fe0ba9 100644 --- a/Sources/SmithyReadWrite/DocumentWritingClosure.swift +++ b/Sources/SmithyReadWrite/DataProviding.swift @@ -7,4 +7,7 @@ import struct Foundation.Data -public typealias DocumentWritingClosure = (T, WritingClosure) throws -> Data +public protocol WireDataProviding: AnyObject { + + func data() async throws -> Data +} diff --git a/Sources/ClientRuntime/PrimitiveTypeExtensions/Document.swift b/Sources/SmithyReadWrite/Document.swift similarity index 51% rename from Sources/ClientRuntime/PrimitiveTypeExtensions/Document.swift rename to Sources/SmithyReadWrite/Document.swift index a41b79619..57672eac9 100644 --- a/Sources/ClientRuntime/PrimitiveTypeExtensions/Document.swift +++ b/Sources/SmithyReadWrite/Document.swift @@ -1,7 +1,16 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import struct Foundation.Data +import class Foundation.JSONSerialization +import class Foundation.NSNull +import class Foundation.NSNumber +import func CoreFoundation.CFGetTypeID +import func CoreFoundation.CFBooleanGetTypeID public enum Document { case array([Document]) @@ -11,52 +20,21 @@ public enum Document { case string(String) case null } -extension Document: Codable { - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - if let value = try? container.decode([String: Document].self) { - self = .object(value) - } else if let value = try? container.decode([Document].self) { - self = .array(value) - } else if let value = try? container.decode(Double.self) { - self = .number(value) - } else if let value = try? container.decode(Bool.self) { - self = .boolean(value) - } else if let value = try? container.decode(String.self) { - self = .string(value) - } else { - self = .null - } - } - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - switch self { - case .array(let value): - try container.encode(value) - case .boolean(let value): - try container.encode(value) - case .number(let value): - try container.encode(value) - case .object(let value): - try container.encode(value) - case .string(let value): - try container.encode(value) - case .null: - try container.encodeNil() - } - } -} + extension Document: Equatable { } + extension Document: ExpressibleByArrayLiteral { public init(arrayLiteral elements: Document...) { self = .array(elements) } } + extension Document: ExpressibleByBooleanLiteral { public init(booleanLiteral value: Bool) { self = .boolean(value) } } + extension Document: ExpressibleByDictionaryLiteral { public init(dictionaryLiteral elements: (String, Document)...) { let dictionary = elements.reduce([String: Document]()) { acc, curr in @@ -67,34 +45,41 @@ extension Document: ExpressibleByDictionaryLiteral { self = .object(dictionary) } } + extension Document: ExpressibleByFloatLiteral { public init(floatLiteral value: Double) { self = .number(value) } } + extension Document: ExpressibleByIntegerLiteral { public init(integerLiteral value: Int) { self = .number(Double(value)) } } + extension Document: ExpressibleByNilLiteral { public init(nilLiteral: ()) { self = .null } } + extension Document: ExpressibleByStringLiteral { public init(stringLiteral value: String) { self = .string(value) } } + // extension to use subscribts to get the values from objects/arrays as normal public extension Document { + subscript(_ key: String) -> Document? { guard case .object(let object) = self else { return nil } return object[key] } + subscript(_ key: Int) -> Document? { switch self { case .array(let array): @@ -106,3 +91,50 @@ public extension Document { } } } + +extension Document { + + private var jsonObject: Any { + switch self { + case .array(let array): + return array.map { $0.jsonObject } + case .boolean(let bool): + return bool + case .number(let double): + return double + case .object(let object): + return object.mapValues { $0.jsonObject } + case .string(let string): + return string + case .null: + return NSNull() + } + } + + public static func make(from jsonObject: Any) throws -> Document { + if let object = jsonObject as? [String: Any] { + return .object(try object.mapValues { try Document.make(from: $0) }) + } else if let array = jsonObject as? [Any] { + return .array(try array.map { try Document.make(from: $0) }) + } else if let nsNumber = jsonObject as? NSNumber, CFGetTypeID(nsNumber) == CFBooleanGetTypeID() { + return .boolean(nsNumber.boolValue) + } else if let nsNumber = jsonObject as? NSNumber { + return .number(nsNumber.doubleValue) + } else if let string = jsonObject as? String { + return .string(string) + } else if jsonObject is NSNull { + return .null + } else { + throw SmithyDocumentError.invalidJSONData + } + } + + public static func make(from data: Data) throws -> Document { + let jsonObject = try JSONSerialization.jsonObject(with: data, options: [.fragmentsAllowed]) + return try Document.make(from: jsonObject) + } +} + +enum SmithyDocumentError: Error { + case invalidJSONData +} diff --git a/Sources/SmithyReadWrite/DocumentReadingClosure.swift b/Sources/SmithyReadWrite/DocumentReadingClosure.swift deleted file mode 100644 index 8b36159f3..000000000 --- a/Sources/SmithyReadWrite/DocumentReadingClosure.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -import struct Foundation.Data - -public typealias DocumentReadingClosure = (Data, ReadingClosure) throws -> T - -public enum DocumentError: Error { - case requiredValueNotPresent -} diff --git a/Sources/SmithyReadWrite/ReadingClosure.swift b/Sources/SmithyReadWrite/ReadingClosure.swift index f05506a42..488619488 100644 --- a/Sources/SmithyReadWrite/ReadingClosure.swift +++ b/Sources/SmithyReadWrite/ReadingClosure.swift @@ -5,4 +5,191 @@ // SPDX-License-Identifier: Apache-2.0 // -public typealias ReadingClosure = (Reader) throws -> T? +import struct Foundation.Data +import struct Foundation.Date +import enum SmithyTimestamps.TimestampFormat + +public typealias ReadingClosure = (Reader) throws -> T + +public func mapReadingClosure( + valueReadingClosure: @escaping ReadingClosure, + keyNodeInfo: Reader.NodeInfo, + valueNodeInfo: Reader.NodeInfo, + isFlattened: Bool +) -> ReadingClosure<[String: T], Reader> { + return { reader in + try reader.readMap( + valueReadingClosure: valueReadingClosure, + keyNodeInfo: keyNodeInfo, + valueNodeInfo: valueNodeInfo, + isFlattened: isFlattened + ) + } +} + +public func mapReadingClosure( + valueReadingClosure: @escaping ReadingClosure, + keyNodeInfo: Reader.NodeInfo, + valueNodeInfo: Reader.NodeInfo, + isFlattened: Bool +) -> ReadingClosure<[String: T]?, Reader> { + return { reader in + try reader.readMapIfPresent( + valueReadingClosure: valueReadingClosure, + keyNodeInfo: keyNodeInfo, + valueNodeInfo: valueNodeInfo, + isFlattened: isFlattened + ) + } +} + +public func listReadingClosure( + memberReadingClosure: @escaping ReadingClosure, + memberNodeInfo: Reader.NodeInfo, + isFlattened: Bool +) -> ReadingClosure<[T], Reader> { + return { reader in + try reader.readList( + memberReadingClosure: memberReadingClosure, + memberNodeInfo: memberNodeInfo, + isFlattened: isFlattened + ) + } +} + +public func listReadingClosure( + memberReadingClosure: @escaping ReadingClosure, + memberNodeInfo: Reader.NodeInfo, + isFlattened: Bool +) -> ReadingClosure<[T]?, Reader> { + return { reader in + try reader.readListIfPresent( + memberReadingClosure: memberReadingClosure, + memberNodeInfo: memberNodeInfo, + isFlattened: isFlattened + ) + } +} + +public func timestampReadingClosure(format: TimestampFormat) -> ReadingClosure { + return { reader in + try reader.readTimestamp(format: format) + } +} + +public func timestampReadingClosure(format: TimestampFormat) -> ReadingClosure { + return { reader in + try reader.readTimestampIfPresent(format: format) + } +} + +public extension String { + + static func read(from reader: Reader) throws -> String { + try reader.read() + } + + static func read(from reader: Reader) throws -> String? { + try reader.readIfPresent() + } +} + +public extension RawRepresentable where RawValue == Int { + + static func read(from reader: Reader) throws -> Self { + try reader.read() + } + + static func read(from reader: Reader) throws -> Self? { + try reader.readIfPresent() + } +} + +public extension RawRepresentable where RawValue == String { + + static func read(from reader: Reader) throws -> Self { + try reader.read() + } + + static func read(from reader: Reader) throws -> Self? { + try reader.readIfPresent() + } +} + +public extension Bool { + + static func read(from reader: Reader) throws -> Bool { + try reader.read() + } + + static func read(from reader: Reader) throws -> Bool? { + try reader.readIfPresent() + } +} + +public extension Int { + + static func read(from reader: Reader) throws -> Int { + try reader.read() + } + + static func read(from reader: Reader) throws -> Int? { + try reader.readIfPresent() + } +} + +public extension Float { + + static func read(from reader: Reader) throws -> Float { + try reader.read() + } + + static func read(from reader: Reader) throws -> Float? { + try reader.readIfPresent() + } +} + +public extension Double { + + static func read(from reader: Reader) throws -> Double { + try reader.read() + } + + static func read(from reader: Reader) throws -> Double? { + try reader.readIfPresent() + } +} + +public extension Data { + + static func read(from reader: Reader) throws -> Data { + try reader.read() + } + + static func read(from reader: Reader) throws -> Data? { + try reader.readIfPresent() + } +} + +public extension Document { + + static func read(from reader: Reader) throws -> Document { + try reader.read() + } + + static func read(from reader: Reader) throws -> Document? { + try reader.readIfPresent() + } +} + +public func optionalFormOf( + readingClosure: @escaping ReadingClosure +) -> ReadingClosure { + return { reader in + do { + return try readingClosure(reader) + } catch ReaderError.requiredValueNotPresent { + return nil + } + } +} diff --git a/Sources/SmithyReadWrite/SmithyReader.swift b/Sources/SmithyReadWrite/SmithyReader.swift new file mode 100644 index 000000000..3fdd960d1 --- /dev/null +++ b/Sources/SmithyReadWrite/SmithyReader.swift @@ -0,0 +1,209 @@ +// +// 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 SmithyTimestamps.TimestampFormat + +public protocol SmithyReader: AnyObject { + associatedtype NodeInfo + + static func from(data: Data) throws -> Self + static func readFrom(_ data: Data, with readingClosure: ReadingClosure) throws -> T + + var hasContent: Bool { get } + subscript(_ nodeInfo: NodeInfo) -> Self { get } + func readIfPresent() throws -> String? + func readIfPresent() throws -> Int8? + func readIfPresent() throws -> Int16? + func readIfPresent() throws -> Int? + func readIfPresent() throws -> Float? + func readIfPresent() throws -> Double? + func readIfPresent() throws -> Bool? + func readIfPresent() throws -> Data? + func readIfPresent() throws -> Document? + func readIfPresent() throws -> T? where T.RawValue == Int + func readIfPresent() throws -> T? where T.RawValue == String + func readTimestampIfPresent(format: TimestampFormat) throws -> Date? + func readMapIfPresent( + valueReadingClosure: ReadingClosure, + keyNodeInfo: NodeInfo, + valueNodeInfo: NodeInfo, + isFlattened: Bool + ) throws -> [String: Value]? + func readListIfPresent( + memberReadingClosure: ReadingClosure, + memberNodeInfo: NodeInfo, + isFlattened: Bool + ) throws -> [Member]? + + /// Attempts to read a `null` value from the source document. + /// - Returns: `true` if the value read is null, `false` if a value is present but it is not null, `nil` if no value is present. + func readNullIfPresent() throws -> Bool? +} + +public extension SmithyReader { + + static func readFrom(_ data: Data, with readingClosure: ReadingClosure) throws -> T { + let reader = try Self.from(data: data) + return try readingClosure(reader) + } + + func read(with readingClosure: ReadingClosure) throws -> T { + try readingClosure(self) + } + + func readIfPresent(with readingClosure: ReadingClosure) throws -> T? { + do { + return try readingClosure(self) + } catch ReaderError.requiredValueNotPresent { + return nil + } + } + + func read() throws -> String { + if let value: String = try readIfPresent() { + return value + } else { + throw ReaderError.requiredValueNotPresent + } + } + + func read() throws -> Int8 { + if let value: Int8 = try readIfPresent() { + return value + } else { + throw ReaderError.requiredValueNotPresent + } + } + + func read() throws -> Int16 { + if let value: Int16 = try readIfPresent() { + return value + } else { + throw ReaderError.requiredValueNotPresent + } + } + + func read() throws -> Int { + if let value: Int = try readIfPresent() { + return value + } else { + throw ReaderError.requiredValueNotPresent + } + } + + func read() throws -> Float { + if let value: Float = try readIfPresent() { + return value + } else { + throw ReaderError.requiredValueNotPresent + } + } + + func read() throws -> Double { + if let value: Double = try readIfPresent() { + return value + } else { + throw ReaderError.requiredValueNotPresent + } + } + + func read() throws -> Bool { + if let value: Bool = try readIfPresent() { + return value + } else { + throw ReaderError.requiredValueNotPresent + } + } + + func read() throws -> Data { + if let value: Data = try readIfPresent() { + return value + } else { + throw ReaderError.requiredValueNotPresent + } + } + + func read() throws -> Document { + if let value: Document = try readIfPresent() { + return value + } else { + throw ReaderError.requiredValueNotPresent + } + } + + func readTimestamp(format: TimestampFormat) throws -> Date { + if let value: Date = try readTimestampIfPresent(format: format) { + return value + } else { + throw ReaderError.requiredValueNotPresent + } + } + + func read() throws -> T where T.RawValue == Int { + if let value: T = try readIfPresent() { + return value + } else { + throw ReaderError.requiredValueNotPresent + } + } + + func read() throws -> T where T.RawValue == String { + if let value: T = try readIfPresent() { + return value + } else { + throw ReaderError.requiredValueNotPresent + } + } + + func readMap( + valueReadingClosure: ReadingClosure, + keyNodeInfo: NodeInfo, + valueNodeInfo: NodeInfo, + isFlattened: Bool + ) throws -> [String: T] { + if let value: [String: T] = try readMapIfPresent( + valueReadingClosure: valueReadingClosure, + keyNodeInfo: keyNodeInfo, + valueNodeInfo: valueNodeInfo, + isFlattened: isFlattened + ) { + return value + } else { + throw ReaderError.requiredValueNotPresent + } + } + + func readList( + memberReadingClosure: ReadingClosure, + memberNodeInfo: NodeInfo, + isFlattened: Bool + ) throws -> [T] { + if let value: [T] = try readListIfPresent( + memberReadingClosure: memberReadingClosure, + memberNodeInfo: memberNodeInfo, + isFlattened: isFlattened + ) { + return value + } else { + throw ReaderError.requiredValueNotPresent + } + } + + func readNull() throws -> Bool { + if let isNull = try readNullIfPresent() { + return isNull + } else { + throw ReaderError.requiredValueNotPresent + } + } +} + +public enum ReaderError: Error { + case requiredValueNotPresent +} diff --git a/Sources/SmithyReadWrite/SmithyWriter.swift b/Sources/SmithyReadWrite/SmithyWriter.swift new file mode 100644 index 000000000..21a21a45c --- /dev/null +++ b/Sources/SmithyReadWrite/SmithyWriter.swift @@ -0,0 +1,63 @@ +// +// 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 SmithyTimestamps.TimestampFormat + +public protocol SmithyWriter: AnyObject { + associatedtype NodeInfo + + init(nodeInfo: NodeInfo) + func data() throws -> Data + + subscript(_ nodeInfo: NodeInfo) -> Self { get } + func write(_ value: Bool?) throws + func write(_ value: String?) throws + func write(_ value: Double?) throws + func write(_ value: Float?) throws + func write(_ value: Int?) throws + func write(_ value: Int8?) throws + func write(_ value: Int16?) throws + func write(_ value: UInt8?) throws + func write(_ value: Data?) throws + func write(_ value: Document?) throws + func writeTimestamp(_ value: Date?, format: TimestampFormat) throws + func write(_ value: T?) throws where T.RawValue == Int + func write(_ value: T?) throws where T.RawValue == String + func writeMap( + _ value: [String: T]?, + valueWritingClosure: WritingClosure, + keyNodeInfo: NodeInfo, + valueNodeInfo: NodeInfo, + isFlattened: Bool + ) throws + func writeList( + _ value: [T]?, + memberWritingClosure: WritingClosure, + memberNodeInfo: NodeInfo, + isFlattened: Bool + ) throws + func writeNull() throws +} + +public extension SmithyWriter { + + static func write( + _ value: T, + rootNodeInfo: NodeInfo, + with writingClosure: WritingClosure + ) throws -> Data? { + let writer = Self(nodeInfo: rootNodeInfo) + try writer.write(value, with: writingClosure) + return try writer.data() + } + + func write(_ value: T, with writingClosure: WritingClosure) throws { + return try writingClosure(value, self) + } +} diff --git a/Sources/SmithyReadWrite/WireResponseErrorClosure.swift b/Sources/SmithyReadWrite/WireResponseErrorClosure.swift new file mode 100644 index 000000000..9fa09697d --- /dev/null +++ b/Sources/SmithyReadWrite/WireResponseErrorClosure.swift @@ -0,0 +1,9 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +/// Defines a closure that can be used to convert a wire response to a Swift `Error`. +public typealias WireResponseErrorClosure = (WireResponse) async throws -> Error diff --git a/Sources/SmithyReadWrite/WireResponseOutputClosure.swift b/Sources/SmithyReadWrite/WireResponseOutputClosure.swift new file mode 100644 index 000000000..5bf7fc67d --- /dev/null +++ b/Sources/SmithyReadWrite/WireResponseOutputClosure.swift @@ -0,0 +1,10 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +/// Defines a closure that can be used to convert a wire response to an output value. +public typealias WireResponseOutputClosure = + (WireResponse) async throws -> OperationStackOutput diff --git a/Sources/SmithyReadWrite/WritingClosure.swift b/Sources/SmithyReadWrite/WritingClosure.swift index d0e36b5b7..b7015cd60 100644 --- a/Sources/SmithyReadWrite/WritingClosure.swift +++ b/Sources/SmithyReadWrite/WritingClosure.swift @@ -5,5 +5,135 @@ // SPDX-License-Identifier: Apache-2.0 // -public typealias WritingClosure = - (T, Writer) throws -> Void +import struct Foundation.Data +import struct Foundation.Date +import enum SmithyTimestamps.TimestampFormat + +public typealias WritingClosure = (T, Writer) throws -> Void + +public func mapWritingClosure( + valueWritingClosure: @escaping WritingClosure, + keyNodeInfo: Writer.NodeInfo, + valueNodeInfo: Writer.NodeInfo, + isFlattened: Bool +) -> WritingClosure<[String: T]?, Writer> { + return { map, writer in + try writer.writeMap( + map, + valueWritingClosure: valueWritingClosure, + keyNodeInfo: keyNodeInfo, + valueNodeInfo: valueNodeInfo, + isFlattened: isFlattened + ) + } +} + +public func listWritingClosure( + memberWritingClosure: @escaping WritingClosure, + memberNodeInfo: Writer.NodeInfo, + isFlattened: Bool +) -> WritingClosure<[T]?, Writer> { + return { array, writer in + try writer.writeList( + array, + memberWritingClosure: memberWritingClosure, + memberNodeInfo: memberNodeInfo, + isFlattened: isFlattened + ) + } +} + +public func timestampWritingClosure(format: TimestampFormat) -> WritingClosure { + return { date, writer in + try writer.writeTimestamp(date, format: format) + } +} + +public extension String { + + static func write(value: String?, to writer: Writer) throws { + try writer.write(value) + } +} + +public extension RawRepresentable where RawValue == Int { + + static func write(value: Self?, to writer: Writer) throws { + try writer.write(value?.rawValue) + } +} + +public extension RawRepresentable where RawValue == String { + + static func write(value: Self?, to writer: Writer) throws { + try writer.write(value?.rawValue) + } +} + +public extension Bool { + + static func write(value: Bool?, to writer: Writer) throws { + try writer.write(value) + } +} + +public extension Int { + + static func write(value: Int?, to writer: Writer) throws { + try writer.write(value) + } +} + +public extension Int8 { + + static func write(value: Int8?, to writer: Writer) throws { + try writer.write(value) + } +} + +public extension Int16 { + + static func write(value: Int16?, to writer: Writer) throws { + try writer.write(value) + } +} + +public extension Double { + + static func write(value: Double?, to writer: Writer) throws { + try writer.write(value) + } +} + +public extension Float { + + static func write(value: Float?, to writer: Writer) throws { + try writer.write(value) + } +} + +public extension Data { + + static func write(value: Data?, to writer: Writer) throws { + try writer.write(value) + } +} + +public extension Document { + + static func write(value: Document?, to writer: Writer) throws { + try writer.write(value) + } +} + +public func sparseFormOf( + writingClosure: @escaping WritingClosure +) -> WritingClosure { + return { value, writer in + if let value { + try writingClosure(value, writer) + } else { + try writer.writeNull() + } + } +} diff --git a/Sources/SmithyTestUtil/Errors/JSONError.swift b/Sources/SmithyTestUtil/Errors/JSONError.swift deleted file mode 100644 index 90f045d89..000000000 --- a/Sources/SmithyTestUtil/Errors/JSONError.swift +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import Foundation -import ClientRuntime - -/// A general error structure for protocols with JSON response -public struct JSONError { - public let errorMessage: String? - public let errorType: String? - - public init(httpResponse: HttpResponse) async throws { - var message: String? - var errorType: String? - if let data = try await httpResponse.body.readData() { - let output: JSONErrorPayload = try JSONDecoder().decode(responseBody: data) - message = output.message - errorType = output.errorType - } - self.errorMessage = message - self.errorType = errorType - } -} diff --git a/Sources/SmithyTestUtil/Errors/JSONErrorPayload.swift b/Sources/SmithyTestUtil/Errors/JSONErrorPayload.swift deleted file mode 100644 index f6d4ea42e..000000000 --- a/Sources/SmithyTestUtil/Errors/JSONErrorPayload.swift +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -public struct JSONErrorPayload: Decodable { - let message: String? - let errorType: String? -} diff --git a/Sources/SmithyTestUtil/Errors/TestBaseError.swift b/Sources/SmithyTestUtil/Errors/TestBaseError.swift new file mode 100644 index 000000000..149ea398f --- /dev/null +++ b/Sources/SmithyTestUtil/Errors/TestBaseError.swift @@ -0,0 +1,33 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import protocol ClientRuntime.BaseError +import enum ClientRuntime.BaseErrorDecodeError +import class ClientRuntime.HttpResponse +import class SmithyJSON.Reader + +public struct TestBaseError: BaseError { + public let code: String + public let message: String? + public let requestID: String? + public var errorBodyReader: Reader { responseReader } + + public let httpResponse: HttpResponse + private let responseReader: Reader + + public init(httpResponse: HttpResponse, responseReader: Reader, noErrorWrapping: Bool) throws { + let code: String? = try responseReader["errorType"].readIfPresent() + let message: String? = try responseReader["message"].readIfPresent() + let requestID: String? = try responseReader["RequestId"].readIfPresent() + guard let code else { throw BaseErrorDecodeError.missingRequiredData } + self.code = code + self.message = message + self.requestID = requestID + self.httpResponse = httpResponse + self.responseReader = responseReader + } +} diff --git a/Sources/SmithyTestUtil/JSONComparator.swift b/Sources/SmithyTestUtil/JSONComparator.swift index 029540616..6eb4349ad 100644 --- a/Sources/SmithyTestUtil/JSONComparator.swift +++ b/Sources/SmithyTestUtil/JSONComparator.swift @@ -14,8 +14,8 @@ public struct JSONComparator { /// - dataB: The second data object to compare to the first data object. /// - Returns: Returns true if the JSON documents, for the corresponding data objects, are equal. public static func jsonData(_ dataA: Data, isEqualTo dataB: Data) throws -> Bool { - let jsonDictA = try JSONSerialization.jsonObject(with: dataA) - let jsonDictB = try JSONSerialization.jsonObject(with: dataB) + let jsonDictA = try JSONSerialization.jsonObject(with: dataA, options: [.fragmentsAllowed]) + let jsonDictB = try JSONSerialization.jsonObject(with: dataB, options: [.fragmentsAllowed]) return anyValuesAreEqual(jsonDictA, jsonDictB) } } diff --git a/Sources/SmithyTestUtil/RequestTestUtil/HttpRequestTestBase.swift b/Sources/SmithyTestUtil/RequestTestUtil/HttpRequestTestBase.swift index 6828cf7fb..32fb6ed7e 100644 --- a/Sources/SmithyTestUtil/RequestTestUtil/HttpRequestTestBase.swift +++ b/Sources/SmithyTestUtil/RequestTestUtil/HttpRequestTestBase.swift @@ -252,13 +252,18 @@ open class HttpRequestTestBase: XCTestCase { private func compareData(contentType: HTTPBodyContentType, _ expected: Data?, _ actual: Data?, file: StaticString, line: UInt) { if let expected, let actual { + let message = { + let expectedStr = String(data: expected, encoding: .utf8) ?? "" + let actualStr = String(data: actual, encoding: .utf8) ?? "" + return "\nActual: \(actualStr)\nExpected: \(expectedStr)" + } switch contentType { case .xml: - XCTAssertXMLDataEqual(actual, expected, file: file, line: line) + XCTAssertXMLDataEqual(actual, expected, message(), file: file, line: line) case .json: - XCTAssertJSONDataEqual(actual, expected, file: file, line: line) + XCTAssertJSONDataEqual(actual, expected, message(), file: file, line: line) case .formURL: - XCTAssertFormURLDataEqual(actual, expected, file: file, line: line) + XCTAssertFormURLDataEqual(actual, expected, message(), file: file, line: line) } } else if expected != nil && actual == nil { XCTFail("actual data in ByteStream is nil but expected is not", file: file, line: line) diff --git a/Sources/SmithyTestUtil/RequestTestUtil/MockDeserializeMiddleware.swift b/Sources/SmithyTestUtil/RequestTestUtil/MockDeserializeMiddleware.swift index 9f86e8bbc..d0ef833a2 100644 --- a/Sources/SmithyTestUtil/RequestTestUtil/MockDeserializeMiddleware.swift +++ b/Sources/SmithyTestUtil/RequestTestUtil/MockDeserializeMiddleware.swift @@ -5,6 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // +import SmithyReadWrite import ClientRuntime public struct MockDeserializeMiddleware: Middleware { @@ -13,10 +14,10 @@ public struct MockDeserializeMiddleware: Middleware { (Context, SdkHttpRequest) async throws -> OperationOutput? public var id: String - let responseClosure: HTTPResponseOutputClosure + let responseClosure: WireResponseOutputClosure let callback: MockDeserializeMiddlewareCallback? - public init(id: String, responseClosure: @escaping HTTPResponseOutputClosure, callback: MockDeserializeMiddlewareCallback? = nil) { + public init(id: String, responseClosure: @escaping WireResponseOutputClosure, callback: MockDeserializeMiddlewareCallback? = nil) { self.id = id self.responseClosure = responseClosure self.callback = callback diff --git a/Sources/SmithyTestUtil/RequestTestUtil/MockMiddlewareError.swift b/Sources/SmithyTestUtil/RequestTestUtil/MockMiddlewareError.swift index 6f2231932..c81b4363c 100644 --- a/Sources/SmithyTestUtil/RequestTestUtil/MockMiddlewareError.swift +++ b/Sources/SmithyTestUtil/RequestTestUtil/MockMiddlewareError.swift @@ -9,10 +9,9 @@ import ClientRuntime public enum MockMiddlewareError: Error { case unknown(Error) -} -extension MockMiddlewareError: HttpResponseErrorBinding { - public static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder?) async throws -> Error { + public static func responseErrorClosure(_ httpResponse: HttpResponse) -> Error { return UnknownServiceError(typeName: "MockMiddlewareError", message: httpResponse.debugDescription) } } + diff --git a/Sources/SmithyTestUtil/RequestTestUtil/MockStreamInput.swift b/Sources/SmithyTestUtil/RequestTestUtil/MockStreamInput.swift index 3675e8357..d26df6cf3 100644 --- a/Sources/SmithyTestUtil/RequestTestUtil/MockStreamInput.swift +++ b/Sources/SmithyTestUtil/RequestTestUtil/MockStreamInput.swift @@ -6,7 +6,7 @@ // import ClientRuntime -public struct MockStreamInput: Encodable { +public struct MockStreamInput { let body: ByteStream public init(body: ByteStream) { diff --git a/Sources/SmithyTimestamps/TimestampSerdeUtils.swift b/Sources/SmithyTimestamps/TimestampSerdeUtils.swift index aefa6c0b8..a749d2755 100644 --- a/Sources/SmithyTimestamps/TimestampSerdeUtils.swift +++ b/Sources/SmithyTimestamps/TimestampSerdeUtils.swift @@ -84,174 +84,6 @@ public struct TimestampFormatter { } } -// MARK: - Encoding Helpers - -/// A struct to encapsulate the encoding logic for Timestamps -struct TimestampEncodable: Encodable { - let date: Date - let format: TimestampFormat - - /// Encodes the date according to the format. - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - switch format { - // We want epoch seconds to be encoded as a number and not a string - case .epochSeconds: - // If the date doesn't have fractional seconds then encode as an Int - // so that it doesn't include a `.0` decimal - date.hasFractionalSeconds - ? try container.encode(date.timeIntervalSince1970) - : try container.encode(Int(date.timeIntervalSince1970)) - default: - let dateString = TimestampFormatter(format: format).string(from: date) - try container.encode(dateString) - } - } -} - -extension KeyedEncodingContainer { - /// Encodes the given date using the given format for the given key. - /// - /// - Parameters: - /// - date: The date to encode - /// - format: The timestamp format to determine how the date is formatted - /// - key: The key to associate the date with - public mutating func encodeTimestamp( - _ date: Date, - format: TimestampFormat, - forKey key: KeyedEncodingContainer.Key - ) throws { - let timestamp = TimestampEncodable(date: date, format: format) - try encode(timestamp, forKey: key) - } -} - -extension SingleValueEncodingContainer { - /// Encodes the given date using the given format for the given key. - /// - /// - Parameters: - /// - date: The date to encode - /// - format: The timestamp format to determine how the date is formatted - /// - key: The key to associate the date with - public mutating func encodeTimestamp( - _ date: Date, - format: TimestampFormat - ) throws { - let timestamp = TimestampEncodable(date: date, format: format) - try encode(timestamp) - } -} - -extension UnkeyedEncodingContainer { - /// Encodes the given date using the given format for the given key. - /// - /// - Parameters: - /// - date: The date to encode - /// - format: The timestamp format to determine how the date is formatted - /// - key: The key to associate the date with - public mutating func encodeTimestamp( - _ date: Date, - format: TimestampFormat - ) throws { - let timestamp = TimestampEncodable(date: date, format: format) - try encode(timestamp) - } -} - -// MARK: - Decoding Helpers - -extension KeyedDecodingContainer { - /// Decodes a date for the given key. It attempts to decode the date using the given format and throws an error if it fails. - /// - /// - Parameters: - /// - format: The timestamp format to use to decode the date. - /// - key: The key that the decoded date is associated with. - /// - /// - Returns: A date, if decodable using the provided format. - /// - /// - Throws: `DecodingError.dataCorrupted` if the encountered encoded value is not able to be converted to a date using the provided format. - public func decodeTimestamp( - _ format: TimestampFormat, - forKey key: KeyedDecodingContainer.Key - ) throws -> Date { - switch format { - case .epochSeconds: - let epochSeconds = try decode(Double.self, forKey: key) - return Date.init(timeIntervalSince1970: epochSeconds) - case .dateTime, .httpDate: - let stringValue = try decode(String.self, forKey: key) - return try timestampStringAsDate(stringValue, format: format, forKey: key) - } - } - - /// Decodes a date for the given key. It attempts to decode the date using the given format and throws an error if it fails. - /// - /// - Parameters: - /// - format: The timestamp format to use to decode the date. - /// - key: The key that the decoded date is associated with. - /// - /// - Returns: A date, if present for the given key and if decodable using the provided format. - /// - /// - Throws: `DecodingError.dataCorrupted` if the encountered encoded value is not able to be converted to a date using the provided format. - public func decodeTimestampIfPresent( - _ format: TimestampFormat, - forKey key: KeyedDecodingContainer.Key - ) throws -> Date? { - switch format { - case .epochSeconds: - let epochSeconds = try decodeIfPresent(Double.self, forKey: key) - return epochSeconds.map(Date.init(timeIntervalSince1970:)) - case .dateTime, .httpDate: - guard let stringValue = try decodeIfPresent(String.self, forKey: key) else { - return nil - } - return try timestampStringAsDate(stringValue, format: format, forKey: key) - } - } - - /// Returns a date for the given string of the given format. - /// Always use this function when decoding a timestamp. - /// - /// - Parameters: - /// - string: The string to convert to a date - /// - format: The timestamp format that the string is represented as. - /// - key: The coding key for the corresponding string value when it was decoded. This is used to provide better error messaging in the case where the conversion fails. - /// - /// - Returns: A date for the given string of the given format. - /// - /// - Throws: `DecodingError.dataCorrupted` if the given string is unable to be converted to a date using the given format. - public func timestampStringAsDate( - _ string: String, - format: TimestampFormat, - forKey key: KeyedDecodingContainer.Key - ) throws -> Date { - guard let date = TimestampFormatter(format: format).date(from: string) else { - throw DecodingError.timestampError( - forKey: key, - in: self, - dateAsString: string, - format: format - ) - } - return date - } -} - -extension DecodingError { - static func timestampError( - forKey key: C.Key, - in container: C, - dateAsString: String, - format: TimestampFormat - ) -> DecodingError where C: KeyedDecodingContainerProtocol { - dataCorruptedError( - forKey: key, - in: container, - debugDescription: "Unable to parse: \(dateAsString) using \(format) format" - ) - } -} - extension Date { /// Creates a date from a string using the given formatters. /// The date returned will be from the first formatter, in the given formatters list, that is able to successfully convert the date to a string. diff --git a/Sources/SmithyXML/DocumentReader.swift b/Sources/SmithyXML/DocumentReader.swift deleted file mode 100644 index 7be72293a..000000000 --- a/Sources/SmithyXML/DocumentReader.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -import struct Foundation.Data -import typealias SmithyReadWrite.ReadingClosure -import enum SmithyReadWrite.DocumentError - -public enum DocumentReader { - - static func read(_ data: Data, readingClosure: ReadingClosure) throws -> T { - let reader = try Reader.from(data: data) - if let value = try readingClosure(reader) { - return value - } else { - throw DocumentError.requiredValueNotPresent - } - } -} diff --git a/Sources/SmithyXML/DocumentWriter.swift b/Sources/SmithyXML/DocumentWriter.swift deleted file mode 100644 index c8c847b02..000000000 --- a/Sources/SmithyXML/DocumentWriter.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -import struct Foundation.Data -import typealias SmithyReadWrite.WritingClosure - -public enum DocumentWriter { - - static func write(_ value: T, rootNodeInfo: NodeInfo, writingClosure: WritingClosure) throws -> Data { - let writer = Writer(rootNodeInfo: rootNodeInfo) - try writingClosure(value, writer) - return writer.xmlString() - } -} diff --git a/Sources/SmithyXML/Writer/NodeInfo.swift b/Sources/SmithyXML/NodeInfo.swift similarity index 100% rename from Sources/SmithyXML/Writer/NodeInfo.swift rename to Sources/SmithyXML/NodeInfo.swift diff --git a/Sources/SmithyXML/Reader/Reader.swift b/Sources/SmithyXML/Reader/Reader.swift index 66ffb7b81..5b7dff712 100644 --- a/Sources/SmithyXML/Reader/Reader.swift +++ b/Sources/SmithyXML/Reader/Reader.swift @@ -5,18 +5,22 @@ // SPDX-License-Identifier: Apache-2.0 // +import protocol SmithyReadWrite.SmithyReader +import enum SmithyReadWrite.Document import typealias SmithyReadWrite.ReadingClosure import struct Foundation.Date import struct Foundation.Data import enum SmithyTimestamps.TimestampFormat import struct SmithyTimestamps.TimestampFormatter -public class Reader { - public internal(set) var content: String? +public final class Reader: SmithyReader { + public typealias ReaderNodeInfo = NodeInfo public internal(set) var children: [Reader] = [] public internal(set) weak var parent: Reader? public let nodeInfo: NodeInfo public var nodeInfoPath: [NodeInfo] { (parent?.nodeInfoPath ?? []) + [nodeInfo] } + public var hasContent: Bool { content != nil } + var content: String? // MARK: - init & deinit @@ -70,121 +74,48 @@ public class Reader { // MARK: - Reading values - public func readIfPresent(readingClosure: ReadingClosure) throws -> T? { - // This guard stops infinite recursion when decoding recursive structs or unions. - guard content != nil || !children.isEmpty else { return nil } - return try readingClosure(self) - } - - public func read(readingClosure: ReadingClosure) throws -> T { - if let value = try readingClosure(self) { - return value - } else { - throw ReaderError.requiredValueNotPresent - } - } - public func readIfPresent() throws -> String? { return content } - public func read() throws -> String { - if let value: String = try readIfPresent() { - return value - } else { - throw ReaderError.requiredValueNotPresent - } - } - public func readIfPresent() throws -> Int8? { guard let content else { return nil } return Int8(content) } - public func read() throws -> Int8 { - if let value: Int8 = try readIfPresent() { - return value - } else { - throw ReaderError.requiredValueNotPresent - } - } - public func readIfPresent() throws -> Int16? { guard let content else { return nil } return Int16(content) } - public func read() throws -> Int16 { - if let value: Int16 = try readIfPresent() { - return value - } else { - throw ReaderError.requiredValueNotPresent - } - } - public func readIfPresent() throws -> Int? { guard let content else { return nil } return Int(content) } - public func read() throws -> Int { - if let value: Int = try readIfPresent() { - return value - } else { - throw ReaderError.requiredValueNotPresent - } - } - public func readIfPresent() throws -> Float? { guard let content else { return nil } return Float(content) } - public func read() throws -> Float { - if let value: Float = try readIfPresent() { - return value - } else { - throw ReaderError.requiredValueNotPresent - } - } - public func readIfPresent() throws -> Double? { guard let content else { return nil } return Double(content) } - public func read() throws -> Double { - if let value: Double = try readIfPresent() { - return value - } else { - throw ReaderError.requiredValueNotPresent - } - } - public func readIfPresent() throws -> Bool? { guard let content else { return nil } return Bool(content) } - public func read() throws -> Bool { - if let value: Bool = try readIfPresent() { - return value - } else { - throw ReaderError.requiredValueNotPresent - } - } - public func readIfPresent() throws -> Data? { guard let content else { return nil } return Data(base64Encoded: Data(content.utf8)) } - public func read() throws -> Data { - if let value: Data = try readIfPresent() { - return value - } else { - throw ReaderError.requiredValueNotPresent - } + public func readIfPresent() throws -> Document? { + // No operation. Smithy document not supported in XML + return nil } public func readTimestampIfPresent(format: TimestampFormat) throws -> Date? { @@ -192,42 +123,18 @@ public class Reader { return TimestampFormatter(format: format).date(from: content) } - public func readTimestamp(format: TimestampFormat) throws -> Date { - if let value: Date = try readTimestampIfPresent(format: format) { - return value - } else { - throw ReaderError.requiredValueNotPresent - } - } - public func readIfPresent() throws -> T? where T.RawValue == Int { guard let rawValue: Int = try readIfPresent() else { return nil } return T(rawValue: rawValue) } - public func read() throws -> T where T.RawValue == Int { - if let value: T = try readIfPresent() { - return value - } else { - throw ReaderError.requiredValueNotPresent - } - } - public func readIfPresent() throws -> T? where T.RawValue == String { guard let rawValue: String = try readIfPresent() else { return nil } return T(rawValue: rawValue) } - public func read() throws -> T where T.RawValue == String { - if let value: T = try readIfPresent() { - return value - } else { - throw ReaderError.requiredValueNotPresent - } - } - public func readMapIfPresent( - valueReadingClosure: ReadingClosure, + valueReadingClosure: ReadingClosure, keyNodeInfo: NodeInfo, valueNodeInfo: NodeInfo, isFlattened: Bool @@ -237,40 +144,22 @@ public class Reader { let entries = (parent?.children ?? []).filter { $0.nodeInfo.name == nodeInfo.name } guard !entries.isEmpty else { return nil } for entry in entries { - guard let key = entry[keyNodeInfo].content, - let value = try valueReadingClosure(entry[valueNodeInfo]) else { continue } + guard let key = entry[keyNodeInfo].content else { continue } + let value = try valueReadingClosure(entry[valueNodeInfo]) dict[key] = value } } else { if content == nil { return nil } let entries = children.filter { $0.nodeInfo.name == "entry" } for entry in entries { - guard let key = entry[keyNodeInfo].content, - let value = try valueReadingClosure(entry[valueNodeInfo]) else { continue } + guard let key = entry[keyNodeInfo].content else { continue } + let value = try valueReadingClosure(entry[valueNodeInfo]) dict[key] = value } } return dict } - public func readMap( - valueReadingClosure: ReadingClosure, - keyNodeInfo: NodeInfo, - valueNodeInfo: NodeInfo, - isFlattened: Bool - ) throws -> [String: T] { - if let value: [String: T] = try readMapIfPresent( - valueReadingClosure: valueReadingClosure, - keyNodeInfo: keyNodeInfo, - valueNodeInfo: valueNodeInfo, - isFlattened: isFlattened - ) { - return value - } else { - throw ReaderError.requiredValueNotPresent - } - } - public func readListIfPresent( memberReadingClosure: ReadingClosure, memberNodeInfo: NodeInfo, @@ -281,37 +170,22 @@ public class Reader { let members = (parent?.children ?? []).filter { $0.nodeInfo.name == nodeInfo.name } guard !members.isEmpty else { return nil } for member in members { - guard let value = try memberReadingClosure(member) else { continue } + let value = try memberReadingClosure(member) list.append(value) } } else { if content == nil { return nil } let members = children.filter { $0.nodeInfo.name == memberNodeInfo.name } for member in members { - guard let value = try memberReadingClosure(member) else { continue } + let value = try memberReadingClosure(member) list.append(value) } } return list } - public func readList( - memberReadingClosure: ReadingClosure, - memberNodeInfo: NodeInfo, - isFlattened: Bool - ) throws -> [T] { - if let value: [T] = try readListIfPresent( - memberReadingClosure: memberReadingClosure, - memberNodeInfo: memberNodeInfo, - isFlattened: isFlattened - ) { - return value - } else { - throw ReaderError.requiredValueNotPresent - } + public func readNullIfPresent() throws -> Bool? { + // Since null is not currently used in XML protocols, this method returns a default value. + return nil } } - -public enum ReaderError: Error { - case requiredValueNotPresent -} diff --git a/Sources/SmithyXML/ReadingClosures.swift b/Sources/SmithyXML/ReadingClosures.swift deleted file mode 100644 index 3f9265a03..000000000 --- a/Sources/SmithyXML/ReadingClosures.swift +++ /dev/null @@ -1,109 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -import struct Foundation.Date -import typealias SmithyReadWrite.ReadingClosure -import enum SmithyTimestamps.TimestampFormat - -public func mapReadingClosure( - valueReadingClosure: @escaping ReadingClosure, - keyNodeInfo: NodeInfo, - valueNodeInfo: NodeInfo, - isFlattened: Bool -) -> ReadingClosure<[String: T], Reader> { - return { reader in - try reader.readMapIfPresent( - valueReadingClosure: valueReadingClosure, - keyNodeInfo: keyNodeInfo, - valueNodeInfo: valueNodeInfo, - isFlattened: isFlattened - ) - } -} - -public func listReadingClosure( - memberReadingClosure: @escaping ReadingClosure, - memberNodeInfo: NodeInfo, - isFlattened: Bool -) -> ReadingClosure<[T], Reader> { - return { reader in - try reader.readListIfPresent( - memberReadingClosure: memberReadingClosure, - memberNodeInfo: memberNodeInfo, - isFlattened: isFlattened - ) - } -} - -public func timestampReadingClosure(format: TimestampFormat) -> ReadingClosure { - return { reader in - try reader.readTimestampIfPresent(format: format) - } -} - -public extension String { - - static var readingClosure: ReadingClosure { - return { reader in - try reader.readIfPresent() - } - } -} - -public extension RawRepresentable where RawValue == Int { - - static var readingClosure: ReadingClosure { - return { reader in - try reader.readIfPresent() - } - } -} - -public extension RawRepresentable where RawValue == String { - - static var readingClosure: ReadingClosure { - return { reader in - try reader.readIfPresent() - } - } -} - -public extension Bool { - - static var readingClosure: ReadingClosure { - return { reader in - try reader.readIfPresent() - } - } -} - -public extension Int { - - static var readingClosure: ReadingClosure { - return { reader in - try reader.readIfPresent() - } - } -} - -public extension Float { - - static var readingClosure: ReadingClosure { - return { reader in - try reader.readIfPresent() - } - } -} - -public extension Double { - - static var readingClosure: ReadingClosure { - return { reader in - try reader.readIfPresent() - } - } -} diff --git a/Sources/SmithyXML/Writer/Writer+libxml2.swift b/Sources/SmithyXML/Writer/Writer+libxml2.swift index 255a2e4c6..85197337f 100644 --- a/Sources/SmithyXML/Writer/Writer+libxml2.swift +++ b/Sources/SmithyXML/Writer/Writer+libxml2.swift @@ -13,7 +13,7 @@ extension Writer { /// Translates this Writer and its children into XML ready to be sent. /// - Returns: A `Data` value containing this writer's UTF-8 XML representation. - func xmlString() -> Data { + public func data() throws -> Data { // Create a libxml document let doc = xmlNewDoc(nil) @@ -50,7 +50,8 @@ extension Writer { // Expose the node name and content as C strings nodeInfo.name.utf8CString.withUnsafeBytes { unsafeName in - content.utf8CString.withUnsafeBytes { unsafeContent in + guard content != nil || !children.isEmpty || isCollection else { return nil } + return (content ?? "").utf8CString.withUnsafeBytes { unsafeContent in // libxml uses C strings with its own xmlChar data type // Recast the C strings to libxml-typed strings @@ -62,9 +63,11 @@ extension Writer { node?.pointee.type = nodeInfo.location.xmlElementType // Encode the content string, set it on the node, then free it - let encoded = xmlEncodeEntitiesReentrant(doc, content) - xmlNodeSetContent(node, encoded) - xmlFree(encoded) + if self.content != nil { + let encoded = xmlEncodeEntitiesReentrant(doc, content) + xmlNodeSetContent(node, encoded) + xmlFree(encoded) + } // Add the child node to its parent xmlAddChild(parentNode, node) diff --git a/Sources/SmithyXML/Writer/Writer.swift b/Sources/SmithyXML/Writer/Writer.swift index 82e75522d..ec1ba9af9 100644 --- a/Sources/SmithyXML/Writer/Writer.swift +++ b/Sources/SmithyXML/Writer/Writer.swift @@ -5,6 +5,8 @@ // SPDX-License-Identifier: Apache-2.0 // +import protocol SmithyReadWrite.SmithyWriter +import enum SmithyReadWrite.Document import struct Foundation.Date import struct Foundation.Data import typealias SmithyReadWrite.WritingClosure @@ -19,19 +21,20 @@ import struct SmithyTimestamps.TimestampFormatter /// This writer will write all Swift types used by Smithy models, and will also write Swift /// `Array` and `Dictionary` (optionally as flattened XML) given a writing closure for /// their enclosed data types. -public class Writer { - var content = "" +public final class Writer: SmithyWriter { + var content: String? var children: [Writer] = [] weak var parent: Writer? let nodeInfo: NodeInfo + 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 rootNodeInfo: The node info for the root XML node. - init(rootNodeInfo: NodeInfo) { - self.nodeInfo = rootNodeInfo + /// - Parameter nodeInfo: The node info for the XML node. + public init(nodeInfo: NodeInfo) { + self.nodeInfo = nodeInfo } private init(nodeInfo: NodeInfo, parent: Writer?) { @@ -50,14 +53,6 @@ public class Writer { return newChild } - /// Detaches this writer from its parent. Typically used when this writer no longer - /// belongs in the tree, either because its data is nil or its contents were flattened - /// into its parents. - public func detach() { - parent?.children.removeAll { $0 === self } - parent = nil - } - // MARK: - Writing values public func write(_ value: Bool?) throws { @@ -69,7 +64,7 @@ public class Writer { } public func write(_ value: Double?) throws { - guard let value else { detach(); return } + guard let value else { return } guard !value.isNaN else { record(string: "NaN") return @@ -85,7 +80,7 @@ public class Writer { } public func write(_ value: Float?) throws { - guard let value else { detach(); return } + guard let value else { return } guard !value.isNaN else { record(string: "NaN") return @@ -120,8 +115,12 @@ public class Writer { try write(value?.base64EncodedString()) } + public func write(_ value: Document?) throws { + // No operation. Smithy document not supported in XML + } + public func writeTimestamp(_ value: Date?, format: TimestampFormat) throws { - guard let value else { detach(); return } + guard let value else { return } record(string: TimestampFormatter(format: format).string(from: value)) } @@ -140,16 +139,17 @@ public class Writer { valueNodeInfo: NodeInfo, isFlattened: Bool ) throws { - guard let value else { detach(); return } + guard let value else { return } if isFlattened { - defer { detach() } guard let parent = self.parent else { return } + parent.isCollection = true for (key, value) in value { let entryWriter = parent[.init(nodeInfo.name)] try entryWriter[keyNodeInfo].write(key) try valueWritingClosure(value, entryWriter[valueNodeInfo]) } } else { + isCollection = true for (key, value) in value { let entryWriter = self[.init("entry")] try entryWriter[keyNodeInfo].write(key) @@ -164,10 +164,10 @@ public class Writer { memberNodeInfo: NodeInfo, isFlattened: Bool ) throws { - guard let value else { detach(); return } + guard let value else { return } if isFlattened { - defer { detach() } guard let parent = self.parent, !nodeInfo.name.isEmpty else { return } + parent.isCollection = true let flattenedMemberNodeInfo = NodeInfo( nodeInfo.name, location: memberNodeInfo.location, @@ -177,14 +177,15 @@ public class Writer { try memberWritingClosure(member, parent[flattenedMemberNodeInfo]) } } else { + isCollection = true for member in value { try memberWritingClosure(member, self[memberNodeInfo]) } } } - public func write(_ value: T, writingClosure: WritingClosure) throws { - try writingClosure(value, self) + public func writeNull() throws { + // Null not defined in XML. No operation. } // MARK: - Private methods @@ -195,7 +196,7 @@ public class Writer { } private func record(string: String?) { - guard let string else { detach(); return } + guard let string else { return } content = string } } diff --git a/Sources/SmithyXML/Writer/XMLReadWrite.swift b/Sources/SmithyXML/Writer/XMLReadWrite.swift deleted file mode 100644 index c16c36b09..000000000 --- a/Sources/SmithyXML/Writer/XMLReadWrite.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -import typealias SmithyReadWrite.DocumentWritingClosure -import typealias SmithyReadWrite.DocumentReadingClosure - -public enum XMLReadWrite { - - public static func documentWritingClosure(rootNodeInfo: NodeInfo) -> DocumentWritingClosure { - return { value, writingClosure in - try DocumentWriter.write(value, rootNodeInfo: rootNodeInfo, writingClosure: writingClosure) - } - } - - public static func documentReadingClosure(rootNodeInfo: NodeInfo) -> DocumentReadingClosure { - return { data, readingClosure in - try DocumentReader.read(data, readingClosure: readingClosure) - } - } -} diff --git a/Sources/SmithyXML/WritingClosures.swift b/Sources/SmithyXML/WritingClosures.swift deleted file mode 100644 index c8a492c37..000000000 --- a/Sources/SmithyXML/WritingClosures.swift +++ /dev/null @@ -1,83 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -import struct Foundation.Date -import typealias SmithyReadWrite.WritingClosure -import enum SmithyTimestamps.TimestampFormat - -public func mapWritingClosure( - valueWritingClosure: @escaping WritingClosure, - keyNodeInfo: NodeInfo, - valueNodeInfo: NodeInfo, - isFlattened: Bool -) -> WritingClosure<[String: T], Writer> { - return { map, writer in - try writer.writeMap( - map, - valueWritingClosure: valueWritingClosure, - keyNodeInfo: keyNodeInfo, - valueNodeInfo: valueNodeInfo, - isFlattened: isFlattened - ) - } -} - -public func listWritingClosure( - memberWritingClosure: @escaping WritingClosure, - memberNodeInfo: NodeInfo, - isFlattened: Bool -) -> WritingClosure<[T], Writer> { - return { array, writer in - try writer.writeList( - array, - memberWritingClosure: memberWritingClosure, - memberNodeInfo: memberNodeInfo, - isFlattened: isFlattened - ) - } -} - -public func timestampWritingClosure(format: TimestampFormat) -> WritingClosure { - return { date, writer in - try writer.writeTimestamp(date, format: format) - } -} - -public extension String { - - static func writingClosure(_ value: String?, to writer: Writer) throws { - try writer.write(value) - } -} - -public extension RawRepresentable where RawValue == Int { - - static func writingClosure(_ value: Self?, to writer: Writer) throws { - try writer.write(value?.rawValue) - } -} - -public extension RawRepresentable where RawValue == String { - - static func writingClosure(_ value: Self?, to writer: Writer) throws { - try writer.write(value?.rawValue) - } -} - -public extension Bool { - - static func writingClosure(_ value: Bool?, to writer: Writer) throws { - try writer.write(value) - } -} - -public extension Int { - - static func writingClosure(_ value: Int?, to writer: Writer) throws { - try writer.write(value) - } -} diff --git a/Sources/WeatherSDK/WeatherClient.swift b/Sources/WeatherSDK/WeatherClient.swift index 441b16284..9e4bba00b 100644 --- a/Sources/WeatherSDK/WeatherClient.swift +++ b/Sources/WeatherSDK/WeatherClient.swift @@ -3,25 +3,17 @@ import ClientRuntime import Foundation import Logging +import SmithyJSON +import SmithyReadWrite public class WeatherClient: Client { public static let clientName = "WeatherClient" let client: ClientRuntime.SdkHttpClient let config: WeatherClient.WeatherClientConfiguration let serviceName = "Weather" - let encoder: ClientRuntime.RequestEncoder - let decoder: ClientRuntime.ResponseDecoder public required init(config: WeatherClient.WeatherClientConfiguration) { client = ClientRuntime.SdkHttpClient(engine: config.httpClientEngine, config: config.httpClientConfiguration) - let encoder = ClientRuntime.JSONEncoder() - encoder.dateEncodingStrategy = .secondsSince1970 - encoder.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - self.encoder = encoder - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - self.decoder = decoder self.config = config } @@ -109,8 +101,6 @@ extension WeatherClient { /// - Returns: `CreateCityOutput` : [no documentation found] public func createCity(input: CreateCityInput) async throws -> CreateCityOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .put) .withServiceName(value: serviceName) .withOperation(value: "createCity") @@ -127,11 +117,11 @@ extension WeatherClient { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) operation.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/json")) - operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(documentWritingClosure: ClientRuntime.JSONReadWrite.documentWritingClosure(encoder: encoder), inputWritingClosure: JSONReadWrite.writingClosure())) + operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(rootNodeInfo: "", inputWritingClosure: CreateCityInput.write(value:to:))) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(CreateCityOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(CreateCityOutput.httpOutput(from:), CreateCityOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -150,8 +140,6 @@ extension WeatherClient { /// - `NoSuchResource` : Error encountered when no resource could be found. public func getCity(input: GetCityInput) async throws -> GetCityOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .get) .withServiceName(value: serviceName) .withOperation(value: "getCity") @@ -170,7 +158,7 @@ extension WeatherClient { operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(GetCityOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(GetCityOutput.httpOutput(from:), GetCityOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -189,8 +177,6 @@ extension WeatherClient { /// - `NoSuchResource` : Error encountered when no resource could be found. public func getCityAnnouncements(input: GetCityAnnouncementsInput) async throws -> GetCityAnnouncementsOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .get) .withServiceName(value: serviceName) .withOperation(value: "getCityAnnouncements") @@ -208,7 +194,7 @@ extension WeatherClient { operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(GetCityAnnouncementsOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(GetCityAnnouncementsOutput.httpOutput(from:), GetCityAnnouncementsOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -227,8 +213,6 @@ extension WeatherClient { /// - `NoSuchResource` : Error encountered when no resource could be found. public func getCityImage(input: GetCityImageInput) async throws -> GetCityImageOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .get) .withServiceName(value: serviceName) .withOperation(value: "getCityImage") @@ -246,7 +230,7 @@ extension WeatherClient { operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(GetCityImageOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(GetCityImageOutput.httpOutput(from:), GetCityImageOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -260,8 +244,6 @@ extension WeatherClient { /// - Returns: `GetCurrentTimeOutput` : [no documentation found] public func getCurrentTime(input: GetCurrentTimeInput) async throws -> GetCurrentTimeOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .get) .withServiceName(value: serviceName) .withOperation(value: "getCurrentTime") @@ -279,7 +261,7 @@ extension WeatherClient { operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(GetCurrentTimeOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(GetCurrentTimeOutput.httpOutput(from:), GetCurrentTimeOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -293,8 +275,6 @@ extension WeatherClient { /// - Returns: `GetForecastOutput` : [no documentation found] public func getForecast(input: GetForecastInput) async throws -> GetForecastOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .get) .withServiceName(value: serviceName) .withOperation(value: "getForecast") @@ -312,7 +292,7 @@ extension WeatherClient { operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(GetForecastOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(GetForecastOutput.httpOutput(from:), GetForecastOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -326,8 +306,6 @@ extension WeatherClient { /// - Returns: `InvokeOutput` : [no documentation found] public func invoke(input: InvokeInput) async throws -> InvokeOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .post) .withServiceName(value: serviceName) .withOperation(value: "invoke") @@ -348,7 +326,7 @@ extension WeatherClient { operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(InvokeOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(InvokeOutput.httpOutput(from:), InvokeOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -367,8 +345,6 @@ extension WeatherClient { /// - `NoSuchResource` : Error encountered when no resource could be found. public func listCities(input: ListCitiesInput) async throws -> ListCitiesOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .get) .withServiceName(value: serviceName) .withOperation(value: "listCities") @@ -387,7 +363,7 @@ extension WeatherClient { operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.QueryItemMiddleware(ListCitiesInput.queryItemProvider(_:))) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(ListCitiesOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(ListCitiesOutput.httpOutput(from:), ListCitiesOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -401,8 +377,6 @@ extension WeatherClient { /// - Returns: `OnlyFakeAuthOutput` : [no documentation found] public func onlyFakeAuth(input: OnlyFakeAuthInput) async throws -> OnlyFakeAuthOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .get) .withServiceName(value: serviceName) .withOperation(value: "onlyFakeAuth") @@ -420,7 +394,7 @@ extension WeatherClient { operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(OnlyFakeAuthOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(OnlyFakeAuthOutput.httpOutput(from:), OnlyFakeAuthOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -434,8 +408,6 @@ extension WeatherClient { /// - Returns: `OnlyFakeAuthOptionalOutput` : [no documentation found] public func onlyFakeAuthOptional(input: OnlyFakeAuthOptionalInput) async throws -> OnlyFakeAuthOptionalOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .get) .withServiceName(value: serviceName) .withOperation(value: "onlyFakeAuthOptional") @@ -453,7 +425,7 @@ extension WeatherClient { operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(OnlyFakeAuthOptionalOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(OnlyFakeAuthOptionalOutput.httpOutput(from:), OnlyFakeAuthOptionalOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -467,8 +439,6 @@ extension WeatherClient { /// - Returns: `OnlyHttpApiKeyAndBearerAuthOutput` : [no documentation found] public func onlyHttpApiKeyAndBearerAuth(input: OnlyHttpApiKeyAndBearerAuthInput) async throws -> OnlyHttpApiKeyAndBearerAuthOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .get) .withServiceName(value: serviceName) .withOperation(value: "onlyHttpApiKeyAndBearerAuth") @@ -486,7 +456,7 @@ extension WeatherClient { operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(OnlyHttpApiKeyAndBearerAuthOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(OnlyHttpApiKeyAndBearerAuthOutput.httpOutput(from:), OnlyHttpApiKeyAndBearerAuthOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -500,8 +470,6 @@ extension WeatherClient { /// - Returns: `OnlyHttpApiKeyAndBearerAuthReversedOutput` : [no documentation found] public func onlyHttpApiKeyAndBearerAuthReversed(input: OnlyHttpApiKeyAndBearerAuthReversedInput) async throws -> OnlyHttpApiKeyAndBearerAuthReversedOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .get) .withServiceName(value: serviceName) .withOperation(value: "onlyHttpApiKeyAndBearerAuthReversed") @@ -519,7 +487,7 @@ extension WeatherClient { operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(OnlyHttpApiKeyAndBearerAuthReversedOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(OnlyHttpApiKeyAndBearerAuthReversedOutput.httpOutput(from:), OnlyHttpApiKeyAndBearerAuthReversedOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -533,8 +501,6 @@ extension WeatherClient { /// - Returns: `OnlyHttpApiKeyAuthOutput` : [no documentation found] public func onlyHttpApiKeyAuth(input: OnlyHttpApiKeyAuthInput) async throws -> OnlyHttpApiKeyAuthOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .get) .withServiceName(value: serviceName) .withOperation(value: "onlyHttpApiKeyAuth") @@ -552,7 +518,7 @@ extension WeatherClient { operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(OnlyHttpApiKeyAuthOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(OnlyHttpApiKeyAuthOutput.httpOutput(from:), OnlyHttpApiKeyAuthOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -566,8 +532,6 @@ extension WeatherClient { /// - Returns: `OnlyHttpApiKeyAuthOptionalOutput` : [no documentation found] public func onlyHttpApiKeyAuthOptional(input: OnlyHttpApiKeyAuthOptionalInput) async throws -> OnlyHttpApiKeyAuthOptionalOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .get) .withServiceName(value: serviceName) .withOperation(value: "onlyHttpApiKeyAuthOptional") @@ -585,7 +549,7 @@ extension WeatherClient { operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(OnlyHttpApiKeyAuthOptionalOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(OnlyHttpApiKeyAuthOptionalOutput.httpOutput(from:), OnlyHttpApiKeyAuthOptionalOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -599,8 +563,6 @@ extension WeatherClient { /// - Returns: `OnlyHttpBearerAuthOutput` : [no documentation found] public func onlyHttpBearerAuth(input: OnlyHttpBearerAuthInput) async throws -> OnlyHttpBearerAuthOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .get) .withServiceName(value: serviceName) .withOperation(value: "onlyHttpBearerAuth") @@ -618,7 +580,7 @@ extension WeatherClient { operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(OnlyHttpBearerAuthOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(OnlyHttpBearerAuthOutput.httpOutput(from:), OnlyHttpBearerAuthOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -632,8 +594,6 @@ extension WeatherClient { /// - Returns: `OnlyHttpBearerAuthOptionalOutput` : [no documentation found] public func onlyHttpBearerAuthOptional(input: OnlyHttpBearerAuthOptionalInput) async throws -> OnlyHttpBearerAuthOptionalOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .get) .withServiceName(value: serviceName) .withOperation(value: "onlyHttpBearerAuthOptional") @@ -651,7 +611,7 @@ extension WeatherClient { operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(OnlyHttpBearerAuthOptionalOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(OnlyHttpBearerAuthOptionalOutput.httpOutput(from:), OnlyHttpBearerAuthOptionalOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -665,8 +625,6 @@ extension WeatherClient { /// - Returns: `OnlySigv4AuthOutput` : [no documentation found] public func onlySigv4Auth(input: OnlySigv4AuthInput) async throws -> OnlySigv4AuthOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .get) .withServiceName(value: serviceName) .withOperation(value: "onlySigv4Auth") @@ -684,7 +642,7 @@ extension WeatherClient { operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(OnlySigv4AuthOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(OnlySigv4AuthOutput.httpOutput(from:), OnlySigv4AuthOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -698,8 +656,6 @@ extension WeatherClient { /// - Returns: `OnlySigv4AuthOptionalOutput` : [no documentation found] public func onlySigv4AuthOptional(input: OnlySigv4AuthOptionalInput) async throws -> OnlySigv4AuthOptionalOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .get) .withServiceName(value: serviceName) .withOperation(value: "onlySigv4AuthOptional") @@ -717,7 +673,7 @@ extension WeatherClient { operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(OnlySigv4AuthOptionalOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(OnlySigv4AuthOptionalOutput.httpOutput(from:), OnlySigv4AuthOptionalOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -731,8 +687,6 @@ extension WeatherClient { /// - Returns: `SameAsServiceOutput` : [no documentation found] public func sameAsService(input: SameAsServiceInput) async throws -> SameAsServiceOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .get) .withServiceName(value: serviceName) .withOperation(value: "sameAsService") @@ -750,7 +704,7 @@ extension WeatherClient { operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(SameAsServiceOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(SameAsServiceOutput.httpOutput(from:), SameAsServiceOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result diff --git a/Sources/WeatherSDK/models/Baz+Codable.swift b/Sources/WeatherSDK/models/Baz+Codable.swift deleted file mode 100644 index 2915f8ac2..000000000 --- a/Sources/WeatherSDK/models/Baz+Codable.swift +++ /dev/null @@ -1,28 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -extension WeatherClientTypes.Baz: Swift.Codable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case bar - case baz - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let bar = self.bar { - try encodeContainer.encode(bar, forKey: .bar) - } - if let baz = self.baz { - try encodeContainer.encode(baz, forKey: .baz) - } - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let bazDecoded = try containerValues.decodeIfPresent(Swift.String.self, forKey: .baz) - baz = bazDecoded - let barDecoded = try containerValues.decodeIfPresent(Swift.String.self, forKey: .bar) - bar = barDecoded - } -} diff --git a/Sources/WeatherSDK/models/Baz+ReadWrite.swift b/Sources/WeatherSDK/models/Baz+ReadWrite.swift new file mode 100644 index 000000000..44fc12687 --- /dev/null +++ b/Sources/WeatherSDK/models/Baz+ReadWrite.swift @@ -0,0 +1,22 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite + +extension WeatherClientTypes.Baz { + + static func write(value: WeatherClientTypes.Baz?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["bar"].write(value.bar) + try writer["baz"].write(value.baz) + } + + static func read(from reader: SmithyJSON.Reader) throws -> WeatherClientTypes.Baz { + guard reader.hasContent else { throw SmithyReadWrite.ReaderError.requiredValueNotPresent } + var value = WeatherClientTypes.Baz() + value.baz = try reader["baz"].readIfPresent() + value.bar = try reader["bar"].readIfPresent() + return value + } +} diff --git a/Sources/WeatherSDK/models/Baz.swift b/Sources/WeatherSDK/models/Baz.swift index 325131cbe..45fa98372 100644 --- a/Sources/WeatherSDK/models/Baz.swift +++ b/Sources/WeatherSDK/models/Baz.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite extension WeatherClientTypes { public struct Baz { diff --git a/Sources/WeatherSDK/models/CityCoordinates+Codable.swift b/Sources/WeatherSDK/models/CityCoordinates+Codable.swift deleted file mode 100644 index bc2ab19c5..000000000 --- a/Sources/WeatherSDK/models/CityCoordinates+Codable.swift +++ /dev/null @@ -1,28 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -extension WeatherClientTypes.CityCoordinates: Swift.Codable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case latitude - case longitude - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let latitude = self.latitude { - try encodeContainer.encode(latitude, forKey: .latitude) - } - if let longitude = self.longitude { - try encodeContainer.encode(longitude, forKey: .longitude) - } - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let latitudeDecoded = try containerValues.decodeIfPresent(Swift.Float.self, forKey: .latitude) - latitude = latitudeDecoded - let longitudeDecoded = try containerValues.decodeIfPresent(Swift.Float.self, forKey: .longitude) - longitude = longitudeDecoded - } -} diff --git a/Sources/WeatherSDK/models/CityCoordinates+ReadWrite.swift b/Sources/WeatherSDK/models/CityCoordinates+ReadWrite.swift new file mode 100644 index 000000000..b8aea92ad --- /dev/null +++ b/Sources/WeatherSDK/models/CityCoordinates+ReadWrite.swift @@ -0,0 +1,22 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite + +extension WeatherClientTypes.CityCoordinates { + + static func write(value: WeatherClientTypes.CityCoordinates?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["latitude"].write(value.latitude) + try writer["longitude"].write(value.longitude) + } + + static func read(from reader: SmithyJSON.Reader) throws -> WeatherClientTypes.CityCoordinates { + guard reader.hasContent else { throw SmithyReadWrite.ReaderError.requiredValueNotPresent } + var value = WeatherClientTypes.CityCoordinates() + value.latitude = try reader["latitude"].readIfPresent() + value.longitude = try reader["longitude"].readIfPresent() + return value + } +} diff --git a/Sources/WeatherSDK/models/CityCoordinates.swift b/Sources/WeatherSDK/models/CityCoordinates.swift index e46b2a3d7..37bc14b57 100644 --- a/Sources/WeatherSDK/models/CityCoordinates.swift +++ b/Sources/WeatherSDK/models/CityCoordinates.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite extension WeatherClientTypes { public struct CityCoordinates { diff --git a/Sources/WeatherSDK/models/CitySummary+Codable.swift b/Sources/WeatherSDK/models/CitySummary+Codable.swift deleted file mode 100644 index 4f74ab717..000000000 --- a/Sources/WeatherSDK/models/CitySummary+Codable.swift +++ /dev/null @@ -1,40 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -extension WeatherClientTypes.CitySummary: Swift.Codable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case `case` = "case" - case cityId - case name - case number - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let `case` = self.`case` { - try encodeContainer.encode(`case`, forKey: .`case`) - } - if let cityId = self.cityId { - try encodeContainer.encode(cityId, forKey: .cityId) - } - if let name = self.name { - try encodeContainer.encode(name, forKey: .name) - } - if let number = self.number { - try encodeContainer.encode(number, forKey: .number) - } - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let cityIdDecoded = try containerValues.decodeIfPresent(Swift.String.self, forKey: .cityId) - cityId = cityIdDecoded - let nameDecoded = try containerValues.decodeIfPresent(Swift.String.self, forKey: .name) - name = nameDecoded - let numberDecoded = try containerValues.decodeIfPresent(Swift.String.self, forKey: .number) - number = numberDecoded - let caseDecoded = try containerValues.decodeIfPresent(Swift.String.self, forKey: .case) - `case` = caseDecoded - } -} diff --git a/Sources/WeatherSDK/models/CitySummary+ReadWrite.swift b/Sources/WeatherSDK/models/CitySummary+ReadWrite.swift new file mode 100644 index 000000000..5c7ca7240 --- /dev/null +++ b/Sources/WeatherSDK/models/CitySummary+ReadWrite.swift @@ -0,0 +1,26 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite + +extension WeatherClientTypes.CitySummary { + + static func write(value: WeatherClientTypes.CitySummary?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["case"].write(value.`case`) + try writer["cityId"].write(value.cityId) + try writer["name"].write(value.name) + try writer["number"].write(value.number) + } + + static func read(from reader: SmithyJSON.Reader) throws -> WeatherClientTypes.CitySummary { + guard reader.hasContent else { throw SmithyReadWrite.ReaderError.requiredValueNotPresent } + var value = WeatherClientTypes.CitySummary() + value.cityId = try reader["cityId"].readIfPresent() + value.name = try reader["name"].readIfPresent() + value.number = try reader["number"].readIfPresent() + value.`case` = try reader["case"].readIfPresent() + return value + } +} diff --git a/Sources/WeatherSDK/models/CitySummary.swift b/Sources/WeatherSDK/models/CitySummary.swift index 2490fb0e0..8e0b3765e 100644 --- a/Sources/WeatherSDK/models/CitySummary.swift +++ b/Sources/WeatherSDK/models/CitySummary.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite extension WeatherClientTypes { public struct CitySummary { diff --git a/Sources/WeatherSDK/models/CreateCityInput+Encodable.swift b/Sources/WeatherSDK/models/CreateCityInput+Encodable.swift deleted file mode 100644 index 1066cc373..000000000 --- a/Sources/WeatherSDK/models/CreateCityInput+Encodable.swift +++ /dev/null @@ -1,24 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -extension CreateCityInput: Swift.Encodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case city - case coordinates - case name - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let city = self.city { - try encodeContainer.encode(city, forKey: .city) - } - if let coordinates = self.coordinates { - try encodeContainer.encode(coordinates, forKey: .coordinates) - } - if let name = self.name { - try encodeContainer.encode(name, forKey: .name) - } - } -} diff --git a/Sources/WeatherSDK/models/CreateCityInput+Write.swift b/Sources/WeatherSDK/models/CreateCityInput+Write.swift new file mode 100644 index 000000000..571fbe79e --- /dev/null +++ b/Sources/WeatherSDK/models/CreateCityInput+Write.swift @@ -0,0 +1,15 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite + +extension CreateCityInput { + + static func write(value: CreateCityInput?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["city"].write(value.city, with: WeatherClientTypes.CitySummary.write(value:to:)) + try writer["coordinates"].write(value.coordinates, with: WeatherClientTypes.CityCoordinates.write(value:to:)) + try writer["name"].write(value.name) + } +} diff --git a/Sources/WeatherSDK/models/CreateCityInput.swift b/Sources/WeatherSDK/models/CreateCityInput.swift index a81370d61..1326d33b2 100644 --- a/Sources/WeatherSDK/models/CreateCityInput.swift +++ b/Sources/WeatherSDK/models/CreateCityInput.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite public struct CreateCityInput { public var city: WeatherClientTypes.CitySummary? diff --git a/Sources/WeatherSDK/models/CreateCityInputBody+Decodable.swift b/Sources/WeatherSDK/models/CreateCityInputBody+Decodable.swift deleted file mode 100644 index 4f7fecd0b..000000000 --- a/Sources/WeatherSDK/models/CreateCityInputBody+Decodable.swift +++ /dev/null @@ -1,27 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct CreateCityInputBody { - let name: Swift.String? - let coordinates: WeatherClientTypes.CityCoordinates? - let city: WeatherClientTypes.CitySummary? -} - -extension CreateCityInputBody: Swift.Decodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case city - case coordinates - case name - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let nameDecoded = try containerValues.decodeIfPresent(Swift.String.self, forKey: .name) - name = nameDecoded - let coordinatesDecoded = try containerValues.decodeIfPresent(WeatherClientTypes.CityCoordinates.self, forKey: .coordinates) - coordinates = coordinatesDecoded - let cityDecoded = try containerValues.decodeIfPresent(WeatherClientTypes.CitySummary.self, forKey: .city) - city = cityDecoded - } -} diff --git a/Sources/WeatherSDK/models/CreateCityOutput+HttpResponseBinding.swift b/Sources/WeatherSDK/models/CreateCityOutput+HttpResponseBinding.swift index c4e33d9df..dbe79cb24 100644 --- a/Sources/WeatherSDK/models/CreateCityOutput+HttpResponseBinding.swift +++ b/Sources/WeatherSDK/models/CreateCityOutput+HttpResponseBinding.swift @@ -1,15 +1,17 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyJSON +import SmithyReadWrite -extension CreateCityOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { - if let data = try await httpResponse.body.readData(), - let responseDecoder = decoder { - let output: CreateCityOutputBody = try responseDecoder.decode(responseBody: data) - self.cityId = output.cityId - } else { - self.cityId = nil - } +extension CreateCityOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> CreateCityOutput { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let reader = responseReader + var value = CreateCityOutput() + value.cityId = try reader["cityId"].readIfPresent() + return value } } diff --git a/Sources/WeatherSDK/models/CreateCityOutput.swift b/Sources/WeatherSDK/models/CreateCityOutput.swift index bd75e94e8..ea559f5b9 100644 --- a/Sources/WeatherSDK/models/CreateCityOutput.swift +++ b/Sources/WeatherSDK/models/CreateCityOutput.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite public struct CreateCityOutput { /// This member is required. diff --git a/Sources/WeatherSDK/models/CreateCityOutputBody+Decodable.swift b/Sources/WeatherSDK/models/CreateCityOutputBody+Decodable.swift deleted file mode 100644 index e90b52396..000000000 --- a/Sources/WeatherSDK/models/CreateCityOutputBody+Decodable.swift +++ /dev/null @@ -1,19 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct CreateCityOutputBody { - let cityId: Swift.String? -} - -extension CreateCityOutputBody: Swift.Decodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case cityId - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let cityIdDecoded = try containerValues.decodeIfPresent(Swift.String.self, forKey: .cityId) - cityId = cityIdDecoded - } -} diff --git a/Sources/WeatherSDK/models/CreateCityOutputError+HttpResponseBinding.swift b/Sources/WeatherSDK/models/CreateCityOutputError+HttpResponseBinding.swift deleted file mode 100644 index 9de323cb5..000000000 --- a/Sources/WeatherSDK/models/CreateCityOutputError+HttpResponseBinding.swift +++ /dev/null @@ -1,13 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime -import SmithyTestUtil - -enum CreateCityOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let defaultError = try await SmithyTestUtil.JSONError(httpResponse: httpResponse) - switch defaultError.errorType { - default: return try await ClientRuntime.UnknownHTTPServiceError.makeError(httpResponse: httpResponse, message: defaultError.errorMessage, typeName: defaultError.errorType) - } - } -} diff --git a/Sources/WeatherSDK/models/CreateCityOutputError+HttpResponseErrorBinding.swift b/Sources/WeatherSDK/models/CreateCityOutputError+HttpResponseErrorBinding.swift new file mode 100644 index 000000000..5378ad201 --- /dev/null +++ b/Sources/WeatherSDK/models/CreateCityOutputError+HttpResponseErrorBinding.swift @@ -0,0 +1,19 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite +import SmithyTestUtil + +enum CreateCityOutputError { + + static func httpError(from httpResponse: ClientRuntime.HttpResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let baseError = try SmithyTestUtil.TestBaseError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: false) + if let error = baseError.customError() { return error } + switch baseError.code { + default: return try ClientRuntime.UnknownHTTPServiceError.makeError(baseError: baseError) + } + } +} diff --git a/Sources/WeatherSDK/models/Foo+Codable.swift b/Sources/WeatherSDK/models/Foo+Codable.swift deleted file mode 100644 index c5c9af5d9..000000000 --- a/Sources/WeatherSDK/models/Foo+Codable.swift +++ /dev/null @@ -1,28 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -extension WeatherClientTypes.Foo: Swift.Codable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case bar - case baz - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let bar = self.bar { - try encodeContainer.encode(bar, forKey: .bar) - } - if let baz = self.baz { - try encodeContainer.encode(baz, forKey: .baz) - } - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let bazDecoded = try containerValues.decodeIfPresent(Swift.String.self, forKey: .baz) - baz = bazDecoded - let barDecoded = try containerValues.decodeIfPresent(Swift.String.self, forKey: .bar) - bar = barDecoded - } -} diff --git a/Sources/WeatherSDK/models/Foo+ReadWrite.swift b/Sources/WeatherSDK/models/Foo+ReadWrite.swift new file mode 100644 index 000000000..f5169de49 --- /dev/null +++ b/Sources/WeatherSDK/models/Foo+ReadWrite.swift @@ -0,0 +1,22 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite + +extension WeatherClientTypes.Foo { + + static func write(value: WeatherClientTypes.Foo?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["bar"].write(value.bar) + try writer["baz"].write(value.baz) + } + + static func read(from reader: SmithyJSON.Reader) throws -> WeatherClientTypes.Foo { + guard reader.hasContent else { throw SmithyReadWrite.ReaderError.requiredValueNotPresent } + var value = WeatherClientTypes.Foo() + value.baz = try reader["baz"].readIfPresent() + value.bar = try reader["bar"].readIfPresent() + return value + } +} diff --git a/Sources/WeatherSDK/models/Foo.swift b/Sources/WeatherSDK/models/Foo.swift index b60bf1c04..303f5c4e6 100644 --- a/Sources/WeatherSDK/models/Foo.swift +++ b/Sources/WeatherSDK/models/Foo.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite extension WeatherClientTypes { public struct Foo { diff --git a/Sources/WeatherSDK/models/GetCityAnnouncementsInput.swift b/Sources/WeatherSDK/models/GetCityAnnouncementsInput.swift index 04e35ef2a..4438c37b9 100644 --- a/Sources/WeatherSDK/models/GetCityAnnouncementsInput.swift +++ b/Sources/WeatherSDK/models/GetCityAnnouncementsInput.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite public struct GetCityAnnouncementsInput { /// This member is required. diff --git a/Sources/WeatherSDK/models/GetCityAnnouncementsInputBody+Decodable.swift b/Sources/WeatherSDK/models/GetCityAnnouncementsInputBody+Decodable.swift deleted file mode 100644 index 3b0f5f1e0..000000000 --- a/Sources/WeatherSDK/models/GetCityAnnouncementsInputBody+Decodable.swift +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct GetCityAnnouncementsInputBody { -} - -extension GetCityAnnouncementsInputBody: Swift.Decodable { - - public init(from decoder: Swift.Decoder) throws { - } -} diff --git a/Sources/WeatherSDK/models/GetCityAnnouncementsOutput+HttpResponseBinding.swift b/Sources/WeatherSDK/models/GetCityAnnouncementsOutput+HttpResponseBinding.swift index c2b0cd8a2..8fc036a1b 100644 --- a/Sources/WeatherSDK/models/GetCityAnnouncementsOutput+HttpResponseBinding.swift +++ b/Sources/WeatherSDK/models/GetCityAnnouncementsOutput+HttpResponseBinding.swift @@ -1,13 +1,16 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyJSON +import SmithyReadWrite -extension GetCityAnnouncementsOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { +extension GetCityAnnouncementsOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> GetCityAnnouncementsOutput { + var value = GetCityAnnouncementsOutput() if let lastUpdatedHeaderValue = httpResponse.headers.value(for: "x-last-updated") { - self.lastUpdated = TimestampFormatter(format: .httpDate).date(from: lastUpdatedHeaderValue) - } else { - self.lastUpdated = nil + value.lastUpdated = TimestampFormatter(format: .httpDate).date(from: lastUpdatedHeaderValue) } + return value } } diff --git a/Sources/WeatherSDK/models/GetCityAnnouncementsOutput.swift b/Sources/WeatherSDK/models/GetCityAnnouncementsOutput.swift index a89d81d24..2d2368d42 100644 --- a/Sources/WeatherSDK/models/GetCityAnnouncementsOutput.swift +++ b/Sources/WeatherSDK/models/GetCityAnnouncementsOutput.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite public struct GetCityAnnouncementsOutput { public var lastUpdated: ClientRuntime.Date? diff --git a/Sources/WeatherSDK/models/GetCityAnnouncementsOutputError+HttpResponseBinding.swift b/Sources/WeatherSDK/models/GetCityAnnouncementsOutputError+HttpResponseBinding.swift deleted file mode 100644 index 3774f7d3a..000000000 --- a/Sources/WeatherSDK/models/GetCityAnnouncementsOutputError+HttpResponseBinding.swift +++ /dev/null @@ -1,14 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime -import SmithyTestUtil - -enum GetCityAnnouncementsOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let defaultError = try await SmithyTestUtil.JSONError(httpResponse: httpResponse) - switch defaultError.errorType { - case "NoSuchResource": return try await NoSuchResource(httpResponse: httpResponse, decoder: decoder, message: defaultError.errorMessage) - default: return try await ClientRuntime.UnknownHTTPServiceError.makeError(httpResponse: httpResponse, message: defaultError.errorMessage, typeName: defaultError.errorType) - } - } -} diff --git a/Sources/WeatherSDK/models/GetCityAnnouncementsOutputError+HttpResponseErrorBinding.swift b/Sources/WeatherSDK/models/GetCityAnnouncementsOutputError+HttpResponseErrorBinding.swift new file mode 100644 index 000000000..41ae8f368 --- /dev/null +++ b/Sources/WeatherSDK/models/GetCityAnnouncementsOutputError+HttpResponseErrorBinding.swift @@ -0,0 +1,20 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite +import SmithyTestUtil + +enum GetCityAnnouncementsOutputError { + + static func httpError(from httpResponse: ClientRuntime.HttpResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let baseError = try SmithyTestUtil.TestBaseError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: false) + if let error = baseError.customError() { return error } + switch baseError.code { + case "NoSuchResource": return try NoSuchResource.makeError(baseError: baseError) + default: return try ClientRuntime.UnknownHTTPServiceError.makeError(baseError: baseError) + } + } +} diff --git a/Sources/WeatherSDK/models/GetCityImageInput.swift b/Sources/WeatherSDK/models/GetCityImageInput.swift index a6ff8288f..12125c4b0 100644 --- a/Sources/WeatherSDK/models/GetCityImageInput.swift +++ b/Sources/WeatherSDK/models/GetCityImageInput.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite public struct GetCityImageInput { /// This member is required. diff --git a/Sources/WeatherSDK/models/GetCityImageInputBody+Decodable.swift b/Sources/WeatherSDK/models/GetCityImageInputBody+Decodable.swift deleted file mode 100644 index 8ecb9634e..000000000 --- a/Sources/WeatherSDK/models/GetCityImageInputBody+Decodable.swift +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct GetCityImageInputBody { -} - -extension GetCityImageInputBody: Swift.Decodable { - - public init(from decoder: Swift.Decoder) throws { - } -} diff --git a/Sources/WeatherSDK/models/GetCityImageOutput+HttpResponseBinding.swift b/Sources/WeatherSDK/models/GetCityImageOutput+HttpResponseBinding.swift index 7cd0cfa65..26ffa427c 100644 --- a/Sources/WeatherSDK/models/GetCityImageOutput+HttpResponseBinding.swift +++ b/Sources/WeatherSDK/models/GetCityImageOutput+HttpResponseBinding.swift @@ -1,16 +1,21 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyJSON +import SmithyReadWrite -extension GetCityImageOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { +extension GetCityImageOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> GetCityImageOutput { + var value = GetCityImageOutput() switch httpResponse.body { case .data(let data): - self.image = .data(data) + value.image = .data(data) case .stream(let stream): - self.image = .stream(stream) + value.image = .stream(stream) case .noStream: - self.image = nil + value.image = nil } + return value } } diff --git a/Sources/WeatherSDK/models/GetCityImageOutput.swift b/Sources/WeatherSDK/models/GetCityImageOutput.swift index d69255663..a6caa2340 100644 --- a/Sources/WeatherSDK/models/GetCityImageOutput.swift +++ b/Sources/WeatherSDK/models/GetCityImageOutput.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite public struct GetCityImageOutput { /// This member is required. diff --git a/Sources/WeatherSDK/models/GetCityImageOutputBody+Decodable.swift b/Sources/WeatherSDK/models/GetCityImageOutputBody+Decodable.swift deleted file mode 100644 index 46256dea6..000000000 --- a/Sources/WeatherSDK/models/GetCityImageOutputBody+Decodable.swift +++ /dev/null @@ -1,19 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct GetCityImageOutputBody { - let image: ClientRuntime.ByteStream? -} - -extension GetCityImageOutputBody: Swift.Decodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case image - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let imageDecoded = try containerValues.decodeIfPresent(ClientRuntime.ByteStream.self, forKey: .image) - image = imageDecoded - } -} diff --git a/Sources/WeatherSDK/models/GetCityImageOutputError+HttpResponseBinding.swift b/Sources/WeatherSDK/models/GetCityImageOutputError+HttpResponseBinding.swift deleted file mode 100644 index b781b44fa..000000000 --- a/Sources/WeatherSDK/models/GetCityImageOutputError+HttpResponseBinding.swift +++ /dev/null @@ -1,14 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime -import SmithyTestUtil - -enum GetCityImageOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let defaultError = try await SmithyTestUtil.JSONError(httpResponse: httpResponse) - switch defaultError.errorType { - case "NoSuchResource": return try await NoSuchResource(httpResponse: httpResponse, decoder: decoder, message: defaultError.errorMessage) - default: return try await ClientRuntime.UnknownHTTPServiceError.makeError(httpResponse: httpResponse, message: defaultError.errorMessage, typeName: defaultError.errorType) - } - } -} diff --git a/Sources/WeatherSDK/models/GetCityImageOutputError+HttpResponseErrorBinding.swift b/Sources/WeatherSDK/models/GetCityImageOutputError+HttpResponseErrorBinding.swift new file mode 100644 index 000000000..0aeaa58e9 --- /dev/null +++ b/Sources/WeatherSDK/models/GetCityImageOutputError+HttpResponseErrorBinding.swift @@ -0,0 +1,20 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite +import SmithyTestUtil + +enum GetCityImageOutputError { + + static func httpError(from httpResponse: ClientRuntime.HttpResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let baseError = try SmithyTestUtil.TestBaseError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: false) + if let error = baseError.customError() { return error } + switch baseError.code { + case "NoSuchResource": return try NoSuchResource.makeError(baseError: baseError) + default: return try ClientRuntime.UnknownHTTPServiceError.makeError(baseError: baseError) + } + } +} diff --git a/Sources/WeatherSDK/models/GetCityInput.swift b/Sources/WeatherSDK/models/GetCityInput.swift index d530d7655..5abe6bf26 100644 --- a/Sources/WeatherSDK/models/GetCityInput.swift +++ b/Sources/WeatherSDK/models/GetCityInput.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite /// The input used to get a city. public struct GetCityInput { diff --git a/Sources/WeatherSDK/models/GetCityInputBody+Decodable.swift b/Sources/WeatherSDK/models/GetCityInputBody+Decodable.swift deleted file mode 100644 index 4e64ca635..000000000 --- a/Sources/WeatherSDK/models/GetCityInputBody+Decodable.swift +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct GetCityInputBody { -} - -extension GetCityInputBody: Swift.Decodable { - - public init(from decoder: Swift.Decoder) throws { - } -} diff --git a/Sources/WeatherSDK/models/GetCityOutput+HttpResponseBinding.swift b/Sources/WeatherSDK/models/GetCityOutput+HttpResponseBinding.swift index 3d21ba80e..5c56d6b1d 100644 --- a/Sources/WeatherSDK/models/GetCityOutput+HttpResponseBinding.swift +++ b/Sources/WeatherSDK/models/GetCityOutput+HttpResponseBinding.swift @@ -1,19 +1,19 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyJSON +import SmithyReadWrite -extension GetCityOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { - if let data = try await httpResponse.body.readData(), - let responseDecoder = decoder { - let output: GetCityOutputBody = try responseDecoder.decode(responseBody: data) - self.city = output.city - self.coordinates = output.coordinates - self.name = output.name - } else { - self.city = nil - self.coordinates = nil - self.name = nil - } +extension GetCityOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> GetCityOutput { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let reader = responseReader + var value = GetCityOutput() + value.city = try reader["city"].readIfPresent(with: WeatherClientTypes.CitySummary.read(from:)) + value.coordinates = try reader["coordinates"].readIfPresent(with: WeatherClientTypes.CityCoordinates.read(from:)) + value.name = try reader["name"].readIfPresent() + return value } } diff --git a/Sources/WeatherSDK/models/GetCityOutput.swift b/Sources/WeatherSDK/models/GetCityOutput.swift index ee4fe056f..84246f545 100644 --- a/Sources/WeatherSDK/models/GetCityOutput.swift +++ b/Sources/WeatherSDK/models/GetCityOutput.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite public struct GetCityOutput { public var city: WeatherClientTypes.CitySummary? diff --git a/Sources/WeatherSDK/models/GetCityOutputBody+Decodable.swift b/Sources/WeatherSDK/models/GetCityOutputBody+Decodable.swift deleted file mode 100644 index 1e8e89ac4..000000000 --- a/Sources/WeatherSDK/models/GetCityOutputBody+Decodable.swift +++ /dev/null @@ -1,27 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct GetCityOutputBody { - let name: Swift.String? - let coordinates: WeatherClientTypes.CityCoordinates? - let city: WeatherClientTypes.CitySummary? -} - -extension GetCityOutputBody: Swift.Decodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case city - case coordinates - case name - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let nameDecoded = try containerValues.decodeIfPresent(Swift.String.self, forKey: .name) - name = nameDecoded - let coordinatesDecoded = try containerValues.decodeIfPresent(WeatherClientTypes.CityCoordinates.self, forKey: .coordinates) - coordinates = coordinatesDecoded - let cityDecoded = try containerValues.decodeIfPresent(WeatherClientTypes.CitySummary.self, forKey: .city) - city = cityDecoded - } -} diff --git a/Sources/WeatherSDK/models/GetCityOutputError+HttpResponseBinding.swift b/Sources/WeatherSDK/models/GetCityOutputError+HttpResponseBinding.swift deleted file mode 100644 index 14edc57ca..000000000 --- a/Sources/WeatherSDK/models/GetCityOutputError+HttpResponseBinding.swift +++ /dev/null @@ -1,14 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime -import SmithyTestUtil - -enum GetCityOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let defaultError = try await SmithyTestUtil.JSONError(httpResponse: httpResponse) - switch defaultError.errorType { - case "NoSuchResource": return try await NoSuchResource(httpResponse: httpResponse, decoder: decoder, message: defaultError.errorMessage) - default: return try await ClientRuntime.UnknownHTTPServiceError.makeError(httpResponse: httpResponse, message: defaultError.errorMessage, typeName: defaultError.errorType) - } - } -} diff --git a/Sources/WeatherSDK/models/GetCityOutputError+HttpResponseErrorBinding.swift b/Sources/WeatherSDK/models/GetCityOutputError+HttpResponseErrorBinding.swift new file mode 100644 index 000000000..9a2efcbab --- /dev/null +++ b/Sources/WeatherSDK/models/GetCityOutputError+HttpResponseErrorBinding.swift @@ -0,0 +1,20 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite +import SmithyTestUtil + +enum GetCityOutputError { + + static func httpError(from httpResponse: ClientRuntime.HttpResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let baseError = try SmithyTestUtil.TestBaseError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: false) + if let error = baseError.customError() { return error } + switch baseError.code { + case "NoSuchResource": return try NoSuchResource.makeError(baseError: baseError) + default: return try ClientRuntime.UnknownHTTPServiceError.makeError(baseError: baseError) + } + } +} diff --git a/Sources/WeatherSDK/models/GetCurrentTimeInput.swift b/Sources/WeatherSDK/models/GetCurrentTimeInput.swift index aec2ce184..944d56051 100644 --- a/Sources/WeatherSDK/models/GetCurrentTimeInput.swift +++ b/Sources/WeatherSDK/models/GetCurrentTimeInput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct GetCurrentTimeInput { diff --git a/Sources/WeatherSDK/models/GetCurrentTimeInputBody+Decodable.swift b/Sources/WeatherSDK/models/GetCurrentTimeInputBody+Decodable.swift deleted file mode 100644 index d7361aa89..000000000 --- a/Sources/WeatherSDK/models/GetCurrentTimeInputBody+Decodable.swift +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct GetCurrentTimeInputBody { -} - -extension GetCurrentTimeInputBody: Swift.Decodable { - - public init(from decoder: Swift.Decoder) throws { - } -} diff --git a/Sources/WeatherSDK/models/GetCurrentTimeOutput+HttpResponseBinding.swift b/Sources/WeatherSDK/models/GetCurrentTimeOutput+HttpResponseBinding.swift index 435849de3..73f49f1c6 100644 --- a/Sources/WeatherSDK/models/GetCurrentTimeOutput+HttpResponseBinding.swift +++ b/Sources/WeatherSDK/models/GetCurrentTimeOutput+HttpResponseBinding.swift @@ -1,15 +1,17 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyJSON +import SmithyReadWrite -extension GetCurrentTimeOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { - if let data = try await httpResponse.body.readData(), - let responseDecoder = decoder { - let output: GetCurrentTimeOutputBody = try responseDecoder.decode(responseBody: data) - self.time = output.time - } else { - self.time = nil - } +extension GetCurrentTimeOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> GetCurrentTimeOutput { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let reader = responseReader + var value = GetCurrentTimeOutput() + value.time = try reader["time"].readTimestampIfPresent(format: .epochSeconds) + return value } } diff --git a/Sources/WeatherSDK/models/GetCurrentTimeOutput.swift b/Sources/WeatherSDK/models/GetCurrentTimeOutput.swift index 741d6918a..ff3e31807 100644 --- a/Sources/WeatherSDK/models/GetCurrentTimeOutput.swift +++ b/Sources/WeatherSDK/models/GetCurrentTimeOutput.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite public struct GetCurrentTimeOutput { /// This member is required. diff --git a/Sources/WeatherSDK/models/GetCurrentTimeOutputBody+Decodable.swift b/Sources/WeatherSDK/models/GetCurrentTimeOutputBody+Decodable.swift deleted file mode 100644 index 08efba7db..000000000 --- a/Sources/WeatherSDK/models/GetCurrentTimeOutputBody+Decodable.swift +++ /dev/null @@ -1,19 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct GetCurrentTimeOutputBody { - let time: ClientRuntime.Date? -} - -extension GetCurrentTimeOutputBody: Swift.Decodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case time - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let timeDecoded = try containerValues.decodeTimestampIfPresent(.dateTime, forKey: .time) - time = timeDecoded - } -} diff --git a/Sources/WeatherSDK/models/GetCurrentTimeOutputError+HttpResponseBinding.swift b/Sources/WeatherSDK/models/GetCurrentTimeOutputError+HttpResponseBinding.swift deleted file mode 100644 index 781bbb144..000000000 --- a/Sources/WeatherSDK/models/GetCurrentTimeOutputError+HttpResponseBinding.swift +++ /dev/null @@ -1,13 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime -import SmithyTestUtil - -enum GetCurrentTimeOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let defaultError = try await SmithyTestUtil.JSONError(httpResponse: httpResponse) - switch defaultError.errorType { - default: return try await ClientRuntime.UnknownHTTPServiceError.makeError(httpResponse: httpResponse, message: defaultError.errorMessage, typeName: defaultError.errorType) - } - } -} diff --git a/Sources/WeatherSDK/models/GetCurrentTimeOutputError+HttpResponseErrorBinding.swift b/Sources/WeatherSDK/models/GetCurrentTimeOutputError+HttpResponseErrorBinding.swift new file mode 100644 index 000000000..7f3873ebf --- /dev/null +++ b/Sources/WeatherSDK/models/GetCurrentTimeOutputError+HttpResponseErrorBinding.swift @@ -0,0 +1,19 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite +import SmithyTestUtil + +enum GetCurrentTimeOutputError { + + static func httpError(from httpResponse: ClientRuntime.HttpResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let baseError = try SmithyTestUtil.TestBaseError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: false) + if let error = baseError.customError() { return error } + switch baseError.code { + default: return try ClientRuntime.UnknownHTTPServiceError.makeError(baseError: baseError) + } + } +} diff --git a/Sources/WeatherSDK/models/GetForecastInput.swift b/Sources/WeatherSDK/models/GetForecastInput.swift index 18a2ac84e..8a290f99b 100644 --- a/Sources/WeatherSDK/models/GetForecastInput.swift +++ b/Sources/WeatherSDK/models/GetForecastInput.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite public struct GetForecastInput { /// This member is required. diff --git a/Sources/WeatherSDK/models/GetForecastInputBody+Decodable.swift b/Sources/WeatherSDK/models/GetForecastInputBody+Decodable.swift deleted file mode 100644 index 6262bf61f..000000000 --- a/Sources/WeatherSDK/models/GetForecastInputBody+Decodable.swift +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct GetForecastInputBody { -} - -extension GetForecastInputBody: Swift.Decodable { - - public init(from decoder: Swift.Decoder) throws { - } -} diff --git a/Sources/WeatherSDK/models/GetForecastOutput+HttpResponseBinding.swift b/Sources/WeatherSDK/models/GetForecastOutput+HttpResponseBinding.swift index f5a820fbf..144658d76 100644 --- a/Sources/WeatherSDK/models/GetForecastOutput+HttpResponseBinding.swift +++ b/Sources/WeatherSDK/models/GetForecastOutput+HttpResponseBinding.swift @@ -1,17 +1,18 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyJSON +import SmithyReadWrite -extension GetForecastOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { - if let data = try await httpResponse.body.readData(), - let responseDecoder = decoder { - let output: GetForecastOutputBody = try responseDecoder.decode(responseBody: data) - self.chanceOfRain = output.chanceOfRain - self.precipitation = output.precipitation - } else { - self.chanceOfRain = nil - self.precipitation = nil - } +extension GetForecastOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> GetForecastOutput { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let reader = responseReader + var value = GetForecastOutput() + value.chanceOfRain = try reader["chanceOfRain"].readIfPresent() + value.precipitation = try reader["precipitation"].readIfPresent(with: WeatherClientTypes.Precipitation.read(from:)) + return value } } diff --git a/Sources/WeatherSDK/models/GetForecastOutput.swift b/Sources/WeatherSDK/models/GetForecastOutput.swift index 52d902f25..e879e4d87 100644 --- a/Sources/WeatherSDK/models/GetForecastOutput.swift +++ b/Sources/WeatherSDK/models/GetForecastOutput.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite public struct GetForecastOutput { public var chanceOfRain: Swift.Float? diff --git a/Sources/WeatherSDK/models/GetForecastOutputBody+Decodable.swift b/Sources/WeatherSDK/models/GetForecastOutputBody+Decodable.swift deleted file mode 100644 index 1bfe108de..000000000 --- a/Sources/WeatherSDK/models/GetForecastOutputBody+Decodable.swift +++ /dev/null @@ -1,23 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct GetForecastOutputBody { - let chanceOfRain: Swift.Float? - let precipitation: WeatherClientTypes.Precipitation? -} - -extension GetForecastOutputBody: Swift.Decodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case chanceOfRain - case precipitation - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let chanceOfRainDecoded = try containerValues.decodeIfPresent(Swift.Float.self, forKey: .chanceOfRain) - chanceOfRain = chanceOfRainDecoded - let precipitationDecoded = try containerValues.decodeIfPresent(WeatherClientTypes.Precipitation.self, forKey: .precipitation) - precipitation = precipitationDecoded - } -} diff --git a/Sources/WeatherSDK/models/GetForecastOutputError+HttpResponseBinding.swift b/Sources/WeatherSDK/models/GetForecastOutputError+HttpResponseBinding.swift deleted file mode 100644 index eb12dd296..000000000 --- a/Sources/WeatherSDK/models/GetForecastOutputError+HttpResponseBinding.swift +++ /dev/null @@ -1,13 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime -import SmithyTestUtil - -enum GetForecastOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let defaultError = try await SmithyTestUtil.JSONError(httpResponse: httpResponse) - switch defaultError.errorType { - default: return try await ClientRuntime.UnknownHTTPServiceError.makeError(httpResponse: httpResponse, message: defaultError.errorMessage, typeName: defaultError.errorType) - } - } -} diff --git a/Sources/WeatherSDK/models/GetForecastOutputError+HttpResponseErrorBinding.swift b/Sources/WeatherSDK/models/GetForecastOutputError+HttpResponseErrorBinding.swift new file mode 100644 index 000000000..e61c35cc2 --- /dev/null +++ b/Sources/WeatherSDK/models/GetForecastOutputError+HttpResponseErrorBinding.swift @@ -0,0 +1,19 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite +import SmithyTestUtil + +enum GetForecastOutputError { + + static func httpError(from httpResponse: ClientRuntime.HttpResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let baseError = try SmithyTestUtil.TestBaseError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: false) + if let error = baseError.customError() { return error } + switch baseError.code { + default: return try ClientRuntime.UnknownHTTPServiceError.makeError(baseError: baseError) + } + } +} diff --git a/Sources/WeatherSDK/models/InvokeInput+Encodable.swift b/Sources/WeatherSDK/models/InvokeInput+Encodable.swift deleted file mode 100644 index 67486ada7..000000000 --- a/Sources/WeatherSDK/models/InvokeInput+Encodable.swift +++ /dev/null @@ -1,16 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -extension InvokeInput: Swift.Encodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case payload - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let payload = self.payload { - try encodeContainer.encode(payload.base64EncodedString(), forKey: .payload) - } - } -} diff --git a/Sources/WeatherSDK/models/InvokeInput+Write.swift b/Sources/WeatherSDK/models/InvokeInput+Write.swift new file mode 100644 index 000000000..012c59741 --- /dev/null +++ b/Sources/WeatherSDK/models/InvokeInput+Write.swift @@ -0,0 +1,13 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite + +extension InvokeInput { + + static func write(value: InvokeInput?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["payload"].write(value.payload) + } +} diff --git a/Sources/WeatherSDK/models/InvokeInput.swift b/Sources/WeatherSDK/models/InvokeInput.swift index d6ecd3f39..ad837bdda 100644 --- a/Sources/WeatherSDK/models/InvokeInput.swift +++ b/Sources/WeatherSDK/models/InvokeInput.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite public struct InvokeInput { public var payload: ClientRuntime.Data? diff --git a/Sources/WeatherSDK/models/InvokeInputBody+Decodable.swift b/Sources/WeatherSDK/models/InvokeInputBody+Decodable.swift deleted file mode 100644 index 1aeed7f48..000000000 --- a/Sources/WeatherSDK/models/InvokeInputBody+Decodable.swift +++ /dev/null @@ -1,19 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct InvokeInputBody { - let payload: ClientRuntime.Data? -} - -extension InvokeInputBody: Swift.Decodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case payload - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let payloadDecoded = try containerValues.decodeIfPresent(ClientRuntime.Data.self, forKey: .payload) - payload = payloadDecoded - } -} diff --git a/Sources/WeatherSDK/models/InvokeOutput+HttpResponseBinding.swift b/Sources/WeatherSDK/models/InvokeOutput+HttpResponseBinding.swift index 378e46d6e..7a23aec9d 100644 --- a/Sources/WeatherSDK/models/InvokeOutput+HttpResponseBinding.swift +++ b/Sources/WeatherSDK/models/InvokeOutput+HttpResponseBinding.swift @@ -1,16 +1,24 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyJSON +import SmithyReadWrite -extension InvokeOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { +extension InvokeOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> InvokeOutput { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let reader = responseReader + var value = InvokeOutput() switch httpResponse.body { case .data(let data): - self.payload = data + value.payload = data case .stream(let stream): - self.payload = try stream.readToEnd() + value.payload = try stream.readToEnd() case .noStream: - self.payload = nil + value.payload = nil } + return value } } diff --git a/Sources/WeatherSDK/models/InvokeOutput.swift b/Sources/WeatherSDK/models/InvokeOutput.swift index 1b2b51a18..13f9ea1ab 100644 --- a/Sources/WeatherSDK/models/InvokeOutput.swift +++ b/Sources/WeatherSDK/models/InvokeOutput.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite public struct InvokeOutput { public var payload: ClientRuntime.Data? diff --git a/Sources/WeatherSDK/models/InvokeOutputBody+Decodable.swift b/Sources/WeatherSDK/models/InvokeOutputBody+Decodable.swift deleted file mode 100644 index c06373481..000000000 --- a/Sources/WeatherSDK/models/InvokeOutputBody+Decodable.swift +++ /dev/null @@ -1,19 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct InvokeOutputBody { - let payload: ClientRuntime.Data? -} - -extension InvokeOutputBody: Swift.Decodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case payload - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let payloadDecoded = try containerValues.decodeIfPresent(ClientRuntime.Data.self, forKey: .payload) - payload = payloadDecoded - } -} diff --git a/Sources/WeatherSDK/models/InvokeOutputError+HttpResponseBinding.swift b/Sources/WeatherSDK/models/InvokeOutputError+HttpResponseBinding.swift deleted file mode 100644 index 9d28c5c6f..000000000 --- a/Sources/WeatherSDK/models/InvokeOutputError+HttpResponseBinding.swift +++ /dev/null @@ -1,13 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime -import SmithyTestUtil - -enum InvokeOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let defaultError = try await SmithyTestUtil.JSONError(httpResponse: httpResponse) - switch defaultError.errorType { - default: return try await ClientRuntime.UnknownHTTPServiceError.makeError(httpResponse: httpResponse, message: defaultError.errorMessage, typeName: defaultError.errorType) - } - } -} diff --git a/Sources/WeatherSDK/models/InvokeOutputError+HttpResponseErrorBinding.swift b/Sources/WeatherSDK/models/InvokeOutputError+HttpResponseErrorBinding.swift new file mode 100644 index 000000000..6db3ccb42 --- /dev/null +++ b/Sources/WeatherSDK/models/InvokeOutputError+HttpResponseErrorBinding.swift @@ -0,0 +1,19 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite +import SmithyTestUtil + +enum InvokeOutputError { + + static func httpError(from httpResponse: ClientRuntime.HttpResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let baseError = try SmithyTestUtil.TestBaseError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: false) + if let error = baseError.customError() { return error } + switch baseError.code { + default: return try ClientRuntime.UnknownHTTPServiceError.makeError(baseError: baseError) + } + } +} diff --git a/Sources/WeatherSDK/models/ListCitiesInput.swift b/Sources/WeatherSDK/models/ListCitiesInput.swift index 5af98dded..a93563f18 100644 --- a/Sources/WeatherSDK/models/ListCitiesInput.swift +++ b/Sources/WeatherSDK/models/ListCitiesInput.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite public struct ListCitiesInput { public var nextToken: Swift.String? diff --git a/Sources/WeatherSDK/models/ListCitiesInputBody+Decodable.swift b/Sources/WeatherSDK/models/ListCitiesInputBody+Decodable.swift deleted file mode 100644 index 710e33f28..000000000 --- a/Sources/WeatherSDK/models/ListCitiesInputBody+Decodable.swift +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct ListCitiesInputBody { -} - -extension ListCitiesInputBody: Swift.Decodable { - - public init(from decoder: Swift.Decoder) throws { - } -} diff --git a/Sources/WeatherSDK/models/ListCitiesOutput+HttpResponseBinding.swift b/Sources/WeatherSDK/models/ListCitiesOutput+HttpResponseBinding.swift index 144d898a8..195d825f0 100644 --- a/Sources/WeatherSDK/models/ListCitiesOutput+HttpResponseBinding.swift +++ b/Sources/WeatherSDK/models/ListCitiesOutput+HttpResponseBinding.swift @@ -1,17 +1,18 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyJSON +import SmithyReadWrite -extension ListCitiesOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { - if let data = try await httpResponse.body.readData(), - let responseDecoder = decoder { - let output: ListCitiesOutputBody = try responseDecoder.decode(responseBody: data) - self.items = output.items - self.nextToken = output.nextToken - } else { - self.items = nil - self.nextToken = nil - } +extension ListCitiesOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> ListCitiesOutput { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let reader = responseReader + var value = ListCitiesOutput() + value.items = try reader["items"].readListIfPresent(memberReadingClosure: WeatherClientTypes.CitySummary.read(from:), memberNodeInfo: "member", isFlattened: false) + value.nextToken = try reader["nextToken"].readIfPresent() + return value } } diff --git a/Sources/WeatherSDK/models/ListCitiesOutput.swift b/Sources/WeatherSDK/models/ListCitiesOutput.swift index 1db3594ee..da9ef501c 100644 --- a/Sources/WeatherSDK/models/ListCitiesOutput.swift +++ b/Sources/WeatherSDK/models/ListCitiesOutput.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite public struct ListCitiesOutput { /// This member is required. diff --git a/Sources/WeatherSDK/models/ListCitiesOutputBody+Decodable.swift b/Sources/WeatherSDK/models/ListCitiesOutputBody+Decodable.swift deleted file mode 100644 index 38076bca2..000000000 --- a/Sources/WeatherSDK/models/ListCitiesOutputBody+Decodable.swift +++ /dev/null @@ -1,32 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct ListCitiesOutputBody { - let nextToken: Swift.String? - let items: [WeatherClientTypes.CitySummary]? -} - -extension ListCitiesOutputBody: Swift.Decodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case items - case nextToken - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let nextTokenDecoded = try containerValues.decodeIfPresent(Swift.String.self, forKey: .nextToken) - nextToken = nextTokenDecoded - let itemsContainer = try containerValues.decodeIfPresent([WeatherClientTypes.CitySummary?].self, forKey: .items) - var itemsDecoded0:[WeatherClientTypes.CitySummary]? = nil - if let itemsContainer = itemsContainer { - itemsDecoded0 = [WeatherClientTypes.CitySummary]() - for structure0 in itemsContainer { - if let structure0 = structure0 { - itemsDecoded0?.append(structure0) - } - } - } - items = itemsDecoded0 - } -} diff --git a/Sources/WeatherSDK/models/ListCitiesOutputError+HttpResponseBinding.swift b/Sources/WeatherSDK/models/ListCitiesOutputError+HttpResponseBinding.swift deleted file mode 100644 index 49595fa8f..000000000 --- a/Sources/WeatherSDK/models/ListCitiesOutputError+HttpResponseBinding.swift +++ /dev/null @@ -1,14 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime -import SmithyTestUtil - -enum ListCitiesOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let defaultError = try await SmithyTestUtil.JSONError(httpResponse: httpResponse) - switch defaultError.errorType { - case "NoSuchResource": return try await NoSuchResource(httpResponse: httpResponse, decoder: decoder, message: defaultError.errorMessage) - default: return try await ClientRuntime.UnknownHTTPServiceError.makeError(httpResponse: httpResponse, message: defaultError.errorMessage, typeName: defaultError.errorType) - } - } -} diff --git a/Sources/WeatherSDK/models/ListCitiesOutputError+HttpResponseErrorBinding.swift b/Sources/WeatherSDK/models/ListCitiesOutputError+HttpResponseErrorBinding.swift new file mode 100644 index 000000000..d13c1a5b8 --- /dev/null +++ b/Sources/WeatherSDK/models/ListCitiesOutputError+HttpResponseErrorBinding.swift @@ -0,0 +1,20 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite +import SmithyTestUtil + +enum ListCitiesOutputError { + + static func httpError(from httpResponse: ClientRuntime.HttpResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let baseError = try SmithyTestUtil.TestBaseError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: false) + if let error = baseError.customError() { return error } + switch baseError.code { + case "NoSuchResource": return try NoSuchResource.makeError(baseError: baseError) + default: return try ClientRuntime.UnknownHTTPServiceError.makeError(baseError: baseError) + } + } +} diff --git a/Sources/WeatherSDK/models/NoSuchResource+Init.swift b/Sources/WeatherSDK/models/NoSuchResource+Init.swift index b037e4dd5..32b7062c8 100644 --- a/Sources/WeatherSDK/models/NoSuchResource+Init.swift +++ b/Sources/WeatherSDK/models/NoSuchResource+Init.swift @@ -1,20 +1,20 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyJSON +import SmithyReadWrite +import SmithyTestUtil extension NoSuchResource { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil, message: Swift.String? = nil, requestID: Swift.String? = nil) async throws { - if let data = try await httpResponse.body.readData(), - let responseDecoder = decoder { - let output: NoSuchResourceBody = try responseDecoder.decode(responseBody: data) - self.properties.message = output.message - self.properties.resourceType = output.resourceType - } else { - self.properties.message = nil - self.properties.resourceType = nil - } - self.httpResponse = httpResponse - self.requestID = requestID - self.message = message + + static func makeError(baseError: SmithyTestUtil.TestBaseError) throws -> NoSuchResource { + let reader = baseError.errorBodyReader + var value = NoSuchResource() + value.properties.message = try reader["message"].readIfPresent() + value.properties.resourceType = try reader["resourceType"].readIfPresent() + value.httpResponse = baseError.httpResponse + value.requestID = baseError.requestID + value.message = baseError.message + return value } } diff --git a/Sources/WeatherSDK/models/NoSuchResourceBody+Decodable.swift b/Sources/WeatherSDK/models/NoSuchResourceBody+Decodable.swift deleted file mode 100644 index 6b4d1e355..000000000 --- a/Sources/WeatherSDK/models/NoSuchResourceBody+Decodable.swift +++ /dev/null @@ -1,23 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct NoSuchResourceBody { - let resourceType: Swift.String? - let message: Swift.String? -} - -extension NoSuchResourceBody: Swift.Decodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case message - case resourceType - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let resourceTypeDecoded = try containerValues.decodeIfPresent(Swift.String.self, forKey: .resourceType) - resourceType = resourceTypeDecoded - let messageDecoded = try containerValues.decodeIfPresent(Swift.String.self, forKey: .message) - message = messageDecoded - } -} diff --git a/Sources/WeatherSDK/models/OnlyFakeAuthInput.swift b/Sources/WeatherSDK/models/OnlyFakeAuthInput.swift index 9dee5845f..9e94d2165 100644 --- a/Sources/WeatherSDK/models/OnlyFakeAuthInput.swift +++ b/Sources/WeatherSDK/models/OnlyFakeAuthInput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct OnlyFakeAuthInput { diff --git a/Sources/WeatherSDK/models/OnlyFakeAuthInputBody+Decodable.swift b/Sources/WeatherSDK/models/OnlyFakeAuthInputBody+Decodable.swift deleted file mode 100644 index 85506c967..000000000 --- a/Sources/WeatherSDK/models/OnlyFakeAuthInputBody+Decodable.swift +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct OnlyFakeAuthInputBody { -} - -extension OnlyFakeAuthInputBody: Swift.Decodable { - - public init(from decoder: Swift.Decoder) throws { - } -} diff --git a/Sources/WeatherSDK/models/OnlyFakeAuthOptionalInput.swift b/Sources/WeatherSDK/models/OnlyFakeAuthOptionalInput.swift index b6949b86b..e252300a3 100644 --- a/Sources/WeatherSDK/models/OnlyFakeAuthOptionalInput.swift +++ b/Sources/WeatherSDK/models/OnlyFakeAuthOptionalInput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct OnlyFakeAuthOptionalInput { diff --git a/Sources/WeatherSDK/models/OnlyFakeAuthOptionalInputBody+Decodable.swift b/Sources/WeatherSDK/models/OnlyFakeAuthOptionalInputBody+Decodable.swift deleted file mode 100644 index b57def6fb..000000000 --- a/Sources/WeatherSDK/models/OnlyFakeAuthOptionalInputBody+Decodable.swift +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct OnlyFakeAuthOptionalInputBody { -} - -extension OnlyFakeAuthOptionalInputBody: Swift.Decodable { - - public init(from decoder: Swift.Decoder) throws { - } -} diff --git a/Sources/WeatherSDK/models/OnlyFakeAuthOptionalOutput+HttpResponseBinding.swift b/Sources/WeatherSDK/models/OnlyFakeAuthOptionalOutput+HttpResponseBinding.swift index 85d2a3de9..121e0da17 100644 --- a/Sources/WeatherSDK/models/OnlyFakeAuthOptionalOutput+HttpResponseBinding.swift +++ b/Sources/WeatherSDK/models/OnlyFakeAuthOptionalOutput+HttpResponseBinding.swift @@ -1,8 +1,12 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyJSON +import SmithyReadWrite -extension OnlyFakeAuthOptionalOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { +extension OnlyFakeAuthOptionalOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> OnlyFakeAuthOptionalOutput { + return OnlyFakeAuthOptionalOutput() } } diff --git a/Sources/WeatherSDK/models/OnlyFakeAuthOptionalOutput.swift b/Sources/WeatherSDK/models/OnlyFakeAuthOptionalOutput.swift index 25f12b256..6d444589e 100644 --- a/Sources/WeatherSDK/models/OnlyFakeAuthOptionalOutput.swift +++ b/Sources/WeatherSDK/models/OnlyFakeAuthOptionalOutput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct OnlyFakeAuthOptionalOutput { diff --git a/Sources/WeatherSDK/models/OnlyFakeAuthOptionalOutputError+HttpResponseBinding.swift b/Sources/WeatherSDK/models/OnlyFakeAuthOptionalOutputError+HttpResponseBinding.swift deleted file mode 100644 index b43abdc7b..000000000 --- a/Sources/WeatherSDK/models/OnlyFakeAuthOptionalOutputError+HttpResponseBinding.swift +++ /dev/null @@ -1,13 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime -import SmithyTestUtil - -enum OnlyFakeAuthOptionalOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let defaultError = try await SmithyTestUtil.JSONError(httpResponse: httpResponse) - switch defaultError.errorType { - default: return try await ClientRuntime.UnknownHTTPServiceError.makeError(httpResponse: httpResponse, message: defaultError.errorMessage, typeName: defaultError.errorType) - } - } -} diff --git a/Sources/WeatherSDK/models/OnlyFakeAuthOptionalOutputError+HttpResponseErrorBinding.swift b/Sources/WeatherSDK/models/OnlyFakeAuthOptionalOutputError+HttpResponseErrorBinding.swift new file mode 100644 index 000000000..58620ba80 --- /dev/null +++ b/Sources/WeatherSDK/models/OnlyFakeAuthOptionalOutputError+HttpResponseErrorBinding.swift @@ -0,0 +1,19 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite +import SmithyTestUtil + +enum OnlyFakeAuthOptionalOutputError { + + static func httpError(from httpResponse: ClientRuntime.HttpResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let baseError = try SmithyTestUtil.TestBaseError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: false) + if let error = baseError.customError() { return error } + switch baseError.code { + default: return try ClientRuntime.UnknownHTTPServiceError.makeError(baseError: baseError) + } + } +} diff --git a/Sources/WeatherSDK/models/OnlyFakeAuthOutput+HttpResponseBinding.swift b/Sources/WeatherSDK/models/OnlyFakeAuthOutput+HttpResponseBinding.swift index 7a6f13c7a..52306216e 100644 --- a/Sources/WeatherSDK/models/OnlyFakeAuthOutput+HttpResponseBinding.swift +++ b/Sources/WeatherSDK/models/OnlyFakeAuthOutput+HttpResponseBinding.swift @@ -1,8 +1,12 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyJSON +import SmithyReadWrite -extension OnlyFakeAuthOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { +extension OnlyFakeAuthOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> OnlyFakeAuthOutput { + return OnlyFakeAuthOutput() } } diff --git a/Sources/WeatherSDK/models/OnlyFakeAuthOutput.swift b/Sources/WeatherSDK/models/OnlyFakeAuthOutput.swift index 51b267c9b..d943b115b 100644 --- a/Sources/WeatherSDK/models/OnlyFakeAuthOutput.swift +++ b/Sources/WeatherSDK/models/OnlyFakeAuthOutput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct OnlyFakeAuthOutput { diff --git a/Sources/WeatherSDK/models/OnlyFakeAuthOutputError+HttpResponseBinding.swift b/Sources/WeatherSDK/models/OnlyFakeAuthOutputError+HttpResponseBinding.swift deleted file mode 100644 index 3390bc446..000000000 --- a/Sources/WeatherSDK/models/OnlyFakeAuthOutputError+HttpResponseBinding.swift +++ /dev/null @@ -1,13 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime -import SmithyTestUtil - -enum OnlyFakeAuthOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let defaultError = try await SmithyTestUtil.JSONError(httpResponse: httpResponse) - switch defaultError.errorType { - default: return try await ClientRuntime.UnknownHTTPServiceError.makeError(httpResponse: httpResponse, message: defaultError.errorMessage, typeName: defaultError.errorType) - } - } -} diff --git a/Sources/WeatherSDK/models/OnlyFakeAuthOutputError+HttpResponseErrorBinding.swift b/Sources/WeatherSDK/models/OnlyFakeAuthOutputError+HttpResponseErrorBinding.swift new file mode 100644 index 000000000..68e8adaca --- /dev/null +++ b/Sources/WeatherSDK/models/OnlyFakeAuthOutputError+HttpResponseErrorBinding.swift @@ -0,0 +1,19 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite +import SmithyTestUtil + +enum OnlyFakeAuthOutputError { + + static func httpError(from httpResponse: ClientRuntime.HttpResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let baseError = try SmithyTestUtil.TestBaseError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: false) + if let error = baseError.customError() { return error } + switch baseError.code { + default: return try ClientRuntime.UnknownHTTPServiceError.makeError(baseError: baseError) + } + } +} diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthInput.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthInput.swift index 5c7dc2de7..443e677d3 100644 --- a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthInput.swift +++ b/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthInput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct OnlyHttpApiKeyAndBearerAuthInput { diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthInputBody+Decodable.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthInputBody+Decodable.swift deleted file mode 100644 index 1bb9402a0..000000000 --- a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthInputBody+Decodable.swift +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct OnlyHttpApiKeyAndBearerAuthInputBody { -} - -extension OnlyHttpApiKeyAndBearerAuthInputBody: Swift.Decodable { - - public init(from decoder: Swift.Decoder) throws { - } -} diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthOutput+HttpResponseBinding.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthOutput+HttpResponseBinding.swift index c066e16c7..d2043e5b9 100644 --- a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthOutput+HttpResponseBinding.swift +++ b/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthOutput+HttpResponseBinding.swift @@ -1,8 +1,12 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyJSON +import SmithyReadWrite -extension OnlyHttpApiKeyAndBearerAuthOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { +extension OnlyHttpApiKeyAndBearerAuthOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> OnlyHttpApiKeyAndBearerAuthOutput { + return OnlyHttpApiKeyAndBearerAuthOutput() } } diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthOutput.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthOutput.swift index 0a033be71..a284ee096 100644 --- a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthOutput.swift +++ b/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthOutput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct OnlyHttpApiKeyAndBearerAuthOutput { diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthOutputError+HttpResponseBinding.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthOutputError+HttpResponseBinding.swift deleted file mode 100644 index 2b5b53b9c..000000000 --- a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthOutputError+HttpResponseBinding.swift +++ /dev/null @@ -1,13 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime -import SmithyTestUtil - -enum OnlyHttpApiKeyAndBearerAuthOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let defaultError = try await SmithyTestUtil.JSONError(httpResponse: httpResponse) - switch defaultError.errorType { - default: return try await ClientRuntime.UnknownHTTPServiceError.makeError(httpResponse: httpResponse, message: defaultError.errorMessage, typeName: defaultError.errorType) - } - } -} diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthOutputError+HttpResponseErrorBinding.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthOutputError+HttpResponseErrorBinding.swift new file mode 100644 index 000000000..9fb987ffd --- /dev/null +++ b/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthOutputError+HttpResponseErrorBinding.swift @@ -0,0 +1,19 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite +import SmithyTestUtil + +enum OnlyHttpApiKeyAndBearerAuthOutputError { + + static func httpError(from httpResponse: ClientRuntime.HttpResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let baseError = try SmithyTestUtil.TestBaseError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: false) + if let error = baseError.customError() { return error } + switch baseError.code { + default: return try ClientRuntime.UnknownHTTPServiceError.makeError(baseError: baseError) + } + } +} diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedInput.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedInput.swift index a95573114..4d23372b4 100644 --- a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedInput.swift +++ b/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedInput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct OnlyHttpApiKeyAndBearerAuthReversedInput { diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedInputBody+Decodable.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedInputBody+Decodable.swift deleted file mode 100644 index 0ff8fccc8..000000000 --- a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedInputBody+Decodable.swift +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct OnlyHttpApiKeyAndBearerAuthReversedInputBody { -} - -extension OnlyHttpApiKeyAndBearerAuthReversedInputBody: Swift.Decodable { - - public init(from decoder: Swift.Decoder) throws { - } -} diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedOutput+HttpResponseBinding.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedOutput+HttpResponseBinding.swift index 8b33690f7..0db99bb05 100644 --- a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedOutput+HttpResponseBinding.swift +++ b/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedOutput+HttpResponseBinding.swift @@ -1,8 +1,12 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyJSON +import SmithyReadWrite -extension OnlyHttpApiKeyAndBearerAuthReversedOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { +extension OnlyHttpApiKeyAndBearerAuthReversedOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> OnlyHttpApiKeyAndBearerAuthReversedOutput { + return OnlyHttpApiKeyAndBearerAuthReversedOutput() } } diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedOutput.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedOutput.swift index 442231c64..13ee0e940 100644 --- a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedOutput.swift +++ b/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedOutput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct OnlyHttpApiKeyAndBearerAuthReversedOutput { diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedOutputError+HttpResponseBinding.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedOutputError+HttpResponseBinding.swift deleted file mode 100644 index 6066752af..000000000 --- a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedOutputError+HttpResponseBinding.swift +++ /dev/null @@ -1,13 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime -import SmithyTestUtil - -enum OnlyHttpApiKeyAndBearerAuthReversedOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let defaultError = try await SmithyTestUtil.JSONError(httpResponse: httpResponse) - switch defaultError.errorType { - default: return try await ClientRuntime.UnknownHTTPServiceError.makeError(httpResponse: httpResponse, message: defaultError.errorMessage, typeName: defaultError.errorType) - } - } -} diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedOutputError+HttpResponseErrorBinding.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedOutputError+HttpResponseErrorBinding.swift new file mode 100644 index 000000000..b8b83478f --- /dev/null +++ b/Sources/WeatherSDK/models/OnlyHttpApiKeyAndBearerAuthReversedOutputError+HttpResponseErrorBinding.swift @@ -0,0 +1,19 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite +import SmithyTestUtil + +enum OnlyHttpApiKeyAndBearerAuthReversedOutputError { + + static func httpError(from httpResponse: ClientRuntime.HttpResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let baseError = try SmithyTestUtil.TestBaseError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: false) + if let error = baseError.customError() { return error } + switch baseError.code { + default: return try ClientRuntime.UnknownHTTPServiceError.makeError(baseError: baseError) + } + } +} diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthInput.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthInput.swift index 38bcb3a92..ed5db727f 100644 --- a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthInput.swift +++ b/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthInput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct OnlyHttpApiKeyAuthInput { diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthInputBody+Decodable.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthInputBody+Decodable.swift deleted file mode 100644 index d8cade4a5..000000000 --- a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthInputBody+Decodable.swift +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct OnlyHttpApiKeyAuthInputBody { -} - -extension OnlyHttpApiKeyAuthInputBody: Swift.Decodable { - - public init(from decoder: Swift.Decoder) throws { - } -} diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalInput.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalInput.swift index f15ca9fd5..05444d4a7 100644 --- a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalInput.swift +++ b/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalInput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct OnlyHttpApiKeyAuthOptionalInput { diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalInputBody+Decodable.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalInputBody+Decodable.swift deleted file mode 100644 index c3bfa702a..000000000 --- a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalInputBody+Decodable.swift +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct OnlyHttpApiKeyAuthOptionalInputBody { -} - -extension OnlyHttpApiKeyAuthOptionalInputBody: Swift.Decodable { - - public init(from decoder: Swift.Decoder) throws { - } -} diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalOutput+HttpResponseBinding.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalOutput+HttpResponseBinding.swift index 0f0c841c7..0f7f13d9c 100644 --- a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalOutput+HttpResponseBinding.swift +++ b/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalOutput+HttpResponseBinding.swift @@ -1,8 +1,12 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyJSON +import SmithyReadWrite -extension OnlyHttpApiKeyAuthOptionalOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { +extension OnlyHttpApiKeyAuthOptionalOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> OnlyHttpApiKeyAuthOptionalOutput { + return OnlyHttpApiKeyAuthOptionalOutput() } } diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalOutput.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalOutput.swift index 6d3fd3135..b7edd2cda 100644 --- a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalOutput.swift +++ b/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalOutput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct OnlyHttpApiKeyAuthOptionalOutput { diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalOutputError+HttpResponseBinding.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalOutputError+HttpResponseBinding.swift deleted file mode 100644 index 91a81aa7d..000000000 --- a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalOutputError+HttpResponseBinding.swift +++ /dev/null @@ -1,13 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime -import SmithyTestUtil - -enum OnlyHttpApiKeyAuthOptionalOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let defaultError = try await SmithyTestUtil.JSONError(httpResponse: httpResponse) - switch defaultError.errorType { - default: return try await ClientRuntime.UnknownHTTPServiceError.makeError(httpResponse: httpResponse, message: defaultError.errorMessage, typeName: defaultError.errorType) - } - } -} diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalOutputError+HttpResponseErrorBinding.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalOutputError+HttpResponseErrorBinding.swift new file mode 100644 index 000000000..9dec7930f --- /dev/null +++ b/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOptionalOutputError+HttpResponseErrorBinding.swift @@ -0,0 +1,19 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite +import SmithyTestUtil + +enum OnlyHttpApiKeyAuthOptionalOutputError { + + static func httpError(from httpResponse: ClientRuntime.HttpResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let baseError = try SmithyTestUtil.TestBaseError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: false) + if let error = baseError.customError() { return error } + switch baseError.code { + default: return try ClientRuntime.UnknownHTTPServiceError.makeError(baseError: baseError) + } + } +} diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOutput+HttpResponseBinding.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOutput+HttpResponseBinding.swift index 4c7de888f..db692d53c 100644 --- a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOutput+HttpResponseBinding.swift +++ b/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOutput+HttpResponseBinding.swift @@ -1,8 +1,12 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyJSON +import SmithyReadWrite -extension OnlyHttpApiKeyAuthOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { +extension OnlyHttpApiKeyAuthOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> OnlyHttpApiKeyAuthOutput { + return OnlyHttpApiKeyAuthOutput() } } diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOutput.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOutput.swift index 0bf71ce53..a9adc2a64 100644 --- a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOutput.swift +++ b/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOutput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct OnlyHttpApiKeyAuthOutput { diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOutputError+HttpResponseBinding.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOutputError+HttpResponseBinding.swift deleted file mode 100644 index 64e459108..000000000 --- a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOutputError+HttpResponseBinding.swift +++ /dev/null @@ -1,13 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime -import SmithyTestUtil - -enum OnlyHttpApiKeyAuthOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let defaultError = try await SmithyTestUtil.JSONError(httpResponse: httpResponse) - switch defaultError.errorType { - default: return try await ClientRuntime.UnknownHTTPServiceError.makeError(httpResponse: httpResponse, message: defaultError.errorMessage, typeName: defaultError.errorType) - } - } -} diff --git a/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOutputError+HttpResponseErrorBinding.swift b/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOutputError+HttpResponseErrorBinding.swift new file mode 100644 index 000000000..db8fc2374 --- /dev/null +++ b/Sources/WeatherSDK/models/OnlyHttpApiKeyAuthOutputError+HttpResponseErrorBinding.swift @@ -0,0 +1,19 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite +import SmithyTestUtil + +enum OnlyHttpApiKeyAuthOutputError { + + static func httpError(from httpResponse: ClientRuntime.HttpResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let baseError = try SmithyTestUtil.TestBaseError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: false) + if let error = baseError.customError() { return error } + switch baseError.code { + default: return try ClientRuntime.UnknownHTTPServiceError.makeError(baseError: baseError) + } + } +} diff --git a/Sources/WeatherSDK/models/OnlyHttpBearerAuthInput.swift b/Sources/WeatherSDK/models/OnlyHttpBearerAuthInput.swift index beb2a0389..0a47f748a 100644 --- a/Sources/WeatherSDK/models/OnlyHttpBearerAuthInput.swift +++ b/Sources/WeatherSDK/models/OnlyHttpBearerAuthInput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct OnlyHttpBearerAuthInput { diff --git a/Sources/WeatherSDK/models/OnlyHttpBearerAuthInputBody+Decodable.swift b/Sources/WeatherSDK/models/OnlyHttpBearerAuthInputBody+Decodable.swift deleted file mode 100644 index 5d985f222..000000000 --- a/Sources/WeatherSDK/models/OnlyHttpBearerAuthInputBody+Decodable.swift +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct OnlyHttpBearerAuthInputBody { -} - -extension OnlyHttpBearerAuthInputBody: Swift.Decodable { - - public init(from decoder: Swift.Decoder) throws { - } -} diff --git a/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalInput.swift b/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalInput.swift index 431db9802..16407ae39 100644 --- a/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalInput.swift +++ b/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalInput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct OnlyHttpBearerAuthOptionalInput { diff --git a/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalInputBody+Decodable.swift b/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalInputBody+Decodable.swift deleted file mode 100644 index 738c81497..000000000 --- a/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalInputBody+Decodable.swift +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct OnlyHttpBearerAuthOptionalInputBody { -} - -extension OnlyHttpBearerAuthOptionalInputBody: Swift.Decodable { - - public init(from decoder: Swift.Decoder) throws { - } -} diff --git a/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalOutput+HttpResponseBinding.swift b/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalOutput+HttpResponseBinding.swift index 9e3fe4f42..c67f65d78 100644 --- a/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalOutput+HttpResponseBinding.swift +++ b/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalOutput+HttpResponseBinding.swift @@ -1,8 +1,12 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyJSON +import SmithyReadWrite -extension OnlyHttpBearerAuthOptionalOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { +extension OnlyHttpBearerAuthOptionalOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> OnlyHttpBearerAuthOptionalOutput { + return OnlyHttpBearerAuthOptionalOutput() } } diff --git a/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalOutput.swift b/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalOutput.swift index fa8fa4185..46f9a5fc2 100644 --- a/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalOutput.swift +++ b/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalOutput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct OnlyHttpBearerAuthOptionalOutput { diff --git a/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalOutputError+HttpResponseBinding.swift b/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalOutputError+HttpResponseBinding.swift deleted file mode 100644 index a4e2e0f50..000000000 --- a/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalOutputError+HttpResponseBinding.swift +++ /dev/null @@ -1,13 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime -import SmithyTestUtil - -enum OnlyHttpBearerAuthOptionalOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let defaultError = try await SmithyTestUtil.JSONError(httpResponse: httpResponse) - switch defaultError.errorType { - default: return try await ClientRuntime.UnknownHTTPServiceError.makeError(httpResponse: httpResponse, message: defaultError.errorMessage, typeName: defaultError.errorType) - } - } -} diff --git a/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalOutputError+HttpResponseErrorBinding.swift b/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalOutputError+HttpResponseErrorBinding.swift new file mode 100644 index 000000000..7134a8e97 --- /dev/null +++ b/Sources/WeatherSDK/models/OnlyHttpBearerAuthOptionalOutputError+HttpResponseErrorBinding.swift @@ -0,0 +1,19 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite +import SmithyTestUtil + +enum OnlyHttpBearerAuthOptionalOutputError { + + static func httpError(from httpResponse: ClientRuntime.HttpResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let baseError = try SmithyTestUtil.TestBaseError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: false) + if let error = baseError.customError() { return error } + switch baseError.code { + default: return try ClientRuntime.UnknownHTTPServiceError.makeError(baseError: baseError) + } + } +} diff --git a/Sources/WeatherSDK/models/OnlyHttpBearerAuthOutput+HttpResponseBinding.swift b/Sources/WeatherSDK/models/OnlyHttpBearerAuthOutput+HttpResponseBinding.swift index a203c809f..73b485185 100644 --- a/Sources/WeatherSDK/models/OnlyHttpBearerAuthOutput+HttpResponseBinding.swift +++ b/Sources/WeatherSDK/models/OnlyHttpBearerAuthOutput+HttpResponseBinding.swift @@ -1,8 +1,12 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyJSON +import SmithyReadWrite -extension OnlyHttpBearerAuthOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { +extension OnlyHttpBearerAuthOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> OnlyHttpBearerAuthOutput { + return OnlyHttpBearerAuthOutput() } } diff --git a/Sources/WeatherSDK/models/OnlyHttpBearerAuthOutput.swift b/Sources/WeatherSDK/models/OnlyHttpBearerAuthOutput.swift index 543b482db..8d01f0f81 100644 --- a/Sources/WeatherSDK/models/OnlyHttpBearerAuthOutput.swift +++ b/Sources/WeatherSDK/models/OnlyHttpBearerAuthOutput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct OnlyHttpBearerAuthOutput { diff --git a/Sources/WeatherSDK/models/OnlyHttpBearerAuthOutputError+HttpResponseBinding.swift b/Sources/WeatherSDK/models/OnlyHttpBearerAuthOutputError+HttpResponseBinding.swift deleted file mode 100644 index 979bc7e5e..000000000 --- a/Sources/WeatherSDK/models/OnlyHttpBearerAuthOutputError+HttpResponseBinding.swift +++ /dev/null @@ -1,13 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime -import SmithyTestUtil - -enum OnlyHttpBearerAuthOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let defaultError = try await SmithyTestUtil.JSONError(httpResponse: httpResponse) - switch defaultError.errorType { - default: return try await ClientRuntime.UnknownHTTPServiceError.makeError(httpResponse: httpResponse, message: defaultError.errorMessage, typeName: defaultError.errorType) - } - } -} diff --git a/Sources/WeatherSDK/models/OnlyHttpBearerAuthOutputError+HttpResponseErrorBinding.swift b/Sources/WeatherSDK/models/OnlyHttpBearerAuthOutputError+HttpResponseErrorBinding.swift new file mode 100644 index 000000000..7143a4816 --- /dev/null +++ b/Sources/WeatherSDK/models/OnlyHttpBearerAuthOutputError+HttpResponseErrorBinding.swift @@ -0,0 +1,19 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite +import SmithyTestUtil + +enum OnlyHttpBearerAuthOutputError { + + static func httpError(from httpResponse: ClientRuntime.HttpResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let baseError = try SmithyTestUtil.TestBaseError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: false) + if let error = baseError.customError() { return error } + switch baseError.code { + default: return try ClientRuntime.UnknownHTTPServiceError.makeError(baseError: baseError) + } + } +} diff --git a/Sources/WeatherSDK/models/OnlySigv4AuthInput.swift b/Sources/WeatherSDK/models/OnlySigv4AuthInput.swift index e8e32da66..c5c2079d8 100644 --- a/Sources/WeatherSDK/models/OnlySigv4AuthInput.swift +++ b/Sources/WeatherSDK/models/OnlySigv4AuthInput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct OnlySigv4AuthInput { diff --git a/Sources/WeatherSDK/models/OnlySigv4AuthInputBody+Decodable.swift b/Sources/WeatherSDK/models/OnlySigv4AuthInputBody+Decodable.swift deleted file mode 100644 index bf62a34c1..000000000 --- a/Sources/WeatherSDK/models/OnlySigv4AuthInputBody+Decodable.swift +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct OnlySigv4AuthInputBody { -} - -extension OnlySigv4AuthInputBody: Swift.Decodable { - - public init(from decoder: Swift.Decoder) throws { - } -} diff --git a/Sources/WeatherSDK/models/OnlySigv4AuthOptionalInput.swift b/Sources/WeatherSDK/models/OnlySigv4AuthOptionalInput.swift index d5ed2ab51..2633da172 100644 --- a/Sources/WeatherSDK/models/OnlySigv4AuthOptionalInput.swift +++ b/Sources/WeatherSDK/models/OnlySigv4AuthOptionalInput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct OnlySigv4AuthOptionalInput { diff --git a/Sources/WeatherSDK/models/OnlySigv4AuthOptionalInputBody+Decodable.swift b/Sources/WeatherSDK/models/OnlySigv4AuthOptionalInputBody+Decodable.swift deleted file mode 100644 index f2348d1c9..000000000 --- a/Sources/WeatherSDK/models/OnlySigv4AuthOptionalInputBody+Decodable.swift +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct OnlySigv4AuthOptionalInputBody { -} - -extension OnlySigv4AuthOptionalInputBody: Swift.Decodable { - - public init(from decoder: Swift.Decoder) throws { - } -} diff --git a/Sources/WeatherSDK/models/OnlySigv4AuthOptionalOutput+HttpResponseBinding.swift b/Sources/WeatherSDK/models/OnlySigv4AuthOptionalOutput+HttpResponseBinding.swift index cc6dad886..31eb8509e 100644 --- a/Sources/WeatherSDK/models/OnlySigv4AuthOptionalOutput+HttpResponseBinding.swift +++ b/Sources/WeatherSDK/models/OnlySigv4AuthOptionalOutput+HttpResponseBinding.swift @@ -1,8 +1,12 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyJSON +import SmithyReadWrite -extension OnlySigv4AuthOptionalOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { +extension OnlySigv4AuthOptionalOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> OnlySigv4AuthOptionalOutput { + return OnlySigv4AuthOptionalOutput() } } diff --git a/Sources/WeatherSDK/models/OnlySigv4AuthOptionalOutput.swift b/Sources/WeatherSDK/models/OnlySigv4AuthOptionalOutput.swift index 60acbebd2..30d5b57c0 100644 --- a/Sources/WeatherSDK/models/OnlySigv4AuthOptionalOutput.swift +++ b/Sources/WeatherSDK/models/OnlySigv4AuthOptionalOutput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct OnlySigv4AuthOptionalOutput { diff --git a/Sources/WeatherSDK/models/OnlySigv4AuthOptionalOutputError+HttpResponseBinding.swift b/Sources/WeatherSDK/models/OnlySigv4AuthOptionalOutputError+HttpResponseBinding.swift deleted file mode 100644 index 74d6b1ac4..000000000 --- a/Sources/WeatherSDK/models/OnlySigv4AuthOptionalOutputError+HttpResponseBinding.swift +++ /dev/null @@ -1,13 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime -import SmithyTestUtil - -enum OnlySigv4AuthOptionalOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let defaultError = try await SmithyTestUtil.JSONError(httpResponse: httpResponse) - switch defaultError.errorType { - default: return try await ClientRuntime.UnknownHTTPServiceError.makeError(httpResponse: httpResponse, message: defaultError.errorMessage, typeName: defaultError.errorType) - } - } -} diff --git a/Sources/WeatherSDK/models/OnlySigv4AuthOptionalOutputError+HttpResponseErrorBinding.swift b/Sources/WeatherSDK/models/OnlySigv4AuthOptionalOutputError+HttpResponseErrorBinding.swift new file mode 100644 index 000000000..83391469d --- /dev/null +++ b/Sources/WeatherSDK/models/OnlySigv4AuthOptionalOutputError+HttpResponseErrorBinding.swift @@ -0,0 +1,19 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite +import SmithyTestUtil + +enum OnlySigv4AuthOptionalOutputError { + + static func httpError(from httpResponse: ClientRuntime.HttpResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let baseError = try SmithyTestUtil.TestBaseError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: false) + if let error = baseError.customError() { return error } + switch baseError.code { + default: return try ClientRuntime.UnknownHTTPServiceError.makeError(baseError: baseError) + } + } +} diff --git a/Sources/WeatherSDK/models/OnlySigv4AuthOutput+HttpResponseBinding.swift b/Sources/WeatherSDK/models/OnlySigv4AuthOutput+HttpResponseBinding.swift index 79bd5553e..a6d1a0d08 100644 --- a/Sources/WeatherSDK/models/OnlySigv4AuthOutput+HttpResponseBinding.swift +++ b/Sources/WeatherSDK/models/OnlySigv4AuthOutput+HttpResponseBinding.swift @@ -1,8 +1,12 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyJSON +import SmithyReadWrite -extension OnlySigv4AuthOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { +extension OnlySigv4AuthOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> OnlySigv4AuthOutput { + return OnlySigv4AuthOutput() } } diff --git a/Sources/WeatherSDK/models/OnlySigv4AuthOutput.swift b/Sources/WeatherSDK/models/OnlySigv4AuthOutput.swift index 4a603d7c9..2476976d9 100644 --- a/Sources/WeatherSDK/models/OnlySigv4AuthOutput.swift +++ b/Sources/WeatherSDK/models/OnlySigv4AuthOutput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct OnlySigv4AuthOutput { diff --git a/Sources/WeatherSDK/models/OnlySigv4AuthOutputError+HttpResponseBinding.swift b/Sources/WeatherSDK/models/OnlySigv4AuthOutputError+HttpResponseBinding.swift deleted file mode 100644 index 5335eee1f..000000000 --- a/Sources/WeatherSDK/models/OnlySigv4AuthOutputError+HttpResponseBinding.swift +++ /dev/null @@ -1,13 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime -import SmithyTestUtil - -enum OnlySigv4AuthOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let defaultError = try await SmithyTestUtil.JSONError(httpResponse: httpResponse) - switch defaultError.errorType { - default: return try await ClientRuntime.UnknownHTTPServiceError.makeError(httpResponse: httpResponse, message: defaultError.errorMessage, typeName: defaultError.errorType) - } - } -} diff --git a/Sources/WeatherSDK/models/OnlySigv4AuthOutputError+HttpResponseErrorBinding.swift b/Sources/WeatherSDK/models/OnlySigv4AuthOutputError+HttpResponseErrorBinding.swift new file mode 100644 index 000000000..1e396cdb0 --- /dev/null +++ b/Sources/WeatherSDK/models/OnlySigv4AuthOutputError+HttpResponseErrorBinding.swift @@ -0,0 +1,19 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite +import SmithyTestUtil + +enum OnlySigv4AuthOutputError { + + static func httpError(from httpResponse: ClientRuntime.HttpResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let baseError = try SmithyTestUtil.TestBaseError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: false) + if let error = baseError.customError() { return error } + switch baseError.code { + default: return try ClientRuntime.UnknownHTTPServiceError.makeError(baseError: baseError) + } + } +} diff --git a/Sources/WeatherSDK/models/OtherStructure+Codable.swift b/Sources/WeatherSDK/models/OtherStructure+Codable.swift deleted file mode 100644 index 7e476288c..000000000 --- a/Sources/WeatherSDK/models/OtherStructure+Codable.swift +++ /dev/null @@ -1,14 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -extension WeatherClientTypes.OtherStructure: Swift.Codable { - - public func encode(to encoder: Swift.Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode([String:String]()) - } - - public init(from decoder: Swift.Decoder) throws { - } -} diff --git a/Sources/WeatherSDK/models/OtherStructure+ReadWrite.swift b/Sources/WeatherSDK/models/OtherStructure+ReadWrite.swift new file mode 100644 index 000000000..f6f59dd01 --- /dev/null +++ b/Sources/WeatherSDK/models/OtherStructure+ReadWrite.swift @@ -0,0 +1,18 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite + +extension WeatherClientTypes.OtherStructure { + + static func write(value: WeatherClientTypes.OtherStructure?, to writer: SmithyJSON.Writer) throws { + guard value != nil else { return } + _ = writer[""] // create an empty structure + } + + static func read(from reader: SmithyJSON.Reader) throws -> WeatherClientTypes.OtherStructure { + guard reader.hasContent else { throw SmithyReadWrite.ReaderError.requiredValueNotPresent } + return WeatherClientTypes.OtherStructure() + } +} diff --git a/Sources/WeatherSDK/models/OtherStructure.swift b/Sources/WeatherSDK/models/OtherStructure.swift index f4602049d..a7bea4ef7 100644 --- a/Sources/WeatherSDK/models/OtherStructure.swift +++ b/Sources/WeatherSDK/models/OtherStructure.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite extension WeatherClientTypes { public struct OtherStructure { diff --git a/Sources/WeatherSDK/models/Precipitation+Codable.swift b/Sources/WeatherSDK/models/Precipitation+Codable.swift deleted file mode 100644 index 93169f310..000000000 --- a/Sources/WeatherSDK/models/Precipitation+Codable.swift +++ /dev/null @@ -1,106 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -extension WeatherClientTypes.Precipitation: Swift.Codable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case baz - case blob - case foo - case hail - case mixed - case other - case rain - case sdkUnknown - case sleet - case snow - } - - public func encode(to encoder: Swift.Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - switch self { - case let .baz(baz): - try container.encode(baz, forKey: .baz) - case let .blob(blob): - try container.encode(blob.base64EncodedString(), forKey: .blob) - case let .foo(foo): - try container.encode(foo, forKey: .foo) - case let .hail(hail): - var hailContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: .hail) - for (dictKey0, stringMap0) in hail { - try hailContainer.encode(stringMap0, forKey: ClientRuntime.Key(stringValue: dictKey0)) - } - case let .mixed(mixed): - try container.encode(mixed.rawValue, forKey: .mixed) - case let .other(other): - try container.encode(other, forKey: .other) - case let .rain(rain): - try container.encode(rain, forKey: .rain) - case let .sleet(sleet): - try container.encode(sleet, forKey: .sleet) - case let .snow(snow): - try container.encode(snow.rawValue, forKey: .snow) - case let .sdkUnknown(sdkUnknown): - try container.encode(sdkUnknown, forKey: .sdkUnknown) - } - } - - public init(from decoder: Swift.Decoder) throws { - let values = try decoder.container(keyedBy: CodingKeys.self) - let rainDecoded = try values.decodeIfPresent(Swift.Bool.self, forKey: .rain) - if let rain = rainDecoded { - self = .rain(rain) - return - } - let sleetDecoded = try values.decodeIfPresent(Swift.Bool.self, forKey: .sleet) - if let sleet = sleetDecoded { - self = .sleet(sleet) - return - } - let hailContainer = try values.decodeIfPresent([Swift.String: Swift.String?].self, forKey: .hail) - var hailDecoded0: [Swift.String:Swift.String]? = nil - if let hailContainer = hailContainer { - hailDecoded0 = [Swift.String:Swift.String]() - for (key0, string0) in hailContainer { - if let string0 = string0 { - hailDecoded0?[key0] = string0 - } - } - } - if let hail = hailDecoded0 { - self = .hail(hail) - return - } - let snowDecoded = try values.decodeIfPresent(WeatherClientTypes.SimpleYesNo.self, forKey: .snow) - if let snow = snowDecoded { - self = .snow(snow) - return - } - let mixedDecoded = try values.decodeIfPresent(WeatherClientTypes.TypedYesNo.self, forKey: .mixed) - if let mixed = mixedDecoded { - self = .mixed(mixed) - return - } - let otherDecoded = try values.decodeIfPresent(WeatherClientTypes.OtherStructure.self, forKey: .other) - if let other = otherDecoded { - self = .other(other) - return - } - let blobDecoded = try values.decodeIfPresent(ClientRuntime.Data.self, forKey: .blob) - if let blob = blobDecoded { - self = .blob(blob) - return - } - let fooDecoded = try values.decodeIfPresent(WeatherClientTypes.Foo.self, forKey: .foo) - if let foo = fooDecoded { - self = .foo(foo) - return - } - let bazDecoded = try values.decodeIfPresent(WeatherClientTypes.Baz.self, forKey: .baz) - if let baz = bazDecoded { - self = .baz(baz) - return - } - self = .sdkUnknown("") - } -} diff --git a/Sources/WeatherSDK/models/Precipitation+ReadWrite.swift b/Sources/WeatherSDK/models/Precipitation+ReadWrite.swift new file mode 100644 index 000000000..b103c998b --- /dev/null +++ b/Sources/WeatherSDK/models/Precipitation+ReadWrite.swift @@ -0,0 +1,61 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite + +extension WeatherClientTypes.Precipitation { + + static func write(value: WeatherClientTypes.Precipitation?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + switch value { + case let .baz(baz): + try writer["baz"].write(baz, with: WeatherClientTypes.Baz.write(value:to:)) + case let .blob(blob): + try writer["blob"].write(blob) + case let .foo(foo): + try writer["foo"].write(foo, with: WeatherClientTypes.Foo.write(value:to:)) + case let .hail(hail): + try writer["hail"].writeMap(hail, valueWritingClosure: Swift.String.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + case let .mixed(mixed): + try writer["mixed"].write(mixed) + case let .other(other): + try writer["other"].write(other, with: WeatherClientTypes.OtherStructure.write(value:to:)) + case let .rain(rain): + try writer["rain"].write(rain) + case let .sleet(sleet): + try writer["sleet"].write(sleet) + case let .snow(snow): + try writer["snow"].write(snow) + case let .sdkUnknown(sdkUnknown): + try writer["sdkUnknown"].write(sdkUnknown) + } + } + + static func read(from reader: SmithyJSON.Reader) throws -> WeatherClientTypes.Precipitation { + 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 "rain": + return .rain(try reader["rain"].read() ?? false) + case "sleet": + return .sleet(try reader["sleet"].read() ?? false) + case "hail": + return .hail(try reader["hail"].readMap(valueReadingClosure: Swift.String.read(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false)) + case "snow": + return .snow(try reader["snow"].read()) + case "mixed": + return .mixed(try reader["mixed"].read()) + case "other": + return .other(try reader["other"].read(with: WeatherClientTypes.OtherStructure.read(from:))) + case "blob": + return .blob(try reader["blob"].read()) + case "foo": + return .foo(try reader["foo"].read(with: WeatherClientTypes.Foo.read(from:))) + case "baz": + return .baz(try reader["baz"].read(with: WeatherClientTypes.Baz.read(from:))) + default: + return .sdkUnknown(name ?? "") + } + } +} diff --git a/Sources/WeatherSDK/models/SameAsServiceInput.swift b/Sources/WeatherSDK/models/SameAsServiceInput.swift index 613cb04b1..460b2d74e 100644 --- a/Sources/WeatherSDK/models/SameAsServiceInput.swift +++ b/Sources/WeatherSDK/models/SameAsServiceInput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct SameAsServiceInput { diff --git a/Sources/WeatherSDK/models/SameAsServiceInputBody+Decodable.swift b/Sources/WeatherSDK/models/SameAsServiceInputBody+Decodable.swift deleted file mode 100644 index c7d307384..000000000 --- a/Sources/WeatherSDK/models/SameAsServiceInputBody+Decodable.swift +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct SameAsServiceInputBody { -} - -extension SameAsServiceInputBody: Swift.Decodable { - - public init(from decoder: Swift.Decoder) throws { - } -} diff --git a/Sources/WeatherSDK/models/SameAsServiceOutput+HttpResponseBinding.swift b/Sources/WeatherSDK/models/SameAsServiceOutput+HttpResponseBinding.swift index c5bcff4d8..98e35c366 100644 --- a/Sources/WeatherSDK/models/SameAsServiceOutput+HttpResponseBinding.swift +++ b/Sources/WeatherSDK/models/SameAsServiceOutput+HttpResponseBinding.swift @@ -1,8 +1,12 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyJSON +import SmithyReadWrite -extension SameAsServiceOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { +extension SameAsServiceOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> SameAsServiceOutput { + return SameAsServiceOutput() } } diff --git a/Sources/WeatherSDK/models/SameAsServiceOutput.swift b/Sources/WeatherSDK/models/SameAsServiceOutput.swift index 35ff2b086..d4b1c56af 100644 --- a/Sources/WeatherSDK/models/SameAsServiceOutput.swift +++ b/Sources/WeatherSDK/models/SameAsServiceOutput.swift @@ -1,6 +1,6 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! - +import SmithyReadWrite public struct SameAsServiceOutput { diff --git a/Sources/WeatherSDK/models/SameAsServiceOutputError+HttpResponseBinding.swift b/Sources/WeatherSDK/models/SameAsServiceOutputError+HttpResponseBinding.swift deleted file mode 100644 index 527f9d948..000000000 --- a/Sources/WeatherSDK/models/SameAsServiceOutputError+HttpResponseBinding.swift +++ /dev/null @@ -1,13 +0,0 @@ -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime -import SmithyTestUtil - -enum SameAsServiceOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let defaultError = try await SmithyTestUtil.JSONError(httpResponse: httpResponse) - switch defaultError.errorType { - default: return try await ClientRuntime.UnknownHTTPServiceError.makeError(httpResponse: httpResponse, message: defaultError.errorMessage, typeName: defaultError.errorType) - } - } -} diff --git a/Sources/WeatherSDK/models/SameAsServiceOutputError+HttpResponseErrorBinding.swift b/Sources/WeatherSDK/models/SameAsServiceOutputError+HttpResponseErrorBinding.swift new file mode 100644 index 000000000..6fd2edd6d --- /dev/null +++ b/Sources/WeatherSDK/models/SameAsServiceOutputError+HttpResponseErrorBinding.swift @@ -0,0 +1,19 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime +import SmithyJSON +import SmithyReadWrite +import SmithyTestUtil + +enum SameAsServiceOutputError { + + static func httpError(from httpResponse: ClientRuntime.HttpResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let baseError = try SmithyTestUtil.TestBaseError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: false) + if let error = baseError.customError() { return error } + switch baseError.code { + default: return try ClientRuntime.UnknownHTTPServiceError.makeError(baseError: baseError) + } + } +} diff --git a/Sources/WeatherSDK/models/SimpleYesNo.swift b/Sources/WeatherSDK/models/SimpleYesNo.swift index a3149fa4d..b40270b91 100644 --- a/Sources/WeatherSDK/models/SimpleYesNo.swift +++ b/Sources/WeatherSDK/models/SimpleYesNo.swift @@ -3,7 +3,8 @@ extension WeatherClientTypes { - public enum SimpleYesNo: Swift.Equatable, Swift.RawRepresentable, Swift.CaseIterable, Swift.Codable, Swift.Hashable { + + public enum SimpleYesNo: Swift.Equatable, Swift.RawRepresentable, Swift.CaseIterable, Swift.Hashable { case no case yes case sdkUnknown(Swift.String) @@ -15,10 +16,12 @@ extension WeatherClientTypes { .sdkUnknown("") ] } + public init?(rawValue: Swift.String) { let value = Self.allCases.first(where: { $0.rawValue == rawValue }) self = value ?? Self.sdkUnknown(rawValue) } + public var rawValue: Swift.String { switch self { case .no: return "NO" @@ -26,10 +29,5 @@ extension WeatherClientTypes { case let .sdkUnknown(s): return s } } - public init(from decoder: Swift.Decoder) throws { - let container = try decoder.singleValueContainer() - let rawValue = try container.decode(RawValue.self) - self = SimpleYesNo(rawValue: rawValue) ?? SimpleYesNo.sdkUnknown(rawValue) - } } } diff --git a/Sources/WeatherSDK/models/TypedYesNo.swift b/Sources/WeatherSDK/models/TypedYesNo.swift index e28484e6b..08949a4ee 100644 --- a/Sources/WeatherSDK/models/TypedYesNo.swift +++ b/Sources/WeatherSDK/models/TypedYesNo.swift @@ -3,7 +3,8 @@ extension WeatherClientTypes { - public enum TypedYesNo: Swift.Equatable, Swift.RawRepresentable, Swift.CaseIterable, Swift.Codable, Swift.Hashable { + + public enum TypedYesNo: Swift.Equatable, Swift.RawRepresentable, Swift.CaseIterable, Swift.Hashable { case no case yes case sdkUnknown(Swift.String) @@ -15,10 +16,12 @@ extension WeatherClientTypes { .sdkUnknown("") ] } + public init?(rawValue: Swift.String) { let value = Self.allCases.first(where: { $0.rawValue == rawValue }) self = value ?? Self.sdkUnknown(rawValue) } + public var rawValue: Swift.String { switch self { case .no: return "NO" @@ -26,10 +29,5 @@ extension WeatherClientTypes { case let .sdkUnknown(s): return s } } - public init(from decoder: Swift.Decoder) throws { - let container = try decoder.singleValueContainer() - let rawValue = try container.decode(RawValue.self) - self = TypedYesNo(rawValue: rawValue) ?? TypedYesNo.sdkUnknown(rawValue) - } } } diff --git a/Tests/ClientRuntimeTests/Idempotency/IdempotencyTokenMiddlewareTests.swift b/Tests/ClientRuntimeTests/Idempotency/IdempotencyTokenMiddlewareTests.swift index d5b88fb62..9e35e8941 100644 --- a/Tests/ClientRuntimeTests/Idempotency/IdempotencyTokenMiddlewareTests.swift +++ b/Tests/ClientRuntimeTests/Idempotency/IdempotencyTokenMiddlewareTests.swift @@ -54,21 +54,9 @@ private struct TestInputType { var tokenMember: String? } -private struct TestOutputType: HttpResponseBinding { - init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder?) async throws { - // no-op - } -} - -private enum TestOutputErrorType: HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder?) async throws -> Error { - return TestError() - } -} - -private struct TestError: Error {} +private struct TestOutputType {} -private struct MockHandler: Handler { +private struct MockHandler: Handler { typealias Output = OperationOutput typealias Context = HttpContext typealias MockHandlerCallback = (Context, I) async throws -> Void diff --git a/Tests/ClientRuntimeTests/MiddlewareTests/MiddlewareStackTests.swift b/Tests/ClientRuntimeTests/MiddlewareTests/MiddlewareStackTests.swift index b66066bd3..7a0aa4a97 100644 --- a/Tests/ClientRuntimeTests/MiddlewareTests/MiddlewareStackTests.swift +++ b/Tests/ClientRuntimeTests/MiddlewareTests/MiddlewareStackTests.swift @@ -1,6 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0. +import SmithyReadWrite import XCTest import SmithyTestUtil @testable import ClientRuntime @@ -10,8 +11,6 @@ class MiddlewareStackTests: XCTestCase { let builtContext = HttpContextBuilder() .withMethod(value: .get) .withPath(value: "/") - .withEncoder(value: JSONEncoder()) - .withDecoder(value: JSONDecoder()) .withOperation(value: "Test Operation") .build() var stack = OperationStack(id: "Test Operation") @@ -36,8 +35,6 @@ class MiddlewareStackTests: XCTestCase { let builtContext = HttpContextBuilder() .withMethod(value: .get) .withPath(value: "/") - .withEncoder(value: JSONEncoder()) - .withDecoder(value: JSONDecoder()) .withOperation(value: "Test Operation") .build() var stack = OperationStack(id: "Test Operation") @@ -57,7 +54,7 @@ class MiddlewareStackTests: XCTestCase { return try await next.handle(context: context, input: requestBuilder) } stack.finalizeStep.intercept(position: .before, middleware: ContentLengthMiddleware()) - stack.deserializeStep.intercept(position: .after, middleware: DeserializeMiddleware(MockOutput.responseClosure(_:), responseErrorClosure(MockMiddlewareError.self, decoder: JSONDecoder()))) + stack.deserializeStep.intercept(position: .after, middleware: DeserializeMiddleware(MockOutput.responseClosure(_:), MockMiddlewareError.responseErrorClosure(_:))) let result = try await stack.handleMiddleware(context: builtContext, input: MockInput(), next: MockHandler(handleCallback: { (_, input) in XCTAssert(input.headers.value(for: "TestHeaderName2") == "TestHeaderValue2") @@ -82,8 +79,6 @@ class MiddlewareStackTests: XCTestCase { let builtContext = HttpContextBuilder() .withMethod(value: .get) .withPath(value: "/headers") - .withEncoder(value: JSONEncoder()) - .withDecoder(value: JSONDecoder()) .withOperation(value: "Test Operation") .build() var stack = OperationStack(id: "Test Operation") diff --git a/Tests/ClientRuntimeTests/MiddlewareTests/OperationStackTests.swift b/Tests/ClientRuntimeTests/MiddlewareTests/OperationStackTests.swift index a25e7779c..0bee7749f 100644 --- a/Tests/ClientRuntimeTests/MiddlewareTests/OperationStackTests.swift +++ b/Tests/ClientRuntimeTests/MiddlewareTests/OperationStackTests.swift @@ -17,8 +17,6 @@ class OperationStackTests: HttpRequestTestBase { let addContextValues = HttpContextBuilder() .withMethod(value: .get) .withPath(value: "/") - .withEncoder(value: JSONEncoder()) - .withDecoder(value: JSONDecoder()) .withOperation(value: "Test Operation") let builtContext = addContextValues.build() diff --git a/Tests/ClientRuntimeTests/MiddlewareTests/ProviderTests.swift b/Tests/ClientRuntimeTests/MiddlewareTests/ProviderTests.swift index 1925a868b..7beda2609 100644 --- a/Tests/ClientRuntimeTests/MiddlewareTests/ProviderTests.swift +++ b/Tests/ClientRuntimeTests/MiddlewareTests/ProviderTests.swift @@ -22,7 +22,7 @@ class ProviderTests: HttpRequestTestBase { var mockInput = MockInput() mockInput.value = 3 - let context = HttpContextBuilder().withDecoder(value: JSONDecoder()).build() + let context = HttpContextBuilder().build() var operationStack = OperationStack(id: "testURLPathOperation") operationStack.initializeStep.intercept(position: .after, middleware: URLPathMiddleware(MockInput.urlPathProvider(_:))) @@ -42,7 +42,7 @@ class ProviderTests: HttpRequestTestBase { var mockInput = MockInput() mockInput.value = 3 - let context = HttpContextBuilder().withDecoder(value: JSONDecoder()).build() + let context = HttpContextBuilder().build() var operationStack = OperationStack(id: "testURLPathOperation") operationStack.serializeStep.intercept(position: .after, middleware: QueryItemMiddleware(MockInput.queryItemProvider(_:))) @@ -72,7 +72,7 @@ class ProviderTests: HttpRequestTestBase { var mockInput = MockInput() mockInput.value = 3 - let context = HttpContextBuilder().withDecoder(value: JSONDecoder()).build() + let context = HttpContextBuilder().build() var operationStack = OperationStack(id: "testURLPathOperation") operationStack.serializeStep.intercept(position: .after, middleware: HeaderMiddleware(MockInput.headerProvider(_:))) diff --git a/Tests/ClientRuntimeTests/NetworkingTests/Http/MiddlewareTests/ContentLengthMiddlewareTests.swift b/Tests/ClientRuntimeTests/NetworkingTests/Http/MiddlewareTests/ContentLengthMiddlewareTests.swift index 7448551f1..bed3b4ffd 100644 --- a/Tests/ClientRuntimeTests/NetworkingTests/Http/MiddlewareTests/ContentLengthMiddlewareTests.swift +++ b/Tests/ClientRuntimeTests/NetworkingTests/Http/MiddlewareTests/ContentLengthMiddlewareTests.swift @@ -14,8 +14,6 @@ class ContentLengthMiddlewareTests: XCTestCase { builtContext = HttpContextBuilder() .withMethod(value: .get) .withPath(value: "/") - .withEncoder(value: JSONEncoder()) - .withDecoder(value: JSONDecoder()) .withOperation(value: "Test Operation") .build() stack = OperationStack(id: "Test Operation") diff --git a/Tests/ClientRuntimeTests/NetworkingTests/Http/MiddlewareTests/FlexibleChecksumsMiddlewareTests.swift b/Tests/ClientRuntimeTests/NetworkingTests/Http/MiddlewareTests/FlexibleChecksumsMiddlewareTests.swift index dc586fba8..5831c491b 100644 --- a/Tests/ClientRuntimeTests/NetworkingTests/Http/MiddlewareTests/FlexibleChecksumsMiddlewareTests.swift +++ b/Tests/ClientRuntimeTests/NetworkingTests/Http/MiddlewareTests/FlexibleChecksumsMiddlewareTests.swift @@ -24,8 +24,6 @@ class FlexibleChecksumsMiddlewareTests: XCTestCase { builtContext = HttpContextBuilder() .withMethod(value: .get) .withPath(value: "/") - .withEncoder(value: JSONEncoder()) - .withDecoder(value: JSONDecoder()) .withOperation(value: "Test Operation") .withLogger(value: testLogger) .build() diff --git a/Tests/ClientRuntimeTests/NetworkingTests/Http/MiddlewareTests/MutateHeaderMiddlewareTests.swift b/Tests/ClientRuntimeTests/NetworkingTests/Http/MiddlewareTests/MutateHeaderMiddlewareTests.swift index 2b5b7ea7e..4db32db94 100644 --- a/Tests/ClientRuntimeTests/NetworkingTests/Http/MiddlewareTests/MutateHeaderMiddlewareTests.swift +++ b/Tests/ClientRuntimeTests/NetworkingTests/Http/MiddlewareTests/MutateHeaderMiddlewareTests.swift @@ -22,8 +22,6 @@ class MutateHeaderMiddlewareTests: XCTestCase { builtContext = HttpContextBuilder() .withMethod(value: .get) .withPath(value: "/headers") - .withEncoder(value: JSONEncoder()) - .withDecoder(value: JSONDecoder()) .withOperation(value: "Test Operation") .build() stack = OperationStack(id: "Test Operation") diff --git a/Tests/ClientRuntimeTests/OrchestratorTests/OrchestratorTests.swift b/Tests/ClientRuntimeTests/OrchestratorTests/OrchestratorTests.swift index 9e1fee559..a5d18d4af 100644 --- a/Tests/ClientRuntimeTests/OrchestratorTests/OrchestratorTests.swift +++ b/Tests/ClientRuntimeTests/OrchestratorTests/OrchestratorTests.swift @@ -8,6 +8,7 @@ import XCTest @testable import ClientRuntime +import SmithyJSON class OrchestratorTests: XCTestCase { struct TestInput { @@ -18,6 +19,21 @@ class OrchestratorTests: XCTestCase { let bar: String } + struct TestBaseError: BaseError { + var code: String { "TestBaseError" } + let message: String? = nil + let requestID: String? = nil + public var errorBodyReader: Reader { responseReader } + + public let httpResponse: HttpResponse + private let responseReader: Reader + + public init(httpResponse: HttpResponse, responseReader: Reader, noErrorWrapping: Bool) throws { + self.httpResponse = httpResponse + self.responseReader = responseReader + } + } + struct TestError: Error, Equatable, LocalizedError { let value: String @@ -184,8 +200,6 @@ class OrchestratorTests: XCTestCase { let attributes = HttpContextBuilder() .withMethod(value: .get) .withPath(value: "/") - .withEncoder(value: JSONEncoder()) - .withDecoder(value: JSONDecoder()) .withOperation(value: "Test") .build() let traceInterceptor = TraceInterceptor(trace: trace) @@ -207,7 +221,9 @@ class OrchestratorTests: XCTestCase { let bar = try! JSONDecoder().decode(String.self, from: data!) return .success(TestOutput(bar: bar)) } else { - return .failure(try await UnknownHTTPServiceError.makeError(httpResponse: response)) + let responseReader = try SmithyJSON.Reader.from(data: try await response.data()) + let baseError = try TestBaseError(httpResponse: response, responseReader: responseReader, noErrorWrapping: true) + return .failure(try UnknownHTTPServiceError.makeError(baseError: baseError)) } }) .retryStrategy(DefaultRetryStrategy(options: RetryStrategyOptions())) diff --git a/Tests/ClientRuntimeTests/Retry/RetryIntegrationTests.swift b/Tests/ClientRuntimeTests/Retry/RetryIntegrationTests.swift index 8047c1002..6a30ab1ba 100644 --- a/Tests/ClientRuntimeTests/Retry/RetryIntegrationTests.swift +++ b/Tests/ClientRuntimeTests/Retry/RetryIntegrationTests.swift @@ -5,7 +5,8 @@ // SPDX-License-Identifier: Apache-2.0 // -import Foundation +import SmithyReadWrite +import SmithyXML import XCTest @testable import ClientRuntime @@ -186,13 +187,12 @@ private struct TestStep { private struct TestInput {} -private struct TestOutputResponse: HttpResponseBinding { +private struct TestOutputResponse { init() {} - init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder?) async throws {} } -private enum TestOutputError: HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder?) async throws -> Error { +private enum TestOutputError { + static func httpError(from httpResponse: HttpResponse) async throws -> Error { RetryIntegrationTestError.dontCallThisMethod // is never called } } diff --git a/Tests/ClientRuntimeTests/SerializationTests/FormURL/Encoder/models/QueryListsInput.swift b/Tests/ClientRuntimeTests/SerializationTests/FormURL/Encoder/models/QueryListsInput.swift deleted file mode 100644 index d394fae09..000000000 --- a/Tests/ClientRuntimeTests/SerializationTests/FormURL/Encoder/models/QueryListsInput.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -@testable import ClientRuntime - -public struct QueryListsInput: Equatable { - public let flattenedListArg: [String]? - public let listArg: [String]? - - public init ( - flattenedListArg: [String]? = nil, - listArg: [String]? = nil - ) - { - self.flattenedListArg = flattenedListArg - self.listArg = listArg - } -} - -extension QueryListsInput: Encodable { - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: Key.self) - if let flattenedListArg = flattenedListArg { - if flattenedListArg.isEmpty { - var flattenedListArgContainer = container.nestedUnkeyedContainer(forKey: Key("FlattenedListArg")) - try flattenedListArgContainer.encodeNil() - } else { - for (idx,string0) in flattenedListArg.enumerated() { - try container.encode(string0, forKey: Key("FlattenedListArg.\(idx.advanced(by: 1))")) - } - } - } - if let listArg = listArg { - var listArgContainer = container.nestedContainer(keyedBy: Key.self, forKey: Key("ListArg")) - for (idx, string0) in listArg.enumerated() { - try listArgContainer.encode(string0, forKey: Key("member.\(idx.advanced(by: 1))")) - } - } - } -} diff --git a/Tests/ClientRuntimeTests/SerializationTests/FormURL/Encoder/models/QueryMapsInput.swift b/Tests/ClientRuntimeTests/SerializationTests/FormURL/Encoder/models/QueryMapsInput.swift deleted file mode 100644 index 58791bdf5..000000000 --- a/Tests/ClientRuntimeTests/SerializationTests/FormURL/Encoder/models/QueryMapsInput.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -@testable import ClientRuntime - -public struct QueryMapsInput: Equatable { - public let flattenedMap: [String:String]? - public let mapArg: [String:String]? - - public init ( - flattenedMap: [String:String]? = nil, - mapArg: [String:String]? = nil - ) - { - self.flattenedMap = flattenedMap - self.mapArg = mapArg - } -} - -extension QueryMapsInput: Encodable { - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: Key.self) - if let flattenedMap = flattenedMap { - if flattenedMap.isEmpty { - let _ = container.nestedContainer(keyedBy: Key.self, forKey: Key("FlattenedMap")) - } else { - for (index, element) in flattenedMap.enumerated() { - let stringKey0 = element.key - let stringValue0 = element.value - var nestedContainer0 = container.nestedContainer(keyedBy: Key.self, forKey: Key("FlattenedMap.\(index.advanced(by: 1))")) - try nestedContainer0.encode(stringKey0, forKey: Key("key")) - try nestedContainer0.encode(stringValue0, forKey: Key("value")) - } - } - } - if let mapArg = mapArg { - var mapArgContainer = container.nestedContainer(keyedBy: Key.self, forKey: Key("MapArg")) - for (index, element0) in mapArg.enumerated() { - let stringKey0 = element0.key - let stringValue0 = element0.value - var entryContainer0 = mapArgContainer.nestedContainer(keyedBy: Key.self, forKey: Key("entry.\(index.advanced(by: 1))")) - var keyContainer0 = entryContainer0.nestedContainer(keyedBy: Key.self, forKey: Key("key")) - try keyContainer0.encode(stringKey0, forKey: Key("")) - var valueContainer0 = entryContainer0.nestedContainer(keyedBy: Key.self, forKey: Key("value")) - try valueContainer0.encode(stringValue0, forKey: Key("")) - } - } - } -} diff --git a/Tests/ClientRuntimeTests/SerializationTests/FormURL/Encoder/FormURLEncoderTests.swift b/Tests/SmithyFormURLTests/FormURLEncoderTests.swift similarity index 62% rename from Tests/ClientRuntimeTests/SerializationTests/FormURL/Encoder/FormURLEncoderTests.swift rename to Tests/SmithyFormURLTests/FormURLEncoderTests.swift index 54a4b6af1..16aa6bc51 100644 --- a/Tests/ClientRuntimeTests/SerializationTests/FormURL/Encoder/FormURLEncoderTests.swift +++ b/Tests/SmithyFormURLTests/FormURLEncoderTests.swift @@ -5,17 +5,20 @@ // SPDX-License-Identifier: Apache-2.0 // +import SmithyFormURL import XCTest @testable import ClientRuntime class FormURLEncoderTests: XCTestCase { - func testFlattenedList() { + func testFlattenedList() throws { let flattenedListArg = ["listArgFlat1", "listArgFlat2"] let payload = QueryListsInput(flattenedListArg: flattenedListArg) - let encoder = FormURLEncoder() + let writer = SmithyFormURL.Writer(nodeInfo: "") - let actualData = try! encoder.encode(payload) + try QueryListsInput.write(value: payload, to: writer) + + let actualData = try writer.data() let actualDataString = String(data: actualData, encoding: .utf8)! let expectedString = """ @@ -24,12 +27,14 @@ class FormURLEncoderTests: XCTestCase { XCTAssertEqual(expectedString, actualDataString) } - func testWrappedList() { + func testWrappedList() throws { let listArg = ["listArg1", "listArg2"] let payload = QueryListsInput(listArg: listArg) - let encoder = FormURLEncoder() + let writer = SmithyFormURL.Writer(nodeInfo: "") + + try QueryListsInput.write(value: payload, to: writer) - let actualData = try! encoder.encode(payload) + let actualData = try writer.data() let actualDataString = String(data: actualData, encoding: .utf8)! let expectedString = """ @@ -38,39 +43,37 @@ class FormURLEncoderTests: XCTestCase { XCTAssertEqual(expectedString, actualDataString) } - func testFlattenedMap() { + func testFlattenedMap() throws { let flattenedMap = ["first": "hello", "second": "konnichiwa"] let payload = QueryMapsInput(flattenedMap: flattenedMap) - let encoder = FormURLEncoder() + let writer = SmithyFormURL.Writer(nodeInfo: "") - let actualData = try! encoder.encode(payload) + try QueryMapsInput.write(value: payload, to: writer) + + let actualData = try writer.data() let actualDataString = String(data: actualData, encoding: .utf8)! - let expectedStrings = [""" + let expectedString = """ FlattenedMap.1.key=first&FlattenedMap.1.value=hello&FlattenedMap.2.key=second&FlattenedMap.2.value=konnichiwa - """, """ - FlattenedMap.1.key=second&FlattenedMap.1.value=konnichiwa&FlattenedMap.2.key=first&FlattenedMap.2.value=hello - """ ] - XCTAssert(expectedStrings.contains(actualDataString)) + XCTAssertEqual(expectedString, actualDataString) } - func testWrappedMap() { + func testWrappedMap() throws { let mapArg = ["first": "hello", "second": "konnichiwa"] let payload = QueryMapsInput(mapArg: mapArg) - let encoder = FormURLEncoder() + let writer = SmithyFormURL.Writer(nodeInfo: "") + + try QueryMapsInput.write(value: payload, to: writer) - let actualData = try! encoder.encode(payload) + let actualData = try writer.data() let actualDataString = String(data: actualData, encoding: .utf8)! - let expectedStrings = [""" + let expectedString = """ MapArg.entry.1.key=first&MapArg.entry.1.value=hello&MapArg.entry.2.key=second&MapArg.entry.2.value=konnichiwa - """, """ - MapArg.entry.1.key=second&MapArg.entry.1.value=konnichiwa&MapArg.entry.2.key=first&MapArg.entry.2.value=hello - """ ] - XCTAssert(expectedStrings.contains(actualDataString)) + XCTAssertEqual(expectedString, actualDataString) } } diff --git a/Tests/SmithyFormURLTests/Models/QueryListsInput.swift b/Tests/SmithyFormURLTests/Models/QueryListsInput.swift new file mode 100644 index 000000000..c62df7b90 --- /dev/null +++ b/Tests/SmithyFormURLTests/Models/QueryListsInput.swift @@ -0,0 +1,33 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import SmithyReadWrite +import SmithyFormURL +@testable import ClientRuntime + +public struct QueryListsInput: Equatable { + public let flattenedListArg: [String]? + public let listArg: [String]? + + public init ( + flattenedListArg: [String]? = nil, + listArg: [String]? = nil + ) + { + self.flattenedListArg = flattenedListArg + self.listArg = listArg + } +} + +extension QueryListsInput { + + static func write(value: QueryListsInput?, to writer: SmithyFormURL.Writer) throws { + guard let value else { return } + try writer["FlattenedListArg"].writeList(value.flattenedListArg, memberWritingClosure: String.write(value:to:), memberNodeInfo: "member", isFlattened: true) + try writer["ListArg"].writeList(value.listArg, memberWritingClosure: String.write(value:to:), memberNodeInfo: "member", isFlattened: false) + } +} diff --git a/Tests/SmithyFormURLTests/Models/QueryMapsInput.swift b/Tests/SmithyFormURLTests/Models/QueryMapsInput.swift new file mode 100644 index 000000000..60512a8f5 --- /dev/null +++ b/Tests/SmithyFormURLTests/Models/QueryMapsInput.swift @@ -0,0 +1,32 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import SmithyFormURL +@testable import ClientRuntime + +public struct QueryMapsInput: Equatable { + public let flattenedMap: [String:String]? + public let mapArg: [String:String]? + + public init ( + flattenedMap: [String:String]? = nil, + mapArg: [String:String]? = nil + ) + { + self.flattenedMap = flattenedMap + self.mapArg = mapArg + } +} + +extension QueryMapsInput: Encodable { + + static func write(value: QueryMapsInput?, to writer: SmithyFormURL.Writer) throws { + guard let value else { return } + try writer["FlattenedMap"].writeMap(value.flattenedMap, valueWritingClosure: String.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) + try writer["MapArg"].writeMap(value.mapArg, valueWritingClosure: String.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + } +} diff --git a/Tests/SmithyJSONTests/ReaderTests.swift b/Tests/SmithyJSONTests/ReaderTests.swift new file mode 100644 index 000000000..6354f67c2 --- /dev/null +++ b/Tests/SmithyJSONTests/ReaderTests.swift @@ -0,0 +1,27 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import XCTest +@testable import SmithyJSON + +class ReaderTests: XCTestCase { + + func test_readsNil() async throws { + let jsonData = Data() + let reader = try SmithyJSON.Reader.from(data: jsonData) + XCTAssertEqual(reader.jsonNode, nil) + } + + func test_readsAJSONObject() async throws { + let jsonData = Data(""" + { "property": "potato" } + """.utf8) + let reader = try SmithyJSON.Reader.from(data: jsonData) + XCTAssertEqual(reader.children.count, 1) + XCTAssertEqual(try reader["property"].readIfPresent(), "potato") + } +} diff --git a/Tests/SmithyJSONTests/WriterTests.swift b/Tests/SmithyJSONTests/WriterTests.swift new file mode 100644 index 000000000..4d5c72595 --- /dev/null +++ b/Tests/SmithyJSONTests/WriterTests.swift @@ -0,0 +1,18 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import XCTest +@testable import SmithyJSON + +class WriterTests: XCTestCase { + + func test_writesJSON() async throws { + let writer = Writer(nodeInfo: "") + try writer["property"].write("potato") + XCTAssertEqual(String(data: try writer.data(), encoding: .utf8), "{\"property\":\"potato\"}") + } +} diff --git a/Tests/SmithyTestUtilTests/RequestTestUtilTests/HttpRequestTestBaseTests.swift b/Tests/SmithyTestUtilTests/RequestTestUtilTests/HttpRequestTestBaseTests.swift index e9d672ea4..18b593143 100644 --- a/Tests/SmithyTestUtilTests/RequestTestUtilTests/HttpRequestTestBaseTests.swift +++ b/Tests/SmithyTestUtilTests/RequestTestUtilTests/HttpRequestTestBaseTests.swift @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0. */ +import SmithyReadWrite +import SmithyJSON import SmithyTestUtil import ClientRuntime import XCTest @@ -94,15 +96,31 @@ class HttpRequestTestBaseTests: HttpRequestTestBase { struct SayHelloInputBodyMiddleware: Middleware { var id: String = "SayHelloInputBodyMiddleware" - func handle(context: HttpContext, - input: MInput, - next: H) async throws -> MOutput where H: Handler, - Self.Context == H.Context, - Self.MInput == H.Input, - Self.MOutput == H.Output { + let rootNodeInfo: SmithyJSON.Writer.NodeInfo + let inputWritingClosure: WritingClosure + + public init( + rootNodeInfo: SmithyJSON.Writer.NodeInfo, + inputWritingClosure: @escaping WritingClosure + ) { + self.rootNodeInfo = rootNodeInfo + self.inputWritingClosure = inputWritingClosure + } - let encoder = context.getEncoder() - let body = ByteStream.data(try encoder.encode(input.operationInput)) + func handle( + context: HttpContext, + input: MInput, + next: H + ) async throws -> MOutput where + H: Handler, + Self.Context == H.Context, + Self.MInput == H.Input, + Self.MOutput == H.Output { + + + let writer = SmithyJSON.Writer(nodeInfo: rootNodeInfo) + try writer.write(input.operationInput, with: inputWritingClosure) + let body = ByteStream.data(try writer.data()) input.builder.withBody(body) return try await next.handle(context: context, input: input) @@ -123,11 +141,11 @@ class HttpRequestTestBaseTests: HttpRequestTestBase { let forbiddenHeader: String? let requiredHeader: String? - init(greeting: String?, - forbiddenQuery: String?, - requiredQuery: String?, - forbiddenHeader: String?, - requiredHeader: String?) { + init(greeting: String? = nil, + forbiddenQuery: String? = nil, + requiredQuery: String? = nil, + forbiddenHeader: String? = nil, + requiredHeader: String? = nil) { self.greeting = greeting self.forbiddenQuery = forbiddenQuery self.requiredQuery = requiredQuery @@ -138,18 +156,9 @@ class HttpRequestTestBaseTests: HttpRequestTestBase { private enum CodingKeys: String, CodingKey { case greeting } - } - - struct SayHelloInputBody: Decodable, Equatable { - public let greeting: String? - private enum CodingKeys: String, CodingKey { - case greeting - } - public init (from decoder: Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let greetingDecoded = try containerValues.decodeIfPresent(String.self, forKey: .greeting) - greeting = greetingDecoded + static func write(value: SayHelloInput, to writer: SmithyJSON.Writer) throws { + try writer["greeting"].write(value.greeting) } } @@ -180,9 +189,9 @@ class HttpRequestTestBaseTests: HttpRequestTestBase { } operationStack.serializeStep.intercept(position: .before, middleware: SayHelloInputQueryItemMiddleware()) operationStack.serializeStep.intercept(position: .before, middleware: SayHelloInputHeaderMiddleware()) - operationStack.serializeStep.intercept(position: .before, middleware: SayHelloInputBodyMiddleware()) + operationStack.serializeStep.intercept(position: .before, middleware: SayHelloInputBodyMiddleware(rootNodeInfo: "", inputWritingClosure: SayHelloInput.write(value:to:))) operationStack.deserializeStep.intercept(position: .after, middleware: MockDeserializeMiddleware( - id: "TestDeserializeMiddleware", responseClosure: { _ in try MockOutput() }) { _, actual in + id: "TestDeserializeMiddleware", responseClosure: { _ in MockOutput() }) { _, actual in let forbiddenQueryParams = ["ForbiddenQuery"] for forbiddenQueryParam in forbiddenQueryParams { @@ -222,13 +231,12 @@ class HttpRequestTestBaseTests: HttpRequestTestBase { }) let context = HttpContextBuilder() - .withEncoder(value: JSONEncoder()) .withMethod(value: .post) .build() _ = try await operationStack.handleMiddleware(context: context, input: input, next: MockHandler { (_, _) in XCTFail("Deserialize was mocked out, this should fail") let httpResponse = HttpResponse(body: .noStream, statusCode: .badRequest) - let mockServiceError = try await MockMiddlewareError.makeError(httpResponse: httpResponse, decoder: context.getDecoder()) + let mockServiceError = MockMiddlewareError.responseErrorClosure(httpResponse) throw mockServiceError }) } diff --git a/Tests/SmithyTimestampsTests/TimestampSerdeUtilsTests.swift b/Tests/SmithyTimestampsTests/TimestampSerdeUtilsTests.swift index 3512e0131..1e9666553 100644 --- a/Tests/SmithyTimestampsTests/TimestampSerdeUtilsTests.swift +++ b/Tests/SmithyTimestampsTests/TimestampSerdeUtilsTests.swift @@ -32,162 +32,64 @@ class TimestampSerdeUtilsTests: XCTestCase { // MARK: - Encoding Tests // Precision difference in linux documented in https://github.com/awslabs/aws-sdk-swift/issues/1006 - func test_timestampEncodable_encodeEpochSecondsDateWithFractionalSeconds() throws { - let encoder: JSONEncoder = JSONEncoder() - let timestampEncodable = TimestampEncodable(date: testDateWithFractionalSeconds, format: .epochSeconds) - let data = try encoder.encode(timestampEncodable) - let dataAsString = String(data: data, encoding: .utf8)! - let dataAsDouble = Double(dataAsString)! - XCTAssertEqual(dataAsDouble, 673351930.12300003, accuracy: 0.001) - - } - - func test_timestampEncodable_encodeEpochSecondsDateWithoutFractionalSeconds() throws { - let encoder: JSONEncoder = JSONEncoder() - let timestampEncodable = TimestampEncodable(date: testDateWithoutFractionalSeconds, format: .epochSeconds) - let data = try encoder.encode(timestampEncodable) - let dataAsString = String(data: data, encoding: .utf8)! - let dataAsInt = Int(dataAsString)! - XCTAssertEqual(dataAsInt, 673351930) + func test_TimestampFormatter_encodeEpochSecondsDateWithFractionalSeconds() throws { + let formatter = TimestampFormatter(format: .epochSeconds) + let timestamp = formatter.string(from: testDateWithFractionalSeconds) + let timestampAsDouble = try XCTUnwrap(TimeInterval(timestamp)) + XCTAssertEqual(timestampAsDouble, 673351930.12300003, accuracy: 0.001) } - func test_timestampEncodable_encodeDateTimeWithFractionalSeconds() throws { - let encoder: JSONEncoder = JSONEncoder() - let timestampEncodable = TimestampEncodable(date: testDateWithFractionalSeconds, format: .dateTime) - let data = try encoder.encode(timestampEncodable) - let dataAsString = String(data: data, encoding: .utf8)! - XCTAssertEqual(dataAsString, "\"1991-05-04T10:12:10.123Z\"") + func test_TimestampFormatter_encodeEpochSecondsDateWithoutFractionalSeconds() throws { + let formatter = TimestampFormatter(format: .epochSeconds) + let timestamp = formatter.string(from: testDateWithoutFractionalSeconds) + let timestampAsInt = try XCTUnwrap(Int(timestamp)) + XCTAssertEqual(timestampAsInt, 673351930) } - func test_timestampEncodable_encodeDateTimeWithoutFractionalSeconds() throws { - let encoder: JSONEncoder = JSONEncoder() - let timestampEncodable = TimestampEncodable(date: testDateWithoutFractionalSeconds, format: .dateTime) - let data = try encoder.encode(timestampEncodable) - let dataAsString = String(data: data, encoding: .utf8)! - XCTAssertEqual(dataAsString, "\"1991-05-04T10:12:10Z\"") + func test_TimestampFormatter_encodeDateTimeWithFractionalSeconds() throws { + let formatter = TimestampFormatter(format: .dateTime) + let timestamp = formatter.string(from: testDateWithFractionalSeconds) + XCTAssertEqual(timestamp, "1991-05-04T10:12:10.123Z") } - func test_timestampEncodable_encodeHttpDateWithFractionalSeconds() throws { - let encoder: JSONEncoder = JSONEncoder() - let timestampEncodable = TimestampEncodable(date: testDateWithFractionalSeconds, format: .httpDate) - let data = try encoder.encode(timestampEncodable) - let dataAsString = String(data: data, encoding: .utf8)! - XCTAssertEqual(dataAsString, "\"Sat, 04 May 1991 10:12:10.123 GMT\"") + func test_TimestampFormatter_encodeDateTimeWithoutFractionalSeconds() throws { + let formatter = TimestampFormatter(format: .dateTime) + let timestamp = formatter.string(from: testDateWithoutFractionalSeconds) + XCTAssertEqual(timestamp, "1991-05-04T10:12:10Z") } - func test_timestampEncodable_encodeHttpDateWithoutFractionalSeconds() throws { - let encoder: JSONEncoder = JSONEncoder() - let timestampEncodable = TimestampEncodable(date: testDateWithoutFractionalSeconds, format: .httpDate) - let data = try encoder.encode(timestampEncodable) - let dataAsString = String(data: data, encoding: .utf8)! - XCTAssertEqual(dataAsString, "\"Sat, 04 May 1991 10:12:10 GMT\"") + func test_TimestampFormatter_encodeHttpDateWithFractionalSeconds() throws { + let formatter = TimestampFormatter(format: .httpDate) + let timestamp = formatter.string(from: testDateWithFractionalSeconds) + XCTAssertEqual(timestamp, "Sat, 04 May 1991 10:12:10.123 GMT") } - func test_encodeTimeStamp_forKeyedContainer_returnsExpectedValue() throws { - let encoder = JSONEncoder() - - struct Container: Encodable { - let timestamp: Date - enum CodingKeys: String, CodingKey { - case timestamp - } - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encodeTimestamp( - timestamp, - format: .dateTime, - forKey: .timestamp - ) - } - } - let container = Container(timestamp: testDateWithFractionalSeconds) - let data = try encoder.encode(container) - let dataAsString = String.init(data: data, encoding: .utf8)! - XCTAssertEqual(dataAsString, "{\"timestamp\":\"1991-05-04T10:12:10.123Z\"}") + func test_TimestampFormatter_encodeHttpDateWithoutFractionalSeconds() throws { + let formatter = TimestampFormatter(format: .httpDate) + let timestamp = formatter.string(from: testDateWithoutFractionalSeconds) + XCTAssertEqual(timestamp, "Sat, 04 May 1991 10:12:10 GMT") } - func test_encodeTimeStamp_forSingleValueContainer_returnsExpectedValue() throws { - let encoder = JSONEncoder() - - struct Container: Encodable { - let timestamp: Date - func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encodeTimestamp(timestamp, format: .dateTime) - } - } - let container = Container(timestamp: testDateWithFractionalSeconds) - let data = try encoder.encode(container) - let dataAsString = String.init(data: data, encoding: .utf8)! - XCTAssertEqual(dataAsString, "\"1991-05-04T10:12:10.123Z\"") - } // MARK: - Decoding Tests - func test_decodeTimestamp_returnsExpectedValue() throws { - struct Container: Decodable { - let timestamp: Date - static var format: TimestampFormat = .dateTime - enum CodingKeys: String, CodingKey { - case timestamp - } - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.timestamp = try container.decodeTimestamp(Self.format, forKey: .timestamp) - } - } - - let subjects: [(TimestampFormat, String, Date)] = [ - (.epochSeconds, "{\"timestamp\":\(testDateWithFractionalSeconds.timeIntervalSince1970)}", testDateWithFractionalSeconds), - (.epochSeconds, "{\"timestamp\":\(testDateWithoutFractionalSeconds.timeIntervalSince1970)}", testDateWithoutFractionalSeconds), - (.dateTime, "{\"timestamp\":\"1991-05-04T10:12:10.123Z\"}", testDateWithFractionalSeconds), - (.dateTime, "{\"timestamp\":\"1991-05-04T10:12:10Z\"}", testDateWithoutFractionalSeconds), - (.httpDate, "{\"timestamp\":\"Sat, 04 May 1991 10:12:10.123 GMT\"}", testDateWithFractionalSeconds), - (.httpDate, "{\"timestamp\":\"Sat, 04 May 1991 10:12:10 GMT\"}", testDateWithoutFractionalSeconds) - ] - - let decoder = JSONDecoder() - - for (format, json, expectedValue) in subjects { - Container.format = format - let data = json.data(using: .utf8)! - let container = try decoder.decode(Container.self, from: data) - XCTAssertEqual(container.timestamp, expectedValue) - } - } - - func test_decodeTimestampIfPresent_returnsExpectedValue() throws { - struct Container: Decodable { - let timestamp: Date? - static var format: TimestampFormat = .dateTime - enum CodingKeys: String, CodingKey { - case timestamp - } - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.timestamp = try container.decodeTimestampIfPresent(Self.format, forKey: .timestamp) - } - } - + func test_TimestampFormatter_decodesExpectedValue() throws { let subjects: [(TimestampFormat, String, Date?)] = [ - (.epochSeconds, "{\"timestamp\":\(testDateWithFractionalSeconds.timeIntervalSince1970)}", testDateWithFractionalSeconds), - (.epochSeconds, "{\"timestamp\":\(testDateWithoutFractionalSeconds.timeIntervalSince1970)}", testDateWithoutFractionalSeconds), - (.epochSeconds, "{}", nil), - (.dateTime, "{\"timestamp\":\"1991-05-04T10:12:10.123Z\"}", testDateWithFractionalSeconds), - (.dateTime, "{\"timestamp\":\"1991-05-04T10:12:10Z\"}", testDateWithoutFractionalSeconds), - (.dateTime, "{}", nil), - (.httpDate, "{\"timestamp\":\"Sat, 04 May 1991 10:12:10.123 GMT\"}", testDateWithFractionalSeconds), - (.httpDate, "{\"timestamp\":\"Sat, 04 May 1991 10:12:10 GMT\"}", testDateWithoutFractionalSeconds), - (.httpDate, "{}", nil) + (.epochSeconds, "\(testDateWithFractionalSeconds.timeIntervalSince1970)", testDateWithFractionalSeconds), + (.epochSeconds, "\(testDateWithoutFractionalSeconds.timeIntervalSince1970)", testDateWithoutFractionalSeconds), + (.epochSeconds, "", nil), + (.dateTime, "1991-05-04T10:12:10.123Z", testDateWithFractionalSeconds), + (.dateTime, "1991-05-04T10:12:10Z", testDateWithoutFractionalSeconds), + (.dateTime, "", nil), + (.httpDate, "Sat, 04 May 1991 10:12:10.123 GMT", testDateWithFractionalSeconds), + (.httpDate, "Sat, 04 May 1991 10:12:10 GMT", testDateWithoutFractionalSeconds), + (.httpDate, "", nil) ] - let decoder = JSONDecoder() - - for (format, json, expectedValue) in subjects { - Container.format = format - let data = json.data(using: .utf8)! - let container = try decoder.decode(Container.self, from: data) - XCTAssertEqual(container.timestamp, expectedValue) + for (format, timestamp, expectedValue) in subjects { + let formatter = TimestampFormatter(format: format) + let date = formatter.date(from: timestamp) + XCTAssertEqual(date, expectedValue) } } } diff --git a/Tests/SmithyXMLTests/FloatWriterTests.swift b/Tests/SmithyXMLTests/FloatWriterTests.swift index d235ad9ae..9b26bab0e 100644 --- a/Tests/SmithyXMLTests/FloatWriterTests.swift +++ b/Tests/SmithyXMLTests/FloatWriterTests.swift @@ -7,6 +7,7 @@ import XCTest import SmithyXML +import SmithyReadWrite class FloatWriterTests: XCTestCase { @@ -23,11 +24,10 @@ class FloatWriterTests: XCTestCase { func test_serializesInfinity() throws { let fp = HasFPElements(f: .infinity, d: .infinity) - let actualData = try SmithyXML.XMLReadWrite.documentWritingClosure( - rootNodeInfo: .init("fp") - )( + let actualData = try Writer.write( fp, - HasFPElements.write(_:to:) + rootNodeInfo: .init("fp"), + with: HasFPElements.write(_:to:) ) let expectedData = Data("InfinityInfinity".utf8) XCTAssertEqual(actualData, expectedData) @@ -35,11 +35,10 @@ class FloatWriterTests: XCTestCase { func test_serializesNegativeInfinity() throws { let fp = HasFPElements(f: -.infinity, d: -.infinity) - let actualData = try SmithyXML.XMLReadWrite.documentWritingClosure( - rootNodeInfo: .init("fp") - )( + let actualData = try Writer.write( fp, - HasFPElements.write(_:to:) + rootNodeInfo: .init("fp"), + with: HasFPElements.write(_:to:) ) let expectedData = Data("-Infinity-Infinity".utf8) XCTAssertEqual(actualData, expectedData) @@ -47,11 +46,10 @@ class FloatWriterTests: XCTestCase { func test_serializesNaN() throws { let fp = HasFPElements(f: .nan, d: .nan) - let actualData = try SmithyXML.XMLReadWrite.documentWritingClosure( - rootNodeInfo: .init("fp") - )( + let actualData = try Writer.write( fp, - HasFPElements.write(_:to:) + rootNodeInfo: .init("fp"), + with: HasFPElements.write(_:to:) ) let expectedData = Data("NaNNaN".utf8) XCTAssertEqual(actualData, expectedData) diff --git a/Tests/SmithyXMLTests/NamespaceReaderTests.swift b/Tests/SmithyXMLTests/NamespaceReaderTests.swift index 2b73ae40a..7b50ad788 100644 --- a/Tests/SmithyXMLTests/NamespaceReaderTests.swift +++ b/Tests/SmithyXMLTests/NamespaceReaderTests.swift @@ -5,6 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // +import SmithyReadWrite import SmithyXML import SmithyReadWrite import ClientRuntime @@ -30,7 +31,7 @@ class NamespaceReaderTests: XCTestCase { """.utf8) let response = HttpResponse(body: .data(xmlData), statusCode: .ok) - let subject = try await SimpleScalarPropertiesOutput.httpBinding(response, responseDocumentBinding) + let subject = try await SimpleScalarPropertiesOutput.httpOutput(from:)(response) XCTAssertEqual(subject.nested?.attrField, "nestedAttrValue") } } @@ -79,26 +80,25 @@ public struct SimpleScalarPropertiesOutput: Swift.Equatable { extension SimpleScalarPropertiesOutput { - static var httpBinding: ClientRuntime.HTTPResponseOutputBinding { - { httpResponse, responseDocumentBinding in - let responseReader = try await responseDocumentBinding(httpResponse) - let reader = responseReader - var value = SimpleScalarPropertiesOutput() - if let fooHeaderValue = httpResponse.headers.value(for: "X-Foo") { - value.foo = fooHeaderValue - } - value.nested = try reader[.init("Nested", namespaceDef: .init(prefix: "xsi", uri: "https://example.com"))].readIfPresent(readingClosure: NestedWithNamespace.readingClosure) - 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.shortValue = try reader["shortValue"].readIfPresent() - value.stringValue = try reader["stringValue"].readIfPresent() - value.trueBooleanValue = try reader["trueBooleanValue"].readIfPresent() - return value + static func httpOutput(from httpResponse: ClientRuntime.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.nested = try reader[.init("Nested", namespaceDef: .init(prefix: "xsi", uri: "https://example.com"))].readIfPresent(with: NestedWithNamespace.read(from:)) + 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.shortValue = try reader["shortValue"].readIfPresent() + value.stringValue = try reader["stringValue"].readIfPresent() + value.trueBooleanValue = try reader["trueBooleanValue"].readIfPresent() + return value } } @@ -112,17 +112,15 @@ public struct NestedWithNamespace: Swift.Equatable { extension NestedWithNamespace { - static func writingClosure(_ value: NestedWithNamespace?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } + static func write(value: NestedWithNamespace?, to writer: SmithyXML.Writer) throws { + guard let value else { return } try writer[.init("xsi:someName", location: .attribute)].write(value.attrField) } - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = NestedWithNamespace() - value.attrField = try reader[.init("xsi:someName", location: .attribute)].readIfPresent() - return value - } + static func read(from reader: Reader) throws -> NestedWithNamespace { + guard reader.hasContent else { throw ReaderError.requiredValueNotPresent } + var value = NestedWithNamespace() + value.attrField = try reader[.init("xsi:someName", location: .attribute)].readIfPresent() + return value } } diff --git a/Tests/SmithyXMLTests/WriterTests.swift b/Tests/SmithyXMLTests/WriterTests.swift index f673d8360..5c75e3299 100644 --- a/Tests/SmithyXMLTests/WriterTests.swift +++ b/Tests/SmithyXMLTests/WriterTests.swift @@ -5,6 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // +import SmithyReadWrite import XCTest @_spi(SmithyXML) import SmithyXML @@ -22,14 +23,13 @@ class WriterTests: XCTestCase { } func test_encodesXMLWithNestedElements() throws { - let data = try SmithyXML.XMLReadWrite.documentWritingClosure( - rootNodeInfo: .init("test") - )( + let data = try Writer.write( HasNestedElements(a: "a", b: "b"), - HasNestedElements.write(_:to:) + rootNodeInfo: .init("test"), + with: HasNestedElements.write(_:to:) ) let expected = "ab" - XCTAssertEqual(String(data: data, encoding: .utf8), expected) + XCTAssertEqual(String(data: try XCTUnwrap(data), encoding: .utf8), expected) } private struct HasNestedElementAndAttribute: Encodable { @@ -44,35 +44,32 @@ class WriterTests: XCTestCase { } func test_encodesXMLWithElementAndAttribute() throws { - let data = try SmithyXML.XMLReadWrite.documentWritingClosure( - rootNodeInfo: .init("test") - )( + let data = try Writer.write( HasNestedElementAndAttribute(a: "a", b: "b"), - HasNestedElementAndAttribute.write(_:to:) + rootNodeInfo: .init("test"), + with: HasNestedElementAndAttribute.write(_:to:) ) let expected = "a" - XCTAssertEqual(String(data: data, encoding: .utf8), expected) + XCTAssertEqual(String(data: try XCTUnwrap(data), encoding: .utf8), expected) } func test_encodesXMLWithElementAndAttributeAndNamespace() throws { - let data = try SmithyXML.XMLReadWrite.documentWritingClosure( - rootNodeInfo: .init("test", namespaceDef: .init(prefix: "", uri: "https://www.def.com/1.0")) - )( + let data = try Writer.write( HasNestedElementAndAttribute(a: "a", b: "b"), - HasNestedElementAndAttribute.write(_:to:) + rootNodeInfo: .init("test", namespaceDef: .init(prefix: "", uri: "https://www.def.com/1.0")), + with: HasNestedElementAndAttribute.write(_:to:) ) let expected = "a" - XCTAssertEqual(String(data: data, encoding: .utf8), expected) + XCTAssertEqual(String(data: try XCTUnwrap(data), encoding: .utf8), expected) } func test_encodesXMLWithElementAndAttributeAndSpecialChars() throws { - let data = try SmithyXML.XMLReadWrite.documentWritingClosure( - rootNodeInfo: .init("test") - )( + let data = try Writer.write( HasNestedElementAndAttribute(a: "''", b: "\"b&s\""), - HasNestedElementAndAttribute.write(_:to:) + rootNodeInfo: .init("test"), + with: HasNestedElementAndAttribute.write(_:to:) ) let expected = "\'<a&z>\'" - XCTAssertEqual(String(data: data, encoding: .utf8), expected) + XCTAssertEqual(String(data: try XCTUnwrap(data), encoding: .utf8), expected) } } diff --git a/Tests/WeatherSDKTests/GetCityAnnouncementsErrorTest.swift b/Tests/WeatherSDKTests/GetCityAnnouncementsErrorTest.swift index fe45a19a4..207c674e2 100644 --- a/Tests/WeatherSDKTests/GetCityAnnouncementsErrorTest.swift +++ b/Tests/WeatherSDKTests/GetCityAnnouncementsErrorTest.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite import SmithyTestUtil @testable import WeatherSDK import XCTest @@ -25,10 +26,7 @@ class GetCityAnnouncementsNoSuchResourceTest: HttpResponseTestBase { return } - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let getCityAnnouncementsOutputError = try await responseErrorClosure(GetCityAnnouncementsOutputError.self, decoder: decoder)(httpResponse) + let getCityAnnouncementsOutputError = try await GetCityAnnouncementsOutputError.httpError(from:)(httpResponse) if let actual = getCityAnnouncementsOutputError as? NoSuchResource { diff --git a/Tests/WeatherSDKTests/GetCityErrorTest.swift b/Tests/WeatherSDKTests/GetCityErrorTest.swift index 0577aeb71..acb59d614 100644 --- a/Tests/WeatherSDKTests/GetCityErrorTest.swift +++ b/Tests/WeatherSDKTests/GetCityErrorTest.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite import SmithyTestUtil @testable import WeatherSDK import XCTest @@ -25,10 +26,7 @@ class GetCityNoSuchResourceTest: HttpResponseTestBase { return } - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let getCityOutputError = try await responseErrorClosure(GetCityOutputError.self, decoder: decoder)(httpResponse) + let getCityOutputError = try await GetCityOutputError.httpError(from:)(httpResponse) if let actual = getCityOutputError as? NoSuchResource { diff --git a/Tests/WeatherSDKTests/GetCityImageErrorTest.swift b/Tests/WeatherSDKTests/GetCityImageErrorTest.swift index c0f27a804..9cd13dfdf 100644 --- a/Tests/WeatherSDKTests/GetCityImageErrorTest.swift +++ b/Tests/WeatherSDKTests/GetCityImageErrorTest.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite import SmithyTestUtil @testable import WeatherSDK import XCTest @@ -25,10 +26,7 @@ class GetCityImageNoSuchResourceTest: HttpResponseTestBase { return } - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let getCityImageOutputError = try await responseErrorClosure(GetCityImageOutputError.self, decoder: decoder)(httpResponse) + let getCityImageOutputError = try await GetCityImageOutputError.httpError(from:)(httpResponse) if let actual = getCityImageOutputError as? NoSuchResource { diff --git a/Tests/WeatherSDKTests/GetCityRequestTest.swift b/Tests/WeatherSDKTests/GetCityRequestTest.swift index d5b36074d..19d1965fc 100644 --- a/Tests/WeatherSDKTests/GetCityRequestTest.swift +++ b/Tests/WeatherSDKTests/GetCityRequestTest.swift @@ -19,18 +19,10 @@ class GetCityRequestTest: HttpRequestTestBase { resolvedHost: "" ) - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let input = GetCityInput( cityId: "123" ) - let encoder = ClientRuntime.JSONEncoder() - encoder.dateEncodingStrategy = .secondsSince1970 - encoder.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") let context = HttpContextBuilder() - .withEncoder(value: encoder) .withMethod(value: .get) .build() var operationStack = OperationStack(id: "WriteGetCityAssertions") @@ -48,7 +40,7 @@ class GetCityRequestTest: HttpRequestTestBase { position: .after, middleware: MockDeserializeMiddleware( id: "TestDeserializeMiddleware", - responseClosure: responseClosure(decoder: decoder), + responseClosure: GetCityOutput.httpOutput(from:), callback: { context, actual in try await self.assertEqual(expected, actual) return OperationOutput(httpResponse: HttpResponse(body: ByteStream.noStream, statusCode: .ok), output: GetCityOutput()) diff --git a/Tests/WeatherSDKTests/GetCityResponseTest.swift b/Tests/WeatherSDKTests/GetCityResponseTest.swift index cd28201fb..729abdeb1 100644 --- a/Tests/WeatherSDKTests/GetCityResponseTest.swift +++ b/Tests/WeatherSDKTests/GetCityResponseTest.swift @@ -32,10 +32,7 @@ class GetCityResponseTest: HttpResponseTestBase { return } - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let actual: GetCityOutput = try await responseClosure(decoder: decoder)(httpResponse) + let actual: GetCityOutput = try await GetCityOutput.httpOutput(from:)(httpResponse) let expected = GetCityOutput( city: WeatherClientTypes.CitySummary( diff --git a/Tests/WeatherSDKTests/ListCitiesErrorTest.swift b/Tests/WeatherSDKTests/ListCitiesErrorTest.swift index 890adb237..960789f68 100644 --- a/Tests/WeatherSDKTests/ListCitiesErrorTest.swift +++ b/Tests/WeatherSDKTests/ListCitiesErrorTest.swift @@ -1,6 +1,7 @@ // Code generated by smithy-swift-codegen. DO NOT EDIT! import ClientRuntime +import SmithyReadWrite import SmithyTestUtil @testable import WeatherSDK import XCTest @@ -25,10 +26,7 @@ class ListCitiesNoSuchResourceTest: HttpResponseTestBase { return } - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let listCitiesOutputError = try await responseErrorClosure(ListCitiesOutputError.self, decoder: decoder)(httpResponse) + let listCitiesOutputError = try await ListCitiesOutputError.httpError(from:)(httpResponse) if let actual = listCitiesOutputError as? NoSuchResource { diff --git a/Tests/WeatherSDKTests/ListCitiesRequestTest.swift b/Tests/WeatherSDKTests/ListCitiesRequestTest.swift index 7edac7363..b22ed5c1d 100644 --- a/Tests/WeatherSDKTests/ListCitiesRequestTest.swift +++ b/Tests/WeatherSDKTests/ListCitiesRequestTest.swift @@ -25,18 +25,10 @@ class ListCitiesRequestTest: HttpRequestTestBase { resolvedHost: "" ) - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let input = ListCitiesInput( pageSize: 50 ) - let encoder = ClientRuntime.JSONEncoder() - encoder.dateEncodingStrategy = .secondsSince1970 - encoder.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") let context = HttpContextBuilder() - .withEncoder(value: encoder) .withMethod(value: .get) .build() var operationStack = OperationStack(id: "WriteListCitiesAssertions") @@ -54,7 +46,7 @@ class ListCitiesRequestTest: HttpRequestTestBase { position: .after, middleware: MockDeserializeMiddleware( id: "TestDeserializeMiddleware", - responseClosure: responseClosure(decoder: decoder), + responseClosure: ListCitiesOutput.httpOutput(from:), callback: { context, actual in try await self.assertEqual(expected, actual) return OperationOutput(httpResponse: HttpResponse(body: ByteStream.noStream, statusCode: .ok), output: ListCitiesOutput()) diff --git a/smithy-swift-codegen-test-utils/src/main/kotlin/software/amazon/smithy/swift/codegen/test/utils/TestProtocolGenerator.kt b/smithy-swift-codegen-test-utils/src/main/kotlin/software/amazon/smithy/swift/codegen/test/utils/TestProtocolGenerator.kt index 51dfc1c8d..024fc6b1e 100644 --- a/smithy-swift-codegen-test-utils/src/main/kotlin/software/amazon/smithy/swift/codegen/test/utils/TestProtocolGenerator.kt +++ b/smithy-swift-codegen-test-utils/src/main/kotlin/software/amazon/smithy/swift/codegen/test/utils/TestProtocolGenerator.kt @@ -5,67 +5,37 @@ package software.amazon.smithy.swift.codegen.test.utils -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.model.shapes.StructureShape import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.swift.codegen.ClientRuntimeTypes -import software.amazon.smithy.swift.codegen.SwiftDependency -import software.amazon.smithy.swift.codegen.SwiftTypes import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ClientProperty -import software.amazon.smithy.swift.codegen.integration.DefaultHttpProtocolCustomizations -import software.amazon.smithy.swift.codegen.integration.DefaultRequestEncoder -import software.amazon.smithy.swift.codegen.integration.DefaultResponseDecoder +import software.amazon.smithy.swift.codegen.integration.DefaultHTTPProtocolCustomizations import software.amazon.smithy.swift.codegen.integration.DefaultServiceConfig -import software.amazon.smithy.swift.codegen.integration.HttpBindingProtocolGenerator +import software.amazon.smithy.swift.codegen.integration.HTTPBindingProtocolGenerator +import software.amazon.smithy.swift.codegen.integration.HTTPProtocolCustomizable import software.amazon.smithy.swift.codegen.integration.HttpBindingResolver import software.amazon.smithy.swift.codegen.integration.HttpProtocolClientGenerator -import software.amazon.smithy.swift.codegen.integration.HttpProtocolCustomizable 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.HttpRequestEncoder -import software.amazon.smithy.swift.codegen.integration.HttpResponseDecoder import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.ServiceConfig -import software.amazon.smithy.swift.codegen.integration.codingKeys.CodingKeysCustomizationJsonName -import software.amazon.smithy.swift.codegen.integration.codingKeys.CodingKeysGenerator -import software.amazon.smithy.swift.codegen.integration.codingKeys.DefaultCodingKeysGenerator -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingErrorGeneratable -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingOutputGenerator -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseGeneratable -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseGenerator -import software.amazon.smithy.swift.codegen.integration.middlewares.handlers.MiddlewareShapeUtils -import software.amazon.smithy.swift.codegen.integration.serde.json.StructDecodeGenerator -import software.amazon.smithy.swift.codegen.integration.serde.json.StructEncodeGenerator +import software.amazon.smithy.swift.codegen.integration.serde.struct.StructDecodeGenerator +import software.amazon.smithy.swift.codegen.integration.serde.struct.StructEncodeGenerator import software.amazon.smithy.swift.codegen.middleware.OperationMiddleware import software.amazon.smithy.swift.codegen.model.ShapeMetadata -import software.amazon.smithy.swift.codegen.model.buildSymbol -import software.amazon.smithy.swift.codegen.utils.errorShapeName +class TestCustomizations : DefaultHTTPProtocolCustomizations() /** * A test JSON-based protocol generator for the Weather service client */ -class TestProtocolGenerator : HttpBindingProtocolGenerator() { +class TestProtocolGenerator : HTTPBindingProtocolGenerator(TestCustomizations()) { override val defaultContentType: String = "application/json" - override val defaultTimestampFormat: TimestampFormatTrait.Format = TimestampFormatTrait.Format.DATE_TIME override val protocol: ShapeId = ShapeId.from("common#fakeProtocol") override val httpProtocolClientGeneratorFactory = HttpProtocolClientGeneratorFactory() - override val httpProtocolCustomizable = RestJsonHttpProtocolCustomizations() - override val codingKeysGenerator: CodingKeysGenerator = DefaultCodingKeysGenerator(CodingKeysCustomizationJsonName()) - override val httpResponseGenerator: HttpResponseGeneratable = HttpResponseGenerator( - unknownServiceErrorSymbol, - defaultTimestampFormat, - HttpResponseBindingOutputGenerator(), - HttpResponseBindingErrorGenerator() - ) - override val shouldRenderDecodableBodyStructForInputShapes = true - override val shouldRenderCodingKeysForEncodable = true override val shouldRenderEncodableConformance = false override fun renderStructEncode( @@ -77,8 +47,7 @@ class TestProtocolGenerator : HttpBindingProtocolGenerator() { defaultTimestampFormat: TimestampFormatTrait.Format, path: String? ) { - val encodeGenerator = StructEncodeGenerator(ctx, members, writer, defaultTimestampFormat, path) - encodeGenerator.render() + StructEncodeGenerator(ctx, shapeContainingMembers, members, shapeMetadata, writer).render() } override fun renderStructDecode( ctx: ProtocolGenerator.GenerationContext, @@ -89,8 +58,7 @@ class TestProtocolGenerator : HttpBindingProtocolGenerator() { defaultTimestampFormat: TimestampFormatTrait.Format, path: String ) { - val decodeGenerator = StructDecodeGenerator(ctx, members, writer, defaultTimestampFormat, path) - decodeGenerator.render() + StructDecodeGenerator(ctx, shapeContainingMembers, members, shapeMetadata, writer).render() } override fun addProtocolSpecificMiddleware(ctx: ProtocolGenerator.GenerationContext, operation: OperationShape) { @@ -113,142 +81,14 @@ class TestProtocolGenerator : HttpBindingProtocolGenerator() { requestTestBuilder, responseTestBuilder, errorTestBuilder, - httpProtocolCustomizable, + customizations, operationMiddleware, getProtocolHttpBindingResolver(ctx, defaultContentType), ).generateProtocolTests() } } -class HttpRequestJsonEncoder( - requestEncoderOptions: MutableMap = mutableMapOf() -) : HttpRequestEncoder(ClientRuntimeTypes.Serde.JSONEncoder, requestEncoderOptions) - -class HttpRequestJsonDecoder( - requestDecoderOptions: MutableMap = mutableMapOf() -) : HttpResponseDecoder(ClientRuntimeTypes.Serde.JSONDecoder, requestDecoderOptions) - -class RestJsonHttpProtocolCustomizations() : DefaultHttpProtocolCustomizations() { - override fun getClientProperties(): List { - val properties = mutableListOf() - val requestEncoderOptions = mutableMapOf() - val responseDecoderOptions = mutableMapOf() - requestEncoderOptions["dateEncodingStrategy"] = ".secondsSince1970" - requestEncoderOptions["nonConformingFloatEncodingStrategy"] = ".convertToString(positiveInfinity: \"Infinity\", negativeInfinity: \"-Infinity\", nan: \"NaN\")" - responseDecoderOptions["dateDecodingStrategy"] = ".secondsSince1970" - responseDecoderOptions["nonConformingFloatDecodingStrategy"] = ".convertFromString(positiveInfinity: \"Infinity\", negativeInfinity: \"-Infinity\", nan: \"NaN\")" - properties.add(HttpRequestJsonEncoder(requestEncoderOptions)) - properties.add(HttpRequestJsonDecoder(responseDecoderOptions)) - return properties - } -} - -class HttpResponseBindingErrorGenerator : HttpResponseBindingErrorGeneratable { - override fun renderServiceError(ctx: ProtocolGenerator.GenerationContext) { - val serviceShape = ctx.service - val serviceName = ctx.service.id.name - val rootNamespace = ctx.settings.moduleName - val fileName = "./$rootNamespace/models/$serviceName+ServiceErrorHelperMethod.swift" - - ctx.delegator.useFileWriter(fileName) { writer -> - with(writer) { - addImport(SwiftDependency.CLIENT_RUNTIME.target) - addImport(SwiftDependency.SMITHY_TEST_UTIL.target) - - openBlock("extension ${ctx.symbolProvider.toSymbol(ctx.service).name}Types {", "}") { - openBlock( - "static func makeServiceError(_ httpResponse: \$N, _ decoder: \$D, _ error: \$N, _ id: String?) async throws -> \$N? {", - "}", - ClientRuntimeTypes.Http.HttpResponse, - ClientRuntimeTypes.Serde.ResponseDecoder, - JSON_ERROR_SYMBOL, - SwiftTypes.Error - ) { - openBlock("switch error.errorType {", "}") { - val serviceErrorShapes = - serviceShape.errors - .map { ctx.model.expectShape(it) as StructureShape } - .toSet() - .sorted() - serviceErrorShapes.forEach { errorShape -> - val errorShapeName = errorShape.errorShapeName(ctx.symbolProvider) - val errorShapeType = ctx.symbolProvider.toSymbol(errorShape).name - write( - "case \$S: return try await \$L(httpResponse: httpResponse, decoder: decoder, message: error.errorMessage, requestID: id)", - errorShapeName, - errorShapeType - ) - } - write("default: return nil") - } - } - } - } - } - } - - override fun renderOperationError( - ctx: ProtocolGenerator.GenerationContext, - op: OperationShape, - unknownServiceErrorSymbol: Symbol - ) { - val operationErrorName = MiddlewareShapeUtils.outputErrorSymbolName(op) - val rootNamespace = ctx.settings.moduleName - val httpBindingSymbol = Symbol.builder() - .definitionFile("./$rootNamespace/models/$operationErrorName+HttpResponseBinding.swift") - .name(operationErrorName) - .build() - - ctx.delegator.useShapeWriter(httpBindingSymbol) { writer -> - writer.addImport(SwiftDependency.CLIENT_RUNTIME.target) - writer.addImport(SwiftDependency.SMITHY_TEST_UTIL.target) - with(writer) { - - openBlock( - "enum \$L: \$N {", - "}", - operationErrorName, - ClientRuntimeTypes.Http.HttpResponseErrorBinding - ) { - openBlock( - "static func makeError(httpResponse: \$N, decoder: \$D) async throws -> \$N {", "}", - ClientRuntimeTypes.Http.HttpResponse, - ClientRuntimeTypes.Serde.ResponseDecoder, - SwiftTypes.Error - ) { - write("let defaultError = try await \$N(httpResponse: httpResponse)", JSON_ERROR_SYMBOL) - - if (ctx.service.errors.isNotEmpty()) { - write("let serviceError = try await ${ctx.symbolProvider.toSymbol(ctx.service).name}Types.makeServiceError(httpResponse, decoder, restJSONError, requestID)") - write("if let error = serviceError { return error }") - } - - openBlock("switch defaultError.errorType {", "}") { - val errorShapes = op.errors - .map { ctx.model.expectShape(it) as StructureShape } - .toSet() - .sorted() - errorShapes.forEach { errorShape -> - var errorShapeName = errorShape.errorShapeName(ctx.symbolProvider) - var errorShapeType = ctx.symbolProvider.toSymbol(errorShape).name - write( - "case \$S: return try await \$L(httpResponse: httpResponse, decoder: decoder, message: defaultError.errorMessage)", - errorShapeName, - errorShapeType - ) - } - write( - "default: return try await \$N.makeError(httpResponse: httpResponse, message: defaultError.errorMessage, typeName: defaultError.errorType)", - unknownServiceErrorSymbol - ) - } - } - } - } - } - } -} - +class RestJsonHTTPProtocolCustomizations() : DefaultHTTPProtocolCustomizations() class HttpProtocolClientGeneratorFactory : software.amazon.smithy.swift.codegen.integration.HttpProtocolClientGeneratorFactory { override fun createHttpProtocolClientGenerator( @@ -257,7 +97,7 @@ class HttpProtocolClientGeneratorFactory : writer: SwiftWriter, serviceName: String, defaultContentType: String, - httpProtocolCustomizable: HttpProtocolCustomizable, + httpProtocolCustomizable: HTTPProtocolCustomizable, operationMiddleware: OperationMiddleware, ): HttpProtocolClientGenerator { val serviceSymbol = ctx.symbolProvider.toSymbol(ctx.service) @@ -265,20 +105,7 @@ class HttpProtocolClientGeneratorFactory : return HttpProtocolClientGenerator(ctx, writer, config, httpBindingResolver, defaultContentType, httpProtocolCustomizable, operationMiddleware) } - private fun getClientProperties(ctx: ProtocolGenerator.GenerationContext): List { - return mutableListOf( - DefaultRequestEncoder(), - DefaultResponseDecoder(), - ) - } - private fun getConfigClass(writer: SwiftWriter, serviceName: String): ServiceConfig { return DefaultServiceConfig(writer, serviceName) } } - -private val JSON_ERROR_SYMBOL: Symbol = buildSymbol { - this.name = "JSONError" - this.namespace = SwiftDependency.SMITHY_TEST_UTIL.target - dependency(SwiftDependency.SMITHY_TEST_UTIL) -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ClientRuntimeTypes.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ClientRuntimeTypes.kt index 3c3944efe..ef47205a9 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ClientRuntimeTypes.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ClientRuntimeTypes.kt @@ -18,35 +18,15 @@ object ClientRuntimeTypes { val HttpClient = runtimeSymbol("HTTPClient") val HttpClientConfiguration = runtimeSymbol("HttpClientConfiguration") val Headers = runtimeSymbol("Headers") - val HttpStatusCode = runtimeSymbol("HttpStatusCode") val SdkHttpClient = runtimeSymbol("SdkHttpClient") val SdkHttpRequestBuilder = runtimeSymbol("SdkHttpRequestBuilder") val SdkHttpRequest = runtimeSymbol("SdkHttpRequest") val HttpResponse = runtimeSymbol("HttpResponse") val HttpResponseBinding = runtimeSymbol("HttpResponseBinding") - val HttpResponseErrorBinding = runtimeSymbol("HttpResponseErrorBinding") val HttpError = runtimeSymbol("HTTPError") val UnknownHttpServiceError = runtimeSymbol("UnknownHTTPServiceError") - val HttpContextBuilder = runtimeSymbol("HttpContextBuilder") val HttpContext = runtimeSymbol("HttpContext") - val HTTPResponseOutputBinding = runtimeSymbol("HTTPResponseOutputBinding") - val HTTPResponseErrorBinding = runtimeSymbol("HTTPResponseErrorBinding") - } - - object Serde { - val RequestEncoder = runtimeSymbol("RequestEncoder") - val ResponseDecoder = runtimeSymbol("ResponseDecoder") - val Key = runtimeSymbol("Key") - val FormURLEncoder = runtimeSymbol("FormURLEncoder") - val JSONDecoder = runtimeSymbol("JSONDecoder") - val JSONEncoder = runtimeSymbol("JSONEncoder") - val JSONWriter = runtimeSymbol("JSONWriter") - val FormURLWriter = runtimeSymbol("FormURLWriter") - val MessageMarshallable = runtimeSymbol("MessageMarshallable") - val MessageUnmarshallable = runtimeSymbol("MessageUnmarshallable") - val JSONReadWrite = runtimeSymbol("JSONReadWrite") - val FormURLReadWrite = runtimeSymbol("FormURLReadWrite") - val JSONReader = runtimeSymbol("JSONReader") + val HttpContextBuilder = runtimeSymbol("HttpContextBuilder") } object EventStream { @@ -88,12 +68,6 @@ object ClientRuntimeTypes { val EnumBodyMiddleware = runtimeSymbol("EnumBodyMiddleware") val IntEnumBodyMiddleware = runtimeSymbol("IntEnumBodyMiddleware") val StringBodyMiddleware = runtimeSymbol("StringBodyMiddleware") - - object Providers { - val URLPathProvider = runtimeSymbol("URLPathProvider") - val QueryItemProvider = runtimeSymbol("QueryItemProvider") - val HeaderProvider = runtimeSymbol("HeaderProvider") - } } object Auth { @@ -103,12 +77,9 @@ object ClientRuntimeTypes { } object Core { - val AttributeKey = runtimeSymbol("AttributeKey") val Endpoint = runtimeSymbol("Endpoint") - val ByteStream = runtimeSymbol("ByteStream") val Date = runtimeSymbol("Date") val Data = runtimeSymbol("Data") - val Document = runtimeSymbol("Document") val SDKURLQueryItem = runtimeSymbol("SDKURLQueryItem") val URL = runtimeSymbol("URL") val ModeledError = runtimeSymbol("ModeledError") @@ -123,8 +94,6 @@ object ClientRuntimeTypes { val DefaultRetryStrategy = runtimeSymbol("DefaultRetryStrategy") val RetryStrategyOptions = runtimeSymbol("RetryStrategyOptions") val DefaultRetryErrorInfoProvider = runtimeSymbol("DefaultRetryErrorInfoProvider") - val DefaultSDKRuntimeConfiguration = runtimeSymbol("DefaultSDKRuntimeConfiguration") - val DateFormatter = runtimeSymbol("DateFormatter") val PaginateToken = runtimeSymbol("PaginateToken") val PaginatorSequence = runtimeSymbol("PaginatorSequence") } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/EnumGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/EnumGenerator.kt index 99ac290d2..0c351561c 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/EnumGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/EnumGenerator.kt @@ -129,6 +129,7 @@ class EnumGenerator( if (isNestedType) { val service = model.expectShape(settings.service) writer.openBlock("extension ${service.nestedNamespaceType(symbolProvider)} {", "}") { + writer.write("") renderEnum() } } else { @@ -140,24 +141,29 @@ class EnumGenerator( private fun renderEnum() { writer.writeShapeDocs(shape) writer.writeAvailableAttribute(null, shape) - writer.openBlock("public enum \$enum.name:L: \$N, \$N, \$N, \$N, \$N {", "}", SwiftTypes.Protocols.Equatable, SwiftTypes.Protocols.RawRepresentable, SwiftTypes.Protocols.CaseIterable, SwiftTypes.Protocols.Codable, SwiftTypes.Protocols.Hashable) { + writer.openBlock( + "public enum \$enum.name:L: \$N, \$N, \$N, \$N {", + "}", + SwiftTypes.Protocols.Equatable, + SwiftTypes.Protocols.RawRepresentable, + SwiftTypes.Protocols.CaseIterable, + SwiftTypes.Protocols.Hashable + ) { createEnumWriterContexts() // add the sdkUnknown case which will always be last writer.write("case sdkUnknown(\$N)", SwiftTypes.String) - writer.write("") // Generate allCases static array generateAllCasesBlock() + writer.write("") // Generate initializer from rawValue generateInitFromRawValueBlock() + writer.write("") // Generate rawValue internal enum generateRawValueEnumBlock() - - // Generate deserializer - generateInitFromDecoderBlock() } } @@ -211,14 +217,6 @@ class EnumGenerator( } } - fun generateInitFromDecoderBlock() { - writer.openBlock("public init(from decoder: \$N) throws {", "}", SwiftTypes.Decoder) { - writer.write("let container = try decoder.singleValueContainer()") - writer.write("let rawValue = try container.decode(RawValue.self)") - writer.write("self = \$enum.name:L(rawValue: rawValue) ?? \$enum.name:L.sdkUnknown(rawValue)") - } - } - /** * Creates an idiomatic name for swift enum cases from Smithy EnumDefinition. * Uses either name or value attributes of EnumDefinition in that order and formats diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/IntEnumGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/IntEnumGenerator.kt index b20ab1cb4..fa1da6d24 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/IntEnumGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/IntEnumGenerator.kt @@ -39,18 +39,26 @@ class IntEnumGenerator( private fun renderEnum() { writer.writeShapeDocs(shape) writer.writeAvailableAttribute(null, shape) - writer.openBlock("public enum \$enum.name:L: \$N, \$N, \$N, \$N, \$N {", "}", SwiftTypes.Protocols.Equatable, SwiftTypes.Protocols.RawRepresentable, SwiftTypes.Protocols.CaseIterable, SwiftTypes.Protocols.Codable, SwiftTypes.Protocols.Hashable) { + writer.openBlock( + "public enum \$enum.name:L: \$N, \$N, \$N, \$N {", + "}", + SwiftTypes.Protocols.Equatable, + SwiftTypes.Protocols.RawRepresentable, + SwiftTypes.Protocols.CaseIterable, + SwiftTypes.Protocols.Hashable + ) { createEnumWriterContexts() // add the sdkUnknown case which will always be last writer.write("case sdkUnknown(\$N)", SwiftTypes.Int) - writer.write("") // Generate allCases static array generateAllCasesBlock() + writer.write("") // Generate initializer from rawValue generateInitFromRawValueBlock() + writer.write("") // Generate rawValue internal enum generateRawValueEnumBlock() diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ShapeValueGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ShapeValueGenerator.kt index 949eba37f..773ed5a2e 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ShapeValueGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ShapeValueGenerator.kt @@ -86,12 +86,13 @@ class ShapeValueGenerator( } private fun documentDecl(writer: SwiftWriter, node: Node) { - writer.writeInline("try decoder.decode(Document.self, from:") - .write("") - .indent() - .write("\"\"\"") - .write(Node.prettyPrintJson(node)) - .write("\"\"\".data(using: .utf8)!)") + writer.addImport(SwiftDependency.SMITHY_READ_WRITE.target) + writer.openBlock( + "try \$N.make(from: Data(\"\"\"", "\"\"\".utf8))", + SmithyReadWriteTypes.Document, + ) { + writer.write(Node.prettyPrintJson(node)) + } } private fun mapDecl(writer: SwiftWriter, block: () -> Unit) { diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SmithyFormURLTypes.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SmithyFormURLTypes.kt new file mode 100644 index 000000000..59d75a39b --- /dev/null +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SmithyFormURLTypes.kt @@ -0,0 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +package software.amazon.smithy.swift.codegen + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.swift.codegen.model.buildSymbol + +object SmithyFormURLTypes { + val Writer = runtimeSymbol("Writer") +} + +private fun runtimeSymbol(name: String): Symbol = buildSymbol { + this.name = name + this.namespace = SwiftDependency.SMITHY_FORM_URL.target + dependency(SwiftDependency.SMITHY_FORM_URL) +} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SmithyJSONTypes.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SmithyJSONTypes.kt new file mode 100644 index 000000000..95cf9eabe --- /dev/null +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SmithyJSONTypes.kt @@ -0,0 +1,19 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +package software.amazon.smithy.swift.codegen + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.swift.codegen.model.buildSymbol + +object SmithyJSONTypes { + val Writer = runtimeSymbol("Writer") + val Reader = runtimeSymbol("Reader") +} + +private fun runtimeSymbol(name: String): Symbol = buildSymbol { + this.name = name + this.namespace = SwiftDependency.SMITHY_JSON.target + dependency(SwiftDependency.SMITHY_JSON) +} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SmithyReadWriteTypes.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SmithyReadWriteTypes.kt index da12a8a2d..982451600 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SmithyReadWriteTypes.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SmithyReadWriteTypes.kt @@ -14,13 +14,13 @@ import software.amazon.smithy.swift.codegen.model.buildSymbol * NOTE: Not all symbols need be added here but it doesn't hurt to define runtime symbols once. */ object SmithyReadWriteTypes { - val WritingClosure = runtimeSymbol("WritingClosure") - val ReadingClosure = runtimeSymbol("ReadingClosure") - val DocumentWritingClosure = runtimeSymbol("DocumentWritingClosure") + val WireResponseOutputBinding = runtimeSymbol("WireResponseOutputBinding") + val Document = runtimeSymbol("Document") + val ReaderError = runtimeSymbol("ReaderError") } private fun runtimeSymbol(name: String): Symbol = buildSymbol { this.name = name this.namespace = SwiftDependency.SMITHY_READ_WRITE.target - dependency(SwiftDependency.SMITHY_XML) + dependency(SwiftDependency.SMITHY_READ_WRITE) } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SmithyTestUtilTypes.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SmithyTestUtilTypes.kt new file mode 100644 index 000000000..220c02733 --- /dev/null +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SmithyTestUtilTypes.kt @@ -0,0 +1,14 @@ +package software.amazon.smithy.swift.codegen + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.swift.codegen.model.buildSymbol + +object SmithyTestUtilTypes { + val TestBaseError = runtimeSymbol("TestBaseError") +} + +private fun runtimeSymbol(name: String): Symbol = buildSymbol { + this.name = name + this.namespace = SwiftDependency.SMITHY_TEST_UTIL.target + dependency(SwiftDependency.SMITHY_TEST_UTIL) +} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SmithyXMLTypes.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SmithyXMLTypes.kt index 999d57a1c..6ce4e0a09 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SmithyXMLTypes.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SmithyXMLTypes.kt @@ -8,10 +8,8 @@ import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.swift.codegen.model.buildSymbol object SmithyXMLTypes { - val XMLReadWrite = runtimeSymbol("XMLReadWrite") val Writer = runtimeSymbol("Writer") val Reader = runtimeSymbol("Reader") - val NodeInfo = runtimeSymbol("NodeInfo") } private fun runtimeSymbol(name: String): Symbol = buildSymbol { diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/StructureGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/StructureGenerator.kt index c5406f546..38d0ede5d 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/StructureGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/StructureGenerator.kt @@ -52,6 +52,7 @@ class StructureGenerator( } fun render() { + writer.addImport(SwiftDependency.SMITHY_READ_WRITE.target) writer.putContext("struct.name", structSymbol.name.toUpperCamelCase()) if (!shape.isError) { renderNonErrorStructure() @@ -142,8 +143,7 @@ class StructureGenerator( val (memberName, memberSymbol) = memberShapeDataContainer.getOrElse(member) { Pair(null, null) } if (memberName == null || memberSymbol == null) continue val terminator = if (index == membersSortedByName.size - 1) "" else "," - val symbolToUse = memberSymbol - writer.write("\$L: \$D$terminator", memberName, symbolToUse) + writer.write("\$L: \$D$terminator", memberName, memberSymbol) } } writer.openBlock("{", "}") { 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 054012ad0..8edfb2a85 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 @@ -50,6 +50,22 @@ enum class SwiftDependency( "https://github.com/smithy-lang/smithy-swift", Resources.computeAbsolutePath("smithy-swift", "", "SMITHY_SWIFT_CI_DIR"), "smithy-swift" + ), + SMITHY_JSON( + "SmithyJSON", + "main", + "0.1.0", + "https://github.com/smithy-lang/smithy-swift", + Resources.computeAbsolutePath("smithy-swift", "", "SMITHY_SWIFT_CI_DIR"), + "smithy-swift" + ), + SMITHY_FORM_URL( + "SmithyFormURL", + "main", + "0.1.0", + "https://github.com/smithy-lang/smithy-swift", + Resources.computeAbsolutePath("smithy-swift", "", "SMITHY_SWIFT_CI_DIR"), + "smithy-swift" ); override fun getDependencies(): List { diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftSymbolProvider.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftSymbolProvider.kt index b24fd821d..3b6bb331f 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftSymbolProvider.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftSymbolProvider.kt @@ -242,8 +242,8 @@ class SwiftSymbolProvider(private val model: Model, swiftSettings: SwiftSettings } override fun documentShape(shape: DocumentShape): Symbol { - return createSymbolBuilder(shape, "Document", "ClientRuntime", true) - .addDependency(SwiftDependency.CLIENT_RUNTIME) + return createSymbolBuilder(shape, "Document", "SmithyReadWrite", true) + .addDependency(SwiftDependency.SMITHY_READ_WRITE) .build() } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftTypes.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftTypes.kt index 993839aa5..b4996f1d2 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftTypes.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftTypes.kt @@ -12,34 +12,19 @@ object SwiftTypes { val Int = builtInSymbol("Int") val Int8 = builtInSymbol("Int8") val Int16 = builtInSymbol("Int16") - val Int32 = builtInSymbol("Int32") - val Int64 = builtInSymbol("Int64") - val UInt = builtInSymbol("UInt") - val UInt8 = builtInSymbol("UInt8") - val UInt32 = builtInSymbol("UInt32") - val UInt64 = builtInSymbol("UInt64") val Float = builtInSymbol("Float") val Double = builtInSymbol("Double") val Bool = builtInSymbol("Bool") - val TimeInterval = builtInSymbol("TimeInterval") val List = builtInSymbol("List") val Set = builtInSymbol("Set") val Map = builtInSymbol("Map") val Error = builtInSymbol("Error") - val Result = builtInSymbol("Result") - val Decoder = builtInSymbol("Decoder") - val Encoder = builtInSymbol("Encoder") - val CodingKey = builtInSymbol("CodingKey") - val DecodingError = builtInSymbol("DecodingError") object Protocols { val Equatable = builtInSymbol("Equatable") val Hashable = builtInSymbol("Hashable") val RawRepresentable = builtInSymbol("RawRepresentable") - val Codable = builtInSymbol("Codable") - val Encodable = builtInSymbol("Encodable") - val Decodable = builtInSymbol("Decodable") val CaseIterable = builtInSymbol("CaseIterable") val CustomDebugStringConvertible = builtInSymbol("CustomDebugStringConvertible") } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/ClientProperty.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/ClientProperty.kt index 025c456dd..1c82fda4a 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/ClientProperty.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/ClientProperty.kt @@ -6,7 +6,6 @@ package software.amazon.smithy.swift.codegen.integration import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.swift.codegen.ClientRuntimeTypes import software.amazon.smithy.swift.codegen.SwiftWriter /** @@ -105,20 +104,3 @@ abstract class HttpResponseDecoder(private val requestDecoder: Symbol, private v writer.write("self.decoder = \$L", name) } } - -class DefaultRequestEncoder(private val requestEncoderOptions: MutableMap = mutableMapOf()) : HttpRequestEncoder(ClientRuntimeTypes.Serde.RequestEncoder, requestEncoderOptions) { - override fun renderInstantiation(writer: SwiftWriter) { - // render nothing as we are relying on an encoder passed via the config object - } - override fun renderInitialization(writer: SwiftWriter, nameOfConfigObject: String) { - writer.write("self.encoder = \$L.encoder", nameOfConfigObject) - } -} -class DefaultResponseDecoder(private val responseDecoderOptions: MutableMap = mutableMapOf()) : HttpResponseDecoder(ClientRuntimeTypes.Serde.ResponseDecoder, responseDecoderOptions) { - override fun renderInstantiation(writer: SwiftWriter) { - // render nothing as we are relying on an encoder passed via the config object - } - override fun renderInitialization(writer: SwiftWriter, nameOfConfigObject: String) { - writer.write("self.decoder = \$L.decoder", nameOfConfigObject) - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/DefaultHttpProtocolCustomizations.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/DefaultHTTPProtocolCustomizations.kt similarity index 61% rename from smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/DefaultHttpProtocolCustomizations.kt rename to smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/DefaultHTTPProtocolCustomizations.kt index b897882d9..147b1c827 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/DefaultHttpProtocolCustomizations.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/DefaultHTTPProtocolCustomizations.kt @@ -5,11 +5,15 @@ package software.amazon.smithy.swift.codegen.integration +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.traits.TimestampFormatTrait import software.amazon.smithy.swift.codegen.AuthSchemeResolverGenerator +import software.amazon.smithy.swift.codegen.ClientRuntimeTypes +import software.amazon.smithy.swift.codegen.SmithyTestUtilTypes import software.amazon.smithy.swift.codegen.SwiftWriter -abstract class DefaultHttpProtocolCustomizations : HttpProtocolCustomizable { +abstract class DefaultHTTPProtocolCustomizations : HTTPProtocolCustomizable { override fun serviceClient( ctx: ProtocolGenerator.GenerationContext, writer: SwiftWriter, @@ -30,4 +34,12 @@ abstract class DefaultHttpProtocolCustomizations : HttpProtocolCustomizable { override fun renderInternals(ctx: ProtocolGenerator.GenerationContext) { AuthSchemeResolverGenerator().render(ctx) } + + override val messageDecoderSymbol: Symbol = ClientRuntimeTypes.EventStream.MessageDecoder + + override val baseErrorSymbol: Symbol = SmithyTestUtilTypes.TestBaseError + + override val unknownServiceErrorSymbol: Symbol = ClientRuntimeTypes.Http.UnknownHttpServiceError + + override val defaultTimestampFormat = TimestampFormatTrait.Format.EPOCH_SECONDS } 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 similarity index 76% rename from smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpBindingProtocolGenerator.kt rename to smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HTTPBindingProtocolGenerator.kt index d96c2cce3..f2cce845d 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 @@ -36,11 +36,8 @@ import software.amazon.smithy.model.traits.StreamingTrait import software.amazon.smithy.model.traits.TimestampFormatTrait import software.amazon.smithy.swift.codegen.ClientRuntimeTypes import software.amazon.smithy.swift.codegen.SwiftDependency -import software.amazon.smithy.swift.codegen.SwiftTypes import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.customtraits.EquatableConformanceTrait -import software.amazon.smithy.swift.codegen.integration.codingKeys.CodingKeysGenerator -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseGeneratable +import software.amazon.smithy.swift.codegen.integration.httpResponse.HTTPResponseGenerator import software.amazon.smithy.swift.codegen.integration.middlewares.AuthSchemeMiddleware import software.amazon.smithy.swift.codegen.integration.middlewares.ContentLengthMiddleware import software.amazon.smithy.swift.codegen.integration.middlewares.ContentMD5Middleware @@ -58,11 +55,10 @@ import software.amazon.smithy.swift.codegen.integration.middlewares.SignerMiddle import software.amazon.smithy.swift.codegen.integration.middlewares.providers.HttpHeaderProvider import software.amazon.smithy.swift.codegen.integration.middlewares.providers.HttpQueryItemProvider import software.amazon.smithy.swift.codegen.integration.middlewares.providers.HttpUrlPathProvider -import software.amazon.smithy.swift.codegen.integration.serde.UnionDecodeGeneratorStrategy -import software.amazon.smithy.swift.codegen.integration.serde.UnionEncodeGeneratorStrategy +import software.amazon.smithy.swift.codegen.integration.serde.union.UnionDecodeGenerator +import software.amazon.smithy.swift.codegen.integration.serde.union.UnionEncodeGenerator import software.amazon.smithy.swift.codegen.middleware.OperationMiddlewareGenerator import software.amazon.smithy.swift.codegen.model.ShapeMetadata -import software.amazon.smithy.swift.codegen.model.bodySymbol import software.amazon.smithy.swift.codegen.model.findStreamingMember import software.amazon.smithy.swift.codegen.model.hasEventStreamMember import software.amazon.smithy.swift.codegen.model.hasTrait @@ -125,12 +121,12 @@ fun formatHeaderOrQueryValue( /** * Abstract implementation useful for all HTTP protocols */ -abstract class HttpBindingProtocolGenerator : ProtocolGenerator { +abstract class HTTPBindingProtocolGenerator( + override val customizations: HTTPProtocolCustomizable, +) : ProtocolGenerator { private val LOGGER = Logger.getLogger(javaClass.name) private val idempotencyTokenValue = "idempotencyTokenGenerator.generateToken()" - override val unknownServiceErrorSymbol: Symbol = ClientRuntimeTypes.Http.UnknownHttpServiceError - override var serviceErrorProtocolSymbol: Symbol = ClientRuntimeTypes.Http.HttpError override fun generateSerializers(ctx: ProtocolGenerator.GenerationContext) { @@ -145,8 +141,8 @@ abstract class HttpBindingProtocolGenerator : ProtocolGenerator { } val httpBindingResolver = getProtocolHttpBindingResolver(ctx, defaultContentType) HttpUrlPathProvider.renderUrlPathMiddleware(ctx, operation, httpBindingResolver) - HttpHeaderProvider.renderHeaderMiddleware(ctx, operation, httpBindingResolver, defaultTimestampFormat) - HttpQueryItemProvider.renderQueryMiddleware(ctx, operation, httpBindingResolver, defaultTimestampFormat) + HttpHeaderProvider.renderHeaderMiddleware(ctx, operation, httpBindingResolver, customizations.defaultTimestampFormat) + HttpQueryItemProvider.renderQueryMiddleware(ctx, operation, httpBindingResolver, customizations.defaultTimestampFormat) inputShapesWithHttpBindings.add(inputShapeId) } } @@ -160,7 +156,7 @@ abstract class HttpBindingProtocolGenerator : ProtocolGenerator { val symbolName = symbol.name val rootNamespace = ctx.settings.moduleName val encodeSymbol = Symbol.builder() - .definitionFile("./$rootNamespace/models/$symbolName+Encodable.swift") + .definitionFile("./$rootNamespace/models/$symbolName+Write.swift") .name(symbolName) .build() var httpBodyMembers = shape.members() @@ -175,26 +171,18 @@ abstract class HttpBindingProtocolGenerator : ProtocolGenerator { } if (httpBodyMembers.isNotEmpty() || shouldRenderEncodableConformance) { ctx.delegator.useShapeWriter(encodeSymbol) { writer -> - val encodableOrNot = encodableProtocol?.let { writer.format(": \$N", it) } ?: "" writer.openBlock( - "extension $symbolName\$L {", + "extension \$L {", "}", - encodableOrNot, + symbolName, ) { writer.addImport(SwiftDependency.CLIENT_RUNTIME.target) - - if (shouldRenderCodingKeysForEncodable) { - codingKeysGenerator?.generateCodingKeysForMembers(ctx, writer, httpBodyMembers) - writer.write("") - } + writer.write("") val path = "properties.".takeIf { shape.hasTrait() } ?: null - renderStructEncode(ctx, shape, shapeMetadata, httpBodyMembers, writer, defaultTimestampFormat, path) + renderStructEncode(ctx, shape, shapeMetadata, httpBodyMembers, writer, customizations.defaultTimestampFormat, path) } } } - if (shouldRenderDecodableBodyStructForInputShapes || httpBodyMembers.isNotEmpty()) { - renderBodyStructAndDecodableExtension(ctx, shape, mapOf()) - } } } @@ -202,20 +190,6 @@ abstract class HttpBindingProtocolGenerator : ProtocolGenerator { val httpOperations = getHttpBindingOperations(ctx) val httpBindingResolver = getProtocolHttpBindingResolver(ctx, defaultContentType) httpResponseGenerator.render(ctx, httpOperations, httpBindingResolver) - - val outputShapesWithMetadata = resolveOutputShapes(ctx) - .filter { !it.key.hasEventStreamMember(ctx.model) } - - for ((shape, metadata) in outputShapesWithMetadata) { - if (shape.members().any { it.isInHttpBody() }) { - renderBodyStructAndDecodableExtension(ctx, shape, metadata) - } - } - - val errorShapes = resolveErrorShapes(ctx) - for (shape in errorShapes) { - renderBodyStructAndDecodableExtension(ctx, shape, mapOf()) - } } override fun generateCodableConformanceForNestedTypes(ctx: ProtocolGenerator.GenerationContext) { @@ -229,96 +203,40 @@ abstract class HttpBindingProtocolGenerator : ProtocolGenerator { fun renderCodableExtension( ctx: ProtocolGenerator.GenerationContext, shape: Shape, - encodable: Boolean = true, - decodable: Boolean = true ) { val symbol: Symbol = ctx.symbolProvider.toSymbol(shape) val symbolName = symbol.name val rootNamespace = ctx.settings.moduleName val encodeSymbol = Symbol.builder() - .definitionFile("./$rootNamespace/models/$symbolName+Codable.swift") + .definitionFile("./$rootNamespace/models/$symbolName+ReadWrite.swift") .name(symbolName) .build() - val extensionProtocol = when (Pair(encodable, decodable)) { - Pair(true, true) -> codableProtocol - Pair(true, false) -> encodableProtocol - Pair(false, true) -> decodableProtocol - else -> return - } - ctx.delegator.useShapeWriter(encodeSymbol) { writer -> - val extensionOrNot = extensionProtocol?.let { writer.format(": \$N", it) } ?: "" - writer.openBlock("extension \$N\$L {", "}", symbol, extensionOrNot) { + writer.openBlock("extension \$N {", "}", symbol) { writer.addImport(SwiftDependency.CLIENT_RUNTIME.target) val members = shape.members().toList() when (shape) { is StructureShape -> { // get all members sorted by name and filter out either all members with other traits OR members with the payload trait val httpBodyMembers = members.filter { it.isInHttpBody() } - if (encodable || decodable) { - codingKeysGenerator?.generateCodingKeysForMembers(ctx, writer, httpBodyMembers) - } val path = "properties.".takeIf { shape.hasTrait() } ?: "" - if (encodable) { - writer.write("") - renderStructEncode(ctx, shape, mapOf(), httpBodyMembers, writer, defaultTimestampFormat, path) - } - if (decodable) { - writer.write("") - renderStructDecode(ctx, shape, mapOf(), httpBodyMembers, writer, defaultTimestampFormat, path) - } + writer.write("") + renderStructEncode(ctx, shape, mapOf(), httpBodyMembers, writer, customizations.defaultTimestampFormat, path) + writer.write("") + renderStructDecode(ctx, shape, mapOf(), httpBodyMembers, writer, customizations.defaultTimestampFormat, path) } is UnionShape -> { // get all members of the union shape - val sdkUnknownMember = MemberShape.builder().id("${shape.id}\$sdkUnknown").target("smithy.api#String").build() - val unionMembersForCodingKeys = members.toMutableList() - unionMembersForCodingKeys.add(0, sdkUnknownMember) - if (encodable || decodable) { - codingKeysGenerator?.generateCodingKeysForMembers(ctx, writer, unionMembersForCodingKeys) - } - if (encodable) { - writer.write("") - UnionEncodeGeneratorStrategy(ctx, shape, members, writer, defaultTimestampFormat).render() - } - if (decodable) { - writer.write("") - UnionDecodeGeneratorStrategy(ctx, shape, members, writer, defaultTimestampFormat).render() - } + writer.write("") + UnionEncodeGenerator(ctx, shape, members, writer).render() + writer.write("") + UnionDecodeGenerator(ctx, shape, members, writer).render() } } } } } - open fun renderBodyStructAndDecodableExtension(ctx: ProtocolGenerator.GenerationContext, shape: Shape, metadata: Map) { - val bodySymbol: Symbol = ctx.symbolProvider.toSymbol(shape).bodySymbol() - val rootNamespace = ctx.settings.moduleName - val httpBodyMembers = shape.members().filter { it.isInHttpBody() }.toList() - - val decodeSymbol = Symbol.builder() - .definitionFile("./$rootNamespace/models/${bodySymbol.name}+Decodable.swift") - .name(bodySymbol.name) - .build() - - ctx.delegator.useShapeWriter(decodeSymbol) { writer -> - val equatableConformance = (": " + SwiftTypes.Protocols.Equatable).takeIf { shape.hasTrait() } ?: "" - writer.openBlock("struct ${decodeSymbol.name}$equatableConformance {", "}") { - httpBodyMembers.forEach { - val memberSymbol = ctx.symbolProvider.toSymbol(it) - writer.write("let \$L: \$T", ctx.symbolProvider.toMemberName(it), memberSymbol) - } - } - writer.write("") - val extensionOrNot = decodableProtocol?.let { writer.format(": \$N", it) } ?: "" - writer.openBlock("extension ${decodeSymbol.name}\$L {", "}", extensionOrNot) { - writer.addImport(SwiftDependency.CLIENT_RUNTIME.target) - codingKeysGenerator?.generateCodingKeysForMembers(ctx, writer, httpBodyMembers) - writer.write("") - renderStructDecode(ctx, shape, metadata, httpBodyMembers, writer, defaultTimestampFormat, "") - } - } - } - private fun resolveInputShapes(ctx: ProtocolGenerator.GenerationContext): Map> { var shapesInfo: MutableMap> = mutableMapOf() val operations = getHttpBindingOperations(ctx) @@ -333,19 +251,6 @@ abstract class HttpBindingProtocolGenerator : ProtocolGenerator { return shapesInfo } - private fun resolveOutputShapes(ctx: ProtocolGenerator.GenerationContext): Map> { - var shapesInfo: MutableMap> = mutableMapOf() - val operations = getHttpBindingOperations(ctx) - for (operation in operations) { - val outputType = ctx.model.expectShape(operation.output.get()) - var metadata = mapOf( - Pair(ShapeMetadata.OPERATION_SHAPE, operation), - ) - shapesInfo.put(outputType, metadata) - } - return shapesInfo - } - fun resolveErrorShapes(ctx: ProtocolGenerator.GenerationContext): Set { val operationErrorShapes = getHttpBindingOperations(ctx) .flatMap { it.errors } @@ -464,7 +369,7 @@ abstract class HttpBindingProtocolGenerator : ProtocolGenerator { writer, serviceSymbol.name, defaultContentType, - httpProtocolCustomizable, + customizations, operationMiddleware, ) clientGenerator.render() @@ -507,21 +412,13 @@ abstract class HttpBindingProtocolGenerator : ProtocolGenerator { override val operationMiddleware = OperationMiddlewareGenerator() - open val codableProtocol: Symbol? = SwiftTypes.Protocols.Codable - open val encodableProtocol: Symbol? = SwiftTypes.Protocols.Encodable - open val decodableProtocol: Symbol? = SwiftTypes.Protocols.Decodable - - protected abstract val defaultTimestampFormat: TimestampFormatTrait.Format - protected abstract val codingKeysGenerator: CodingKeysGenerator? protected abstract val httpProtocolClientGeneratorFactory: HttpProtocolClientGeneratorFactory - protected abstract val httpResponseGenerator: HttpResponseGeneratable - protected abstract val shouldRenderDecodableBodyStructForInputShapes: Boolean - protected abstract val shouldRenderCodingKeysForEncodable: Boolean + protected val httpResponseGenerator = HTTPResponseGenerator(customizations) protected abstract val shouldRenderEncodableConformance: Boolean protected abstract fun renderStructEncode( ctx: ProtocolGenerator.GenerationContext, shapeContainingMembers: Shape, - shapeMetaData: Map, + shapeMetadata: Map, members: List, writer: SwiftWriter, defaultTimestampFormat: TimestampFormatTrait.Format, @@ -530,7 +427,7 @@ abstract class HttpBindingProtocolGenerator : ProtocolGenerator { protected abstract fun renderStructDecode( ctx: ProtocolGenerator.GenerationContext, shapeContainingMembers: Shape, - shapeMetaData: Map, + shapeMetadata: Map, members: List, writer: SwiftWriter, defaultTimestampFormat: TimestampFormatTrait.Format, diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolCustomizable.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HTTPProtocolCustomizable.kt similarity index 75% rename from smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolCustomizable.kt rename to smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HTTPProtocolCustomizable.kt index 3f89a5575..2a7413361 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolCustomizable.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HTTPProtocolCustomizable.kt @@ -9,10 +9,12 @@ import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.traits.TimestampFormatTrait import software.amazon.smithy.protocoltests.traits.HttpRequestTestCase import software.amazon.smithy.swift.codegen.SwiftWriter -interface HttpProtocolCustomizable { +interface HTTPProtocolCustomizable { + fun renderInternals(ctx: ProtocolGenerator.GenerationContext) { // Default implementation is no-op } @@ -49,4 +51,20 @@ interface HttpProtocolCustomizable { fun alwaysHasHttpBody(): Boolean { return false } + + interface ServiceErrorCustomRenderer { + fun render(writer: SwiftWriter) + } + + fun serviceErrorCustomRenderer(ctx: ProtocolGenerator.GenerationContext): ServiceErrorCustomRenderer? { + return null + } + + val baseErrorSymbol: Symbol + + val messageDecoderSymbol: Symbol + + val unknownServiceErrorSymbol: Symbol + + val defaultTimestampFormat: TimestampFormatTrait.Format } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolClientGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolClientGenerator.kt index a507b0d7f..5cfc094b9 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolClientGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolClientGenerator.kt @@ -25,7 +25,7 @@ open class HttpProtocolClientGenerator( serviceConfig: ServiceConfig, private val httpBindingResolver: HttpBindingResolver, private val defaultContentType: String, - private val httpProtocolCustomizable: HttpProtocolCustomizable, + private val httpProtocolCustomizable: HTTPProtocolCustomizable, private val operationMiddleware: OperationMiddleware ) { private val model: Model = ctx.model diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolClientGeneratorFactory.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolClientGeneratorFactory.kt index 45cde7098..047409bbc 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolClientGeneratorFactory.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolClientGeneratorFactory.kt @@ -15,7 +15,7 @@ interface HttpProtocolClientGeneratorFactory { writer: SwiftWriter, serviceName: String, defaultContentType: String, - httpProtocolCustomizable: HttpProtocolCustomizable, + httpProtocolCustomizable: HTTPProtocolCustomizable, operationMiddleware: OperationMiddleware ): HttpProtocolClientGenerator } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolServiceClient.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolServiceClient.kt index 1200c2a6d..b837bc652 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolServiceClient.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolServiceClient.kt @@ -30,12 +30,6 @@ open class HttpProtocolServiceClient( writer.write("let client: \$N", ClientRuntimeTypes.Http.SdkHttpClient) writer.write("let config: \$L", serviceConfig.typeName) writer.write("let serviceName = \$S", serviceName) - if (properties.any { it is HttpRequestEncoder }) { - writer.write("let encoder: \$N", ClientRuntimeTypes.Serde.RequestEncoder) - } - if (properties.any { it is HttpResponseDecoder }) { - writer.write("let decoder: \$N", ClientRuntimeTypes.Serde.ResponseDecoder) - } writer.write("") properties.forEach { prop -> prop.addImportsAndDependencies(writer) diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolTestGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolTestGenerator.kt index b50939508..76fd66e8b 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolTestGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolTestGenerator.kt @@ -32,7 +32,7 @@ class HttpProtocolTestGenerator( private val requestTestBuilder: HttpProtocolUnitTestRequestGenerator.Builder, private val responseTestBuilder: HttpProtocolUnitTestResponseGenerator.Builder, private val errorTestBuilder: HttpProtocolUnitTestErrorGenerator.Builder, - private val httpProtocolCustomizable: HttpProtocolCustomizable, + private val httpProtocolCustomizable: HTTPProtocolCustomizable, private val operationMiddleware: OperationMiddleware, private val httpBindingResolver: HttpBindingResolver, private val imports: List = listOf(), diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolUnitTestErrorGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolUnitTestErrorGenerator.kt index 85f787598..4d9195f4e 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolUnitTestErrorGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolUnitTestErrorGenerator.kt @@ -7,6 +7,7 @@ package software.amazon.smithy.swift.codegen.integration import software.amazon.smithy.codegen.core.CodegenException import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.protocoltests.traits.HttpResponseTestCase +import software.amazon.smithy.swift.codegen.SwiftDependency import software.amazon.smithy.swift.codegen.integration.serde.readwrite.ResponseErrorClosureUtils import software.amazon.smithy.swift.codegen.model.toUpperCamelCase @@ -41,6 +42,7 @@ open class HttpProtocolUnitTestErrorGenerator protected constructor(builder: Bui } val responseErrorClosure = ResponseErrorClosureUtils(ctx, writer, operation).render() + writer.addImport(SwiftDependency.SMITHY_READ_WRITE.target) writer.write( "let \$L = try await \$L(httpResponse)", operationErrorVariableName, diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolUnitTestGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolUnitTestGenerator.kt index 527eced5a..bad68d46b 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolUnitTestGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolUnitTestGenerator.kt @@ -74,7 +74,7 @@ protected constructor(builder: Builder) { var operation: OperationShape? = null var writer: SwiftWriter? = null var serviceName: String? = null - var httpProtocolCustomizable: HttpProtocolCustomizable? = null + var httpProtocolCustomizable: HTTPProtocolCustomizable? = null var operationMiddleware: OperationMiddleware? = null var httpBindingResolver: HttpBindingResolver? = null @@ -85,7 +85,7 @@ protected constructor(builder: Builder) { fun operation(operation: OperationShape): Builder = apply { this.operation = operation } fun writer(writer: SwiftWriter): Builder = apply { this.writer = writer } fun serviceName(serviceName: String): Builder = apply { this.serviceName = serviceName } - fun httpProtocolCustomizable(httpProtocolCustomizable: HttpProtocolCustomizable): Builder = apply { this.httpProtocolCustomizable = httpProtocolCustomizable } + fun httpProtocolCustomizable(httpProtocolCustomizable: HTTPProtocolCustomizable): Builder = apply { this.httpProtocolCustomizable = httpProtocolCustomizable } fun operationMiddleware(operationMiddleware: OperationMiddleware): Builder = apply { this.operationMiddleware = operationMiddleware } fun httpBindingResolver(httpBindingResolver: HttpBindingResolver): Builder = apply { this.httpBindingResolver = httpBindingResolver } abstract fun build(): HttpProtocolUnitTestGenerator diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolUnitTestRequestGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolUnitTestRequestGenerator.kt index 66b68e5a9..3cbc1bca5 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolUnitTestRequestGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolUnitTestRequestGenerator.kt @@ -95,9 +95,6 @@ open class HttpProtocolUnitTestRequestGenerator protected constructor(builder: B val hasIdempotencyTokenTrait = idempotentMember != null val httpMethod = resolveHttpMethod(operation) writer.swiftFunctionParameterIndent { - if (ctx.service.requestWireProtocol != WireProtocol.XML) { - writer.write(" .withEncoder(value: encoder)") - } writer.write(" .withMethod(value: .$httpMethod)") if (hasIdempotencyTokenTrait) { writer.write(" .withIdempotencyTokenGenerator(value: QueryIdempotencyTestTokenGenerator())") diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/ProtocolGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/ProtocolGenerator.kt index 21ce62747..81b2354b4 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/ProtocolGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/ProtocolGenerator.kt @@ -51,8 +51,6 @@ interface ProtocolGenerator { val DefaultServiceErrorProtocolSymbol: Symbol = ClientRuntimeTypes.Core.ServiceError - val DefaultUnknownServiceErrorSymbol: Symbol = ClientRuntimeTypes.Http.UnknownHttpServiceError - val DefaultRetryErrorInfoProviderSymbol: Symbol = ClientRuntimeTypes.Core.DefaultRetryErrorInfoProvider } @@ -85,9 +83,6 @@ interface ProtocolGenerator { * Symbol that should be used when the deserialized service error type cannot be determined * It defaults to the UnknownServiceError available in smithy-swift's client-runtime. */ - val unknownServiceErrorSymbol: Symbol - get() = DefaultUnknownServiceErrorSymbol - val retryErrorInfoProviderSymbol: Symbol get() = DefaultRetryErrorInfoProviderSymbol @@ -133,7 +128,7 @@ interface ProtocolGenerator { HttpTraitResolver(ctx, defaultContentType) val operationMiddleware: OperationMiddleware - val httpProtocolCustomizable: HttpProtocolCustomizable + val customizations: HTTPProtocolCustomizable val defaultContentType: String /** diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/CodingKeysCustomizable.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/CodingKeysCustomizable.kt deleted file mode 100644 index 88b602689..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/CodingKeysCustomizable.kt +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.codingKeys - -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator - -interface CodingKeysCustomizable { - fun shouldHandleMember(member: MemberShape): Boolean - fun handleMember(ctx: ProtocolGenerator.GenerationContext, writer: SwiftWriter, member: MemberShape) -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/CodingKeysCustomizationJsonName.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/CodingKeysCustomizationJsonName.kt deleted file mode 100644 index 8ab8357e4..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/CodingKeysCustomizationJsonName.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.codingKeys - -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.traits.JsonNameTrait -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.model.getTrait - -class CodingKeysCustomizationJsonName : CodingKeysCustomizable { - override fun shouldHandleMember(member: MemberShape): Boolean { - return member.hasTrait(JsonNameTrait::class.java) - } - - override fun handleMember(ctx: ProtocolGenerator.GenerationContext, writer: SwiftWriter, member: MemberShape) { - val jsonName = member.getTrait()!!.value - val modifiedMemberName = ctx.symbolProvider.toMemberName(member) - writer.write("case $modifiedMemberName = \"$jsonName\"") - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/CodingKeysCustomizationXmlName.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/CodingKeysCustomizationXmlName.kt deleted file mode 100644 index af7141be0..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/CodingKeysCustomizationXmlName.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.codingKeys - -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.traits.XmlNameTrait -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.serde.xml.trait.XMLNameTraitGenerator -import software.amazon.smithy.swift.codegen.model.hasTrait - -class CodingKeysCustomizationXmlName : CodingKeysCustomizable { - override fun shouldHandleMember(member: MemberShape): Boolean { - return member.hasTrait() - } - - override fun handleMember(ctx: ProtocolGenerator.GenerationContext, writer: SwiftWriter, member: MemberShape) { - val xmlName = XMLNameTraitGenerator.construct(member, member.memberName) - val modifiedMemberName = ctx.symbolProvider.toMemberName(member) - writer.write("case $modifiedMemberName = \"$xmlName\"") - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/CodingKeysGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/CodingKeysGenerator.kt deleted file mode 100644 index 5192ed3bf..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/CodingKeysGenerator.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.codingKeys - -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator - -interface CodingKeysGenerator { - fun generateCodingKeysForMembers(ctx: ProtocolGenerator.GenerationContext, writer: SwiftWriter, members: List) -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/DefaultCodingKeysCustomizable.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/DefaultCodingKeysCustomizable.kt deleted file mode 100644 index 55c16f864..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/DefaultCodingKeysCustomizable.kt +++ /dev/null @@ -1,14 +0,0 @@ -package software.amazon.smithy.swift.codegen.integration.codingKeys - -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator - -class DefaultCodingKeysCustomizable : CodingKeysCustomizable { - override fun shouldHandleMember(member: MemberShape): Boolean { - return false - } - - override fun handleMember(ctx: ProtocolGenerator.GenerationContext, writer: SwiftWriter, member: MemberShape) { - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/DefaultCodingKeysGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/DefaultCodingKeysGenerator.kt deleted file mode 100644 index b44cc21fb..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/codingKeys/DefaultCodingKeysGenerator.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.codingKeys - -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.swift.codegen.SwiftTypes -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator - -class DefaultCodingKeysGenerator(private val codingKeysCustomizations: CodingKeysCustomizable) : CodingKeysGenerator { - override fun generateCodingKeysForMembers(ctx: ProtocolGenerator.GenerationContext, writer: SwiftWriter, members: List) { - val membersSortedByName: List = members.sortedBy { it.memberName } - if (membersSortedByName.isEmpty()) { - return - } - - writer.openBlock("enum CodingKeys: \$N, \$N {", "}", SwiftTypes.String, SwiftTypes.CodingKey) { - for (member in membersSortedByName) { - val originalMemberName = member.memberName - val modifiedMemberName = ctx.symbolProvider.toMemberName(member) - - if (codingKeysCustomizations.shouldHandleMember(member)) { - codingKeysCustomizations.handleMember(ctx, writer, member) - } else { - when { - originalMemberName == modifiedMemberName -> { - writer.write("case $modifiedMemberName") - } - else -> { - writer.write("case \$L = \$S", modifiedMemberName, originalMemberName) - } - } - } - } - } - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponseBindingErrorGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponseBindingErrorGenerator.kt new file mode 100644 index 000000000..772c68d3b --- /dev/null +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponseBindingErrorGenerator.kt @@ -0,0 +1,125 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.swift.codegen.integration.httpResponse + +import software.amazon.smithy.aws.traits.protocols.RestXmlTrait +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.swift.codegen.ClientRuntimeTypes +import software.amazon.smithy.swift.codegen.SwiftDependency +import software.amazon.smithy.swift.codegen.SwiftTypes +import software.amazon.smithy.swift.codegen.integration.HTTPProtocolCustomizable +import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.addImports +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.responseWireProtocol +import software.amazon.smithy.swift.codegen.integration.serde.struct.readerSymbol +import software.amazon.smithy.swift.codegen.model.getTrait +import software.amazon.smithy.swift.codegen.model.toUpperCamelCase +import software.amazon.smithy.swift.codegen.utils.errorShapeName + +class HTTPResponseBindingErrorGenerator( + val customizations: HTTPProtocolCustomizable, +) { + + fun renderServiceError(ctx: ProtocolGenerator.GenerationContext) { + val serviceShape = ctx.service + val serviceName = ctx.service.id.name + val rootNamespace = ctx.settings.moduleName + val fileName = "./$rootNamespace/models/$serviceName+HTTPServiceError.swift" + + ctx.delegator.useFileWriter(fileName) { writer -> + with(writer) { + addImport(SwiftDependency.CLIENT_RUNTIME.target) + addImport(customizations.baseErrorSymbol.namespace) + openBlock( + "func httpServiceError(baseError: \$N) throws -> \$N? {", + "}", + customizations.baseErrorSymbol, + SwiftTypes.Error + ) { + customizations.serviceErrorCustomRenderer(ctx)?.let { it.render(writer) } + val serviceErrorShapes = + serviceShape.errors + .map { ctx.model.expectShape(it) as StructureShape } + .toSet() + .sorted() + if (serviceErrorShapes.isNotEmpty()) { + openBlock("switch baseError.code {", "}") { + serviceErrorShapes.forEach { errorShape -> + val errorShapeName = errorShape.errorShapeName(ctx.symbolProvider) + val errorShapeType = ctx.symbolProvider.toSymbol(errorShape).name + write( + "case \$S: return try \$L.makeError(baseError: baseError)", + errorShapeName, + errorShapeType + ) + } + write("default: return nil") + } + } else { + write("return nil") + } + } + } + } + } + + fun renderOperationError(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, unknownServiceErrorSymbol: Symbol) { + val operationErrorName = "${op.toUpperCamelCase()}OutputError" + val rootNamespace = ctx.settings.moduleName + val httpBindingSymbol = Symbol.builder() + .definitionFile("./$rootNamespace/models/$operationErrorName+HttpResponseErrorBinding.swift") + .name(operationErrorName) + .build() + + ctx.delegator.useShapeWriter(httpBindingSymbol) { writer -> + writer.addImport(SwiftDependency.CLIENT_RUNTIME.target) + writer.addImport(customizations.baseErrorSymbol.namespace) + writer.addImports(ctx.service.responseWireProtocol) + writer.openBlock("enum \$L {", "}", operationErrorName) { + writer.write("") + writer.openBlock( + "static func httpError(from httpResponse: \$N) async throws -> \$N {", "}", + ClientRuntimeTypes.Http.HttpResponse, + SwiftTypes.Error, + ) { + val errorShapes = op.errors + .map { ctx.model.expectShape(it) as StructureShape } + .toSet() + .sorted() + writer.write("let data = try await httpResponse.data()") + writer.write("let responseReader = try \$N.from(data: data)", ctx.service.readerSymbol) + val noErrorWrapping = ctx.service.getTrait()?.isNoErrorWrapping ?: false + writer.write( + "let baseError = try \$N(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: \$L)", + customizations.baseErrorSymbol, + noErrorWrapping + ) + writer.write("if let error = baseError.customError() { return error }") + if (ctx.service.errors.isNotEmpty() || customizations.serviceErrorCustomRenderer(ctx) != null) { + writer.write("if let error = try httpServiceError(baseError: baseError) { return error }") + } + writer.openBlock("switch baseError.code {", "}") { + errorShapes.forEach { errorShape -> + val errorShapeName = errorShape.errorShapeName(ctx.symbolProvider) + val errorShapeType = ctx.symbolProvider.toSymbol(errorShape).name + writer.write( + "case \$S: return try \$L.makeError(baseError: baseError)", + errorShapeName, + errorShapeType + ) + } + writer.write( + "default: return try \$N.makeError(baseError: baseError)", + unknownServiceErrorSymbol + ) + } + } + } + } + } +} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponseBindingErrorInitGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponseBindingErrorInitGenerator.kt new file mode 100644 index 000000000..33afdbe47 --- /dev/null +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponseBindingErrorInitGenerator.kt @@ -0,0 +1,85 @@ +package software.amazon.smithy.swift.codegen.integration.httpResponse + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.knowledge.HttpBinding +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.swift.codegen.SwiftDependency +import software.amazon.smithy.swift.codegen.SwiftWriter +import software.amazon.smithy.swift.codegen.declareSection +import software.amazon.smithy.swift.codegen.integration.HTTPProtocolCustomizable +import software.amazon.smithy.swift.codegen.integration.HttpBindingDescriptor +import software.amazon.smithy.swift.codegen.integration.HttpBindingResolver +import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator +import software.amazon.smithy.swift.codegen.integration.SectionId +import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.HTTPResponseTraitPayload +import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.HTTPResponseTraitQueryParams +import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.HTTPResponseTraitResponseCode +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.addImports +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.responseWireProtocol + +class HTTPResponseBindingErrorInitGenerator( + val customizations: HTTPProtocolCustomizable, +) { + + object XMLHttpResponseBindingErrorInit : SectionId + object XMLHttpResponseBindingErrorInitMemberAssignment : SectionId + + fun render( + ctx: ProtocolGenerator.GenerationContext, + structureShape: StructureShape, + httpBindingResolver: HttpBindingResolver, + ) { + val responseBindings = httpBindingResolver.responseBindings(structureShape) + val headerBindings = responseBindings + .filter { it.location == HttpBinding.Location.HEADER } + .sortedBy { it.memberName } + val needsReader = responseBindings.filter { it.location == HttpBinding.Location.DOCUMENT }.isNotEmpty() + val needsResponse = responseBindings.filter { it.location == HttpBinding.Location.HEADER || it.location == HttpBinding.Location.PREFIX_HEADERS }.isNotEmpty() + val rootNamespace = ctx.settings.moduleName + val errorShape = ctx.symbolProvider.toSymbol(structureShape) + + val httpBindingSymbol = Symbol.builder() + .definitionFile("./$rootNamespace/models/${errorShape.name}+Init.swift") + .name(errorShape.name) + .build() + + ctx.delegator.useShapeWriter(httpBindingSymbol) { writer -> + writer.addImport(SwiftDependency.CLIENT_RUNTIME.target) + writer.addImport(customizations.baseErrorSymbol.namespace) + writer.addImports(ctx.service.responseWireProtocol) + writer.openBlock("extension \$N {", "}", errorShape) { + writer.write("") + writer.openBlock( + "static func makeError(baseError: \$N) throws -> \$N {", + "}", + customizations.baseErrorSymbol, + errorShape, + ) { + if (needsReader) { + writer.write("let reader = baseError.errorBodyReader") + } + if (needsResponse) { + writer.write("let httpResponse = baseError.httpResponse") + } + writer.write("var value = \$N()", errorShape) + HTTPResponseHeaders(ctx, true, headerBindings, customizations.defaultTimestampFormat, writer).render() + HTTPResponsePrefixHeaders(ctx, responseBindings, writer).render() + httpResponseTraitPayload(ctx, responseBindings, structureShape, writer) + HTTPResponseTraitQueryParams(ctx, responseBindings, writer).render() + HTTPResponseTraitResponseCode(ctx, responseBindings, writer).render() + writer.write("value.httpResponse = baseError.httpResponse") + writer.write("value.requestID = baseError.requestID") + writer.write("value.message = baseError.message") + writer.declareSection(XMLHttpResponseBindingErrorInitMemberAssignment) + writer.write("return value") + } + } + writer.write("") + } + } + + private fun httpResponseTraitPayload(ctx: ProtocolGenerator.GenerationContext, responseBindings: List, errorShape: Shape, writer: SwiftWriter) { + HTTPResponseTraitPayload(ctx, responseBindings, errorShape, writer, customizations).render() + } +} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/XMLHttpResponseBindingOutputGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponseBindingOutputGenerator.kt similarity index 64% rename from smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/XMLHttpResponseBindingOutputGenerator.kt rename to smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponseBindingOutputGenerator.kt index c776e507f..ab1b5e5c8 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/XMLHttpResponseBindingOutputGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponseBindingOutputGenerator.kt @@ -7,31 +7,34 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.StreamingTrait import software.amazon.smithy.model.traits.TimestampFormatTrait import software.amazon.smithy.swift.codegen.ClientRuntimeTypes -import software.amazon.smithy.swift.codegen.SmithyXMLTypes import software.amazon.smithy.swift.codegen.SwiftDependency import software.amazon.smithy.swift.codegen.SwiftWriter +import software.amazon.smithy.swift.codegen.integration.HTTPProtocolCustomizable import software.amazon.smithy.swift.codegen.integration.HttpBindingDescriptor import software.amazon.smithy.swift.codegen.integration.HttpBindingResolver import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.XMLHttpResponseTraitPayload -import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.XMLHttpResponseTraitQueryParams -import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.XMLHttpResponseTraitResponseCode +import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.HTTPResponseTraitPayload +import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.HTTPResponseTraitQueryParams +import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.HTTPResponseTraitResponseCode import software.amazon.smithy.swift.codegen.integration.middlewares.handlers.MiddlewareShapeUtils import software.amazon.smithy.swift.codegen.integration.serde.readwrite.AWSProtocol +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.addImports import software.amazon.smithy.swift.codegen.integration.serde.readwrite.awsProtocol +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.responseWireProtocol +import software.amazon.smithy.swift.codegen.integration.serde.struct.readerSymbol import software.amazon.smithy.swift.codegen.model.hasTrait -class XMLHttpResponseBindingOutputGenerator : HttpResponseBindingOutputGeneratable { +class HTTPResponseBindingOutputGenerator( + val customizations: HTTPProtocolCustomizable, +) { - override fun render( + fun render( ctx: ProtocolGenerator.GenerationContext, op: OperationShape, httpBindingResolver: HttpBindingResolver, - defaultTimestampFormat: TimestampFormatTrait.Format + defaultTimestampFormat: TimestampFormatTrait.Format, ) { - if (op.output.isEmpty) { - return - } + if (op.output.isEmpty) { return } val outputShape = ctx.model.expectShape(op.outputShape) val outputSymbol = MiddlewareShapeUtils.outputSymbol(ctx.symbolProvider, ctx.model, op) val responseBindings = httpBindingResolver.responseBindings(op) @@ -46,33 +49,30 @@ class XMLHttpResponseBindingOutputGenerator : HttpResponseBindingOutputGeneratab ctx.delegator.useShapeWriter(httpBindingSymbol) { writer -> writer.addImport(SwiftDependency.CLIENT_RUNTIME.target) - writer.addImport(SwiftDependency.SMITHY_XML.target) - writer.addImport(SwiftDependency.SMITHY_READ_WRITE.target) + writer.addImports(ctx.service.responseWireProtocol) writer.openBlock("extension \$N {", "}", outputSymbol) { writer.write("") writer.openBlock( - "static var httpBinding: \$N<\$N, \$N> {", + "static func httpOutput(from httpResponse: \$N) async throws -> \$N {", "}", - ClientRuntimeTypes.Http.HTTPResponseOutputBinding, + ClientRuntimeTypes.Http.HttpResponse, outputSymbol, - SmithyXMLTypes.Reader, ) { - writer.openBlock("{ httpResponse, responseDocumentClosure in", "}") { - if (responseBindings.isEmpty()) { - writer.write("return \$N()", outputSymbol) - } else { - if (needsAReader(ctx, responseBindings)) { - writer.write("let responseReader = try await responseDocumentClosure(httpResponse)") - writer.write("let reader = \$L", reader(ctx, op, writer)) - } - writer.write("var value = \$N()", outputSymbol) - XMLHttpResponseHeaders(ctx, false, headerBindings, defaultTimestampFormat, writer).render() - XMLHttpResponsePrefixHeaders(ctx, responseBindings, writer).render() - XMLHttpResponseTraitPayload(ctx, responseBindings, outputShape, writer).render() - XMLHttpResponseTraitQueryParams(ctx, responseBindings, writer).render() - XMLHttpResponseTraitResponseCode(ctx, responseBindings, writer).render() - writer.write("return value") + if (responseBindings.isEmpty()) { + writer.write("return \$N()", outputSymbol) + } else { + if (needsAReader(ctx, responseBindings)) { + writer.write("let data = try await httpResponse.data()") + writer.write("let responseReader = try \$N.from(data: data)", ctx.service.readerSymbol) + writer.write("let reader = \$L", reader(ctx, op, writer)) } + writer.write("var value = \$N()", outputSymbol) + HTTPResponseHeaders(ctx, false, headerBindings, defaultTimestampFormat, writer).render() + HTTPResponsePrefixHeaders(ctx, responseBindings, writer).render() + HTTPResponseTraitPayload(ctx, responseBindings, outputShape, writer, customizations).render() + HTTPResponseTraitQueryParams(ctx, responseBindings, writer).render() + HTTPResponseTraitResponseCode(ctx, responseBindings, writer).render() + writer.write("return value") } } } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseBindingRenderable.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponseBindingRenderable.kt similarity index 83% rename from smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseBindingRenderable.kt rename to smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponseBindingRenderable.kt index 483253dfc..a82283e7e 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseBindingRenderable.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponseBindingRenderable.kt @@ -5,6 +5,6 @@ package software.amazon.smithy.swift.codegen.integration.httpResponse -interface HttpResponseBindingRenderable { +interface HTTPResponseBindingRenderable { fun render() } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponseGenerator.kt similarity index 50% rename from smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseGenerator.kt rename to smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponseGenerator.kt index 4d107b3d9..90ae184e1 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponseGenerator.kt @@ -5,23 +5,21 @@ package software.amazon.smithy.swift.codegen.integration.httpResponse -import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.model.traits.TimestampFormatTrait +import software.amazon.smithy.swift.codegen.integration.HTTPProtocolCustomizable import software.amazon.smithy.swift.codegen.integration.HttpBindingResolver import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -class HttpResponseGenerator( - val unknownServiceErrorSymbol: Symbol, - val defaultTimestampFormat: TimestampFormatTrait.Format, - val httpResponseBindingOutputGenerator: HttpResponseBindingOutputGeneratable, - val httpResponseBindingErrorGenerator: HttpResponseBindingErrorGeneratable, - val httpResponseBindingErrorInitGeneratorFactory: HttpResponseBindingErrorInitGeneratorFactory? = null -) : HttpResponseGeneratable { +class HTTPResponseGenerator( + val customizations: HTTPProtocolCustomizable, +) { + val httpResponseBindingOutputGenerator = HTTPResponseBindingOutputGenerator(customizations) + val httpResponseBindingErrorGenerator = HTTPResponseBindingErrorGenerator(customizations) + val httpResponseBindingErrorInitGenerator = HTTPResponseBindingErrorInitGenerator(customizations) - override fun render(ctx: ProtocolGenerator.GenerationContext, httpOperations: List, httpBindingResolver: HttpBindingResolver) { + fun render(ctx: ProtocolGenerator.GenerationContext, httpOperations: List, httpBindingResolver: HttpBindingResolver) { val visitedOutputShapes: MutableSet = mutableSetOf() for (operation in httpOperations) { if (operation.output.isPresent) { @@ -29,16 +27,16 @@ class HttpResponseGenerator( if (visitedOutputShapes.contains(outputShapeId)) { continue } - httpResponseBindingOutputGenerator.render(ctx, operation, httpBindingResolver, defaultTimestampFormat) + httpResponseBindingOutputGenerator.render(ctx, operation, httpBindingResolver, customizations.defaultTimestampFormat) visitedOutputShapes.add(outputShapeId) } } - if (ctx.service.errors.isNotEmpty()) { + if (ctx.service.errors.isNotEmpty() || customizations.serviceErrorCustomRenderer(ctx) != null) { httpResponseBindingErrorGenerator.renderServiceError(ctx) } httpOperations.forEach { - httpResponseBindingErrorGenerator.renderOperationError(ctx, it, unknownServiceErrorSymbol) + httpResponseBindingErrorGenerator.renderOperationError(ctx, it, customizations.unknownServiceErrorSymbol) } val modeledOperationErrors = httpOperations @@ -52,21 +50,7 @@ class HttpResponseGenerator( val modeledErrors = modeledOperationErrors + modeledServiceErrors modeledErrors.forEach { - httpResponseBindingErrorInitGenerator(ctx, it, httpBindingResolver, defaultTimestampFormat) + httpResponseBindingErrorInitGenerator.render(ctx, it, httpBindingResolver) } } - - fun httpResponseBindingErrorInitGenerator( - ctx: ProtocolGenerator.GenerationContext, - structureShape: StructureShape, - httpBindingResolver: HttpBindingResolver, - defaultTimestampFormat: TimestampFormatTrait.Format - ) { - val httpResponseBindingErrorInitGenerator = httpResponseBindingErrorInitGeneratorFactory?.let { - it.construct(ctx, structureShape, httpBindingResolver, defaultTimestampFormat) - } ?: run { - HttpResponseBindingErrorInitGenerator(ctx, structureShape, httpBindingResolver, defaultTimestampFormat) - } - httpResponseBindingErrorInitGenerator.render() - } } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/XMLHttpResponseHeaders.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponseHeaders.kt similarity index 99% rename from smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/XMLHttpResponseHeaders.kt rename to smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponseHeaders.kt index 816706736..dee085452 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/XMLHttpResponseHeaders.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponseHeaders.kt @@ -27,7 +27,7 @@ import software.amazon.smithy.swift.codegen.integration.serde.TimestampHelpers import software.amazon.smithy.swift.codegen.model.hasTrait import software.amazon.smithy.swift.codegen.model.isBoxed -class XMLHttpResponseHeaders( +class HTTPResponseHeaders( val ctx: ProtocolGenerator.GenerationContext, val error: Boolean, val bindings: List, diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/XMLHttpResponsePrefixHeaders.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponsePrefixHeaders.kt similarity index 98% rename from smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/XMLHttpResponsePrefixHeaders.kt rename to smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponsePrefixHeaders.kt index 47e314241..2bfdaeeb5 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/XMLHttpResponsePrefixHeaders.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HTTPResponsePrefixHeaders.kt @@ -16,7 +16,7 @@ import software.amazon.smithy.swift.codegen.SwiftWriter import software.amazon.smithy.swift.codegen.integration.HttpBindingDescriptor import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -class XMLHttpResponsePrefixHeaders( +class HTTPResponsePrefixHeaders( val ctx: ProtocolGenerator.GenerationContext, val responseBindings: List, val writer: SwiftWriter diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseBindingErrorGeneratable.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseBindingErrorGeneratable.kt deleted file mode 100644 index 6a9b2b02d..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseBindingErrorGeneratable.kt +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.httpResponse - -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator - -interface HttpResponseBindingErrorGeneratable { - fun renderServiceError(ctx: ProtocolGenerator.GenerationContext) - fun renderOperationError(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, unknownServiceErrorSymbol: Symbol) -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseBindingErrorInitGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseBindingErrorInitGenerator.kt deleted file mode 100644 index f366407bf..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseBindingErrorInitGenerator.kt +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.httpResponse - -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.knowledge.HttpBinding -import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.swift.codegen.ClientRuntimeTypes -import software.amazon.smithy.swift.codegen.SwiftDependency -import software.amazon.smithy.swift.codegen.SwiftTypes -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.declareSection -import software.amazon.smithy.swift.codegen.integration.HttpBindingDescriptor -import software.amazon.smithy.swift.codegen.integration.HttpBindingResolver -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.SectionId -import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.HttpResponseTraitPayload -import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.HttpResponseTraitPayloadFactory -import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.HttpResponseTraitQueryParams -import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.HttpResponseTraitResponseCode - -interface HttpResponseBindingErrorInitGeneratorFactory { - fun construct( - ctx: ProtocolGenerator.GenerationContext, - structureShape: StructureShape, - httpBindingResolver: HttpBindingResolver, - defaultTimestampFormat: TimestampFormatTrait.Format - ): HttpResponseBindingRenderable -} - -class HttpResponseBindingErrorInitGenerator( - val ctx: ProtocolGenerator.GenerationContext, - val shape: StructureShape, - val httpBindingResolver: HttpBindingResolver, - val defaultTimestampFormat: TimestampFormatTrait.Format, - val httpResponseTraitPayloadFactory: HttpResponseTraitPayloadFactory? = null -) : HttpResponseBindingRenderable { - - object HttpResponseBindingErrorInit : SectionId - object HttpResponseBindingErrorInitMemberAssignment : SectionId - - override fun render() { - val responseBindings = httpBindingResolver.responseBindings(shape) - val headerBindings = responseBindings - .filter { it.location == HttpBinding.Location.HEADER } - .sortedBy { it.memberName } - val rootNamespace = ctx.settings.moduleName - val errorShapeName = ctx.symbolProvider.toSymbol(shape).name - - val httpBindingSymbol = Symbol.builder() - .definitionFile("./$rootNamespace/models/$errorShapeName+Init.swift") - .name(errorShapeName) - .build() - - ctx.delegator.useShapeWriter(httpBindingSymbol) { writer -> - writer.addImport(SwiftDependency.CLIENT_RUNTIME.target) - writer.openBlock("extension \$L {", "}", errorShapeName) { - writer.declareSection(HttpResponseBindingErrorInit) { - writer.write( - "public init(httpResponse: \$N, decoder: \$D, message: \$D, requestID: \$D) async throws {", - ClientRuntimeTypes.Http.HttpResponse, - ClientRuntimeTypes.Serde.ResponseDecoder, - SwiftTypes.String, - SwiftTypes.String - ) - } - writer.indent() - HttpResponseHeaders(ctx, true, headerBindings, defaultTimestampFormat, writer).render() - HttpResponsePrefixHeaders(ctx, responseBindings, writer).render() - httpResponseTraitPayload(ctx, responseBindings, shape, writer) - HttpResponseTraitQueryParams(ctx, responseBindings, writer).render() - HttpResponseTraitResponseCode(ctx, responseBindings, writer).render() - writer.write("self.httpResponse = httpResponse") - writer.write("self.requestID = requestID") - writer.write("self.message = message") - writer.declareSection(HttpResponseBindingErrorInitMemberAssignment) - writer.dedent() - writer.write("}") - } - writer.write("") - } - } - - fun httpResponseTraitPayload(ctx: ProtocolGenerator.GenerationContext, responseBindings: List, errorShape: Shape, writer: SwiftWriter) { - val responseTraitPayload = httpResponseTraitPayloadFactory?.let { - it.construct(ctx, responseBindings, errorShape, writer) - } ?: run { - HttpResponseTraitPayload(ctx, responseBindings, errorShape, writer) - } - responseTraitPayload.render() - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseBindingOutputGeneratable.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseBindingOutputGeneratable.kt deleted file mode 100644 index e155de254..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseBindingOutputGeneratable.kt +++ /dev/null @@ -1,15 +0,0 @@ -package software.amazon.smithy.swift.codegen.integration.httpResponse - -import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.swift.codegen.integration.HttpBindingResolver -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator - -interface HttpResponseBindingOutputGeneratable { - fun render( - ctx: ProtocolGenerator.GenerationContext, - op: OperationShape, - httpBindingResolver: HttpBindingResolver, - defaultTimestampFormat: TimestampFormatTrait.Format - ) -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseBindingOutputGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseBindingOutputGenerator.kt deleted file mode 100644 index b2db95c91..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseBindingOutputGenerator.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.httpResponse - -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.knowledge.HttpBinding -import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.swift.codegen.ClientRuntimeTypes -import software.amazon.smithy.swift.codegen.SwiftDependency -import software.amazon.smithy.swift.codegen.integration.HttpBindingResolver -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.HttpResponseTraitPayload -import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.HttpResponseTraitQueryParams -import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.HttpResponseTraitResponseCode -import software.amazon.smithy.swift.codegen.integration.middlewares.handlers.MiddlewareShapeUtils - -class HttpResponseBindingOutputGenerator() : HttpResponseBindingOutputGeneratable { - - override fun render( - ctx: ProtocolGenerator.GenerationContext, - op: OperationShape, - httpBindingResolver: HttpBindingResolver, - defaultTimestampFormat: TimestampFormatTrait.Format - ) { - if (op.output.isEmpty) { - return - } - val outputShape = ctx.model.expectShape(op.outputShape) - val outputShapeName = MiddlewareShapeUtils.outputSymbol(ctx.symbolProvider, ctx.model, op).name - var responseBindings = httpBindingResolver.responseBindings(op) - val headerBindings = responseBindings - .filter { it.location == HttpBinding.Location.HEADER } - .sortedBy { it.memberName } - val rootNamespace = ctx.settings.moduleName - val httpBindingSymbol = Symbol.builder() - .definitionFile("./$rootNamespace/models/$outputShapeName+HttpResponseBinding.swift") - .name(outputShapeName) - .build() - - ctx.delegator.useShapeWriter(httpBindingSymbol) { writer -> - writer.addImport(SwiftDependency.CLIENT_RUNTIME.target) - writer.openBlock("extension $outputShapeName: \$N {", "}", ClientRuntimeTypes.Http.HttpResponseBinding) { - writer.openBlock( - "public init(httpResponse: \$N, decoder: \$D) async throws {", - "}", - ClientRuntimeTypes.Http.HttpResponse, - ClientRuntimeTypes.Serde.ResponseDecoder - ) { - HttpResponseHeaders(ctx, false, headerBindings, defaultTimestampFormat, writer).render() - HttpResponsePrefixHeaders(ctx, responseBindings, writer).render() - HttpResponseTraitPayload(ctx, responseBindings, outputShape, writer).render() - HttpResponseTraitQueryParams(ctx, responseBindings, writer).render() - HttpResponseTraitResponseCode(ctx, responseBindings, writer).render() - } - } - writer.write("") - } - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseGeneratable.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseGeneratable.kt deleted file mode 100644 index a9506453d..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseGeneratable.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.httpResponse - -import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.swift.codegen.integration.HttpBindingResolver -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator - -interface HttpResponseGeneratable { - fun render(ctx: ProtocolGenerator.GenerationContext, httpOperations: List, httpBindingResolver: HttpBindingResolver) -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseHeaders.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseHeaders.kt deleted file mode 100644 index 03f3d61ca..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponseHeaders.kt +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.httpResponse - -import software.amazon.smithy.codegen.core.CodegenException -import software.amazon.smithy.model.knowledge.HttpBinding -import software.amazon.smithy.model.knowledge.HttpBindingIndex -import software.amazon.smithy.model.shapes.BlobShape -import software.amazon.smithy.model.shapes.BooleanShape -import software.amazon.smithy.model.shapes.ListShape -import software.amazon.smithy.model.shapes.NumberShape -import software.amazon.smithy.model.shapes.ShapeType -import software.amazon.smithy.model.shapes.StringShape -import software.amazon.smithy.model.shapes.TimestampShape -import software.amazon.smithy.model.traits.EnumTrait -import software.amazon.smithy.model.traits.MediaTypeTrait -import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.swift.codegen.ClientRuntimeTypes -import software.amazon.smithy.swift.codegen.SwiftTypes -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.HttpBindingDescriptor -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.serde.TimestampHelpers -import software.amazon.smithy.swift.codegen.model.hasTrait -import software.amazon.smithy.swift.codegen.model.isBoxed - -class HttpResponseHeaders( - val ctx: ProtocolGenerator.GenerationContext, - val error: Boolean, - val bindings: List, - val defaultTimestampFormat: TimestampFormatTrait.Format, - val writer: SwiftWriter -) { - fun render() { - bindings.forEach { hdrBinding -> - val memberTarget = ctx.model.expectShape(hdrBinding.member.target) - val path = "properties.".takeIf { error } ?: "" - val memberName = ctx.symbolProvider.toMemberName(hdrBinding.member) - val headerName = hdrBinding.locationName - val headerDeclaration = "${memberName}HeaderValue" - val isBoxed = ctx.symbolProvider.toSymbol(hdrBinding.member).isBoxed() - writer.write("if let $headerDeclaration = httpResponse.headers.value(for: \$S) {", headerName) - writer.indent() - when (memberTarget) { - is NumberShape -> { - if (memberTarget.isIntEnumShape) { - val enumSymbol = ctx.symbolProvider.toSymbol(memberTarget) - writer.write( - "self.$path\$L = \$L(rawValue: \$L(\$L) ?? 0)", - memberName, enumSymbol, SwiftTypes.Int, headerDeclaration - ) - } else { - val memberValue = stringToNumber(memberTarget, headerDeclaration, true) - writer.write("self.$path\$L = \$L", memberName, memberValue) - } - } - is BlobShape -> { - val memberValue = "$headerDeclaration.data(using: .utf8)" - writer.write("self.$path\$L = $memberValue", memberName) - } - is BooleanShape -> { - val memberValue = "${SwiftTypes.Bool}($headerDeclaration) ?? false" - writer.write("self.$path\$L = $memberValue", memberName) - } - is StringShape -> { - val memberValue = when { - memberTarget.hasTrait() -> { - val enumSymbol = ctx.symbolProvider.toSymbol(memberTarget) - "$enumSymbol(rawValue: $headerDeclaration)" - } - memberTarget.hasTrait() -> { - "try $headerDeclaration.base64DecodedString()" - } - else -> { - headerDeclaration - } - } - writer.write("self.$path\$L = $memberValue", memberName) - } - is TimestampShape -> { - val bindingIndex = HttpBindingIndex.of(ctx.model) - val tsFormat = bindingIndex.determineTimestampFormat( - hdrBinding.member, - HttpBinding.Location.HEADER, - defaultTimestampFormat - ) - var memberValue = stringToDate(headerDeclaration, tsFormat) - writer.write("self.$path\$L = \$L", memberName, memberValue) - } - is ListShape -> { - // member > boolean, number, string, or timestamp - // headers are List, get the internal mapping function contents (if any) to convert - // to the target symbol type - - // we also have to handle multiple comma separated values (e.g. 'X-Foo': "1, 2, 3"`) - var splitFn = "splitHeaderListValues" - var splitFnPrefix = "" - var invalidHeaderListErrorName = "invalidNumbersHeaderList" - val conversion = when (val collectionMemberTarget = ctx.model.expectShape(memberTarget.member.target)) { - is BooleanShape -> { - invalidHeaderListErrorName = "invalidBooleanHeaderList" - "${SwiftTypes.Bool}(\$0)" - } - is NumberShape -> { - if (collectionMemberTarget.isIntEnumShape) { - val enumSymbol = ctx.symbolProvider.toSymbol(collectionMemberTarget) - "${SwiftTypes.Int}(\$0).map({ intValue in $enumSymbol(rawValue: intValue) })" - } else { - "${stringToNumber(collectionMemberTarget, "\$0", false)}" - } - } - is TimestampShape -> { - val bindingIndex = HttpBindingIndex.of(ctx.model) - val tsFormat = bindingIndex.determineTimestampFormat( - hdrBinding.member, - HttpBinding.Location.HEADER, - defaultTimestampFormat - ) - if (tsFormat == TimestampFormatTrait.Format.HTTP_DATE) { - splitFn = "splitHttpDateHeaderListValues" - } - invalidHeaderListErrorName = "invalidTimestampHeaderList" - "(${stringToDate("\$0", tsFormat)} ?? ${ClientRuntimeTypes.Core.Date}())" - } - is StringShape -> { - invalidHeaderListErrorName = "invalidStringHeaderList" - when { - collectionMemberTarget.hasTrait() -> { - val enumSymbol = ctx.symbolProvider.toSymbol(collectionMemberTarget) - "($enumSymbol(rawValue: \$0) ?? $enumSymbol(rawValue: \"Bar\")!)" - } - collectionMemberTarget.hasTrait() -> { - "try \$0.base64EncodedString()" - } - else -> "" - } - } - else -> throw CodegenException("invalid member type for header collection: binding: $hdrBinding; member: $memberName") - } - val mapFn = if (conversion.isNotEmpty()) ".map { $conversion }" else "" - var memberValue = "${memberName}HeaderValues$mapFn" - if (memberTarget.isSetShape) { - memberValue = "${SwiftTypes.Set}(${memberName}HeaderValues)" - } - writer.write("if let ${memberName}HeaderValues = try $splitFnPrefix$splitFn(${memberName}HeaderValue) {") - writer.indent() - // render map function - val collectionMemberTargetShape = ctx.model.expectShape(memberTarget.member.target) - val collectionMemberTargetSymbol = ctx.symbolProvider.toSymbol(collectionMemberTargetShape) - if (!collectionMemberTargetSymbol.isBoxed() || collectionMemberTargetShape.isIntEnumShape()) { - writer.openBlock("self.\$L = try \$LHeaderValues.map {", "}", memberName, memberName) { - val transformedHeaderDeclaration = "${memberName}Transformed" - writer.openBlock("guard let \$L = \$L else {", "}", transformedHeaderDeclaration, conversion) { - writer.write("throw HeaderDeserializationError.\$L(value: \$LHeaderValue)", invalidHeaderListErrorName, memberName) - } - writer.write("return \$L", transformedHeaderDeclaration) - } - } else { - writer.write("self.\$L\$L = \$L", path, memberName, memberValue) - } - writer.dedent() - writer.write("} else {") - writer.indent() - writer.write("self.$path\$L = nil", memberName) - writer.dedent() - writer.write("}") - } - else -> throw CodegenException("unknown deserialization: header binding: $hdrBinding; member: `$memberName`") - } - writer.dedent() - writer.write("} else {") - writer.indent() - var assignmentValue = "nil" - when (memberTarget) { - is NumberShape -> { - assignmentValue = if (isBoxed) "nil" else "0" - } - is BooleanShape -> { - assignmentValue = if (isBoxed) "nil" else "false" - } - } - writer.write("self.$path$memberName = $assignmentValue") - writer.dedent() - writer.write("}") - } - } - - private fun stringToNumber(shape: NumberShape, stringValue: String, zeroDefaultValue: Boolean): String { - val defaultValue = if (zeroDefaultValue) " ?? 0" else "" - return when (shape.type) { - ShapeType.BYTE -> "${SwiftTypes.Int8}($stringValue)$defaultValue" - ShapeType.SHORT -> "${SwiftTypes.Int16}($stringValue)$defaultValue" - ShapeType.INTEGER -> "${SwiftTypes.Int}($stringValue)$defaultValue" - ShapeType.LONG -> "${SwiftTypes.Int}($stringValue)$defaultValue" - ShapeType.FLOAT -> "${SwiftTypes.Float}($stringValue)$defaultValue" - ShapeType.DOUBLE -> "${SwiftTypes.Double}($stringValue)$defaultValue" - else -> throw CodegenException("unknown number shape: $shape") - } - } - - private fun stringToDate(stringValue: String, tsFormat: TimestampFormatTrait.Format): String { - val timestampFormat = TimestampHelpers.generateTimestampFormatEnumValue(tsFormat) - return "TimestampFormatter(format: .$timestampFormat).date(from: $stringValue)" - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponsePrefixHeaders.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponsePrefixHeaders.kt deleted file mode 100644 index 3383f2783..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/HttpResponsePrefixHeaders.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.httpResponse - -import software.amazon.smithy.codegen.core.CodegenException -import software.amazon.smithy.model.knowledge.HttpBinding -import software.amazon.smithy.model.shapes.ListShape -import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.model.shapes.SetShape -import software.amazon.smithy.model.shapes.StringShape -import software.amazon.smithy.swift.codegen.SwiftTypes -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.HttpBindingDescriptor -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator - -class HttpResponsePrefixHeaders( - val ctx: ProtocolGenerator.GenerationContext, - val responseBindings: List, - val writer: SwiftWriter -) { - fun render() { - val binding = responseBindings.firstOrNull { it.location == HttpBinding.Location.PREFIX_HEADERS } - if (binding != null) { - renderBinding(binding) - } - } - - private fun renderBinding(binding: HttpBindingDescriptor) { - val targetShape = ctx.model.expectShape(binding.member.target) as? MapShape - ?: throw CodegenException("prefixHeader bindings can only be attached to Map shapes") - - val targetValueShape = ctx.model.expectShape(targetShape.value.target) - val targetValueSymbol = ctx.symbolProvider.toSymbol(targetValueShape) - val prefix = binding.locationName - val memberName = ctx.symbolProvider.toMemberName(binding.member) - - val keyCollName = "keysFor${memberName.capitalize()}" - val filter = if (prefix.isNotEmpty()) ".filter({ $0.starts(with: \"$prefix\") })" else "" - - writer.write("let $keyCollName = httpResponse.headers.dictionary.keys\$L", filter) - writer.openBlock("if (!$keyCollName.isEmpty) {") - .write("var mapMember = [\$N: ${targetValueSymbol.name}]()", SwiftTypes.String) - .openBlock("for headerKey in $keyCollName {") - .call { - val mapMemberValue = when (targetValueShape) { - is StringShape -> "httpResponse.headers.dictionary[headerKey]?[0]" - is ListShape -> "httpResponse.headers.dictionary[headerKey]" - is SetShape -> "Set(httpResponse.headers.dictionary[headerKey])" - else -> throw CodegenException("invalid httpPrefixHeaders usage on ${binding.member}") - } - writer.write("let mapMemberValue = $mapMemberValue") - if (prefix.isNotEmpty()) { - writer.write("let mapMemberKey = headerKey.removePrefix(\$S)", prefix) - writer.write("mapMember[mapMemberKey] = mapMemberValue") - } else { - writer.write("mapMember[headerKey] = mapMemberValue") - } - } - .closeBlock("}") - .write("self.\$L = mapMember", memberName) - .closeBlock("} else {") - writer.indent() - writer.write("self.\$L = [:]", memberName) - writer.dedent() - writer.write("}") - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/XMLHttpResponseBindingErrorInitGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/XMLHttpResponseBindingErrorInitGenerator.kt deleted file mode 100644 index d9cd740ae..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/XMLHttpResponseBindingErrorInitGenerator.kt +++ /dev/null @@ -1,89 +0,0 @@ -package software.amazon.smithy.swift.codegen.integration.httpResponse - -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.knowledge.HttpBinding -import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.swift.codegen.ClientRuntimeTypes -import software.amazon.smithy.swift.codegen.SmithyXMLTypes -import software.amazon.smithy.swift.codegen.SwiftDependency -import software.amazon.smithy.swift.codegen.SwiftTypes -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.declareSection -import software.amazon.smithy.swift.codegen.integration.HttpBindingDescriptor -import software.amazon.smithy.swift.codegen.integration.HttpBindingResolver -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.SectionId -import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.HttpResponseTraitPayload -import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.HttpResponseTraitPayloadFactory -import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.XMLHttpResponseTraitQueryParams -import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.XMLHttpResponseTraitResponseCode - -class XMLHttpResponseBindingErrorInitGenerator( - val ctx: ProtocolGenerator.GenerationContext, - val shape: StructureShape, - val httpBindingResolver: HttpBindingResolver, - val defaultTimestampFormat: TimestampFormatTrait.Format, - val httpResponseTraitPayloadFactory: HttpResponseTraitPayloadFactory? = null -) : HttpResponseBindingRenderable { - - object XMLHttpResponseBindingErrorInit : SectionId - object XMLHttpResponseBindingErrorInitMemberAssignment : SectionId - - override fun render() { - val responseBindings = httpBindingResolver.responseBindings(shape) - val headerBindings = responseBindings - .filter { it.location == HttpBinding.Location.HEADER } - .sortedBy { it.memberName } - val rootNamespace = ctx.settings.moduleName - val errorShape = ctx.symbolProvider.toSymbol(shape) - - val httpBindingSymbol = Symbol.builder() - .definitionFile("./$rootNamespace/models/${errorShape.name}+Init.swift") - .name(errorShape.name) - .build() - - ctx.delegator.useShapeWriter(httpBindingSymbol) { writer -> - writer.addImport(SwiftDependency.CLIENT_RUNTIME.target) - writer.addImport(SwiftDependency.SMITHY_XML.target) - writer.openBlock("extension \$N {", "}", errorShape) { - writer.write("") - writer.declareSection(XMLHttpResponseBindingErrorInit) { - writer.write( - "static func responseErrorBinding(httpResponse: \$N, reader: \$N, message: \$D, requestID: \$D) async throws -> \$N {", - ClientRuntimeTypes.Http.HttpResponse, - SmithyXMLTypes.Reader, - SwiftTypes.String, - SwiftTypes.String, - SwiftTypes.Error, - ) - } - writer.indent() - writer.write("var value = \$N()", errorShape) - XMLHttpResponseHeaders(ctx, true, headerBindings, defaultTimestampFormat, writer).render() - XMLHttpResponsePrefixHeaders(ctx, responseBindings, writer).render() - httpResponseTraitPayload(ctx, responseBindings, shape, writer) - XMLHttpResponseTraitQueryParams(ctx, responseBindings, writer).render() - XMLHttpResponseTraitResponseCode(ctx, responseBindings, writer).render() - writer.write("value.httpResponse = httpResponse") - writer.write("value.requestID = requestID") - writer.write("value.message = message") - writer.declareSection(XMLHttpResponseBindingErrorInitMemberAssignment) - writer.write("return value") - writer.dedent() - writer.write("}") - } - writer.write("") - } - } - - fun httpResponseTraitPayload(ctx: ProtocolGenerator.GenerationContext, responseBindings: List, errorShape: Shape, writer: SwiftWriter) { - val responseTraitPayload = httpResponseTraitPayloadFactory?.let { - it.construct(ctx, responseBindings, errorShape, writer) - } ?: run { - HttpResponseTraitPayload(ctx, responseBindings, errorShape, writer) - } - responseTraitPayload.render() - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/XMLHttpResponseTraitPayload.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HTTPResponseTraitPayload.kt similarity index 68% rename from smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/XMLHttpResponseTraitPayload.kt rename to smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HTTPResponseTraitPayload.kt index b320cbf9d..46c646b36 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/XMLHttpResponseTraitPayload.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HTTPResponseTraitPayload.kt @@ -8,26 +8,28 @@ package software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTra import software.amazon.smithy.model.knowledge.HttpBinding import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.swift.codegen.SwiftWriter +import software.amazon.smithy.swift.codegen.integration.HTTPProtocolCustomizable import software.amazon.smithy.swift.codegen.integration.HttpBindingDescriptor import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingRenderable +import software.amazon.smithy.swift.codegen.integration.httpResponse.HTTPResponseBindingRenderable -class XMLHttpResponseTraitPayload( +class HTTPResponseTraitPayload( val ctx: ProtocolGenerator.GenerationContext, val responseBindings: List, val outputShape: Shape, val writer: SwiftWriter, - val httpResponseTraitWithoutPayloadFactory: HttpResponseTraitWithoutHttpPayloadFactory? = null -) : HttpResponseBindingRenderable { + val customizations: HTTPProtocolCustomizable, + val httpResponseTraitWithoutPayloadFactory: HTTPResponseTraitWithoutHTTPPayloadFactory? = null, +) : HTTPResponseBindingRenderable { override fun render() { val httpPayload = responseBindings.firstOrNull { it.location == HttpBinding.Location.PAYLOAD } if (httpPayload != null) { - XMLHttpResponseTraitWithHttpPayload(ctx, httpPayload, writer, outputShape).render() + HTTPResponseTraitWithHTTPPayload(ctx, httpPayload, writer, outputShape, customizations).render() } else { val httpResponseTraitWithoutPayload = httpResponseTraitWithoutPayloadFactory?.let { it.construct(ctx, responseBindings, outputShape, writer) } ?: run { - XMLHttpResponseTraitWithoutHttpPayload(ctx, responseBindings, outputShape, writer) + HTTPResponseTraitWithoutHTTPPayload(ctx, responseBindings, outputShape, writer, customizations) } httpResponseTraitWithoutPayload.render() } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/XMLHttpResponseTraitQueryParams.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HTTPResponseTraitQueryParams.kt similarity index 96% rename from smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/XMLHttpResponseTraitQueryParams.kt rename to smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HTTPResponseTraitQueryParams.kt index 93f3e1a48..145a9b435 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/XMLHttpResponseTraitQueryParams.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HTTPResponseTraitQueryParams.kt @@ -11,7 +11,7 @@ import software.amazon.smithy.swift.codegen.SwiftWriter import software.amazon.smithy.swift.codegen.integration.HttpBindingDescriptor import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -class XMLHttpResponseTraitQueryParams( +class HTTPResponseTraitQueryParams( val ctx: ProtocolGenerator.GenerationContext, val responseBindings: List, val writer: SwiftWriter diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/XMLHttpResponseTraitResponseCode.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HTTPResponseTraitResponseCode.kt similarity index 96% rename from smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/XMLHttpResponseTraitResponseCode.kt rename to smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HTTPResponseTraitResponseCode.kt index 6fab47ce1..ee430237a 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/XMLHttpResponseTraitResponseCode.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HTTPResponseTraitResponseCode.kt @@ -10,7 +10,7 @@ import software.amazon.smithy.swift.codegen.SwiftWriter import software.amazon.smithy.swift.codegen.integration.HttpBindingDescriptor import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -class XMLHttpResponseTraitResponseCode( +class HTTPResponseTraitResponseCode( val ctx: ProtocolGenerator.GenerationContext, val responseBindings: List, val writer: SwiftWriter diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/XMLHttpResponseTraitWithHttpPayload.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HTTPResponseTraitWithHTTPPayload.kt similarity index 80% rename from smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/XMLHttpResponseTraitWithHttpPayload.kt rename to smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HTTPResponseTraitWithHTTPPayload.kt index f06703f61..99c01fe5d 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/XMLHttpResponseTraitWithHttpPayload.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HTTPResponseTraitWithHTTPPayload.kt @@ -10,22 +10,25 @@ import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.ShapeType import software.amazon.smithy.model.traits.StreamingTrait import software.amazon.smithy.swift.codegen.ClientRuntimeTypes +import software.amazon.smithy.swift.codegen.SmithyReadWriteTypes +import software.amazon.smithy.swift.codegen.SwiftDependency import software.amazon.smithy.swift.codegen.SwiftTypes import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.declareSection +import software.amazon.smithy.swift.codegen.integration.HTTPProtocolCustomizable import software.amazon.smithy.swift.codegen.integration.HttpBindingDescriptor import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingRenderable -import software.amazon.smithy.swift.codegen.integration.serde.xml.MemberShapeDecodeXMLGenerator +import software.amazon.smithy.swift.codegen.integration.httpResponse.HTTPResponseBindingRenderable +import software.amazon.smithy.swift.codegen.integration.serde.member.MemberShapeDecodeGenerator import software.amazon.smithy.swift.codegen.model.hasTrait import software.amazon.smithy.swift.codegen.model.isEnum -class XMLHttpResponseTraitWithHttpPayload( +class HTTPResponseTraitWithHTTPPayload( val ctx: ProtocolGenerator.GenerationContext, val binding: HttpBindingDescriptor, val writer: SwiftWriter, val shapeContainingMembers: Shape, -) : HttpResponseBindingRenderable { + val customizations: HTTPProtocolCustomizable, +) : HTTPResponseBindingRenderable { override fun render() { val memberName = ctx.symbolProvider.toMemberName(binding.member) @@ -35,9 +38,10 @@ class XMLHttpResponseTraitWithHttpPayload( ctx.model.getShape(binding.member.target).get().hasTrait() && target.type == ShapeType.BLOB when (target.type) { ShapeType.DOCUMENT -> { - // Smithy Document is currently not used in AWS. - // If this is eventually required by a model, a Swift compile error will occur. - writer.write("#error(\"Not implemented\")") + writer.addImport(SwiftDependency.SMITHY_READ_WRITE.target) + writer.openBlock("if let data = try await httpResponse.body.readData() {", "}") { + writer.write("value.\$L = try \$N.make(from: data)", memberName, SmithyReadWriteTypes.Document) + } } ShapeType.STRING -> { writer.openBlock("if let data = try await httpResponse.body.readData(), let output = \$N(data: data, encoding: .utf8) {", "}", SwiftTypes.String) { @@ -82,9 +86,8 @@ class XMLHttpResponseTraitWithHttpPayload( ShapeType.STRUCTURE, ShapeType.UNION -> { if (target.hasTrait()) { writer.openBlock("if case .stream(let stream) = httpResponse.body {", "}") { - writer.declareSection(HttpResponseTraitWithHttpPayload.MessageDecoderSectionId) { - writer.write("let messageDecoder: \$D", ClientRuntimeTypes.EventStream.MessageDecoder) - } + writer.addImport(customizations.messageDecoderSymbol.namespace) + writer.write("let messageDecoder = \$N()", customizations.messageDecoderSymbol) writer.write( "let decoderStream = \$N(stream: stream, messageDecoder: messageDecoder, unmarshalClosure: \$N.unmarshal)", ClientRuntimeTypes.EventStream.MessageDecoderStream, @@ -93,7 +96,7 @@ class XMLHttpResponseTraitWithHttpPayload( writer.write("value.\$L = decoderStream.toAsyncStream()", memberName) } } else { - MemberShapeDecodeXMLGenerator(ctx, writer, shapeContainingMembers) + MemberShapeDecodeGenerator(ctx, writer, shapeContainingMembers) .render(binding.member, true) } } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/XMLHttpResponseTraitWithoutHttpPayload.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HTTPResponseTraitWithoutHTTPPayload.kt similarity index 63% rename from smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/XMLHttpResponseTraitWithoutHttpPayload.kt rename to smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HTTPResponseTraitWithoutHTTPPayload.kt index d425348f8..82c708d51 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/XMLHttpResponseTraitWithoutHttpPayload.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HTTPResponseTraitWithoutHTTPPayload.kt @@ -13,20 +13,21 @@ import software.amazon.smithy.model.traits.HttpQueryTrait import software.amazon.smithy.model.traits.StreamingTrait import software.amazon.smithy.swift.codegen.ClientRuntimeTypes import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.declareSection +import software.amazon.smithy.swift.codegen.integration.HTTPProtocolCustomizable import software.amazon.smithy.swift.codegen.integration.HttpBindingDescriptor import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingRenderable +import software.amazon.smithy.swift.codegen.integration.httpResponse.HTTPResponseBindingRenderable +import software.amazon.smithy.swift.codegen.integration.serde.member.MemberShapeDecodeGenerator import software.amazon.smithy.swift.codegen.integration.serde.readwrite.isRPCBound -import software.amazon.smithy.swift.codegen.integration.serde.xml.MemberShapeDecodeXMLGenerator import software.amazon.smithy.swift.codegen.model.targetOrSelf -class XMLHttpResponseTraitWithoutHttpPayload( +class HTTPResponseTraitWithoutHTTPPayload( val ctx: ProtocolGenerator.GenerationContext, val responseBindings: List, val outputShape: Shape, - val writer: SwiftWriter -) : HttpResponseBindingRenderable { + val writer: SwiftWriter, + val customizations: HTTPProtocolCustomizable, +) : HTTPResponseBindingRenderable { override fun render() { val bodyMembers = responseBindings.filter { it.location == HttpBinding.Location.DOCUMENT } val bodyMembersWithoutQueryTrait = bodyMembers @@ -50,28 +51,26 @@ class XMLHttpResponseTraitWithoutHttpPayload( val memberName = ctx.symbolProvider.toMemberName(streamingMember.member) when (shape.type) { ShapeType.UNION -> { - writer.openBlock("if case let .stream(stream) = httpResponse.body, let responseDecoder = decoder {", "} else {") { - writer.declareSection(HttpResponseTraitWithHttpPayload.MessageDecoderSectionId) { - write("let messageDecoder: \$D", ClientRuntimeTypes.EventStream.MessageDecoder) - } + writer.openBlock("if case let .stream(stream) = httpResponse.body {", "}") { + writer.addImport(customizations.messageDecoderSymbol.namespace) + writer.write("let messageDecoder = \$N()", customizations.messageDecoderSymbol) writer.write( - "let decoderStream = \$L<\$N>(stream: stream, messageDecoder: messageDecoder, unmarshalClosure: xmlUnmarshalClosure())", + "let decoderStream = \$L<\$N>(stream: stream, messageDecoder: messageDecoder, unmarshalClosure: \$N.unmarshal)", ClientRuntimeTypes.EventStream.MessageDecoderStream, - symbol + symbol, + symbol, ) - writer.write("self.\$L = decoderStream.toAsyncStream()", memberName) + writer.write("value.\$L = decoderStream.toAsyncStream()", memberName) if (ctx.service.isRPCBound && initialResponseMembers.isNotEmpty()) { writeInitialResponseMembers(initialResponseMembers) } } - writer.indent() - writer.write("self.\$L = nil", memberName).closeBlock("}") } ShapeType.BLOB -> { writer.write("switch httpResponse.body {") .write("case .data(let data):") .indent() - writer.write("self.\$L = .data(data)", memberName) + writer.write("value.\$L = .data(data)", memberName) // For binary streams, we need to set the member to the stream directly. // this allows us to stream the data directly to the user @@ -79,11 +78,11 @@ class XMLHttpResponseTraitWithoutHttpPayload( writer.dedent() .write("case .stream(let stream):") .indent() - writer.write("self.\$L = .stream(stream)", memberName) + writer.write("value.\$L = .stream(stream)", memberName) writer.dedent() .write("case .noStream:") .indent() - .write("self.\$L = nil", memberName).closeBlock("}") + .write("value.\$L = nil", memberName).closeBlock("}") } else -> { throw CodegenException("member shape ${streamingMember.member} cannot stream") @@ -93,41 +92,24 @@ class XMLHttpResponseTraitWithoutHttpPayload( fun writeNonStreamingMembers(members: Set) { members.sortedBy { it.memberName }.forEach { - MemberShapeDecodeXMLGenerator(ctx, writer, outputShape).render(it.member) + MemberShapeDecodeGenerator(ctx, writer, outputShape).render(it.member) } } private fun writeInitialResponseMembers(initialResponseMembers: Set) { - writer.apply { - write("if let initialDataWithoutHttp = await messageDecoder.awaitInitialResponse() {") - indent() - write("let decoder = JSONDecoder()") - write("do {") - indent() - write("let response = try decoder.decode([String: String].self, from: initialDataWithoutHttp)") - initialResponseMembers.forEach { responseMember -> - val responseMemberName = ctx.symbolProvider.toMemberName(responseMember.member) - write("self.$responseMemberName = response[\"$responseMemberName\"]") - } - dedent() - write("} catch {") - indent() - write("print(\"Error decoding JSON: \\(error)\")") - initialResponseMembers.forEach { responseMember -> - val responseMemberName = ctx.symbolProvider.toMemberName(responseMember.member) - write("self.$responseMemberName = nil") - } - dedent() - write("}") - dedent() - write("} else {") - indent() + writer.openBlock( + "if let initialDataWithoutHttp = await messageDecoder.awaitInitialResponse() {", + "}" + ) { + writer.write("let payloadReader = try Reader.from(data: initialDataWithoutHttp)") initialResponseMembers.forEach { responseMember -> val responseMemberName = ctx.symbolProvider.toMemberName(responseMember.member) - write("self.$responseMemberName = nil") + writer.write( + "value.\$L = try payloadReader[\$S].readIfPresent()", + responseMemberName, + responseMemberName, + ) } - dedent() - write("}") } } } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HTTPResponseTraitWithoutHTTPPayloadFactory.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HTTPResponseTraitWithoutHTTPPayloadFactory.kt new file mode 100644 index 000000000..363344254 --- /dev/null +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HTTPResponseTraitWithoutHTTPPayloadFactory.kt @@ -0,0 +1,75 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits + +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.swift.codegen.SwiftWriter +import software.amazon.smithy.swift.codegen.integration.HttpBindingDescriptor +import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator +import software.amazon.smithy.swift.codegen.integration.httpResponse.HTTPResponseBindingRenderable + +interface HTTPResponseTraitWithoutHTTPPayloadFactory { + fun construct( + ctx: ProtocolGenerator.GenerationContext, + responseBindings: List, + outputShape: Shape, + writer: SwiftWriter + ): HTTPResponseBindingRenderable +} + +class InitialResponse( + private val ctx: ProtocolGenerator.GenerationContext, + private val writer: SwiftWriter, +) { + + private fun writeInitialResponseMembers(initialResponseMembers: Set) { + writer.apply { + write("if let initialDataWithoutHttp = await messageDecoder.awaitInitialResponse() {") + indent() + write("let decoder = JSONDecoder()") + write("do {") + indent() + write("let response = try decoder.decode([String: String].self, from: initialDataWithoutHttp)") + initialResponseMembers.forEach { responseMember -> + val responseMemberName = ctx.symbolProvider.toMemberName(responseMember.member) + write("self.$responseMemberName = response[\"$responseMemberName\"]") + } + dedent() + write("} catch {") + indent() + write("print(\"Error decoding JSON: \\(error)\")") + initialResponseMembers.forEach { responseMember -> + val responseMemberName = ctx.symbolProvider.toMemberName(responseMember.member) + write("self.$responseMemberName = nil") + } + dedent() + write("}") + dedent() + write("} else {") + indent() + initialResponseMembers.forEach { responseMember -> + val responseMemberName = ctx.symbolProvider.toMemberName(responseMember.member) + write("self.$responseMemberName = nil") + } + dedent() + write("}") + } + } + + private fun isRPCService(ctx: ProtocolGenerator.GenerationContext): Boolean { + return rpcBoundProtocols.contains(ctx.protocol.name) + } + + /** + * A set of RPC-bound Smithy protocols + */ + private val rpcBoundProtocols = setOf( + "awsJson1_0", + "awsJson1_1", + "awsQuery", + "ec2Query", + ) +} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HttpResponseTraitPayload.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HttpResponseTraitPayload.kt deleted file mode 100644 index 6a157ffbf..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HttpResponseTraitPayload.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits - -import software.amazon.smithy.model.knowledge.HttpBinding -import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.HttpBindingDescriptor -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingRenderable - -interface HttpResponseTraitPayloadFactory { - fun construct( - ctx: ProtocolGenerator.GenerationContext, - responseBindings: List, - errorShape: Shape, - writer: SwiftWriter - ): HttpResponseBindingRenderable -} - -class HttpResponseTraitPayload( - val ctx: ProtocolGenerator.GenerationContext, - val responseBindings: List, - val outputShape: Shape, - val writer: SwiftWriter, - val httpResponseTraitWithoutPayloadFactory: HttpResponseTraitWithoutHttpPayloadFactory? = null -) : HttpResponseBindingRenderable { - override fun render() { - val httpPayload = responseBindings.firstOrNull { it.location == HttpBinding.Location.PAYLOAD } - if (httpPayload != null) { - HttpResponseTraitWithHttpPayload(ctx, httpPayload, writer).render() - } else { - val httpResponseTraitWithoutPayload = httpResponseTraitWithoutPayloadFactory?.let { - it.construct(ctx, responseBindings, outputShape, writer) - } ?: run { - HttpResponseTraitWithoutHttpPayload(ctx, responseBindings, outputShape, writer) - } - httpResponseTraitWithoutPayload.render() - } - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HttpResponseTraitQueryParams.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HttpResponseTraitQueryParams.kt deleted file mode 100644 index c90853df5..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HttpResponseTraitQueryParams.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits - -import software.amazon.smithy.model.knowledge.HttpBinding -import software.amazon.smithy.model.traits.HttpQueryTrait -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.HttpBindingDescriptor -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator - -class HttpResponseTraitQueryParams( - val ctx: ProtocolGenerator.GenerationContext, - val responseBindings: List, - val writer: SwiftWriter -) { - fun render() { - val bodyMembers = responseBindings.filter { it.location == HttpBinding.Location.DOCUMENT } - - val bodyMembersWithQueryTrait = bodyMembers - .filter { it.member.hasTrait(HttpQueryTrait::class.java) } - .map { ctx.symbolProvider.toMemberName(it.member) } - .toMutableSet() - bodyMembersWithQueryTrait.sorted().forEach { - writer.write("self.$it = nil") - } - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HttpResponseTraitResponseCode.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HttpResponseTraitResponseCode.kt deleted file mode 100644 index a3f0d4440..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HttpResponseTraitResponseCode.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits - -import software.amazon.smithy.model.traits.HttpResponseCodeTrait -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.HttpBindingDescriptor -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator - -class HttpResponseTraitResponseCode( - val ctx: ProtocolGenerator.GenerationContext, - val responseBindings: List, - val writer: SwiftWriter -) { - fun render() { - val responseCodeTraitMembers = responseBindings - .filter { it.member.hasTrait(HttpResponseCodeTrait::class.java) } - .toMutableSet() - if (responseCodeTraitMembers.isNotEmpty()) { - responseCodeTraitMembers.forEach { - writer.write("self.${it.locationName.decapitalize()} = httpResponse.statusCode.rawValue") - } - } - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HttpResponseTraitWithHttpPayload.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HttpResponseTraitWithHttpPayload.kt deleted file mode 100644 index 87d571e63..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HttpResponseTraitWithHttpPayload.kt +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits - -import software.amazon.smithy.codegen.core.CodegenException -import software.amazon.smithy.model.shapes.ShapeType -import software.amazon.smithy.model.traits.StreamingTrait -import software.amazon.smithy.swift.codegen.ClientRuntimeTypes -import software.amazon.smithy.swift.codegen.SwiftTypes -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.declareSection -import software.amazon.smithy.swift.codegen.integration.HttpBindingDescriptor -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.SectionId -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingRenderable -import software.amazon.smithy.swift.codegen.model.hasTrait -import software.amazon.smithy.swift.codegen.model.isEnum - -class HttpResponseTraitWithHttpPayload( - val ctx: ProtocolGenerator.GenerationContext, - val binding: HttpBindingDescriptor, - val writer: SwiftWriter -) : HttpResponseBindingRenderable { - - object MessageDecoderSectionId : SectionId - - override fun render() { - val memberName = ctx.symbolProvider.toMemberName(binding.member) - val target = ctx.model.expectShape(binding.member.target) - val symbol = ctx.symbolProvider.toSymbol(target) - val isBinaryStream = - ctx.model.getShape(binding.member.target).get().hasTrait() && target.type == ShapeType.BLOB - when (target.type) { - ShapeType.DOCUMENT -> { - writer.openBlock("if let data = try await httpResponse.body.readData(), let responseDecoder = decoder {", "} else {") { - writer.write( - "let output: \$N = try responseDecoder.decode(responseBody: data)", - symbol - ) - writer.write("self.\$L = output", memberName) - } - writer.indent() - writer.write("self.\$L = nil", memberName).closeBlock("}") - } - ShapeType.STRING -> { - writer.openBlock("if let data = try await httpResponse.body.readData(), let output = \$N(data: data, encoding: .utf8) {", "} else {", SwiftTypes.String) { - if (target.isEnum) { - writer.write("self.\$L = \$L(rawValue: output)", memberName, symbol) - } else { - writer.write("self.\$L = output", memberName) - } - } - writer.indent() - writer.write("self.\$L = nil", memberName).closeBlock("}") - } - ShapeType.ENUM -> { - writer.openBlock("if let data = try await httpResponse.body.readData(), let output = \$N(data: data, encoding: .utf8) {", "} else {", SwiftTypes.String) { - writer.write("self.\$L = \$L(rawValue: output)", memberName, symbol) - } - writer.indent() - writer.write("self.\$L = nil", memberName).closeBlock("}") - } - ShapeType.BLOB -> { - writer.write("switch httpResponse.body {") - .write("case .data(let data):") - .indent() - if (isBinaryStream) { - writer.write("self.\$L = .data(data)", memberName) - } else { - writer.write("self.\$L = data", memberName) - } - - // For binary streams, we need to set the member to the stream directly. - // this allows us to stream the data directly to the user - // without having to buffer it in memory. - writer.dedent() - .write("case .stream(let stream):") - .indent() - if (isBinaryStream) { - writer.write("self.\$L = .stream(stream)", memberName) - } else { - writer.write("self.\$L = try stream.readToEnd()", memberName) - } - writer.dedent() - .write("case .noStream:") - .indent() - .write("self.\$L = nil", memberName).closeBlock("}") - } - ShapeType.STRUCTURE, ShapeType.UNION -> { - if (target.hasTrait()) { - writer.openBlock("if case let .stream(stream) = httpResponse.body, let responseDecoder = decoder {", "} else {") { - writer.declareSection(MessageDecoderSectionId) { - writer.write("let messageDecoder: \$D", ClientRuntimeTypes.EventStream.MessageDecoder) - } - writer.write("let decoderStream = \$L<\$N>(stream: stream, messageDecoder: messageDecoder, unmarshalClosure: jsonUnmarshalClosure(responseDecoder: responseDecoder))", ClientRuntimeTypes.EventStream.MessageDecoderStream, symbol) - writer.write("self.\$L = decoderStream.toAsyncStream()", memberName) - } - } else { - writer.openBlock("if let data = try await httpResponse.body.readData(), let responseDecoder = decoder {", "} else {") { - writer.write("let output: \$N = try responseDecoder.decode(responseBody: data)", symbol) - writer.write("self.\$L = output", memberName) - } - } - writer.indent() - writer.write("self.\$L = nil", memberName).closeBlock("}") - } - else -> throw CodegenException("member shape ${binding.member} serializer not implemented yet") - } - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HttpResponseTraitWithoutHttpPayload.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HttpResponseTraitWithoutHttpPayload.kt deleted file mode 100644 index a94804360..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/httpResponse/bindingTraits/HttpResponseTraitWithoutHttpPayload.kt +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits - -import software.amazon.smithy.codegen.core.CodegenException -import software.amazon.smithy.model.knowledge.HttpBinding -import software.amazon.smithy.model.shapes.BooleanShape -import software.amazon.smithy.model.shapes.ByteShape -import software.amazon.smithy.model.shapes.DoubleShape -import software.amazon.smithy.model.shapes.FloatShape -import software.amazon.smithy.model.shapes.IntegerShape -import software.amazon.smithy.model.shapes.LongShape -import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.model.shapes.ShapeType -import software.amazon.smithy.model.shapes.ShortShape -import software.amazon.smithy.model.traits.ErrorTrait -import software.amazon.smithy.model.traits.HttpQueryTrait -import software.amazon.smithy.model.traits.StreamingTrait -import software.amazon.smithy.swift.codegen.ClientRuntimeTypes -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.declareSection -import software.amazon.smithy.swift.codegen.integration.HttpBindingDescriptor -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingRenderable -import software.amazon.smithy.swift.codegen.model.hasTrait -import software.amazon.smithy.swift.codegen.model.isBoxed -import software.amazon.smithy.swift.codegen.model.targetOrSelf - -interface HttpResponseTraitWithoutHttpPayloadFactory { - fun construct( - ctx: ProtocolGenerator.GenerationContext, - responseBindings: List, - outputShape: Shape, - writer: SwiftWriter - ): HttpResponseBindingRenderable -} - -class HttpResponseTraitWithoutHttpPayload( - val ctx: ProtocolGenerator.GenerationContext, - val responseBindings: List, - val outputShape: Shape, - val writer: SwiftWriter -) : HttpResponseBindingRenderable { - override fun render() { - val bodyMembers = responseBindings.filter { it.location == HttpBinding.Location.DOCUMENT } - val bodyMembersWithoutQueryTrait = bodyMembers - .filter { !it.member.hasTrait(HttpQueryTrait::class.java) } - .toMutableSet() - val streamingMember = bodyMembers.firstOrNull { it.member.targetOrSelf(ctx.model).hasTrait(StreamingTrait::class.java) } - if (streamingMember != null) { - val initialResponseMembers = bodyMembers.filter { - val targetShape = it.member.targetOrSelf(ctx.model) - targetShape?.hasTrait(StreamingTrait::class.java) == false - }.toSet() - writeStreamingMember(streamingMember, initialResponseMembers) - } else if (bodyMembersWithoutQueryTrait.isNotEmpty()) { - writeNonStreamingMembers(bodyMembersWithoutQueryTrait) - } - } - - fun writeStreamingMember(streamingMember: HttpBindingDescriptor, initialResponseMembers: Set) { - val shape = ctx.model.expectShape(streamingMember.member.target) - val symbol = ctx.symbolProvider.toSymbol(shape) - val memberName = ctx.symbolProvider.toMemberName(streamingMember.member) - when (shape.type) { - ShapeType.UNION -> { - writer.openBlock("if case let .stream(stream) = httpResponse.body, let responseDecoder = decoder {", "} else {") { - writer.declareSection(HttpResponseTraitWithHttpPayload.MessageDecoderSectionId) { - writer.write("let messageDecoder: \$D", ClientRuntimeTypes.EventStream.MessageDecoder) - } - writer.write( - "let decoderStream = \$L<\$N>(stream: stream, messageDecoder: messageDecoder, unmarshalClosure: jsonUnmarshalClosure(responseDecoder: responseDecoder))", - ClientRuntimeTypes.EventStream.MessageDecoderStream, - symbol - ) - writer.write("self.\$L = decoderStream.toAsyncStream()", memberName) - if (isRPCService(ctx) && initialResponseMembers.isNotEmpty()) { - writeInitialResponseMembers(initialResponseMembers) - } - } - writer.indent() - writer.write("self.\$L = nil", memberName).closeBlock("}") - } - ShapeType.BLOB -> { - writer.write("switch httpResponse.body {") - .write("case .data(let data):") - .indent() - writer.write("self.\$L = .data(data)", memberName) - - // For binary streams, we need to set the member to the stream directly. - // this allows us to stream the data directly to the user - // without having to buffer it in memory. - writer.dedent() - .write("case .stream(let stream):") - .indent() - writer.write("self.\$L = .stream(stream)", memberName) - writer.dedent() - .write("case .noStream:") - .indent() - .write("self.\$L = nil", memberName).closeBlock("}") - } - else -> { - throw CodegenException("member shape ${streamingMember.member} cannot stream") - } - } - } - - fun writeNonStreamingMembers(members: Set) { - val outputShapeName = ctx.symbolProvider.toSymbol(outputShape).name - val memberNames = members.map { ctx.symbolProvider.toMemberName(it.member) } - writer.write("if let data = try await httpResponse.body.readData(),") - writer.indent() - writer.write("let responseDecoder = decoder {") - writer.write("let output: ${outputShapeName}Body = try responseDecoder.decode(responseBody: data)") - memberNames.sorted().forEach { - writer.write("self.$path$it = output.$it") - } - writer.dedent() - writer.write("} else {") - writer.indent() - members.sortedBy { it.memberName }.forEach { - val memberName = ctx.symbolProvider.toMemberName(it.member) - val type = ctx.model.expectShape(it.member.target) - val value = if (ctx.symbolProvider.toSymbol(it.member).isBoxed()) "nil" else { - when (type) { - is IntegerShape, is ByteShape, is ShortShape, is LongShape -> 0 - is FloatShape, is DoubleShape -> 0.0 - is BooleanShape -> false - else -> "nil" - } - } - writer.write("self.$path$memberName = $value") - } - writer.dedent() - writer.write("}") - } - - private val path: String = "properties.".takeIf { outputShape.hasTrait() } ?: "" - - private fun writeInitialResponseMembers(initialResponseMembers: Set) { - writer.apply { - write("if let initialDataWithoutHttp = await messageDecoder.awaitInitialResponse() {") - indent() - write("let decoder = JSONDecoder()") - write("do {") - indent() - write("let response = try decoder.decode([String: String].self, from: initialDataWithoutHttp)") - initialResponseMembers.forEach { responseMember -> - val responseMemberName = ctx.symbolProvider.toMemberName(responseMember.member) - write("self.$responseMemberName = response[\"$responseMemberName\"]") - } - dedent() - write("} catch {") - indent() - write("print(\"Error decoding JSON: \\(error)\")") - initialResponseMembers.forEach { responseMember -> - val responseMemberName = ctx.symbolProvider.toMemberName(responseMember.member) - write("self.$responseMemberName = nil") - } - dedent() - write("}") - dedent() - write("} else {") - indent() - initialResponseMembers.forEach { responseMember -> - val responseMemberName = ctx.symbolProvider.toMemberName(responseMember.member) - write("self.$responseMemberName = nil") - } - dedent() - write("}") - } - } - - private fun isRPCService(ctx: ProtocolGenerator.GenerationContext): Boolean { - return rpcBoundProtocols.contains(ctx.protocol.name) - } - - /** - * A set of RPC-bound Smithy protocols - */ - private val rpcBoundProtocols = setOf( - "awsJson1_0", - "awsJson1_1", - "awsQuery", - "ec2Query", - ) -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/middlewares/ContentLengthMiddleware.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/middlewares/ContentLengthMiddleware.kt index 957c9487e..aa8bafe41 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/middlewares/ContentLengthMiddleware.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/middlewares/ContentLengthMiddleware.kt @@ -10,7 +10,12 @@ import software.amazon.smithy.swift.codegen.middleware.MiddlewarePosition import software.amazon.smithy.swift.codegen.middleware.MiddlewareRenderable import software.amazon.smithy.swift.codegen.middleware.MiddlewareStep -class ContentLengthMiddleware(val model: Model, private val alwaysIntercept: Boolean, private val requiresLength: Boolean, private val unsignedPayload: Boolean) : MiddlewareRenderable { +class ContentLengthMiddleware( + val model: Model, + private val alwaysIntercept: Boolean, + private val requiresLength: Boolean, + private val unsignedPayload: Boolean +) : MiddlewareRenderable { override val name = "ContentLengthMiddleware" diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/middlewares/OperationInputBodyMiddleware.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/middlewares/OperationInputBodyMiddleware.kt index 0d203b251..c8addb428 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/middlewares/OperationInputBodyMiddleware.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/middlewares/OperationInputBodyMiddleware.kt @@ -20,10 +20,12 @@ import software.amazon.smithy.swift.codegen.ClientRuntimeTypes import software.amazon.smithy.swift.codegen.SwiftWriter import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.middlewares.handlers.MiddlewareShapeUtils -import software.amazon.smithy.swift.codegen.integration.serde.readwrite.DocumentWritingClosureUtils +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.NodeInfoUtils import software.amazon.smithy.swift.codegen.integration.serde.readwrite.WireProtocol import software.amazon.smithy.swift.codegen.integration.serde.readwrite.WritingClosureUtils +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.addImports import software.amazon.smithy.swift.codegen.integration.serde.readwrite.requestWireProtocol +import software.amazon.smithy.swift.codegen.integration.serde.struct.writerSymbol import software.amazon.smithy.swift.codegen.middleware.MiddlewarePosition import software.amazon.smithy.swift.codegen.middleware.MiddlewareRenderable import software.amazon.smithy.swift.codegen.middleware.MiddlewareStep @@ -59,15 +61,15 @@ class OperationInputBodyMiddleware( op: OperationShape ) { val writingClosureUtils = WritingClosureUtils(ctx, writer) - val documentWritingClosureUtils = DocumentWritingClosureUtils(ctx, writer) + val nodeInfoUtils = NodeInfoUtils(ctx, writer, ctx.service.requestWireProtocol) val inputShape = MiddlewareShapeUtils.inputShape(model, op) val inputSymbol = symbolProvider.toSymbol(inputShape) val outputSymbol = MiddlewareShapeUtils.outputSymbol(symbolProvider, model, op) - val writerSymbol = documentWritingClosureUtils.writerSymbol() + val writerSymbol = ctx.service.writerSymbol var payloadShape = inputShape var keyPath = "\\.self" var payloadWritingClosure = writingClosureUtils.writingClosure(payloadShape) - var documentWritingClosure = documentWritingClosureUtils.closure(payloadShape) + var rootNodeInfo = nodeInfoUtils.nodeInfo(payloadShape, true) var isPayloadMember = false val defaultBody = "\"{}\"".takeIf { ctx.service.hasTrait() || ctx.service.hasTrait() || ctx.service.hasTrait() } ?: "nil" var payloadMember = inputShape.members().find { it.hasTrait() } @@ -85,23 +87,24 @@ class OperationInputBodyMiddleware( val memberName = ctx.symbolProvider.toMemberName(it) keyPath = writer.format("\\.\$L", memberName) payloadWritingClosure = writingClosureUtils.writingClosure(it) - documentWritingClosure = documentWritingClosureUtils.closure(it) + rootNodeInfo = nodeInfoUtils.nodeInfo(it, true) isPayloadMember = true } val isStreaming = payloadShape.hasTrait() val payloadSymbol = ctx.symbolProvider.toSymbol(payloadShape) val requestWireProtocol = ctx.service.requestWireProtocol + writer.addImports(ctx.service.requestWireProtocol) when (payloadShape) { is UnionShape -> { if (isStreaming) { addEventStreamMiddleware(writer, inputSymbol, outputSymbol, payloadSymbol, keyPath, defaultBody, requestWireProtocol, sendInitialRequest) } else { - addAggregateMiddleware(writer, inputSymbol, outputSymbol, payloadSymbol, writerSymbol, documentWritingClosure, payloadWritingClosure, keyPath, defaultBody, isPayloadMember) + addAggregateMiddleware(writer, inputSymbol, outputSymbol, payloadSymbol, writerSymbol, rootNodeInfo, payloadWritingClosure, keyPath, defaultBody, isPayloadMember) } } is StructureShape, is DocumentShape -> { - addAggregateMiddleware(writer, inputSymbol, outputSymbol, payloadSymbol, writerSymbol, documentWritingClosure, payloadWritingClosure, keyPath, defaultBody, isPayloadMember) + addAggregateMiddleware(writer, inputSymbol, outputSymbol, payloadSymbol, writerSymbol, rootNodeInfo, payloadWritingClosure, keyPath, defaultBody, isPayloadMember) } is BlobShape -> { addBlobStreamMiddleware(writer, inputSymbol, outputSymbol, keyPath, isStreaming) @@ -118,35 +121,35 @@ class OperationInputBodyMiddleware( } } - private fun addAggregateMiddleware(writer: SwiftWriter, inputSymbol: Symbol, outputSymbol: Symbol, payloadSymbol: Symbol, writerSymbol: Symbol, documentWritingClosure: String, payloadWritingClosure: String, keyPath: String, defaultBody: String, isPayloadMember: Boolean) { + private fun addAggregateMiddleware(writer: SwiftWriter, inputSymbol: Symbol, outputSymbol: Symbol, payloadSymbol: Symbol, writerSymbol: Symbol, rootNodeInfo: String, payloadWritingClosure: String, keyPath: String, defaultBody: String, isPayloadMember: Boolean) { if (isPayloadMember) { - addPayloadBodyMiddleware(writer, inputSymbol, outputSymbol, payloadSymbol, writerSymbol, documentWritingClosure, payloadWritingClosure, keyPath, defaultBody) + addPayloadBodyMiddleware(writer, inputSymbol, outputSymbol, payloadSymbol, writerSymbol, rootNodeInfo, payloadWritingClosure, keyPath, defaultBody) } else { - addBodyMiddleware(writer, inputSymbol, outputSymbol, writerSymbol, documentWritingClosure, payloadWritingClosure) + addBodyMiddleware(writer, inputSymbol, outputSymbol, writerSymbol, rootNodeInfo, payloadWritingClosure) } } - private fun addBodyMiddleware(writer: SwiftWriter, inputSymbol: Symbol, outputSymbol: Symbol, writerSymbol: Symbol, documentWritingClosure: String, payloadWritingClosure: String) { + private fun addBodyMiddleware(writer: SwiftWriter, inputSymbol: Symbol, outputSymbol: Symbol, writerSymbol: Symbol, rootNodeInfo: String, payloadWritingClosure: String) { writer.write( - "\$N<\$N, \$N, \$N>(documentWritingClosure: \$L, inputWritingClosure: \$L)", + "\$N<\$N, \$N, \$N>(rootNodeInfo: \$L, inputWritingClosure: \$L)", ClientRuntimeTypes.Middleware.BodyMiddleware, inputSymbol, outputSymbol, writerSymbol, - documentWritingClosure, + rootNodeInfo, payloadWritingClosure, ) } - private fun addPayloadBodyMiddleware(writer: SwiftWriter, inputSymbol: Symbol, outputSymbol: Symbol, payloadSymbol: Symbol, writerSymbol: Symbol, documentWritingClosure: String, payloadWritingClosure: String, keyPath: String, defaultBody: String) { + private fun addPayloadBodyMiddleware(writer: SwiftWriter, inputSymbol: Symbol, outputSymbol: Symbol, payloadSymbol: Symbol, writerSymbol: Symbol, rootNodeInfo: String, payloadWritingClosure: String, keyPath: String, defaultBody: String) { writer.write( - "\$N<\$N, \$N, \$N, \$N>(documentWritingClosure: \$L, inputWritingClosure: \$L, keyPath: \$L, defaultBody: \$L)", + "\$N<\$N, \$N, \$N, \$N>(rootNodeInfo: \$L, inputWritingClosure: \$L, keyPath: \$L, defaultBody: \$L)", ClientRuntimeTypes.Middleware.PayloadBodyMiddleware, inputSymbol, outputSymbol, payloadSymbol, writerSymbol, - documentWritingClosure, + rootNodeInfo, payloadWritingClosure, keyPath, defaultBody @@ -154,21 +157,16 @@ class OperationInputBodyMiddleware( } private fun addEventStreamMiddleware(writer: SwiftWriter, inputSymbol: Symbol, outputSymbol: Symbol, payloadSymbol: Symbol, keyPath: String, defaultBody: String, requestWireProtocol: WireProtocol, sendInitialRequest: Boolean) { - val marshalClosure = when (requestWireProtocol) { - WireProtocol.XML -> ", marshalClosure: $payloadSymbol.marshal" - WireProtocol.JSON -> ", marshalClosure: jsonMarshalClosure(requestEncoder: encoder)" - else -> "" - } writer.write( - "\$N<\$N, \$N, \$N>(keyPath: \$L, defaultBody: \$L\$L\$L)", + "\$N<\$N, \$N, \$N>(keyPath: \$L, defaultBody: \$L, marshalClosure: \$N.marshal\$L)", ClientRuntimeTypes.Middleware.EventStreamBodyMiddleware, inputSymbol, outputSymbol, payloadSymbol, keyPath, defaultBody, - marshalClosure, - if (sendInitialRequest) ", initialRequestMessage: try input.makeInitialRequestMessage(encoder: encoder)" else "" + payloadSymbol, + if (sendInitialRequest) ", initialRequestMessage: try input.makeInitialRequestMessage()" else "" ) } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/MemberShapeDecodeGeneratable.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/MemberShapeDecodeGeneratable.kt deleted file mode 100644 index 032487c31..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/MemberShapeDecodeGeneratable.kt +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.serde - -interface MemberShapeDecodeGeneratable { - fun render() -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/MemberShapeEncodeGeneratable.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/MemberShapeEncodeGeneratable.kt deleted file mode 100644 index 6d61484af..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/MemberShapeEncodeGeneratable.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.serde - -import software.amazon.smithy.model.shapes.ShapeType - -interface MemberShapeEncodeGeneratable { - fun render() -} - -class MemberShapeEncodeConstants { - companion object { - val primitiveSymbols: MutableSet = hashSetOf( - ShapeType.INTEGER, ShapeType.BYTE, ShapeType.SHORT, - ShapeType.LONG, ShapeType.FLOAT, ShapeType.DOUBLE, ShapeType.BOOLEAN - ) - val floatingPointPrimitiveSymbols: MutableSet = hashSetOf( - ShapeType.FLOAT, ShapeType.DOUBLE - ) - } -} - -fun getDefaultValueOfShapeType(shapeType: ShapeType): Comparable<*> { - return when (shapeType) { - ShapeType.INTEGER, ShapeType.BYTE, ShapeType.SHORT, ShapeType.LONG -> 0 - ShapeType.FLOAT, ShapeType.DOUBLE -> 0.0 - else -> false // PrimitiveBoolean case - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/TimestampEncodeGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/TimestampEncodeGenerator.kt deleted file mode 100644 index 7a1a46221..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/TimestampEncodeGenerator.kt +++ /dev/null @@ -1,43 +0,0 @@ -package software.amazon.smithy.swift.codegen.integration.serde - -import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.model.traits.TimestampFormatTrait.Format -import software.amazon.smithy.swift.codegen.SwiftWriter - -class TimestampEncodeGenerator( - val container: String, - val property: String, - val codingKey: String?, - val format: TimestampFormatTrait.Format -) { - // Generates and returns the Swift code for encoding a timestamp - // For example, `try encodeContainer.encodeTimestamp(sampleTime, format: .dateTime, forKey: .sampleTime)` - fun generate(writer: SwiftWriter) { - val swiftFormat = TimestampHelpers.generateTimestampFormatEnumValue(this.format) - writer.writeInline("try \$L.encodeTimestamp(\$L, format: .\$L", this.container, this.property, swiftFormat) - if (this.codingKey != null) { - writer.writeInline(", forKey: \$L", this.codingKey) - } - writer.writeInline(")") - writer.ensureNewline() - } -} - -class TimestampDecodeGenerator( - val memberName: String, - val container: String, - val codingKey: String, - val format: TimestampFormatTrait.Format, - val optional: Boolean = false -) { - // Generates and returns the Swift code for encoding a timestamp - // For example, `try encodeContainer.encodeTimestamp(sampleTime, format: .dateTime, forKey: .sampleTime)` - fun generate(writer: SwiftWriter) { - val decodeVerb = if (optional) "decodeTimestampIfPresent" else "decodeTimestamp" - val swiftFormat = TimestampHelpers.generateTimestampFormatEnumValue(this.format) - writer.write( - "let \$L = try \$L.\$L(.\$L, forKey: \$L)", - this.memberName, this.container, decodeVerb, swiftFormat, this.codingKey - ) - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/TimestampFormatHelpers.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/TimestampFormatHelpers.kt index 915375589..95fac72d0 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/TimestampFormatHelpers.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/TimestampFormatHelpers.kt @@ -5,9 +5,7 @@ package software.amazon.smithy.swift.codegen.integration.serde -import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.swift.codegen.model.getTrait class TimestampHelpers { companion object { @@ -20,24 +18,5 @@ class TimestampHelpers { else -> throw Exception("Invalid timestamp format") } } - - // Returns the timestamp format for a given member - // The logic for determining the format is the following: - // 1. Use the format defined in the member's timestamp format trait if it exists - // 2. Use the format defined in the member's target's timestamp format trait if it exists - // 3. Use the provided default timestamp format - fun getTimestampFormat(member: Shape, memberTarget: Shape?, defaultTimestampFormat: TimestampFormatTrait.Format): TimestampFormatTrait.Format { - var formatTrait = defaultTimestampFormat - - member.getTrait()?.let { - formatTrait = it.format - } ?: run { - memberTarget?.getTrait()?.let { - formatTrait = it.format - } - } - - return formatTrait - } } } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/UnionDecodeGeneratorStrategy.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/UnionDecodeGeneratorStrategy.kt deleted file mode 100644 index 779cdbdc5..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/UnionDecodeGeneratorStrategy.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.serde - -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.serde.json.UnionDecodeGenerator -import software.amazon.smithy.swift.codegen.integration.serde.readwrite.WireProtocol -import software.amazon.smithy.swift.codegen.integration.serde.readwrite.responseWireProtocol -import software.amazon.smithy.swift.codegen.integration.serde.xml.UnionDecodeXMLGenerator - -class UnionDecodeGeneratorStrategy( - private val ctx: ProtocolGenerator.GenerationContext, - private val shapeContainingMembers: Shape, - private val members: List, - private val writer: SwiftWriter, - private val defaultTimestampFormat: TimestampFormatTrait.Format -) { - fun render() { - when (ctx.service.responseWireProtocol) { - WireProtocol.XML -> { - UnionDecodeXMLGenerator(ctx, shapeContainingMembers, members, writer).render() - } - else -> { - UnionDecodeGenerator(ctx, members, writer, defaultTimestampFormat).render() - } - } - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/UnionEncodeGeneratorStrategy.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/UnionEncodeGeneratorStrategy.kt deleted file mode 100644 index a7eaf27a9..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/UnionEncodeGeneratorStrategy.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.serde - -import software.amazon.smithy.aws.traits.protocols.RestXmlTrait -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.UnionShape -import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.serde.json.UnionEncodeGenerator -import software.amazon.smithy.swift.codegen.integration.serde.xml.UnionEncodeXMLGenerator - -class UnionEncodeGeneratorStrategy( - private val ctx: ProtocolGenerator.GenerationContext, - private val union: UnionShape, - private val members: List, - private val writer: SwiftWriter, - private val defaultTimestampFormat: TimestampFormatTrait.Format -) { - fun render() { - when (ctx.protocol) { - RestXmlTrait.ID -> { - UnionEncodeXMLGenerator(ctx, union, members, writer).render() - } - else -> { - UnionEncodeGenerator(ctx, members, writer, defaultTimestampFormat).render() - } - } - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/formurl/FormURLEncodeCustomizable.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/formurl/FormURLEncodeCustomizable.kt deleted file mode 100644 index 8ef75ab1b..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/formurl/FormURLEncodeCustomizable.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.serde.formurl - -import software.amazon.smithy.model.shapes.Shape - -interface FormURLEncodeCustomizable { - fun alwaysUsesFlattenedCollections(): Boolean - fun customNameTraitGenerator(memberShape: Shape, defaultName: String): String - fun shouldSerializeEmptyLists(): Boolean -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/formurl/FormURLUtils.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/formurl/FormURLUtils.kt deleted file mode 100644 index 3d9ec86a6..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/formurl/FormURLUtils.kt +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.serde.formurl - -fun String.indexAdvancedBy1(indexVariableName: String): String { - return "$this.\\($indexVariableName.advanced(by: 1))" -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/formurl/MemberShapeEncodeFormURLGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/formurl/MemberShapeEncodeFormURLGenerator.kt deleted file mode 100644 index c6d2404b0..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/formurl/MemberShapeEncodeFormURLGenerator.kt +++ /dev/null @@ -1,428 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.serde.formurl - -import software.amazon.smithy.model.shapes.BlobShape -import software.amazon.smithy.model.shapes.CollectionShape -import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.SetShape -import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.model.shapes.TimestampShape -import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.model.traits.XmlFlattenedTrait -import software.amazon.smithy.swift.codegen.ClientRuntimeTypes -import software.amazon.smithy.swift.codegen.SwiftTypes -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.serde.MemberShapeEncodeConstants -import software.amazon.smithy.swift.codegen.integration.serde.MemberShapeEncodeGeneratable -import software.amazon.smithy.swift.codegen.integration.serde.TimestampEncodeGenerator -import software.amazon.smithy.swift.codegen.integration.serde.TimestampHelpers -import software.amazon.smithy.swift.codegen.integration.serde.getDefaultValueOfShapeType -import software.amazon.smithy.swift.codegen.model.isBoxed -import software.amazon.smithy.swift.codegen.removeSurroundingBackticks - -abstract class MemberShapeEncodeFormURLGenerator( - private val ctx: ProtocolGenerator.GenerationContext, - private val customizations: FormURLEncodeCustomizable, - private val writer: SwiftWriter, - private val defaultTimestampFormat: TimestampFormatTrait.Format -) : MemberShapeEncodeGeneratable { - - fun renderListMember( - member: MemberShape, - memberTarget: CollectionShape, - containerName: String - ) { - val memberName = ctx.symbolProvider.toMemberName(member) - val resolvedMemberName = customizations.customNameTraitGenerator(member, member.memberName) - val nestedContainer = "${memberName.removeSurroundingBackticks()}Container" - writer.openBlock("if let $memberName = $memberName {", "}") { - writer.openBlock("if !$memberName.isEmpty {", "}") { - if (member.hasTrait(XmlFlattenedTrait::class.java) || customizations.alwaysUsesFlattenedCollections()) { - renderFlattenedListMemberItems(memberName, member, memberTarget, containerName) - } else { - writer.write("var $nestedContainer = $containerName.nestedContainer(keyedBy: \$N.self, forKey: \$N(\"$resolvedMemberName\"))", ClientRuntimeTypes.Serde.Key, ClientRuntimeTypes.Serde.Key) - renderListMemberItems(memberName, memberTarget, nestedContainer) - } - } - if (customizations.shouldSerializeEmptyLists()) { - writer.openBlock("else {", "}") { - writer.write("var $nestedContainer = $containerName.nestedContainer(keyedBy: \$N.self, forKey: \$N(\"$resolvedMemberName\"))", ClientRuntimeTypes.Serde.Key, ClientRuntimeTypes.Serde.Key) - writer.write("try $nestedContainer.encode(\"\", forKey: \$N(\"\"))", ClientRuntimeTypes.Serde.Key) - } - } - } - } - - private fun renderListMemberItems( - memberName: String, - memberTarget: CollectionShape, - containerName: String, - level: Int = 0 - ) { - val nestedMember = memberTarget.member - val nestedMemberResolvedName = customizations.customNameTraitGenerator(nestedMember, "member").indexAdvancedBy1("index$level") - - val nestedMemberTarget = ctx.model.expectShape(memberTarget.member.target) - val nestedMemberTargetName = "${nestedMemberTarget.id.name.toLowerCase()}$level" - writer.openBlock("for (index$level, $nestedMemberTargetName) in $memberName.enumerated() {", "}") { - when (nestedMemberTarget) { - is CollectionShape -> { - val isBoxed = ctx.symbolProvider.toSymbol(memberTarget.member).isBoxed() - if (isBoxed && !(nestedMemberTarget is SetShape)) { - writer.openBlock("if let $nestedMemberTargetName = $nestedMemberTargetName {", "}") { - renderNestedListEntryMember(nestedMemberTargetName, nestedMemberTarget, nestedMember, nestedMemberResolvedName, containerName, level) - } - } else { - renderNestedListEntryMember(nestedMemberTargetName, nestedMemberTarget, nestedMember, nestedMemberResolvedName, containerName, level) - } - } - is MapShape -> { - val nestedContainerName = "${memberName.removeSurroundingBackticks()}Container$level" - writer.write("var $nestedContainerName = $containerName.nestedContainer(keyedBy: \$N.self, forKey: \$N(\"${nestedMemberResolvedName}\"))", ClientRuntimeTypes.Serde.Key, ClientRuntimeTypes.Serde.Key) - writer.openBlock("if let $nestedMemberTargetName = $nestedMemberTargetName {", "}") { - renderWrappedMapMemberItem(nestedMemberTargetName, nestedMemberTarget, nestedContainerName, level) - } - } - is TimestampShape -> { - val codingKey = writer.format("\$L(\"\$L\")", ClientRuntimeTypes.Serde.Key, nestedMemberResolvedName) - TimestampEncodeGenerator( - containerName, - nestedMemberTargetName, - codingKey, - TimestampHelpers.getTimestampFormat(nestedMember, nestedMemberTarget, defaultTimestampFormat) - ).generate(writer) - } - is BlobShape -> { - renderBlobMemberName(nestedMemberTargetName, nestedMemberResolvedName, containerName, false) - } - else -> { - renderItem(writer, containerName, nestedMemberTargetName, nestedMemberResolvedName) - } - } - } - } - - private fun renderNestedListEntryMember(nestedMemberTargetName: String, nestedMemberTarget: CollectionShape, nestedMember: MemberShape, nestedMemberResolvedName: String, containerName: String, level: Int) { - var nestedContainerName = "${nestedMemberTargetName.removeSurroundingBackticks()}Container$level" - writer.write("var $nestedContainerName = $containerName.nestedContainer(keyedBy: \$N.self, forKey: \$N(\"${nestedMemberResolvedName}\"))", ClientRuntimeTypes.Serde.Key, ClientRuntimeTypes.Serde.Key) - renderListMemberItems(nestedMemberTargetName, nestedMemberTarget, nestedContainerName, level + 1) - } - - private fun renderFlattenedListMemberItems( - memberName: String, - member: MemberShape, - memberTarget: CollectionShape, - containerName: String, - level: Int = 0 - ) { - val nestedMember = memberTarget.member - val nestedMemberTarget = ctx.model.expectShape(memberTarget.member.target) - val nestedMemberTargetName = "${nestedMemberTarget.id.name.toLowerCase()}$level" - val defaultMemberName = if (level == 0) member.memberName else "member" - val resolvedMemberName = customizations.customNameTraitGenerator(member, defaultMemberName).indexAdvancedBy1("index$level") - val nestedContainerName = "${memberName.removeSurroundingBackticks()}Container$level" - - writer.openBlock("for (index$level, $nestedMemberTargetName) in $memberName.enumerated() {", "}") { - when (nestedMemberTarget) { - is CollectionShape -> { - val isBoxed = ctx.symbolProvider.toSymbol(memberTarget.member).isBoxed() - if (isBoxed && !(nestedMemberTarget is SetShape)) { - writer.openBlock("if let $nestedMemberTargetName = $nestedMemberTargetName {", "}") { - renderFlattenedListContainer(nestedMemberTargetName, nestedMemberTarget, nestedMember, memberName, member, containerName, level) - } - } else { - renderFlattenedListContainer(nestedMemberTargetName, nestedMemberTarget, nestedMember, memberName, member, containerName, level) - } - } - is MapShape -> { - writer.write("var $nestedContainerName = $containerName.nestedContainer(keyedBy: Key.self, forKey: Key(\"${resolvedMemberName}\"))") - writer.openBlock("if let $nestedMemberTargetName = $nestedMemberTargetName {", "}") { - renderWrappedMapMemberItem(nestedMemberTargetName, nestedMemberTarget, nestedContainerName, level) - } - } - is TimestampShape -> { - writer.write("var $nestedContainerName = $containerName.nestedContainer(keyedBy: \$N.self, forKey: \$N(\"$resolvedMemberName\"))", ClientRuntimeTypes.Serde.Key, ClientRuntimeTypes.Serde.Key) - val codingKey = writer.format("\$L(\"\")", ClientRuntimeTypes.Serde.Key) - TimestampEncodeGenerator( - nestedContainerName, - nestedMemberTargetName, - codingKey, - TimestampHelpers.getTimestampFormat(nestedMember, nestedMemberTarget, defaultTimestampFormat) - ).generate(writer) - } - is BlobShape -> { - renderBlobMemberName(nestedMemberTargetName, resolvedMemberName, containerName, false) - } - else -> { - writer.write("var $nestedContainerName = $containerName.nestedContainer(keyedBy: \$N.self, forKey: \$N(\"$resolvedMemberName\"))", ClientRuntimeTypes.Serde.Key, ClientRuntimeTypes.Serde.Key) - writer.write("try $nestedContainerName.encode($nestedMemberTargetName, forKey: \$N(\"\"))", ClientRuntimeTypes.Serde.Key) - } - } - } - } - private fun renderFlattenedListContainer(nestedMemberTargetName: String, nestedMemberTarget: CollectionShape, nestedMember: MemberShape, memberName: String, member: MemberShape, containerName: String, level: Int) { - var nestedContainerName = "${nestedMemberTargetName.removeSurroundingBackticks()}Container$level" - val defaultMemberName = if (level == 0) memberName else "member" - val memberResolvedName = customizations.customNameTraitGenerator(member, defaultMemberName) - writer.write("var $nestedContainerName = $containerName.nestedContainer(keyedBy: \$N.self, forKey: \$N(\"${memberResolvedName}\"))", ClientRuntimeTypes.Serde.Key, ClientRuntimeTypes.Serde.Key) - renderFlattenedListMemberItems(nestedMemberTargetName, nestedMember, nestedMemberTarget, nestedContainerName, level + 1) - } - - fun renderMapMember(member: MemberShape, memberTarget: MapShape, containerName: String) { - val memberName = ctx.symbolProvider.toMemberName(member) - val resolvedMemberName = customizations.customNameTraitGenerator(member, member.memberName) - - writer.openBlock("if let $memberName = $memberName {", "}") { - if (member.hasTrait(XmlFlattenedTrait::class.java) || customizations.alwaysUsesFlattenedCollections()) { - writer.openBlock("if !$memberName.isEmpty {", "}") { - renderFlattenedMapMemberItem(memberName, member, memberTarget, containerName) - } - } else { - val nestedContainer = "${memberName.removeSurroundingBackticks()}Container" - writer.write("var $nestedContainer = $containerName.nestedContainer(keyedBy: \$N.self, forKey: \$N(\"$resolvedMemberName\"))", ClientRuntimeTypes.Serde.Key, ClientRuntimeTypes.Serde.Key) - renderWrappedMapMemberItem(memberName, memberTarget, nestedContainer) - } - } - } - - private fun renderWrappedMapMemberItem(memberName: String, mapShape: MapShape, containerName: String, level: Int = 0) { - val keyTargetShape = ctx.model.expectShape(mapShape.key.target) - val valueTargetShape = ctx.model.expectShape(mapShape.value.target) - - val resolvedCodingKeys = Pair( - customizations.customNameTraitGenerator(mapShape.key, "key"), - customizations.customNameTraitGenerator(mapShape.value, "value") - ) - - val nestedKeyValueName = Pair("${keyTargetShape.id.name.toLowerCase()}Key$level", "${valueTargetShape.id.name.toLowerCase()}Value$level") - val entryContainerName = "entryContainer$level" - val index = "index$level" - val element = "element$level" - // Sorting the unordered map is needed for passing protocol codegen tests - writer.openBlock("for ($index, $element) in $memberName.sorted(by: { $$0.key < $$1.key }).enumerated() {", "}") { - writer.write("let ${nestedKeyValueName.first} = $element.key") - writer.write("let ${nestedKeyValueName.second} = $element.value") - - val entry = "entry".indexAdvancedBy1(index) - writer.write("var $entryContainerName = $containerName.nestedContainer(keyedBy: \$N.self, forKey: \$N(\"$entry\"))", ClientRuntimeTypes.Serde.Key, ClientRuntimeTypes.Serde.Key) - renderMapKey(nestedKeyValueName, resolvedCodingKeys, mapShape, entryContainerName, level) - when (valueTargetShape) { - is MapShape -> { - renderMapNestedValue(nestedKeyValueName, resolvedCodingKeys, mapShape, valueTargetShape, entryContainerName, level) { valueContainer -> - renderWrappedMapMemberItem(nestedKeyValueName.second, valueTargetShape, valueContainer, level + 1) - } - } - is CollectionShape -> { - renderMapNestedValue(nestedKeyValueName, resolvedCodingKeys, mapShape, valueTargetShape, entryContainerName, level) { valueContainer -> - renderListMemberItems(nestedKeyValueName.second, valueTargetShape, valueContainer, level + 1) - } - } - is TimestampShape -> { - renderMapValue(nestedKeyValueName, resolvedCodingKeys, mapShape, entryContainerName, level) { valueContainer -> - val codingKey = writer.format("\$L(\"\")", ClientRuntimeTypes.Serde.Key) - TimestampEncodeGenerator( - valueContainer, - nestedKeyValueName.second, - codingKey, - TimestampHelpers.getTimestampFormat(mapShape.value, valueTargetShape, defaultTimestampFormat) - ).generate(writer) - } - } - is BlobShape -> { - renderMapValue(nestedKeyValueName, resolvedCodingKeys, mapShape, entryContainerName, level) { valueContainer -> - renderBlobMemberName(nestedKeyValueName.second, "", valueContainer, false) - } - } - else -> { - renderMapValue(nestedKeyValueName, resolvedCodingKeys, mapShape, entryContainerName, level) - } - } - } - } - - private fun renderFlattenedMapMemberItem(memberName: String, member: MemberShape, mapShape: MapShape, containerName: String, level: Int = 0) { - val keyTargetShape = ctx.model.expectShape(mapShape.key.target) - val valueTargetShape = ctx.model.expectShape(mapShape.value.target) - - val resolvedMemberName = if (level == 0) customizations.customNameTraitGenerator(member, member.memberName) else "entry" - - val resolvedCodingKeys = Pair( - customizations.customNameTraitGenerator(mapShape.key, "key"), - customizations.customNameTraitGenerator(mapShape.value, "value") - ) - - val nestedKeyValueName = Pair("${keyTargetShape.id.name.toLowerCase()}Key$level", "${valueTargetShape.id.name.toLowerCase()}Value$level") - val nestedContainer = "nestedContainer$level" - val index = "index$level" - val element = "element$level" - // Sorting the unordered map is needed for passing protocol codegen tests - writer.openBlock("for ($index, $element) in $memberName.sorted(by: { $$0.key < $$1.key }).enumerated() {", "}") { - writer.write("let ${nestedKeyValueName.first} = $element.key") - writer.write("let ${nestedKeyValueName.second} = $element.value") - - val entry = resolvedMemberName.toString().indexAdvancedBy1(index) - writer.write("var $nestedContainer = $containerName.nestedContainer(keyedBy: \$N.self, forKey: \$N(\"$entry\"))", ClientRuntimeTypes.Serde.Key, ClientRuntimeTypes.Serde.Key) - when (valueTargetShape) { - is MapShape -> { - renderMapKey(nestedKeyValueName, resolvedCodingKeys, mapShape, nestedContainer, level) - renderMapNestedValue(nestedKeyValueName, resolvedCodingKeys, mapShape, valueTargetShape, nestedContainer, level) { nestedValueContainer -> - renderFlattenedMapMemberItem(nestedKeyValueName.second, mapShape.value, valueTargetShape, nestedValueContainer, level + 1) - } - } - is CollectionShape -> { - renderMapKey(nestedKeyValueName, resolvedCodingKeys, mapShape, nestedContainer, level) - renderMapNestedValue(nestedKeyValueName, resolvedCodingKeys, mapShape, valueTargetShape, nestedContainer, level) { nestedValueContainer -> - renderListMemberItems(nestedKeyValueName.second, valueTargetShape, nestedValueContainer, level + 1) - } - } - is TimestampShape -> { - renderMapKey(nestedKeyValueName, resolvedCodingKeys, mapShape, nestedContainer, level) - renderMapValue(nestedKeyValueName, resolvedCodingKeys, mapShape, nestedContainer, level) { valueContainer -> - val codingKey = writer.format("\$L(\"\")", ClientRuntimeTypes.Serde.Key) - TimestampEncodeGenerator( - valueContainer, - nestedKeyValueName.second, - codingKey, - TimestampHelpers.getTimestampFormat(mapShape.value, valueTargetShape, defaultTimestampFormat) - ).generate(writer) - } - } - is BlobShape -> { - renderMapKey(nestedKeyValueName, resolvedCodingKeys, mapShape, nestedContainer, level) - renderMapValue(nestedKeyValueName, resolvedCodingKeys, mapShape, nestedContainer, level) { valueContainer -> - renderBlobMemberName(nestedKeyValueName.second, "", valueContainer, false) - } - } - else -> { - renderMapKey(nestedKeyValueName, resolvedCodingKeys, mapShape, nestedContainer, level) - renderMapValue(nestedKeyValueName, resolvedCodingKeys, mapShape, nestedContainer, level) - } - } - } - } - - private fun renderMapKey( - nestedKeyValueName: Pair, - resolvedCodingKeys: Pair, - mapShape: MapShape, - nestedContainer: String, - level: Int - ) { - val nestedKeyContainer = "keyContainer$level" - writer.write("var $nestedKeyContainer = $nestedContainer.nestedContainer(keyedBy: \$N.self, forKey: \$N(\"${resolvedCodingKeys.first}\"))", ClientRuntimeTypes.Serde.Key, ClientRuntimeTypes.Serde.Key) - writer.write("try $nestedKeyContainer.encode(${nestedKeyValueName.first}, forKey: \$N(\"\"))", ClientRuntimeTypes.Serde.Key) - } - - private fun renderMapValue( - nestedKeyValueName: Pair, - resolvedCodingKeys: Pair, - mapShape: MapShape, - entryContainerName: String, - level: Int, - customValueRenderer: ((String) -> Unit)? = null - ) { - val valueContainerName = "valueContainer$level" - writer.write("var $valueContainerName = $entryContainerName.nestedContainer(keyedBy: \$N.self, forKey: \$N(\"${resolvedCodingKeys.second}\"))", ClientRuntimeTypes.Serde.Key, ClientRuntimeTypes.Serde.Key) - if (customValueRenderer != null) { - customValueRenderer(valueContainerName) - } else { - writer.write("try $valueContainerName.encode(${nestedKeyValueName.second}, forKey: \$N(\"\"))", ClientRuntimeTypes.Serde.Key) - } - } - - private fun renderMapNestedValue( - nestedKeyValueName: Pair, - resolvedCodingKeys: Pair, - mapShape: MapShape, - valueTargetShape: Shape, - entryContainerName: String, - level: Int, - nextRenderer: (String) -> Unit - ) { - val nextContainer = "valueContainer${level + 1}" - writer.write("var $nextContainer = $entryContainerName.nestedContainer(keyedBy: \$N.self, forKey: \$N(\"${resolvedCodingKeys.second}\"))", ClientRuntimeTypes.Serde.Key, ClientRuntimeTypes.Serde.Key) - nextRenderer(nextContainer) - } - - fun renderTimestampMember(member: MemberShape, memberTarget: TimestampShape, containerName: String) { - val symbol = ctx.symbolProvider.toSymbol(memberTarget) - val memberName = ctx.symbolProvider.toMemberName(member) - val resolvedMemberName = customizations.customNameTraitGenerator(member, member.memberName) - val isBoxed = symbol.isBoxed() - val codingKey = "${ClientRuntimeTypes.Serde.Key}(\"$resolvedMemberName\")" - if (isBoxed) { - writer.openBlock("if let $memberName = $memberName {", "}") { - TimestampEncodeGenerator( - containerName, - memberName, - codingKey, - TimestampHelpers.getTimestampFormat(member, memberTarget, defaultTimestampFormat) - ).generate(writer) - } - } else { - TimestampEncodeGenerator( - containerName, - memberName, - codingKey, - TimestampHelpers.getTimestampFormat(member, memberTarget, defaultTimestampFormat) - ).generate(writer) - } - } - - fun renderBlobMember(member: MemberShape, memberTarget: BlobShape, containerName: String) { - val memberName = ctx.symbolProvider.toMemberName(member) - val resolvedMemberName = customizations.customNameTraitGenerator(member, member.memberName) - val symbol = ctx.symbolProvider.toSymbol(memberTarget) - val isBoxed = symbol.isBoxed() - renderBlobMemberName(memberName, resolvedMemberName, containerName, isBoxed) - } - - private fun renderBlobMemberName(memberName: String, resolvedMemberName: String, containerName: String, isBoxed: Boolean) { - val encodeLine = "try $containerName.encode($memberName.base64EncodedString(), forKey: ${ClientRuntimeTypes.Serde.Key}(\"$resolvedMemberName\"))" - if (isBoxed) { - writer.openBlock("if let $memberName = $memberName {", "}") { - writer.write(encodeLine) - } - } else { - writer.write(encodeLine) - } - } - - fun renderScalarMember(member: MemberShape, memberTarget: Shape, containerName: String) { - val symbol = ctx.symbolProvider.toSymbol(member) - val memberName = ctx.symbolProvider.toMemberName(member) - val resolvedMemberName = customizations.customNameTraitGenerator(member, member.memberName) - val isBoxed = symbol.isBoxed() - if (isBoxed) { - writer.openBlock("if let $memberName = $memberName {", "}") { - renderItem(writer, containerName, memberName, resolvedMemberName) - } - } else { - if (MemberShapeEncodeConstants.primitiveSymbols.contains(memberTarget.type)) { - val defaultValue = getDefaultValueOfShapeType(memberTarget.type) - writer.openBlock("if $memberName != $defaultValue {", "}") { - if (MemberShapeEncodeConstants.floatingPointPrimitiveSymbols.contains(memberTarget.type)) { - writer.write( - "try $containerName.encode(\$N($memberName), forKey: \$N(\"$resolvedMemberName\"))", - SwiftTypes.String, ClientRuntimeTypes.Serde.Key - ) - } else { - writer.write("try $containerName.encode($memberName, forKey: \$N(\"$resolvedMemberName\"))", ClientRuntimeTypes.Serde.Key) - } - } - } else { - writer.write("try $containerName.encode($memberName, forKey: \$N(\"$resolvedMemberName\"))", ClientRuntimeTypes.Serde.Key) - } - } - } - - private fun renderItem(writer: SwiftWriter, containerName: String, memberName: String, resolvedMemberName: String) { - writer.write("try $containerName.encode($memberName, forKey: \$N(\"${resolvedMemberName}\"))", ClientRuntimeTypes.Serde.Key) - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/formurl/StructEncodeFormURLGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/formurl/StructEncodeFormURLGenerator.kt deleted file mode 100644 index 7422b6b11..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/formurl/StructEncodeFormURLGenerator.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.serde.formurl - -import software.amazon.smithy.model.shapes.BlobShape -import software.amazon.smithy.model.shapes.CollectionShape -import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.model.shapes.TimestampShape -import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.swift.codegen.ClientRuntimeTypes -import software.amazon.smithy.swift.codegen.SwiftTypes -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.model.ShapeMetadata - -class StructEncodeFormURLGenerator( - private val ctx: ProtocolGenerator.GenerationContext, - private val customizations: FormURLEncodeCustomizable, - private val shapeContainingMembers: Shape, - private val shapeMetadata: Map, - private val members: List, - private val writer: SwiftWriter, - private val defaultTimestampFormat: TimestampFormatTrait.Format -) : MemberShapeEncodeFormURLGenerator(ctx, customizations, writer, defaultTimestampFormat) { - override fun render() { - writer.openBlock("public func encode(to encoder: \$N) throws {", "}", SwiftTypes.Encoder) { - val containerName = "container" - renderEncodeBody(containerName) - addConstantMembers(containerName) - } - } - - private fun renderEncodeBody(containerName: String) { - writer.write("var $containerName = encoder.container(keyedBy: \$N.self)", ClientRuntimeTypes.Serde.Key) - - val membersSortedByName: List = members.sortedBy { it.memberName } - membersSortedByName.forEach { member -> - renderSingleMember(member, containerName) - } - } - - private fun renderSingleMember(member: MemberShape, containerName: String) { - val memberTarget = ctx.model.expectShape(member.target) - when (memberTarget) { - - is CollectionShape -> { - renderListMember(member, memberTarget, containerName) - } - is MapShape -> { - renderMapMember(member, memberTarget, containerName) - } - is TimestampShape -> { - renderTimestampMember(member, memberTarget, containerName) - } - is BlobShape -> { - renderBlobMember(member, memberTarget, containerName) - } - else -> { - renderScalarMember(member, memberTarget, containerName) - } - } - } - - private fun addConstantMembers(containerName: String) { - if (shapeMetadata.containsKey(ShapeMetadata.OPERATION_SHAPE) && shapeMetadata.containsKey(ShapeMetadata.SERVICE_VERSION)) { - val operationShape = shapeMetadata[ShapeMetadata.OPERATION_SHAPE] as OperationShape - val version = shapeMetadata[ShapeMetadata.SERVICE_VERSION] as String - writer.write("try $containerName.encode(\"${operationShape.id.name}\", forKey:\$N(\"Action\"))", ClientRuntimeTypes.Serde.Key) - writer.write("try $containerName.encode(\"${version}\", forKey:\$N(\"Version\"))", ClientRuntimeTypes.Serde.Key) - } - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/formurl/trait/Ec2QueryNameTraitGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/formurl/trait/Ec2QueryNameTraitGenerator.kt deleted file mode 100644 index 1761570c2..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/formurl/trait/Ec2QueryNameTraitGenerator.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.serde.formurl.trait - -import software.amazon.smithy.aws.traits.protocols.Ec2QueryNameTrait -import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.swift.codegen.integration.serde.xml.trait.XMLNameTraitGenerator -import software.amazon.smithy.swift.codegen.model.getTrait - -class Ec2QueryNameTraitGenerator(val xmlNameValue: String) { - companion object { - fun construct(shape: Shape, defaultMemberName: String): Ec2QueryNameTraitGenerator { - shape.getTrait()?.let { - return Ec2QueryNameTraitGenerator(it.value) - } - val generator = XMLNameTraitGenerator.construct(shape, defaultMemberName) - return Ec2QueryNameTraitGenerator(generator.xmlNameValue.capitalize()) - } - } - override fun toString(): String { - return xmlNameValue - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/MemberShapeDecodeGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/MemberShapeDecodeGenerator.kt deleted file mode 100644 index 2f44b5077..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/MemberShapeDecodeGenerator.kt +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ -package software.amazon.smithy.swift.codegen.integration.serde.json - -import software.amazon.smithy.model.shapes.CollectionShape -import software.amazon.smithy.model.shapes.ListShape -import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.SetShape -import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.model.shapes.TimestampShape -import software.amazon.smithy.model.traits.SparseTrait -import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.swift.codegen.ClientRuntimeTypes -import software.amazon.smithy.swift.codegen.SwiftTypes -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.serde.MemberShapeDecodeGeneratable -import software.amazon.smithy.swift.codegen.integration.serde.TimestampDecodeGenerator -import software.amazon.smithy.swift.codegen.integration.serde.TimestampHelpers -import software.amazon.smithy.swift.codegen.model.defaultValue -import software.amazon.smithy.swift.codegen.model.hasTrait -import software.amazon.smithy.swift.codegen.model.isBoxed -import software.amazon.smithy.swift.codegen.model.toMemberNames -import software.amazon.smithy.swift.codegen.removeSurroundingBackticks - -/* -Includes functions to help render conformance to Decodable protocol for shapes - */ -abstract class MemberShapeDecodeGenerator( - private val ctx: ProtocolGenerator.GenerationContext, - private val writer: SwiftWriter, - private val defaultTimestampFormat: TimestampFormatTrait.Format, - private val path: String -) : MemberShapeDecodeGeneratable { - fun renderDecodeForTimestamp(ctx: ProtocolGenerator.GenerationContext, target: Shape, member: MemberShape, containerName: String) { - val memberName = ctx.symbolProvider.toMemberName(member) - val timestampFormat = TimestampHelpers.getTimestampFormat(member, target, defaultTimestampFormat) - val codingKey = writer.format(".\$L", memberName) - val decodedMemberName = writer.format("\$LDecoded", memberName) - TimestampDecodeGenerator( - decodedMemberName, - containerName, - codingKey, - timestampFormat, - true - ).generate(writer) - renderAssigningDecodedMember(member, decodedMemberName) - } - - fun writeDecodeForPrimitive(shape: Shape, member: MemberShape, containerName: String, ignoreDefaultValues: Boolean = false) { - var symbol = ctx.symbolProvider.toSymbol(member) - val memberName = ctx.symbolProvider.toMemberNames(member).second - val defaultValue = symbol.defaultValue() - val decodeVerb = if (symbol.isBoxed() || !defaultValue.isNullOrEmpty()) "decodeIfPresent" else "decode" - val decodedMemberName = "${memberName}Decoded" - - // no need to assign nil to a member that is optional - val defaultValueLiteral = if (!ignoreDefaultValues && defaultValue != null && defaultValue != "nil") " ?? $defaultValue" else "" - - writer.write("let \$L = try \$L.$decodeVerb(\$N.self, forKey: .\$L)$defaultValueLiteral", decodedMemberName, containerName, symbol, memberName) - renderAssigningDecodedMember(member, decodedMemberName) - } - - private fun determineSymbolForShape(currShape: Shape, topLevel: Boolean): String { - var mappedSymbol = when (currShape) { - is MapShape -> { - val mapIsSparse = currShape.hasTrait() - val targetShape = ctx.model.expectShape(currShape.value.target) - val valueEvaluated = determineSymbolForShape(targetShape, topLevel) - val terminator = if (topLevel || mapIsSparse) "?" else "" - "[${SwiftTypes.String}: $valueEvaluated$terminator]" - } - is ListShape -> { - val listIsSparse = currShape.hasTrait() - val targetShape = ctx.model.expectShape(currShape.member.target) - val nestedShape = determineSymbolForShape(targetShape, topLevel) - val terminator = if (topLevel || listIsSparse) "?" else "" - "[$nestedShape$terminator]" - } - is SetShape -> { - val targetShape = ctx.model.expectShape(currShape.member.target) - val nestedShape = determineSymbolForShape(targetShape, topLevel) - "${SwiftTypes.Set}<$nestedShape>" - } - is TimestampShape -> { - val timestampFormat = TimestampHelpers.getTimestampFormat(currShape, null, defaultTimestampFormat) - if (timestampFormat == TimestampFormatTrait.Format.EPOCH_SECONDS) "${ClientRuntimeTypes.Core.Date}" else "${SwiftTypes.String}" - } - else -> { - "${ctx.symbolProvider.toSymbol(currShape)}" - } - } - return mappedSymbol - } - - fun renderDecodeListMember( - shape: CollectionShape, - memberName: String, - containerName: String, - topLevelMember: MemberShape, - parentMember: Shape, - level: Int = 0 - ) { - val symbolName = determineSymbolForShape(shape, true) - val originalSymbol = ctx.symbolProvider.toSymbol(parentMember) - val decodedMemberName = "${memberName.removeSurroundingBackticks()}Decoded$level" - var insertMethod = when (shape) { - is SetShape -> "insert" - is ListShape -> "append" - else -> "append" - } - val nestedTarget = ctx.model.expectShape(shape.member.target) - if (level == 0) { - insertMethod = when (ctx.model.expectShape(topLevelMember.target)) { - is SetShape -> "insert" - is ListShape -> "append" - else -> "append" - } - val listContainerName = "${memberName.removeSurroundingBackticks()}Container" - val decodeVerb = if (originalSymbol.isBoxed()) "decodeIfPresent" else "decode" - writer.write( - "let \$L = try $containerName.$decodeVerb(\$L.self, forKey: .\$L)", - listContainerName, - symbolName, - memberName - ) - - writer.write("var \$L:\$T = nil", decodedMemberName, originalSymbol) - writer.openBlock("if let \$L = \$L {", "}", listContainerName, listContainerName) { - writer.write("\$L = \$N()", decodedMemberName, originalSymbol) - renderDecodeListTarget(nestedTarget, decodedMemberName, listContainerName, insertMethod, topLevelMember, shape, level) - } - renderAssigningDecodedMember(topLevelMember, decodedMemberName) - } else { - writer.openBlock("if let \$L = \$L {", "}", memberName, memberName) { - val previousDecodedMemberName = "${memberName.removeSurroundingBackticks()}Decoded${level - 1}" - val symbolName = determineSymbolForShape(shape, false) - writer.write("\$L = \$L()", previousDecodedMemberName, symbolName) - renderDecodeListTarget(nestedTarget, containerName, memberName, insertMethod, topLevelMember, shape, level) - } - } - } - - /* - Simple assignment of the decode value to the member. - Can be overridden to allow post processing of the decoded value before assigning it to the member. - */ - open fun renderAssigningDecodedMember(topLevelMember: MemberShape, decodedMemberName: String) { - val topLevelMemberName = ctx.symbolProvider.toMemberName(topLevelMember) - writer.write("\$L\$L = \$L", path, topLevelMemberName, decodedMemberName) - } - - private fun renderDecodeListTarget(shape: Shape, decodedMemberName: String, collectionName: String, insertMethod: String, topLevelMember: MemberShape, parentMember: Shape, level: Int = 0) { - val isSparse = parentMember.hasTrait() - val iteratorName = "${shape.type.name.lowercase()}$level" - val symbolName = determineSymbolForShape(shape, false) - val terminator = "?" - writer.openBlock("for $iteratorName in $collectionName {", "}") { - when (shape) { - is TimestampShape -> { - val timestampFormat = TimestampHelpers.getTimestampFormat(shape, null, defaultTimestampFormat) - - if (timestampFormat == TimestampFormatTrait.Format.EPOCH_SECONDS) { // if decoding a double decode as normal from [[Date]].self - if (!isSparse) { - writer.openBlock("if let $iteratorName = $iteratorName {", "}") { - writer.write("${decodedMemberName}$terminator.$insertMethod($iteratorName)") - } - } else { - writer.write("${decodedMemberName}$terminator.$insertMethod($iteratorName)") - } - } else { // decode date as a string manually - val dateName = "date$level" - val swiftTimestampName = TimestampHelpers.generateTimestampFormatEnumValue(timestampFormat) - if (!isSparse) { - writer.openBlock("if let $iteratorName = $iteratorName {", "}") { - writer.write( - "let \$L = try containerValues.timestampStringAsDate(\$L, format: .\$L, forKey: .\$L)", - dateName, iteratorName, swiftTimestampName, topLevelMember.memberName - ) - writer.write("${decodedMemberName}$terminator.$insertMethod($dateName)") - } - } else { - writer.write( - "let \$L = try containerValues.timestampStringAsDate(\$L, format: .\$L, forKey: .\$L)", - dateName, iteratorName, swiftTimestampName, topLevelMember.memberName - ) - writer.write("${decodedMemberName}$terminator.$insertMethod($dateName)") - } - } - } - is CollectionShape -> { - val nestedDecodedMemberName = "${iteratorName}Decoded$level" - writer.write("var \$L: \$L? = nil", nestedDecodedMemberName, symbolName) - renderDecodeListMember(shape, iteratorName, nestedDecodedMemberName, topLevelMember, parentMember, level + 1) - writer.openBlock("if let $nestedDecodedMemberName = $nestedDecodedMemberName {", "}") { - writer.write("$decodedMemberName$terminator.$insertMethod($nestedDecodedMemberName)") - } - } - is MapShape -> { - val nestedDecodedMemberName = "${collectionName}Decoded$level" - writer.write("var \$L: \$L? = nil", nestedDecodedMemberName, symbolName) - renderDecodeMapMember(shape, iteratorName, nestedDecodedMemberName, topLevelMember, level + 1) - writer.openBlock("if let $nestedDecodedMemberName = $nestedDecodedMemberName {", "}") { - writer.write("$decodedMemberName$terminator.$insertMethod($nestedDecodedMemberName)") - } - } - else -> { - if (!isSparse) { - writer.openBlock("if let $iteratorName = $iteratorName {", "}") { - writer.write("${decodedMemberName}$terminator.$insertMethod($iteratorName)") - } - } else { - writer.write("${decodedMemberName}$terminator.$insertMethod($iteratorName)") - } - } - } - } - } - - fun renderDecodeMapMember( - shape: MapShape, - memberName: String, - containerName: String, - topLevelMember: MemberShape, - level: Int = 0 - ) { - val symbolName = determineSymbolForShape(shape, true) - val originalSymbol = ctx.symbolProvider.toSymbol(topLevelMember) - val decodedMemberName = "${memberName.removeSurroundingBackticks()}Decoded$level" - val nestedTarget = ctx.model.expectShape(shape.value.target) - if (level == 0) { - val topLevelContainerName = "${memberName.removeSurroundingBackticks()}Container" - val decodeVerb = if (originalSymbol.isBoxed()) "decodeIfPresent" else "decode" - writer.write( - "let \$L = try $containerName.$decodeVerb(\$L.self, forKey: .\$L)", - topLevelContainerName, - symbolName, - memberName - ) - writer.write("var \$L: \$T = nil", decodedMemberName, originalSymbol) - writer.openBlock("if let \$L = \$L {", "}", topLevelContainerName, topLevelContainerName) { - writer.write("\$L = \$N()", decodedMemberName, originalSymbol) - renderDecodeMapTarget(topLevelContainerName, decodedMemberName, nestedTarget, topLevelMember, level) - } - renderAssigningDecodedMember(topLevelMember, decodedMemberName) - } else { - writer.openBlock("if let \$L = \$L {", "}", memberName, memberName) { - val previousDecodedMemberName = "${memberName.removeSurroundingBackticks()}Decoded${level - 1}" - val symbolName = determineSymbolForShape(shape, false) - writer.write("\$L = \$L()", containerName, symbolName) - renderDecodeMapTarget(memberName, containerName, nestedTarget, topLevelMember, level) - } - } - } - - private fun renderDecodeMapTarget( - mapName: String, - decodedMemberName: String, - valueTargetShape: Shape, - topLevelMember: MemberShape, - level: Int = 0 - ) { - val topLevelShape = ctx.model.expectShape(topLevelMember.target) - val isSparse = topLevelShape.hasTrait() - val valueIterator = "${valueTargetShape.id.name.lowercase()}$level" - val symbolName = determineSymbolForShape(valueTargetShape, false) - val terminator = "?" - writer.openBlock("for (key$level, $valueIterator) in $mapName {", "}") { - when (valueTargetShape) { - is CollectionShape -> { - val nestedDecodedMemberName = "${valueIterator}Decoded$level" - writer.write("var \$L: \$L? = nil", nestedDecodedMemberName, symbolName) - renderDecodeListMember(valueTargetShape, valueIterator, nestedDecodedMemberName, topLevelMember, valueTargetShape.member, level + 1) - writer.write("$decodedMemberName?[key$level] = $nestedDecodedMemberName") - } - is MapShape -> { - val nestedDecodedMemberName = "${valueIterator}Decoded$level" - writer.write("var \$L: \$L? = nil", nestedDecodedMemberName, symbolName) - renderDecodeMapMember(valueTargetShape, valueIterator, nestedDecodedMemberName, topLevelMember, level + 1) - writer.write("$decodedMemberName?[key$level] = $nestedDecodedMemberName") - } - is TimestampShape -> { - val timestampFormat = TimestampHelpers.getTimestampFormat(valueTargetShape, null, defaultTimestampFormat) - - if (timestampFormat == TimestampFormatTrait.Format.EPOCH_SECONDS) { // if decoding a double decode as normal from [[Date]].self - if (!isSparse) { - writer.openBlock("if let $valueIterator = $valueIterator {", "}") { - writer.write("${decodedMemberName}$terminator[key$level] = $valueIterator") - } - } else { - writer.write("${decodedMemberName}$terminator[key$level] = $valueIterator") - } - } else { // decode date as a string manually - val dateName = "date$level" - val swiftTimestampName = TimestampHelpers.generateTimestampFormatEnumValue(timestampFormat) - writer.write( - "let \$L = try containerValues.timestampStringAsDate(\$L, format: .\$L, forKey: .\$L)", - dateName, valueIterator, swiftTimestampName, topLevelMember.memberName - ) - writer.write("${decodedMemberName}$terminator[key$level] = $dateName") - } - } - else -> { - if (!isSparse) { - writer.openBlock("if let $valueIterator = $valueIterator {", "}") { - writer.write("${decodedMemberName}$terminator[key$level] = $valueIterator") - } - } else { - writer.write("${decodedMemberName}$terminator[key$level] = $valueIterator") - } - } - } - } - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/MemberShapeEncodeGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/MemberShapeEncodeGenerator.kt deleted file mode 100644 index 913e8f2da..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/MemberShapeEncodeGenerator.kt +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ -package software.amazon.smithy.swift.codegen.integration.serde.json - -import software.amazon.smithy.model.knowledge.NullableIndex -import software.amazon.smithy.model.shapes.BlobShape -import software.amazon.smithy.model.shapes.CollectionShape -import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.model.shapes.ShapeType -import software.amazon.smithy.model.shapes.StringShape -import software.amazon.smithy.model.shapes.TimestampShape -import software.amazon.smithy.model.traits.BoxTrait -import software.amazon.smithy.model.traits.EnumTrait -import software.amazon.smithy.model.traits.SparseTrait -import software.amazon.smithy.model.traits.StreamingTrait -import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.swift.codegen.ClientRuntimeTypes -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.serde.MemberShapeEncodeGeneratable -import software.amazon.smithy.swift.codegen.integration.serde.TimestampEncodeGenerator -import software.amazon.smithy.swift.codegen.integration.serde.TimestampHelpers -import software.amazon.smithy.swift.codegen.integration.serde.getDefaultValueOfShapeType -import software.amazon.smithy.swift.codegen.model.hasTrait -import software.amazon.smithy.swift.codegen.model.isBoxed -import software.amazon.smithy.swift.codegen.removeSurroundingBackticks -import software.amazon.smithy.swift.codegen.utils.toLowerCamelCase - -/* -Includes functions to help render conformance to Encodable protocol for shapes - */ -abstract class MemberShapeEncodeGenerator( - private val ctx: ProtocolGenerator.GenerationContext, - private val writer: SwiftWriter, - private val defaultTimestampFormat: TimestampFormatTrait.Format -) : MemberShapeEncodeGeneratable { - - private val dictKey = "dictKey" - private val nullableIndex = NullableIndex.of(ctx.model) - - /* - Add custom extensions to be rendered to handle optional shapes and - special types like enum, timestamp, blob - */ - private fun getShapeExtension(shape: Shape, memberName: String, isBoxed: Boolean, isUnwrapped: Boolean = true): String { - // target shape type to deserialize is either the shape itself or member.target - val target = when (shape) { - is MemberShape -> ctx.model.expectShape(shape.target) - else -> shape - } - val optional = if ((isBoxed && isUnwrapped) || !isBoxed) "" else "?" - val memberNameOptional = "$memberName$optional" - return when (target) { - is StringShape -> if (target.hasTrait()) "$memberNameOptional.rawValue" else memberName - is BlobShape -> if (target.hasTrait()) "$memberNameOptional" else "$memberNameOptional.base64EncodedString()" - else -> memberName - } - } - - // Render encoding of a member of list type - fun renderEncodeListMember( - targetShape: Shape, - memberName: String, - containerName: String, - level: Int = 0 - ) { - when (targetShape) { - is CollectionShape -> { - val topLevelContainerName = "${memberName.removeSurroundingBackticks()}Container" - - if (level == 0) { - writer.write( - "var \$L = $containerName.nestedUnkeyedContainer(forKey: .\$L)", - topLevelContainerName, - memberName - ) - renderEncodeList(ctx, memberName, topLevelContainerName, targetShape, level) - } else { - writer.write("var \$L = $containerName.nestedUnkeyedContainer()", topLevelContainerName) - renderEncodeList(ctx, memberName, topLevelContainerName, targetShape, level) - } - } - // this only gets called in a recursive loop where there is a map nested deeply inside a list - is MapShape -> { - val topLevelContainerName = "${memberName.removeSurroundingBackticks()}Container" - writer.write("var \$L = $containerName.nestedContainer(keyedBy: \$N.self)", topLevelContainerName, ClientRuntimeTypes.Serde.Key) - renderEncodeMap(ctx, memberName, topLevelContainerName, targetShape, level) - } - else -> { - renderSimpleShape(targetShape, memberName, containerName, null, false) - } - } - } - - private fun renderSimpleShape( - memberShape: Shape, - memberName: String, - containerName: String, - codingKey: String?, - isBoxed: Boolean, - path: String? = null - ) { - val targetShape = when (memberShape) { - is MemberShape -> ctx.model.expectShape(memberShape.target) - else -> memberShape - } - when (targetShape) { - is TimestampShape -> { - TimestampEncodeGenerator( - containerName, - memberName, - codingKey, - TimestampHelpers.getTimestampFormat(memberShape, targetShape, defaultTimestampFormat) - ).generate(writer) - } - else -> { - val extension = getShapeExtension(memberShape, memberName, isBoxed) - if (codingKey != null) { - writer.write("try \$L.encode(\$L, forKey: \$L)", containerName, extension, codingKey) - } else { - writer.write("try \$L.encode(\$L)", containerName, extension) - } - } - } - } - - // Iterate over and render encoding for all members of a list - private fun renderEncodeList( - ctx: ProtocolGenerator.GenerationContext, - collectionName: String, - topLevelContainerName: String, - listShape: CollectionShape, - level: Int = 0 - ) { - val listIsSparse = listShape.hasTrait() - val targetShape = ctx.model.expectShape(listShape.member.target) - val iteratorName = "${targetShape.id.name.lowercase()}$level" - writer.openBlock("for $iteratorName in $collectionName {", "}") { - if (listIsSparse) { - writer.openBlock("guard let \$L = \$L else {", "}", iteratorName, iteratorName) { - writer.write("try \$L.encodeNil()", topLevelContainerName) - writer.write("continue") - } - } - when (targetShape) { - is CollectionShape, is MapShape -> { - renderEncodeListMember(targetShape, iteratorName, topLevelContainerName, level + 1) - } - else -> { - val isBoxed = ctx.symbolProvider.toSymbol(targetShape).isBoxed() && targetShape.hasTrait() - if (isBoxed) { - writer.openBlock("if let \$L = \$L {", "}", iteratorName, iteratorName) { - renderSimpleShape(targetShape, iteratorName, topLevelContainerName, null, isBoxed) - } - } else { - renderSimpleShape(targetShape, iteratorName, topLevelContainerName, null, isBoxed) - } - } - } - } - } - - // Render encoding of a member of Map type - fun renderEncodeMapMember(targetShape: Shape, memberName: String, containerName: String, level: Int = 0) { - val keyName = if (level == 0) ".$memberName" else "${ClientRuntimeTypes.Serde.Key}(stringValue: $dictKey${level - 1})" - when (targetShape) { - is CollectionShape -> { - val topLevelContainerName = "${memberName.removeSurroundingBackticks()}Container" - writer.write("var \$L = $containerName.nestedUnkeyedContainer(forKey: \$L)", topLevelContainerName, keyName) - renderEncodeList(ctx, memberName, topLevelContainerName, targetShape, level) - } - is MapShape -> { - val topLevelContainerName = "${memberName.removeSurroundingBackticks()}Container" - writer.write( - "var \$L = $containerName.nestedContainer(keyedBy: \$N.self, forKey: \$L)", - topLevelContainerName, - ClientRuntimeTypes.Serde.Key, - keyName - ) - renderEncodeMap(ctx, memberName, topLevelContainerName, targetShape, level) - } - else -> { - val isBoxed = ctx.symbolProvider.toSymbol(targetShape).isBoxed() && targetShape.hasTrait() - if (isBoxed && level == 0) { - writer.openBlock("if let \$L = \$L {", "}", memberName, memberName) { - renderSimpleShape(targetShape, memberName, containerName, keyName, isBoxed) - } - } else { - renderSimpleShape(targetShape, memberName, containerName, keyName, isBoxed) - } - } - } - } - - // Iterate over and render encoding for all members of a map - private fun renderEncodeMap( - ctx: ProtocolGenerator.GenerationContext, - mapName: String, - topLevelContainerName: String, - mapShape: MapShape, - level: Int = 0 - ) { - val keyIterator = "$dictKey$level" - val valueIterator = "${mapShape.id.name.toLowerCamelCase()}$level" - val target = ctx.model.expectShape(mapShape.value.target) - val mapIsSparse = mapShape.hasTrait() - writer.openBlock("for ($keyIterator, $valueIterator) in $mapName {", "}") { - if (mapIsSparse) { - writer.openBlock("guard let \$L = \$L else {", "}", valueIterator, valueIterator) { - writer.write("try \$L.encodeNil(forKey: \$L(stringValue: \$L))", topLevelContainerName, ClientRuntimeTypes.Serde.Key, keyIterator) - writer.write("continue") - } - } - when (target) { - is CollectionShape, is MapShape -> { - renderEncodeMapMember(target, valueIterator, topLevelContainerName, level + 1) - } - else -> { - val keyEnumName = "${ClientRuntimeTypes.Serde.Key}(stringValue: $dictKey$level)" - renderSimpleShape(target, valueIterator, topLevelContainerName, keyEnumName, target.hasTrait(BoxTrait::class.java)) - } - } - } - } - - // Render default encoding of a member - fun renderSimpleEncodeMember( - target: Shape, - member: MemberShape, - containerName: String, - path: String? = null - ) { - val symbol = ctx.symbolProvider.toSymbol(member) - val memberName = ctx.symbolProvider.toMemberName(member) - val isBoxed = symbol.isBoxed() - val memberWithExtension = getShapeExtension(member, memberName, isBoxed, true) - if (isBoxed) { - writer.openBlock("if let $memberName = self.${path ?: ""}$memberName {", "}") { - renderSimpleShape(member, memberName, containerName, ".$memberName", isBoxed, path) - } - } else { - val primitiveSymbols: MutableSet = hashSetOf( - ShapeType.INTEGER, ShapeType.BYTE, ShapeType.SHORT, - ShapeType.LONG, ShapeType.FLOAT, ShapeType.DOUBLE, ShapeType.BOOLEAN - ) - if (primitiveSymbols.contains(target.type)) { - val defaultValue = getDefaultValueOfShapeType(target.type) - writer.openBlock("if $memberName != $defaultValue {", "}") { - renderSimpleShape(member, memberName, containerName, ".$memberName", isBoxed, path) - } - } else - renderSimpleShape(member, memberName, containerName, ".$memberName", isBoxed, path) - } - } - - fun renderEncodeAssociatedType( - target: Shape, - member: MemberShape, - containerName: String - ) { - val symbol = ctx.symbolProvider.toSymbol(member) - val memberName = ctx.symbolProvider.toMemberName(member) - val isBoxed = symbol.isBoxed() - renderSimpleShape(member, memberName, containerName, ".$memberName", isBoxed) - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/StructDecodeGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/StructDecodeGenerator.kt deleted file mode 100644 index 09d229f85..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/StructDecodeGenerator.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.serde.json - -import software.amazon.smithy.model.shapes.CollectionShape -import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.TimestampShape -import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.swift.codegen.SwiftTypes -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.model.toMemberNames - -/** - * Generates decode function for members bound to the payload. - * - * e.g. - * ``` - public init(from decoder: Decoder) throws { - let values = try decoder.container(keyedBy: CodingKeys.self) - member1 = try values.decodeIfPresent(Int.self, forKey: .member1) - let intListContainer = try values.decodeIfPresent([Int].self, forKey: .intList) - var intListDecoded0 = [Int]() - if let intListContainer = intListContainer { - for integer0 in intListContainer { - intListDecoded0.append(integer0) - } - } - intList = intListDecoded0 - let intMapContainer = try values.decodeIfPresent([String:Int].self, forKey: .intMap) - var intMapDecoded0 = [String:Int]() - if let intMapContainer = intMapContainer { - for (key0, integer0) in intMapContainer { - intMapDecoded0[key0] = integer0 - } - } - intMap = intMapDecoded0 - } - * ``` - */ -class StructDecodeGenerator( - private val ctx: ProtocolGenerator.GenerationContext, - private val members: List, - private val writer: SwiftWriter, - private val defaultTimestampFormat: TimestampFormatTrait.Format, - private val path: String -) : MemberShapeDecodeGenerator(ctx, writer, defaultTimestampFormat, path) { - override fun render() { - val containerName = "containerValues" - writer.openBlock("public init(from decoder: \$N) throws {", "}", SwiftTypes.Decoder) { - if (members.isNotEmpty()) { - writer.write("let \$L = try decoder.container(keyedBy: CodingKeys.self)", containerName) - members.forEach { member -> - val target = ctx.model.expectShape(member.target) - val memberNames = ctx.symbolProvider.toMemberNames(member) - when (target) { - is CollectionShape -> renderDecodeListMember(target, memberNames.second, containerName, member, parentMember = member) - is MapShape -> renderDecodeMapMember(target, memberNames.second, containerName, member) - is TimestampShape -> renderDecodeForTimestamp(ctx, target, member, containerName) - else -> writeDecodeForPrimitive(target, member, containerName) - } - } - } - } - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/StructEncodeGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/StructEncodeGenerator.kt deleted file mode 100644 index 60e98cde9..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/StructEncodeGenerator.kt +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.serde.json - -import software.amazon.smithy.model.shapes.CollectionShape -import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.swift.codegen.SwiftTypes -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator - -/** - * Generates encode function for members bound to the payload. - * - * e.g. - * ``` - * public func encode(to encoder: Encoder) throws { - * var container = encoder.container(keyedBy: CodingKeys.self) - * try container.encode(booleanList, forKey: .booleanList) - * try container.encode(enumList, forKey: .enumList) - * try container.encode(integerList, forKey: .integerList) - * var nestedStringListContainer = container.nestedUnkeyedContainer(forKey: .nestedStringList) - * if let nestedStringList = nestedStringList { - * for stringlist0 in nestedStringList { - * try nestedStringListContainer.encode(stringlist0) - * } - * } - * try container.encode(stringList, forKey: .stringList) - * try container.encode(stringSet, forKey: .stringSet) - * try container.encode(structureList, forKey: .structureList) - * var timestampListContainer = container.nestedUnkeyedContainer(forKey: .timestampList) - * if let timestampList = timestampList { - * for timestamp0 in timestampList { - * try timestampListContainer.encode(timestamp0.timeIntervalSince1970) - * } -* } - * } - * ``` - */ -class StructEncodeGenerator( - private val ctx: ProtocolGenerator.GenerationContext, - private val members: List, - private val writer: SwiftWriter, - private val defaultTimestampFormat: TimestampFormatTrait.Format, - private val path: String? = null -) : MemberShapeEncodeGenerator(ctx, writer, defaultTimestampFormat) { - override fun render() { - val containerName = "encodeContainer" - writer.openBlock("public func encode(to encoder: \$N) throws {", "}", SwiftTypes.Encoder) { - if (members.isNotEmpty()) { - writer.write("var \$L = encoder.container(keyedBy: CodingKeys.self)", containerName) - val membersSortedByName: List = members.sortedBy { it.memberName } - membersSortedByName.forEach { member -> - val target = ctx.model.expectShape(member.target) - val memberName = ctx.symbolProvider.toMemberName(member) - when (target) { - is CollectionShape -> { - writer.openBlock("if let $memberName = ${path ?: ""}$memberName {", "}") { - renderEncodeListMember(target, memberName, containerName) - } - } - is MapShape -> { - writer.openBlock("if let $memberName = ${path ?: ""}$memberName {", "}") { - renderEncodeMapMember(target, memberName, containerName) - } - } - else -> { - renderSimpleEncodeMember( - target, member, containerName, path - ) - } - } - } - } else { - writer.write("var container = encoder.singleValueContainer()") - writer.write("try container.encode([String:String]())") - } - } - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/UnionDecodeGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/UnionDecodeGenerator.kt deleted file mode 100644 index 5a269f625..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/UnionDecodeGenerator.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ -package software.amazon.smithy.swift.codegen.integration.serde.json - -import software.amazon.smithy.model.shapes.CollectionShape -import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.TimestampShape -import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.swift.codegen.SwiftTypes -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator - -class UnionDecodeGenerator( - private val ctx: ProtocolGenerator.GenerationContext, - private val members: List, - private val writer: SwiftWriter, - private val defaultTimestampFormat: TimestampFormatTrait.Format -) : MemberShapeDecodeGenerator(ctx, writer, defaultTimestampFormat, "") { - override fun render() { - val containerName = "values" - writer.openBlock("public init(from decoder: \$N) throws {", "}", SwiftTypes.Decoder) { - writer.write("let \$L = try decoder.container(keyedBy: CodingKeys.self)", containerName) - members.forEach { member -> - val target = ctx.model.expectShape(member.target) - val memberName = ctx.symbolProvider.toMemberName(member) - when (target) { - is CollectionShape -> renderDecodeListMember(target, memberName, containerName, member, member) - is MapShape -> renderDecodeMapMember(target, memberName, containerName, member) - is TimestampShape -> renderDecodeForTimestamp(ctx, target, member, containerName) - else -> writeDecodeForPrimitive(target, member, containerName, ignoreDefaultValues = true) - } - } - writer.write("self = .sdkUnknown(\"\")") - } - } - - override fun renderAssigningDecodedMember(topLevelMember: MemberShape, decodedMemberName: String) { - val topLevelMemberName = ctx.symbolProvider.toMemberName(topLevelMember) - writer.openBlock("if let \$L = \$L {", "}", topLevelMemberName, decodedMemberName) { - writer.write("self = .\$L(\$L)", topLevelMemberName, topLevelMemberName) - writer.write("return") - } - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/UnionEncodeGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/UnionEncodeGenerator.kt deleted file mode 100644 index f17d94e83..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/json/UnionEncodeGenerator.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ -package software.amazon.smithy.swift.codegen.integration.serde.json - -import software.amazon.smithy.model.shapes.CollectionShape -import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.swift.codegen.SwiftTypes -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator - -class UnionEncodeGenerator( - private val ctx: ProtocolGenerator.GenerationContext, - private val members: List, - private val writer: SwiftWriter, - private val defaultTimestampFormat: TimestampFormatTrait.Format -) : MemberShapeEncodeGenerator(ctx, writer, defaultTimestampFormat) { - override fun render() { - val containerName = "container" - writer.openBlock("public func encode(to encoder: \$N) throws {", "}", SwiftTypes.Encoder) { - writer.write("var \$L = encoder.container(keyedBy: CodingKeys.self)", containerName) - writer.openBlock("switch self {", "}") { - val membersSortedByName: List = members.sortedBy { it.memberName } - membersSortedByName.forEach { member -> - val target = ctx.model.expectShape(member.target) - val memberName = ctx.symbolProvider.toMemberName(member) - writer.write("case let .\$L(\$L):", memberName, memberName) - writer.indent() - when (target) { - is CollectionShape -> { - renderEncodeListMember(target, memberName, containerName) - } - is MapShape -> { - renderEncodeMapMember(target, memberName, containerName) - } - else -> { - renderEncodeAssociatedType(target, member, containerName) - } - } - writer.dedent() - } - writer.write("case let .sdkUnknown(sdkUnknown):") - writer.indent() - writer.write("try container.encode(sdkUnknown, forKey: .sdkUnknown)") - writer.dedent() - } - } - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/MemberShapeDecodeXMLGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/member/MemberShapeDecodeGenerator.kt similarity index 83% rename from smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/MemberShapeDecodeXMLGenerator.kt rename to smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/member/MemberShapeDecodeGenerator.kt index d562601c5..3f221d894 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/MemberShapeDecodeXMLGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/member/MemberShapeDecodeGenerator.kt @@ -3,8 +3,11 @@ * SPDX-License-Identifier: Apache-2.0. */ -package software.amazon.smithy.swift.codegen.integration.serde.xml +package software.amazon.smithy.swift.codegen.integration.serde.member +import software.amazon.smithy.model.node.Node +import software.amazon.smithy.model.node.NumberNode +import software.amazon.smithy.model.node.StringNode import software.amazon.smithy.model.shapes.BigDecimalShape import software.amazon.smithy.model.shapes.BigIntegerShape import software.amazon.smithy.model.shapes.BooleanShape @@ -26,23 +29,26 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.TimestampShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.DefaultTrait +import software.amazon.smithy.model.traits.SparseTrait import software.amazon.smithy.model.traits.TimestampFormatTrait import software.amazon.smithy.model.traits.XmlFlattenedTrait import software.amazon.smithy.swift.codegen.SwiftWriter import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.serde.json.TimestampUtils +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.NodeInfoUtils import software.amazon.smithy.swift.codegen.integration.serde.readwrite.ReadingClosureUtils +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.responseWireProtocol import software.amazon.smithy.swift.codegen.model.getTrait import software.amazon.smithy.swift.codegen.model.hasTrait import software.amazon.smithy.swift.codegen.model.isError import software.amazon.smithy.swift.codegen.swiftEnumCaseName -class MemberShapeDecodeXMLGenerator( +open class MemberShapeDecodeGenerator( private val ctx: ProtocolGenerator.GenerationContext, private val writer: SwiftWriter, private val shapeContainingMembers: Shape, ) { - private val nodeInfoUtils = NodeInfoUtils(ctx, writer) + private val nodeInfoUtils = NodeInfoUtils(ctx, writer, ctx.service.responseWireProtocol) private val readingClosureUtils = ReadingClosureUtils(ctx, writer) fun render(member: MemberShape, isPayload: Boolean = false) { @@ -67,7 +73,7 @@ class MemberShapeDecodeXMLGenerator( private fun renderStructOrUnionExp(memberShape: MemberShape, isPayload: Boolean): String { val readingClosure = readingClosureUtils.readingClosure(memberShape) return writer.format( - "try \$L.\$L(readingClosure: \$L)", + "try \$L.\$L(with: \$L)", reader(memberShape, isPayload), readMethodName("read"), readingClosure @@ -75,7 +81,8 @@ class MemberShapeDecodeXMLGenerator( } private fun renderListExp(memberShape: MemberShape, listShape: ListShape): String { - val memberReadingClosure = readingClosureUtils.readingClosure(listShape.member) + val isSparse = listShape.hasTrait() + val memberReadingClosure = readingClosureUtils.readingClosure(listShape.member, isSparse) val memberNodeInfo = nodeInfoUtils.nodeInfo(listShape.member) val isFlattened = memberShape.hasTrait() return writer.format( @@ -89,7 +96,8 @@ class MemberShapeDecodeXMLGenerator( } private fun renderMapExp(memberShape: MemberShape, mapShape: MapShape): String { - val valueReadingClosure = ReadingClosureUtils(ctx, writer).readingClosure(mapShape.value) + val isSparse = mapShape.hasTrait() + val valueReadingClosure = ReadingClosureUtils(ctx, writer).readingClosure(mapShape.value, isSparse) val keyNodeInfo = nodeInfoUtils.nodeInfo(mapShape.key) val valueNodeInfo = nodeInfoUtils.nodeInfo(mapShape.value) val isFlattened = memberShape.hasTrait() @@ -106,7 +114,7 @@ class MemberShapeDecodeXMLGenerator( private fun renderTimestampExp(memberShape: MemberShape, timestampShape: TimestampShape): String { val memberTimestampFormatTrait = memberShape.getTrait() - val swiftTimestampFormatCase = TimestampUtils.timestampFormat(memberTimestampFormatTrait, timestampShape) + val swiftTimestampFormatCase = TimestampUtils.timestampFormat(ctx, memberTimestampFormatTrait, timestampShape) return writer.format( "try \$L.\$L(format: \$L)", reader(memberShape, false), @@ -125,7 +133,8 @@ class MemberShapeDecodeXMLGenerator( } private fun readMethodName(baseName: String): String { - return "${baseName}${"".takeIf { shapeContainingMembers.isUnionShape } ?: "IfPresent"}" + val extension = "".takeIf { shapeContainingMembers.isUnionShape } ?: "IfPresent" + return writer.format("\$L\$L", baseName, extension) } private fun reader(memberShape: MemberShape, isPayload: Boolean): String { @@ -141,9 +150,9 @@ class MemberShapeDecodeXMLGenerator( if (it.isNullNode) { return "" } // Provide a default value dependent on the type. return when (targetShape) { - is EnumShape -> " ?? ${swiftEnumCaseName(it.expectStringNode().value, "")}" - is IntEnumShape -> " ?? ${swiftEnumCaseName(it.expectStringNode().value, "")}" - is StringShape -> " ?? ${it.expectStringNode().value}" + is EnumShape -> " ?? .${swiftEnumCaseName(it.expectStringNode().value, "")}" + is IntEnumShape -> intEnumDefaultValue(it) + is StringShape -> " ?? \"${it.expectStringNode().value}\"" is ByteShape -> " ?? ${it.expectNumberNode().value}" is ShortShape -> " ?? ${it.expectNumberNode().value}" is IntegerShape -> " ?? ${it.expectNumberNode().value}" @@ -162,4 +171,12 @@ class MemberShapeDecodeXMLGenerator( } } ?: "" // If there is no default trait, provide no default value. } + + private fun intEnumDefaultValue(node: Node): String { + return when (node) { + is StringNode -> " ?? .${node.value}" + is NumberNode -> " ?? .init(rawValue: ${node.value})" + else -> "" + } + } } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/MemberShapeEncodeXMLGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/member/MemberShapeEncodeGenerator.kt similarity index 68% rename from smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/MemberShapeEncodeXMLGenerator.kt rename to smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/member/MemberShapeEncodeGenerator.kt index b3f415b50..b153e6b37 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/MemberShapeEncodeXMLGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/member/MemberShapeEncodeGenerator.kt @@ -7,47 +7,55 @@ package software.amazon.smithy.swift.codegen.integration.serde.json import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.TimestampShape import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.model.traits.SparseTrait import software.amazon.smithy.model.traits.TimestampFormatTrait import software.amazon.smithy.model.traits.XmlFlattenedTrait import software.amazon.smithy.swift.codegen.SwiftWriter import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.serde.MemberShapeEncodeGeneratable +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.AWSProtocol +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.NodeInfoUtils +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.WireProtocol import software.amazon.smithy.swift.codegen.integration.serde.readwrite.WritingClosureUtils -import software.amazon.smithy.swift.codegen.integration.serde.xml.NodeInfoUtils +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.awsProtocol +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.requestWireProtocol import software.amazon.smithy.swift.codegen.model.getTrait import software.amazon.smithy.swift.codegen.model.hasTrait -abstract class MemberShapeEncodeXMLGenerator( +abstract class MemberShapeEncodeGenerator( private val ctx: ProtocolGenerator.GenerationContext, private val writer: SwiftWriter, -) : MemberShapeEncodeGeneratable { +) { + + abstract fun render() private val writingClosureUtils = WritingClosureUtils(ctx, writer) - private val nodeInfoUtils = NodeInfoUtils(ctx, writer) + private val nodeInfoUtils = NodeInfoUtils(ctx, writer, ctx.service.requestWireProtocol) - fun writeMember(memberShape: MemberShape, unionMember: Boolean) { - val prefix = "value.".takeIf { !unionMember } ?: "" + fun writeMember(memberShape: MemberShape, unionMember: Boolean, errorMember: Boolean) { + val prefix1 = "value.".takeIf { !unionMember } ?: "" + val prefix2 = "properties.".takeIf { errorMember } ?: "" + val prefix = prefix1 + prefix2 val targetShape = ctx.model.expectShape(memberShape.target) + val isSparse = targetShape.hasTrait() when (targetShape) { is StructureShape, is UnionShape -> { writeStructureOrUnionMember(memberShape, prefix) } is ListShape -> { - writeListMember(memberShape, targetShape, prefix) + writeListMember(memberShape, targetShape, prefix, isSparse) } is MapShape -> { - writeMapMember(memberShape, targetShape, prefix) + writeMapMember(memberShape, targetShape, prefix, isSparse) } is TimestampShape -> { writeTimestampMember(memberShape, targetShape, prefix) } else -> { - writePropertyMember(memberShape, targetShape, prefix) + writePropertyMember(memberShape, prefix) } } } @@ -57,7 +65,7 @@ abstract class MemberShapeEncodeXMLGenerator( val propertyKey = nodeInfoUtils.nodeInfo(memberShape) val writingClosure = writingClosureUtils.writingClosure(memberShape) writer.write( - "try writer[\$L].write(\$L\$L, writingClosure: \$L)", + "try writer[\$L].write(\$L\$L, with: \$L)", propertyKey, prefix, memberName, @@ -69,7 +77,7 @@ abstract class MemberShapeEncodeXMLGenerator( val memberName = ctx.symbolProvider.toMemberName(memberShape) val timestampKey = nodeInfoUtils.nodeInfo(memberShape) val memberTimestampFormatTrait = memberShape.getTrait() - val swiftTimestampFormatCase = TimestampUtils.timestampFormat(memberTimestampFormatTrait, timestampShape) + val swiftTimestampFormatCase = TimestampUtils.timestampFormat(ctx, memberTimestampFormatTrait, timestampShape) writer.write( "try writer[\$L].writeTimestamp(\$L\$L, format: \$L)", timestampKey, @@ -79,9 +87,9 @@ abstract class MemberShapeEncodeXMLGenerator( ) } - private fun writePropertyMember(memberShape: MemberShape, targetShape: Shape, prefix: String) { - val memberName = ctx.symbolProvider.toMemberName(memberShape) + private fun writePropertyMember(memberShape: MemberShape, prefix: String) { val propertyNodeInfo = nodeInfoUtils.nodeInfo(memberShape) + val memberName = ctx.symbolProvider.toMemberName(memberShape) writer.write( "try writer[\$L].write(\$L\$L)", propertyNodeInfo, @@ -90,11 +98,11 @@ abstract class MemberShapeEncodeXMLGenerator( ) } - private fun writeListMember(member: MemberShape, listShape: ListShape, prefix: String) { + private fun writeListMember(member: MemberShape, listShape: ListShape, prefix: String, isSparse: Boolean) { val memberName = ctx.symbolProvider.toMemberName(member) - val listMemberWriter = writingClosureUtils.writingClosure(listShape.member) + val listMemberWriter = writingClosureUtils.writingClosure(listShape.member, isSparse) val listKey = nodeInfoUtils.nodeInfo(member) - val isFlattened = member.hasTrait() + val isFlattened = member.hasTrait() || ctx.service.awsProtocol == AWSProtocol.EC2_QUERY val memberNodeInfo = nodeInfoUtils.nodeInfo(listShape.member) writer.write( "try writer[\$L].writeList(\$L\$L, memberWritingClosure: \$L, memberNodeInfo: \$L, isFlattened: \$L)", @@ -107,12 +115,12 @@ abstract class MemberShapeEncodeXMLGenerator( ) } - private fun writeMapMember(member: MemberShape, mapShape: MapShape, prefix: String) { + private fun writeMapMember(member: MemberShape, mapShape: MapShape, prefix: String, isSparse: Boolean) { val memberName = ctx.symbolProvider.toMemberName(member) val mapKey = nodeInfoUtils.nodeInfo(member) val keyNodeInfo = nodeInfoUtils.nodeInfo(mapShape.key) val valueNodeInfo = nodeInfoUtils.nodeInfo(mapShape.value) - val valueWriter = writingClosureUtils.writingClosure(mapShape.value) + val valueWriter = writingClosureUtils.writingClosure(mapShape.value, isSparse) val isFlattened = member.hasTrait() writer.write( "try writer[\$L].writeMap(\$L\$L, valueWritingClosure: \$L, keyNodeInfo: \$L, valueNodeInfo: \$L, isFlattened: \$L)", @@ -129,12 +137,19 @@ abstract class MemberShapeEncodeXMLGenerator( object TimestampUtils { - fun timestampFormat(memberTimestampFormatTrait: TimestampFormatTrait?, timestampShape: TimestampShape): String { - val timestampFormatTrait = memberTimestampFormatTrait ?: timestampShape.getTrait() ?: TimestampFormatTrait(TimestampFormatTrait.DATE_TIME) - return when (timestampFormatTrait.value) { + fun timestampFormat(ctx: ProtocolGenerator.GenerationContext, memberTimestampFormatTrait: TimestampFormatTrait?, timestampShape: TimestampShape): String { + val timestampFormat = memberTimestampFormatTrait?.value ?: timestampShape.getTrait()?.value ?: defaultTimestampFormat(ctx) + return when (timestampFormat) { TimestampFormatTrait.EPOCH_SECONDS -> ".epochSeconds" TimestampFormatTrait.HTTP_DATE -> ".httpDate" else -> ".dateTime" } } + + private fun defaultTimestampFormat(ctx: ProtocolGenerator.GenerationContext): String { + return when (ctx.service.requestWireProtocol) { + WireProtocol.XML, WireProtocol.FORM_URL -> TimestampFormatTrait.DATE_TIME + WireProtocol.JSON -> TimestampFormatTrait.EPOCH_SECONDS + } + } } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/DocumentReadingClosureUtils.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/DocumentReadingClosureUtils.kt deleted file mode 100644 index 0e0dec009..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/DocumentReadingClosureUtils.kt +++ /dev/null @@ -1,69 +0,0 @@ -package software.amazon.smithy.swift.codegen.integration.serde.readwrite - -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.swift.codegen.ClientRuntimeTypes -import software.amazon.smithy.swift.codegen.SmithyXMLTypes -import software.amazon.smithy.swift.codegen.SwiftDependency -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.serde.xml.NodeInfoUtils - -class DocumentReadingClosureUtils( - val ctx: ProtocolGenerator.GenerationContext, - val writer: SwiftWriter -) { - fun closure(memberShape: MemberShape): String { - val rootNodeInfo = NodeInfoUtils(ctx, writer).nodeInfo(memberShape, true) - return closure(rootNodeInfo) - } - - fun closure(valueShape: Shape): String { - val rootNodeInfo = NodeInfoUtils(ctx, writer).nodeInfo(valueShape) - return closure(rootNodeInfo) - } - private fun closure(rootNodeInfo: String): String { - when (ctx.service.responseWireProtocol) { - WireProtocol.XML -> { - return writer.format("\$N.documentReadingClosure(rootNodeInfo: \$L)", readWriteSymbol(), rootNodeInfo) - } - WireProtocol.FORM_URL, WireProtocol.JSON -> { - return writer.format("\$N.documentReadingClosure(decoder: decoder)", readWriteSymbol()) - } - } - } - - fun readerSymbol(): Symbol { - when (ctx.service.responseWireProtocol) { - WireProtocol.XML -> { - writer.addImport(SwiftDependency.SMITHY_XML.target) - return SmithyXMLTypes.Reader - } - WireProtocol.FORM_URL -> { - // not used - throw Exception("FormURL decoding not implemented") - } - WireProtocol.JSON -> { - return ClientRuntimeTypes.Serde.JSONReader - } - } - } - - private fun readWriteSymbol(): Symbol { - when (ctx.service.responseWireProtocol) { - WireProtocol.XML -> { - writer.addImport(SwiftDependency.SMITHY_XML.target) - return SmithyXMLTypes.XMLReadWrite - } - WireProtocol.FORM_URL -> { - writer.addImport(SwiftDependency.CLIENT_RUNTIME.target) - return ClientRuntimeTypes.Serde.FormURLReadWrite - } - WireProtocol.JSON -> { - writer.addImport(SwiftDependency.CLIENT_RUNTIME.target) - return ClientRuntimeTypes.Serde.JSONReadWrite - } - } - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/DocumentWritingClosureUtils.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/DocumentWritingClosureUtils.kt deleted file mode 100644 index 9300b2415..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/DocumentWritingClosureUtils.kt +++ /dev/null @@ -1,68 +0,0 @@ -package software.amazon.smithy.swift.codegen.integration.serde.readwrite - -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.swift.codegen.ClientRuntimeTypes -import software.amazon.smithy.swift.codegen.SmithyXMLTypes -import software.amazon.smithy.swift.codegen.SwiftDependency -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.serde.xml.NodeInfoUtils - -class DocumentWritingClosureUtils( - val ctx: ProtocolGenerator.GenerationContext, - val writer: SwiftWriter -) { - fun closure(memberShape: MemberShape): String { - val rootNodeInfo = NodeInfoUtils(ctx, writer).nodeInfo(memberShape, true) - return closure(rootNodeInfo) - } - - fun closure(valueShape: Shape): String { - val rootNodeInfo = NodeInfoUtils(ctx, writer).nodeInfo(valueShape) - return closure(rootNodeInfo) - } - private fun closure(rootNodeInfo: String): String { - when (ctx.service.requestWireProtocol) { - WireProtocol.XML -> { - return writer.format("\$N.documentWritingClosure(rootNodeInfo: \$L)", readWriteSymbol(), rootNodeInfo) - } - WireProtocol.FORM_URL, WireProtocol.JSON -> { - return writer.format("\$N.documentWritingClosure(encoder: encoder)", readWriteSymbol()) - } - } - } - - fun writerSymbol(): Symbol { - when (ctx.service.requestWireProtocol) { - WireProtocol.XML -> { - writer.addImport(SwiftDependency.SMITHY_XML.target) - return SmithyXMLTypes.Writer - } - WireProtocol.FORM_URL -> { - return ClientRuntimeTypes.Serde.FormURLWriter - } - WireProtocol.JSON -> { - return ClientRuntimeTypes.Serde.JSONWriter - } - } - } - - private fun readWriteSymbol(): Symbol { - when (ctx.service.requestWireProtocol) { - WireProtocol.XML -> { - writer.addImport(SwiftDependency.SMITHY_XML.target) - return SmithyXMLTypes.XMLReadWrite - } - WireProtocol.FORM_URL -> { - writer.addImport(SwiftDependency.CLIENT_RUNTIME.target) - return ClientRuntimeTypes.Serde.FormURLReadWrite - } - WireProtocol.JSON -> { - writer.addImport(SwiftDependency.CLIENT_RUNTIME.target) - return ClientRuntimeTypes.Serde.JSONReadWrite - } - } - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/NodeInfoUtils.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/NodeInfoUtils.kt similarity index 50% rename from smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/NodeInfoUtils.kt rename to smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/NodeInfoUtils.kt index 58e5180a3..d89e71a61 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/NodeInfoUtils.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/NodeInfoUtils.kt @@ -1,7 +1,9 @@ -package software.amazon.smithy.swift.codegen.integration.serde.xml +package software.amazon.smithy.swift.codegen.integration.serde.readwrite +import software.amazon.smithy.aws.traits.protocols.Ec2QueryNameTrait import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.traits.JsonNameTrait import software.amazon.smithy.model.traits.XmlAttributeTrait import software.amazon.smithy.model.traits.XmlNameTrait import software.amazon.smithy.model.traits.XmlNamespaceTrait @@ -12,10 +14,13 @@ import software.amazon.smithy.swift.codegen.model.hasTrait class NodeInfoUtils( val ctx: ProtocolGenerator.GenerationContext, - val writer: SwiftWriter + val writer: SwiftWriter, + val wireProtocol: WireProtocol, ) { - fun nodeInfo(shape: Shape): String { + val awsProtocol = ctx.service.awsProtocol + fun nodeInfo(shape: Shape, forRootNode: Boolean = false): String { + if (wireProtocol != WireProtocol.XML && forRootNode) return "\"\"" val xmlName = shape.getTrait()?.value val symbol = ctx.symbolProvider.toSymbol(shape) val resolvedName = xmlName ?: symbol.name @@ -27,14 +32,10 @@ class NodeInfoUtils( } fun nodeInfo(member: MemberShape, forRootNode: Boolean = false): String { + if (wireProtocol != WireProtocol.XML && forRootNode) return "\"\"" val targetShape = ctx.model.expectShape(member.target) - val resolvedName = if (forRootNode) { - val xmlName = member.getTrait()?.value ?: targetShape.getTrait()?.value - xmlName ?: ctx.symbolProvider.toSymbol(targetShape).name - } else { - member.getTrait()?.value ?: member.memberName - } + val resolvedName = resolvedName(member, forRootNode) val xmlAttributeParam = ", location: .attribute".takeIf { member.hasTrait() } ?: "" @@ -44,7 +45,42 @@ class NodeInfoUtils( return nodeInfo(resolvedName, xmlAttributeParam, xmlNamespaceParam) } + private fun resolvedName(member: MemberShape, forRootNode: Boolean): String { + val targetShape = ctx.model.expectShape(member.target) + when (wireProtocol) { + WireProtocol.XML -> { + if (forRootNode) { + val xmlName = member.getTrait()?.value ?: targetShape.getTrait()?.value + return xmlName ?: ctx.symbolProvider.toSymbol(targetShape).name + } else { + return member.getTrait()?.value ?: member.memberName + } + } + WireProtocol.FORM_URL -> { + if (forRootNode) { + return "\"\"" + } else { + if (ctx.service.awsProtocol == AWSProtocol.EC2_QUERY) { + return member.getTrait()?.value ?: member.getTrait()?.value?.capitalize() ?: member.memberName.capitalize() + } else { + return member.getTrait()?.value ?: member.memberName + } + } + } + WireProtocol.JSON -> { + if (forRootNode) { + return "\"\"" + } else if (awsProtocol == AWSProtocol.REST_JSON_1) { + return member.getTrait()?.value ?: member.memberName + } else { + return member.memberName + } + } + } + } + private fun namespaceParam(xmlNamespaceTrait: XmlNamespaceTrait?): String { + if (wireProtocol != WireProtocol.XML) { return "" } return xmlNamespaceTrait?.let { writer.format( ", namespaceDef: .init(prefix: \$S, uri: \$S)", @@ -55,7 +91,7 @@ class NodeInfoUtils( } private fun nodeInfo(resolvedName: String, xmlAttributeParam: String, xmlNamespaceParam: String): String { - if (xmlAttributeParam == "" && xmlNamespaceParam == "") { + if (xmlAttributeParam == "" && xmlNamespaceParam == "" || wireProtocol != WireProtocol.XML) { return writer.format("\$S", resolvedName) } else { return writer.format( diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/ReadingClosureUtils.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/ReadingClosureUtils.kt index 15bec9a19..878feb0ef 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/ReadingClosureUtils.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/ReadingClosureUtils.kt @@ -10,7 +10,6 @@ import software.amazon.smithy.model.traits.XmlFlattenedTrait import software.amazon.smithy.swift.codegen.SwiftWriter import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.serde.json.TimestampUtils -import software.amazon.smithy.swift.codegen.integration.serde.xml.NodeInfoUtils import software.amazon.smithy.swift.codegen.model.getTrait import software.amazon.smithy.swift.codegen.model.hasTrait @@ -18,18 +17,20 @@ class ReadingClosureUtils( val ctx: ProtocolGenerator.GenerationContext, val writer: SwiftWriter ) { - private val nodeInfoUtils = NodeInfoUtils(ctx, writer) + private val nodeInfoUtils = NodeInfoUtils(ctx, writer, ctx.service.responseWireProtocol) - fun readingClosure(member: MemberShape): String { + fun readingClosure(member: MemberShape, isSparse: Boolean = false): String { val target = ctx.model.expectShape(member.target) val memberTimestampFormatTrait = member.getTrait() - return readingClosure(target, memberTimestampFormatTrait) + val baseClosure = readingClosure(target, memberTimestampFormatTrait) + if (isSparse) { + return writer.format("optionalFormOf(readingClosure: \$L)", baseClosure) + } else { + return baseClosure + } } private fun readingClosure(shape: Shape, memberTimestampFormatTrait: TimestampFormatTrait? = null): String { - if (ctx.service.responseWireProtocol == WireProtocol.JSON) { - return "JSONReadWrite.readingClosure()" - } when (shape) { is MapShape -> { val valueReadingClosure = readingClosure(shape.value) @@ -37,7 +38,7 @@ class ReadingClosureUtils( val valueNodeInfo = nodeInfoUtils.nodeInfo(shape.value) val isFlattened = shape.hasTrait() return writer.format( - "SmithyXML.mapReadingClosure(valueReadingClosure: \$L, keyNodeInfo: \$L, valueNodeInfo: \$L, isFlattened: \$L)", + "mapReadingClosure(valueReadingClosure: \$L, keyNodeInfo: \$L, valueNodeInfo: \$L, isFlattened: \$L)", valueReadingClosure, keyNodeInfo, valueNodeInfo, @@ -49,7 +50,7 @@ class ReadingClosureUtils( val memberNodeInfo = nodeInfoUtils.nodeInfo(shape.member) val isFlattened = shape.hasTrait() return writer.format( - "SmithyXML.listReadingClosure(memberReadingClosure: \$L, memberNodeInfo: \$L, isFlattened: \$L)", + "listReadingClosure(memberReadingClosure: \$L, memberNodeInfo: \$L, isFlattened: \$L)", memberReadingClosure, memberNodeInfo, isFlattened @@ -57,12 +58,12 @@ class ReadingClosureUtils( } is TimestampShape -> { return writer.format( - "SmithyXML.timestampReadingClosure(format: \$L)", - TimestampUtils.timestampFormat(memberTimestampFormatTrait, shape) + "timestampReadingClosure(format: \$L)", + TimestampUtils.timestampFormat(ctx, memberTimestampFormatTrait, shape) ) } else -> { - return writer.format("\$N.readingClosure", ctx.symbolProvider.toSymbol(shape)) + return writer.format("\$N.read(from:)", ctx.symbolProvider.toSymbol(shape)) } } } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/ResponseClosureUtils.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/ResponseClosureUtils.kt index 965583b42..895f264f3 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/ResponseClosureUtils.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/ResponseClosureUtils.kt @@ -11,16 +11,8 @@ class ResponseClosureUtils( ) { fun render(): String { - return when (ctx.service.responseWireProtocol) { - WireProtocol.XML -> { - val outputShape = ctx.model.expectShape(op.outputShape) - val outputSymbol = ctx.symbolProvider.toSymbol(outputShape) - writer.format( - "responseClosure(\$N.httpBinding, responseDocumentBinding)", - outputSymbol - ) - } - else -> "responseClosure(decoder: decoder)" - } + val outputShape = ctx.model.expectShape(op.outputShape) + val outputSymbol = ctx.symbolProvider.toSymbol(outputShape) + return writer.format("\$N.httpOutput(from:)", outputSymbol) } } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/ResponseErrorClosureUtils.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/ResponseErrorClosureUtils.kt index 95659701f..2549df16c 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/ResponseErrorClosureUtils.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/ResponseErrorClosureUtils.kt @@ -13,12 +13,6 @@ class ResponseErrorClosureUtils( fun render(): String { val outputErrorSymbol = MiddlewareShapeUtils.outputErrorSymbol(op) - return when (ctx.service.responseWireProtocol) { - WireProtocol.XML -> writer.format( - "responseErrorClosure(\$N.httpBinding, responseDocumentBinding)", - outputErrorSymbol - ) - else -> writer.format("responseErrorClosure(\$N.self, decoder: decoder)", outputErrorSymbol) - } + return writer.format("\$N.httpError(from:)", outputErrorSymbol) } } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/ServiceUtils.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/ServiceUtils.kt index a88ff8a7d..6a0c51aad 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/ServiceUtils.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/ServiceUtils.kt @@ -7,6 +7,8 @@ import software.amazon.smithy.aws.traits.protocols.Ec2QueryTrait import software.amazon.smithy.aws.traits.protocols.RestJson1Trait import software.amazon.smithy.aws.traits.protocols.RestXmlTrait import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.swift.codegen.SwiftDependency +import software.amazon.smithy.swift.codegen.SwiftWriter import software.amazon.smithy.swift.codegen.model.hasTrait // An enum expressing the six defined AWS protocols used with Smithy. @@ -52,3 +54,19 @@ val ServiceShape.isRPCBound: Boolean AWSProtocol.AWS_JSON_1_0, AWSProtocol.AWS_JSON_1_1, AWSProtocol.AWS_QUERY, AWSProtocol.EC2_QUERY -> true else -> false } + +// Adds the imports needed for models of this protocol +fun SwiftWriter.addImports(wireProtocol: WireProtocol) { + addImport(SwiftDependency.SMITHY_READ_WRITE.target) + when (wireProtocol) { + WireProtocol.XML -> { + addImport(SwiftDependency.SMITHY_XML.target) + } + WireProtocol.FORM_URL -> { + addImport(SwiftDependency.SMITHY_FORM_URL.target) + } + WireProtocol.JSON -> { + addImport(SwiftDependency.SMITHY_JSON.target) + } + } +} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/WritingClosureUtils.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/WritingClosureUtils.kt index 012f601f4..d1fa4ecd4 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/WritingClosureUtils.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/readwrite/WritingClosureUtils.kt @@ -5,12 +5,12 @@ import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.TimestampShape +import software.amazon.smithy.model.traits.SparseTrait import software.amazon.smithy.model.traits.TimestampFormatTrait import software.amazon.smithy.model.traits.XmlFlattenedTrait import software.amazon.smithy.swift.codegen.SwiftWriter import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.serde.json.TimestampUtils -import software.amazon.smithy.swift.codegen.integration.serde.xml.NodeInfoUtils import software.amazon.smithy.swift.codegen.model.getTrait import software.amazon.smithy.swift.codegen.model.hasTrait @@ -19,32 +19,28 @@ class WritingClosureUtils( val writer: SwiftWriter ) { - private val nodeInfoUtils = NodeInfoUtils(ctx, writer) + private val nodeInfoUtils = NodeInfoUtils(ctx, writer, ctx.service.requestWireProtocol) - fun writingClosure(member: MemberShape): String { + fun writingClosure(member: MemberShape, isSparse: Boolean): String { val target = ctx.model.expectShape(member.target) val memberTimestampFormatTrait = member.getTrait() - return writingClosure(target, memberTimestampFormatTrait) + return makeWritingClosure(target, memberTimestampFormatTrait, isSparse) } fun writingClosure(shape: Shape): String { - return writingClosure(shape, null) + return makeWritingClosure(shape, null, false) } - private fun writingClosure(shape: Shape, memberTimestampFormatTrait: TimestampFormatTrait? = null): String { - when (ctx.service.requestWireProtocol) { - WireProtocol.JSON -> return "JSONReadWrite.writingClosure()" - WireProtocol.FORM_URL -> return "FormURLReadWrite.writingClosure()" - else -> {} - } - return when (shape) { + private fun makeWritingClosure(shape: Shape, memberTimestampFormatTrait: TimestampFormatTrait?, isSparse: Boolean): String { + val base = when (shape) { is MapShape -> { val keyNodeInfo = nodeInfoUtils.nodeInfo(shape.key) val valueNodeInfo = nodeInfoUtils.nodeInfo(shape.value) - val valueWriter = writingClosure(shape.value) + val mapIsSparse = shape.hasTrait() + val valueWriter = writingClosure(shape.value, mapIsSparse) val isFlattened = shape.hasTrait() writer.format( - "SmithyXML.mapWritingClosure(valueWritingClosure: \$L, keyNodeInfo: \$L, valueNodeInfo: \$L, isFlattened: \$L)", + "mapWritingClosure(valueWritingClosure: \$L, keyNodeInfo: \$L, valueNodeInfo: \$L, isFlattened: \$L)", valueWriter, keyNodeInfo, valueNodeInfo, @@ -53,10 +49,11 @@ class WritingClosureUtils( } is ListShape -> { val memberNodeInfo = nodeInfoUtils.nodeInfo(shape.member) - val memberWriter = writingClosure(shape.member) + val listIsSparse = shape.hasTrait() + val memberWriter = writingClosure(shape.member, listIsSparse) val isFlattened = shape.hasTrait() writer.format( - "SmithyXML.listWritingClosure(memberWritingClosure: \$L, memberNodeInfo: \$L, isFlattened: \$L)", + "listWritingClosure(memberWritingClosure: \$L, memberNodeInfo: \$L, isFlattened: \$L)", memberWriter, memberNodeInfo, isFlattened @@ -64,13 +61,18 @@ class WritingClosureUtils( } is TimestampShape -> { writer.format( - "SmithyXML.timestampWritingClosure(format: \$L)", - TimestampUtils.timestampFormat(memberTimestampFormatTrait, shape) + "timestampWritingClosure(format: \$L)", + TimestampUtils.timestampFormat(ctx, memberTimestampFormatTrait, shape) ) } else -> { - writer.format("\$N.writingClosure(_:to:)", ctx.symbolProvider.toSymbol(shape)) + writer.format("\$N.write(value:to:)", ctx.symbolProvider.toSymbol(shape)) } } + return if (isSparse) { + writer.format("sparseFormOf(writingClosure: \$L)", base) + } else { + base + } } } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/struct/StructDecodeGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/struct/StructDecodeGenerator.kt new file mode 100644 index 000000000..c510506b4 --- /dev/null +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/struct/StructDecodeGenerator.kt @@ -0,0 +1,63 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.swift.codegen.integration.serde.struct + +import software.amazon.smithy.aws.traits.customizations.S3UnwrappedXmlOutputTrait +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.swift.codegen.SmithyReadWriteTypes +import software.amazon.smithy.swift.codegen.SwiftWriter +import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator +import software.amazon.smithy.swift.codegen.integration.serde.member.MemberShapeDecodeGenerator +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.addImports +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.responseWireProtocol +import software.amazon.smithy.swift.codegen.model.ShapeMetadata +import software.amazon.smithy.swift.codegen.model.hasTrait + +open class StructDecodeGenerator( + private val ctx: ProtocolGenerator.GenerationContext, + private val shapeContainingMembers: Shape, + private val members: List, + private val metadata: Map, + private val writer: SwiftWriter, +) : MemberShapeDecodeGenerator(ctx, writer, shapeContainingMembers) { + + fun render() { + writer.addImports(ctx.service.responseWireProtocol) + val symbol = ctx.symbolProvider.toSymbol(shapeContainingMembers) + writer.openBlock( + "static func read(from reader: \$N) throws -> \$N {", "}", + ctx.service.readerSymbol, + symbol, + ) { + // S3:SelectObjectContent's event stream output contains EndEvent, which has empty payload. + // The additional condition here allows returning new instance of a struct even if value isn't found in reader + // as long as the struct has no properties. + val additionalCondition = "|| Mirror(reflecting: self).children.isEmpty ".takeIf { + ctx.settings.sdkId == "S3" && members.isEmpty() + } ?: "" + writer.write( + "guard reader.hasContent \$Lelse { throw \$N.requiredValueNotPresent }", + additionalCondition, + SmithyReadWriteTypes.ReaderError, + ) + if (members.isEmpty()) { + writer.write("return \$N()", symbol) + } else { + writer.write("var value = \$N()", symbol) + if (isUnwrapped) { + writer.write("let reader = reader.parent ?? reader") + } + members.forEach { render(it) } + writer.write("return value") + } + } + } + + private val isUnwrapped: Boolean = + (metadata[ShapeMetadata.OPERATION_SHAPE] as? OperationShape)?.hasTrait() ?: false +} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/struct/StructEncodeGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/struct/StructEncodeGenerator.kt new file mode 100644 index 000000000..dba1250f2 --- /dev/null +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/struct/StructEncodeGenerator.kt @@ -0,0 +1,83 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.swift.codegen.integration.serde.struct + +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.ServiceShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.swift.codegen.SmithyFormURLTypes +import software.amazon.smithy.swift.codegen.SmithyJSONTypes +import software.amazon.smithy.swift.codegen.SmithyXMLTypes +import software.amazon.smithy.swift.codegen.SwiftWriter +import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator +import software.amazon.smithy.swift.codegen.integration.serde.json.MemberShapeEncodeGenerator +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.WireProtocol +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.addImports +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.requestWireProtocol +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.responseWireProtocol +import software.amazon.smithy.swift.codegen.model.ShapeMetadata +import software.amazon.smithy.swift.codegen.model.isError +import java.lang.Exception + +class StructEncodeGenerator( + private val ctx: ProtocolGenerator.GenerationContext, + private val shapeContainingMembers: Shape, + private val members: List, + private val metadata: Map, + private val writer: SwiftWriter +) : MemberShapeEncodeGenerator(ctx, writer) { + + override fun render() { + writer.addImports(ctx.service.requestWireProtocol) + val structSymbol = ctx.symbolProvider.toSymbol(shapeContainingMembers) + writer.openBlock( + "static func write(value: \$N?, to writer: \$N) throws {", "}", + structSymbol, + ctx.service.writerSymbol, + ) { + writer.write( + "guard \$L else { return }", + "value != nil".takeIf { members.isEmpty() } ?: "let value" + ) + if (members.isEmpty()) { + writer.write("_ = writer[\"\"] // create an empty structure") + } + val isErrorMember = shapeContainingMembers.isError + members.sortedBy { it.memberName }.forEach { writeMember(it, false, isErrorMember) } + writeExtraMembers() + } + } + + private fun writeExtraMembers() { + when (ctx.service.requestWireProtocol) { + WireProtocol.FORM_URL -> { + if (metadata.containsKey(ShapeMetadata.OPERATION_SHAPE) && metadata.containsKey(ShapeMetadata.SERVICE_VERSION)) { + val operationShape = metadata[ShapeMetadata.OPERATION_SHAPE] as OperationShape + val version = metadata[ShapeMetadata.SERVICE_VERSION] as String + writer.write("try writer[\"Action\"].write(\$S)", operationShape.id.name) + writer.write("try writer[\"Version\"].write(\$S)", version) + } + } + else -> listOf() + } + } +} + +val ServiceShape.writerSymbol: Symbol + get() = when (requestWireProtocol) { + WireProtocol.XML -> SmithyXMLTypes.Writer + WireProtocol.JSON -> SmithyJSONTypes.Writer + WireProtocol.FORM_URL -> SmithyFormURLTypes.Writer + } + +val ServiceShape.readerSymbol: Symbol + get() = when (responseWireProtocol) { + WireProtocol.XML -> SmithyXMLTypes.Reader + WireProtocol.JSON -> SmithyJSONTypes.Reader + WireProtocol.FORM_URL -> throw Exception("Reading from Form URL data not supported") + } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/union/UnionDecodeGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/union/UnionDecodeGenerator.kt new file mode 100644 index 000000000..399ae0d3f --- /dev/null +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/union/UnionDecodeGenerator.kt @@ -0,0 +1,64 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.swift.codegen.integration.serde.union + +import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.traits.JsonNameTrait +import software.amazon.smithy.swift.codegen.SmithyReadWriteTypes +import software.amazon.smithy.swift.codegen.SwiftWriter +import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator +import software.amazon.smithy.swift.codegen.integration.serde.member.MemberShapeDecodeGenerator +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.AWSProtocol +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.addImports +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.awsProtocol +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.requestWireProtocol +import software.amazon.smithy.swift.codegen.integration.serde.struct.readerSymbol +import software.amazon.smithy.swift.codegen.model.getTrait + +class UnionDecodeGenerator( + private val ctx: ProtocolGenerator.GenerationContext, + private val shapeContainingMembers: Shape, + private val members: List, + private val writer: SwiftWriter, +) : MemberShapeDecodeGenerator(ctx, writer, shapeContainingMembers) { + + fun render() { + writer.addImports(ctx.service.requestWireProtocol) + val symbol = ctx.symbolProvider.toSymbol(shapeContainingMembers) + writer.openBlock( + "static func read(from reader: \$N) throws -> \$N {", "}", + ctx.service.readerSymbol, + symbol, + ) { + writer.write( + "guard reader.hasContent else { throw \$N.requiredValueNotPresent }", + SmithyReadWriteTypes.ReaderError, + ) + writer.write("let name = reader.children.filter { $$0.hasContent && $$0.nodeInfo.name != \"__type\" }.first?.nodeInfo.name") + writer.openBlock("switch name {", "}") { + members.forEach { + writer.write("case \$S:", memberName(it)) + writer.indent() + render(it) + writer.dedent() + } + writer.write("default:") + writer.indent() + writer.write("return .sdkUnknown(name ?? \"\")") + writer.dedent() + } + } + } + + private fun memberName(member: MemberShape): String { + if (ctx.service.awsProtocol == AWSProtocol.REST_JSON_1) { + return member.getTrait()?.value ?: member.memberName + } else { + return member.memberName + } + } +} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/UnionEncodeXMLGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/union/UnionEncodeGenerator.kt similarity index 63% rename from smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/UnionEncodeXMLGenerator.kt rename to smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/union/UnionEncodeGenerator.kt index 1d9957718..eb20dec6c 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/UnionEncodeXMLGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/union/UnionEncodeGenerator.kt @@ -3,43 +3,44 @@ * SPDX-License-Identifier: Apache-2.0. */ -package software.amazon.smithy.swift.codegen.integration.serde.xml +package software.amazon.smithy.swift.codegen.integration.serde.union import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.swift.codegen.SmithyXMLTypes -import software.amazon.smithy.swift.codegen.SwiftDependency import software.amazon.smithy.swift.codegen.SwiftWriter import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.serde.json.MemberShapeEncodeXMLGenerator +import software.amazon.smithy.swift.codegen.integration.serde.json.MemberShapeEncodeGenerator +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.addImports +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.requestWireProtocol +import software.amazon.smithy.swift.codegen.integration.serde.struct.writerSymbol -class UnionEncodeXMLGenerator( +class UnionEncodeGenerator( private val ctx: ProtocolGenerator.GenerationContext, private val shapeContainingMembers: Shape, private val members: List, private val writer: SwiftWriter -) : MemberShapeEncodeXMLGenerator(ctx, writer) { +) : MemberShapeEncodeGenerator(ctx, writer) { override fun render() { - writer.addImport(SwiftDependency.SMITHY_XML.target) + writer.addImports(ctx.service.requestWireProtocol) val structSymbol = ctx.symbolProvider.toSymbol(shapeContainingMembers) writer.openBlock( - "static func writingClosure(_ value: \$N?, to writer: \$N) throws {", "}", + "static func write(value: \$N?, to writer: \$N) throws {", "}", structSymbol, - SmithyXMLTypes.Writer + ctx.service.writerSymbol, ) { - writer.write("guard let value else { writer.detach(); return }") + writer.write("guard let value else { return }") writer.openBlock("switch value {", "}") { val membersSortedByName: List = members.sortedBy { it.memberName } membersSortedByName.forEach { member -> val memberName = ctx.symbolProvider.toMemberName(member) writer.write("case let .\$L(\$L):", memberName, memberName) writer.indent() - writeMember(member, true) + writeMember(member, true, false) writer.dedent() } writer.write("case let .sdkUnknown(sdkUnknown):") writer.indent() - writer.write("try writer[.init(\"sdkUnknown\")].write(sdkUnknown)") + writer.write("try writer[\"sdkUnknown\"].write(sdkUnknown)") writer.dedent() } } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/StructDecodeXMLGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/StructDecodeXMLGenerator.kt deleted file mode 100644 index c4c193b07..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/StructDecodeXMLGenerator.kt +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.serde.xml - -import software.amazon.smithy.aws.traits.customizations.S3UnwrappedXmlOutputTrait -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.swift.codegen.SmithyReadWriteTypes -import software.amazon.smithy.swift.codegen.SmithyXMLTypes -import software.amazon.smithy.swift.codegen.SwiftDependency -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.serde.MemberShapeDecodeGeneratable -import software.amazon.smithy.swift.codegen.model.ShapeMetadata -import software.amazon.smithy.swift.codegen.model.hasTrait - -open class StructDecodeXMLGenerator( - private val ctx: ProtocolGenerator.GenerationContext, - private val shapeContainingMembers: Shape, - private val members: List, - private val metadata: Map, - private val writer: SwiftWriter, -) : MemberShapeDecodeGeneratable { - private val memberGenerator = MemberShapeDecodeXMLGenerator(ctx, writer, shapeContainingMembers) - - override fun render() { - writer.addImport(SwiftDependency.SMITHY_XML.target) - writer.addImport(SwiftDependency.SMITHY_READ_WRITE.target) - val symbol = ctx.symbolProvider.toSymbol(shapeContainingMembers) - writer.openBlock( - "static var readingClosure: \$N<\$N, \$N> {", "}", - SmithyReadWriteTypes.ReadingClosure, - symbol, - SmithyXMLTypes.Reader - ) { - writer.openBlock("return { reader in", "}") { - // S3:SelectObjectContent's event stream output contains EndEvent, which has empty payload. - // The additional condition here allows returning new instance of a struct even if value isn't found in reader - // as long as the struct has no properties. - val additionalCondition = "|| Mirror(reflecting: self).children.isEmpty ".takeIf { - ctx.settings.sdkId == "S3" && members.isEmpty() - } ?: "" - writer.write("guard reader.content != nil \$Lelse { return nil }", additionalCondition) - if (members.isEmpty()) { - writer.write("return \$N()", symbol) - } else { - writer.write("var value = \$N()", symbol) - if (isUnwrapped) { - writer.write("let reader = reader.parent ?? reader") - } - members.forEach { memberGenerator.render(it) } - writer.write("return value") - } - } - } - } - - private val isUnwrapped: Boolean = - (metadata[ShapeMetadata.OPERATION_SHAPE] as? OperationShape)?.hasTrait() ?: false -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/StructEncodeXMLGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/StructEncodeXMLGenerator.kt deleted file mode 100644 index 66b6f96e5..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/StructEncodeXMLGenerator.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.serde.json - -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.swift.codegen.SmithyXMLTypes -import software.amazon.smithy.swift.codegen.SwiftDependency -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator - -class StructEncodeXMLGenerator( - private val ctx: ProtocolGenerator.GenerationContext, - private val shapeContainingMembers: Shape, - private val members: List, - private val writer: SwiftWriter -) : MemberShapeEncodeXMLGenerator(ctx, writer) { - - override fun render() { - writer.addImport(SwiftDependency.SMITHY_XML.target) - val structSymbol = ctx.symbolProvider.toSymbol(shapeContainingMembers) - writer.openBlock( - "static func writingClosure(_ value: \$N?, to writer: \$N) throws {", "}", - structSymbol, - SmithyXMLTypes.Writer - ) { - writer.write( - "guard \$L else { writer.detach(); return }", - "value != nil".takeIf { members.isEmpty() } ?: "let value" - ) - members.sortedBy { it.memberName }.forEach { writeMember(it, false) } - } - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/UnionDecodeXMLGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/UnionDecodeXMLGenerator.kt deleted file mode 100644 index 07740d3f2..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/UnionDecodeXMLGenerator.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.serde.xml - -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.swift.codegen.SmithyReadWriteTypes -import software.amazon.smithy.swift.codegen.SmithyXMLTypes -import software.amazon.smithy.swift.codegen.SwiftDependency -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.serde.MemberShapeDecodeGeneratable - -class UnionDecodeXMLGenerator( - private val ctx: ProtocolGenerator.GenerationContext, - private val shapeContainingMembers: Shape, - private val members: List, - private val writer: SwiftWriter, -) : MemberShapeDecodeGeneratable { - private val memberGenerator = MemberShapeDecodeXMLGenerator(ctx, writer, shapeContainingMembers) - override fun render() { - writer.addImport(SwiftDependency.SMITHY_XML.target) - writer.addImport(SwiftDependency.SMITHY_READ_WRITE.target) - val symbol = ctx.symbolProvider.toSymbol(shapeContainingMembers) - writer.openBlock( - "static var readingClosure: \$N<\$N, \$N> {", "}", - SmithyReadWriteTypes.ReadingClosure, - symbol, - SmithyXMLTypes.Reader, - ) { - writer.openBlock("return { reader in", "}") { - writer.write("guard reader.content != nil else { return nil }") - writer.write("let name = reader.children.first?.nodeInfo.name") - writer.openBlock("switch name {", "}") { - members.forEach { - writer.write("case \$S:", it.memberName) - writer.indent() - memberGenerator.render(it) - writer.dedent() - } - writer.write("default:") - writer.indent() - writer.write("return .sdkUnknown(name ?? \"\")") - writer.dedent() - } - } - } - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/collection/CollectionMemberCodingKey.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/collection/CollectionMemberCodingKey.kt deleted file mode 100644 index 67b6004e1..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/collection/CollectionMemberCodingKey.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.serde.xml.collection - -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.serde.xml.trait.XMLNameTraitGenerator - -class CollectionMemberCodingKey( - val namespace: String, - val memberTagName: String -) { - companion object { - fun construct(memberShape: MemberShape, level: Int = 0): CollectionMemberCodingKey { - val memberTagName = XMLNameTraitGenerator.construct(memberShape, "member").toString() - val namespace = "KeyVal$level" - return CollectionMemberCodingKey(namespace, memberTagName) - } - } - fun renderStructs(writer: SwiftWriter) { - writer.write("struct $namespace{struct $memberTagName{}}") - } - fun keyTag(): String { - return "$namespace.$memberTagName" - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/collection/MapKeyValue.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/collection/MapKeyValue.kt deleted file mode 100644 index 532cb5888..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/collection/MapKeyValue.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.serde.xml.collection - -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.serde.xml.trait.XMLNameTraitGenerator - -class MapKeyValue( - val namespace: String, - val keyTagName: String, - val valueTagName: String -) { - companion object { - fun constructMapKeyValue(keyMemberShape: MemberShape, valueMemberShape: MemberShape, level: Int): MapKeyValue { - val keyTagName = XMLNameTraitGenerator.construct(keyMemberShape, "key").toString() - val valueTagName = XMLNameTraitGenerator.construct(valueMemberShape, "value").toString() - val namespace = "KeyVal$level" - return MapKeyValue(namespace, keyTagName, valueTagName) - } - } - fun renderStructs(writer: SwiftWriter) { - writer.write("struct $namespace{struct $keyTagName{}; struct $valueTagName{}}") - } - fun keyTag(): String { - return "$namespace.$keyTagName" - } - fun valueTag(): String { - return "$namespace.$valueTagName" - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/trait/XMLNameTraitGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/trait/XMLNameTraitGenerator.kt deleted file mode 100644 index 4167edd8a..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/serde/xml/trait/XMLNameTraitGenerator.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.integration.serde.xml.trait - -import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.model.traits.XmlNameTrait -import software.amazon.smithy.swift.codegen.model.getTrait -import software.amazon.smithy.swift.codegen.removeSurroundingBackticks - -class XMLNameTraitGenerator(val xmlNameValue: String) { - companion object { - fun construct(shape: Shape, defaultMemberName: String): XMLNameTraitGenerator { - shape.getTrait()?.let { - return XMLNameTraitGenerator(it.value.toString()) - } - val unquotedDefaultMemberName = defaultMemberName.removeSurroundingBackticks() - return XMLNameTraitGenerator(unquotedDefaultMemberName) - } - } - override fun toString(): String { - return xmlNameValue - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/middleware/MiddlewareExecutionGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/middleware/MiddlewareExecutionGenerator.kt index b39387ddb..947c639aa 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/middleware/MiddlewareExecutionGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/middleware/MiddlewareExecutionGenerator.kt @@ -7,13 +7,10 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.swift.codegen.ClientRuntimeTypes import software.amazon.smithy.swift.codegen.ClientRuntimeTypes.Middleware.OperationStack import software.amazon.smithy.swift.codegen.SwiftWriter +import software.amazon.smithy.swift.codegen.integration.HTTPProtocolCustomizable import software.amazon.smithy.swift.codegen.integration.HttpBindingResolver -import software.amazon.smithy.swift.codegen.integration.HttpProtocolCustomizable import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.middlewares.handlers.MiddlewareShapeUtils -import software.amazon.smithy.swift.codegen.integration.serde.readwrite.WireProtocol -import software.amazon.smithy.swift.codegen.integration.serde.readwrite.requestWireProtocol -import software.amazon.smithy.swift.codegen.integration.serde.readwrite.responseWireProtocol import software.amazon.smithy.swift.codegen.model.toLowerCamelCase import software.amazon.smithy.swift.codegen.model.toUpperCamelCase import software.amazon.smithy.swift.codegen.swiftFunctionParameterIndent @@ -23,7 +20,7 @@ class MiddlewareExecutionGenerator( private val ctx: ProtocolGenerator.GenerationContext, private val writer: SwiftWriter, private val httpBindingResolver: HttpBindingResolver, - private val httpProtocolCustomizable: HttpProtocolCustomizable, + private val httpProtocolCustomizable: HTTPProtocolCustomizable, private val operationMiddleware: OperationMiddleware, private val operationStackName: String, private val httpMethodCallback: HttpMethodCallback? = null @@ -83,12 +80,6 @@ class MiddlewareExecutionGenerator( // FIXME it over indents if i add another indent, come up with better way to properly indent or format for swift - if (ctx.service.requestWireProtocol != WireProtocol.XML) { - writer.write(" .withEncoder(value: encoder)") - } - if (ctx.service.responseWireProtocol != WireProtocol.XML) { - writer.write(" .withDecoder(value: decoder)") - } writer.write(" .withMethod(value: .$httpMethod)") writer.write(" .withServiceName(value: serviceName)") writer.write(" .withOperation(value: \"${op.toLowerCamelCase()}\")") diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/model/SymbolExt.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/model/SymbolExt.kt index 70ab54dd0..6d496a165 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/model/SymbolExt.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/model/SymbolExt.kt @@ -51,14 +51,6 @@ fun Symbol.defaultValue(): String? { return if (default.isPresent) default.get() else null } -fun Symbol.bodySymbol(): Symbol { - return Symbol.builder() - .name("${name}Body") - .putProperty(SymbolProperty.BOXED_KEY, isBoxed()) - .putProperty(SymbolProperty.DEFAULT_VALUE_KEY, defaultValue()) - .build() -} - /** * Mark a symbol as being boxed (nullable) i.e. `T?` */ diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/swiftintegrations/InitialRequestIntegration.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/swiftintegrations/InitialRequestIntegration.kt index f980ed0bd..866eb1279 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/swiftintegrations/InitialRequestIntegration.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/swiftintegrations/InitialRequestIntegration.kt @@ -13,8 +13,10 @@ import software.amazon.smithy.swift.codegen.SwiftSettings import software.amazon.smithy.swift.codegen.core.SwiftCodegenContext import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.SwiftIntegration -import software.amazon.smithy.swift.codegen.integration.serde.readwrite.DocumentWritingClosureUtils +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.NodeInfoUtils import software.amazon.smithy.swift.codegen.integration.serde.readwrite.WritingClosureUtils +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.requestWireProtocol +import software.amazon.smithy.swift.codegen.integration.serde.struct.writerSymbol import software.amazon.smithy.swift.codegen.model.hasTrait class InitialRequestIntegration : SwiftIntegration { @@ -39,15 +41,19 @@ class InitialRequestIntegration : SwiftIntegration { .build() protocolGenerationContext.delegator.useShapeWriter(inputStruct) { writer -> writer.apply { - addImport(SwiftDependency.CLIENT_RUNTIME.target) - openBlock("extension ${symbol.fullName} {", "}") { + addImport(protocolGenerationContext.service.writerSymbol.namespace) + openBlock("extension \$N {", "}", symbol) { + writer.addImport(SwiftDependency.CLIENT_RUNTIME.target) openBlock( - "func makeInitialRequestMessage(encoder: ClientRuntime.RequestEncoder) throws -> EventStream.Message {", + "func makeInitialRequestMessage() throws -> EventStream.Message {", "}" ) { - val documentWritingClosure = DocumentWritingClosureUtils(protocolGenerationContext, writer).closure(it) + val nodeInfoUtils = NodeInfoUtils(protocolGenerationContext, writer, protocolGenerationContext.service.requestWireProtocol) + val rootNodeInfo = nodeInfoUtils.nodeInfo(it, true) val valueWritingClosure = WritingClosureUtils(protocolGenerationContext, writer).writingClosure(it) - write("let initialRequestPayload = try \$L(self, \$L)", documentWritingClosure, valueWritingClosure) + writer.write("let writer = \$N(nodeInfo: \$L)", protocolGenerationContext.service.writerSymbol, rootNodeInfo) + writer.write("try writer.write(self, with: \$L)", valueWritingClosure) + writer.write("let initialRequestPayload = try writer.data()") openBlock( "let initialRequestMessage = EventStream.Message(", ")" diff --git a/smithy-swift-codegen/src/test/kotlin/AuthSchemeResolverGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/AuthSchemeResolverGeneratorTests.kt index a0788b8ce..a7c1b304a 100644 --- a/smithy-swift-codegen/src/test/kotlin/AuthSchemeResolverGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/AuthSchemeResolverGeneratorTests.kt @@ -96,7 +96,7 @@ class AuthSchemeResolverGeneratorTests { } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestJsonProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestJsonProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "Example", "2023-11-02", "Example") } context.generator.initializeMiddleware(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/ContentMd5MiddlewareTests.kt b/smithy-swift-codegen/src/test/kotlin/ContentMd5MiddlewareTests.kt index 96d189bec..0d7493376 100644 --- a/smithy-swift-codegen/src/test/kotlin/ContentMd5MiddlewareTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/ContentMd5MiddlewareTests.kt @@ -7,40 +7,12 @@ class ContentMd5MiddlewareTests { val context = setupTests("Isolated/contentmd5checksum.smithy", "aws.protocoltests.restxml#RestXml") val contents = getFileContents(context.manifest, "/RestXml/RestXmlProtocolClient.swift") val expectedContents = """ - public func idempotencyTokenWithStructure(input: IdempotencyTokenWithStructureInput) async throws -> IdempotencyTokenWithStructureOutput { - let context = ClientRuntime.HttpContextBuilder() - .withMethod(value: .put) - .withServiceName(value: serviceName) - .withOperation(value: "idempotencyTokenWithStructure") - .withIdempotencyTokenGenerator(value: config.idempotencyTokenGenerator) - .withLogger(value: config.logger) - .withPartitionID(value: config.partitionID) - .withAuthSchemes(value: config.authSchemes ?? []) - .withAuthSchemeResolver(value: config.authSchemeResolver) - .withUnsignedPayloadTrait(value: false) - .withSocketTimeout(value: config.httpClientConfiguration.socketTimeout) - .build() - var operation = ClientRuntime.OperationStack(id: "idempotencyTokenWithStructure") - operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.IdempotencyTokenMiddleware(keyPath: \.token)) - operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(IdempotencyTokenWithStructureInput.urlPathProvider(_:))) - operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.ContentMD5Middleware()) - operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) - operation.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/xml")) - operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(documentWritingClosure: SmithyXML.XMLReadWrite.documentWritingClosure(rootNodeInfo: "IdempotencyToken"), inputWritingClosure: IdempotencyTokenWithStructureInput.writingClosure(_:to:))) - operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) - operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(IdempotencyTokenWithStructureOutput.httpBinding, responseDocumentBinding), responseErrorClosure(IdempotencyTokenWithStructureOutputError.httpBinding, responseDocumentBinding))) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) - let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) - return result - } """ contents.shouldContainOnlyOnce(expectedContents) } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.initializeMiddleware(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/EnumGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/EnumGeneratorTests.kt index ca3b0d2af..c5956d941 100644 --- a/smithy-swift-codegen/src/test/kotlin/EnumGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/EnumGeneratorTests.kt @@ -37,41 +37,36 @@ class EnumGeneratorTests { contents.shouldContain(SwiftWriter.GENERATED_FILE_HEADER) - val expectedGeneratedEnum = - """ - /// Really long multi-line Documentation for the enum - public enum MyEnum: Swift.Equatable, Swift.RawRepresentable, Swift.CaseIterable, Swift.Codable, Swift.Hashable { - /// Documentation for BAR - case bar - case fooBazXap - case sdkUnknown(Swift.String) - - public static var allCases: [MyEnum] { - return [ - .bar, - .fooBazXap, - .sdkUnknown("") - ] - } - public init?(rawValue: Swift.String) { - let value = Self.allCases.first(where: { ${'$'}0.rawValue == rawValue }) - self = value ?? Self.sdkUnknown(rawValue) - } - public var rawValue: Swift.String { - switch self { - case .bar: return "BAR" - case .fooBazXap: return "FOO_BAZ@-. XAP - . " - case let .sdkUnknown(s): return s - } - } - public init(from decoder: Swift.Decoder) throws { - let container = try decoder.singleValueContainer() - let rawValue = try container.decode(RawValue.self) - self = MyEnum(rawValue: rawValue) ?? MyEnum.sdkUnknown(rawValue) - } - } - """.trimIndent() + val expectedGeneratedEnum = """ +/// Really long multi-line Documentation for the enum +public enum MyEnum: Swift.Equatable, Swift.RawRepresentable, Swift.CaseIterable, Swift.Hashable { + /// Documentation for BAR + case bar + case fooBazXap + case sdkUnknown(Swift.String) + + public static var allCases: [MyEnum] { + return [ + .bar, + .fooBazXap, + .sdkUnknown("") + ] + } + + public init?(rawValue: Swift.String) { + let value = Self.allCases.first(where: { ${'$'}0.rawValue == rawValue }) + self = value ?? Self.sdkUnknown(rawValue) + } + public var rawValue: Swift.String { + switch self { + case .bar: return "BAR" + case .fooBazXap: return "FOO_BAZ@-. XAP - . " + case let .sdkUnknown(s): return s + } + } +} +""" contents.shouldContain(expectedGeneratedEnum) } @@ -102,41 +97,36 @@ class EnumGeneratorTests { contents.shouldContain(SwiftWriter.GENERATED_FILE_HEADER) - val expectedGeneratedEnum = - """ - /// Really long multi-line Documentation for the enum - public enum MyEnum: Swift.Equatable, Swift.RawRepresentable, Swift.CaseIterable, Swift.Codable, Swift.Hashable { - /// ""${'"'} T2 instances are Burstable Performance Instances that provide a baseline level of CPU performance with the ability to burst above the baseline.""${'"'} - case t2Micro - case t2Nano - case sdkUnknown(Swift.String) - - public static var allCases: [MyEnum] { - return [ - .t2Micro, - .t2Nano, - .sdkUnknown("") - ] - } - public init?(rawValue: Swift.String) { - let value = Self.allCases.first(where: { ${'$'}0.rawValue == rawValue }) - self = value ?? Self.sdkUnknown(rawValue) - } - public var rawValue: Swift.String { - switch self { - case .t2Micro: return "t2.micro" - case .t2Nano: return "t2.nano" - case let .sdkUnknown(s): return s - } - } - public init(from decoder: Swift.Decoder) throws { - let container = try decoder.singleValueContainer() - let rawValue = try container.decode(RawValue.self) - self = MyEnum(rawValue: rawValue) ?? MyEnum.sdkUnknown(rawValue) - } - } - """.trimIndent() + val expectedGeneratedEnum = """ +/// Really long multi-line Documentation for the enum +public enum MyEnum: Swift.Equatable, Swift.RawRepresentable, Swift.CaseIterable, Swift.Hashable { + /// ""${'"'} T2 instances are Burstable Performance Instances that provide a baseline level of CPU performance with the ability to burst above the baseline.""${'"'} + case t2Micro + case t2Nano + case sdkUnknown(Swift.String) + + public static var allCases: [MyEnum] { + return [ + .t2Micro, + .t2Nano, + .sdkUnknown("") + ] + } + + public init?(rawValue: Swift.String) { + let value = Self.allCases.first(where: { ${'$'}0.rawValue == rawValue }) + self = value ?? Self.sdkUnknown(rawValue) + } + public var rawValue: Swift.String { + switch self { + case .t2Micro: return "t2.micro" + case .t2Nano: return "t2.nano" + case let .sdkUnknown(s): return s + } + } +} +""" contents.shouldContain(expectedGeneratedEnum) } @@ -150,47 +140,43 @@ class EnumGeneratorTests { .getFileString("example/models/Suit.swift").get() Assertions.assertNotNull(suitEnumShape) - var expectedGeneratedEnum = - """ - extension ExampleClientTypes { - public enum Suit: Swift.Equatable, Swift.RawRepresentable, Swift.CaseIterable, Swift.Codable, Swift.Hashable { - case club - case diamond - case heart - case spade - case sdkUnknown(Swift.String) - - public static var allCases: [Suit] { - return [ - .club, - .diamond, - .heart, - .spade, - .sdkUnknown("") - ] - } - public init?(rawValue: Swift.String) { - let value = Self.allCases.first(where: { ${'$'}0.rawValue == rawValue }) - self = value ?? Self.sdkUnknown(rawValue) - } - public var rawValue: Swift.String { - switch self { - case .club: return "CLUB" - case .diamond: return "DIAMOND" - case .heart: return "HEART" - case .spade: return "SPADE" - case let .sdkUnknown(s): return s - } - } - public init(from decoder: Swift.Decoder) throws { - let container = try decoder.singleValueContainer() - let rawValue = try container.decode(RawValue.self) - self = Suit(rawValue: rawValue) ?? Suit.sdkUnknown(rawValue) - } - } - } - """.trimIndent() + var expectedGeneratedEnum = """ +extension ExampleClientTypes { + + public enum Suit: Swift.Equatable, Swift.RawRepresentable, Swift.CaseIterable, Swift.Hashable { + case club + case diamond + case heart + case spade + case sdkUnknown(Swift.String) + + public static var allCases: [Suit] { + return [ + .club, + .diamond, + .heart, + .spade, + .sdkUnknown("") + ] + } + public init?(rawValue: Swift.String) { + let value = Self.allCases.first(where: { ${'$'}0.rawValue == rawValue }) + self = value ?? Self.sdkUnknown(rawValue) + } + + public var rawValue: Swift.String { + switch self { + case .club: return "CLUB" + case .diamond: return "DIAMOND" + case .heart: return "HEART" + case .spade: return "SPADE" + case let .sdkUnknown(s): return s + } + } + } +} +""" suitEnumShape.shouldContain(expectedGeneratedEnum) } diff --git a/smithy-swift-codegen/src/test/kotlin/EventStreamsInitialResponseTests.kt b/smithy-swift-codegen/src/test/kotlin/EventStreamsInitialResponseTests.kt index e80af270c..f7255782c 100644 --- a/smithy-swift-codegen/src/test/kotlin/EventStreamsInitialResponseTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/EventStreamsInitialResponseTests.kt @@ -4,9 +4,9 @@ */ import io.kotest.matchers.string.shouldContainOnlyOnce -import mocks.MockHttpAWSJson11ProtocolGenerator +import mocks.MockHTTPAWSJson11ProtocolGenerator import org.junit.jupiter.api.Test -import software.amazon.smithy.swift.codegen.integration.HttpBindingProtocolGenerator +import software.amazon.smithy.swift.codegen.integration.HTTPBindingProtocolGenerator class EventStreamsInitialResponseTests { @Test @@ -14,7 +14,7 @@ class EventStreamsInitialResponseTests { val context = setupInitialMessageTests( "event-stream-initial-request-response.smithy", "com.test#Example", - MockHttpAWSJson11ProtocolGenerator() + MockHTTPAWSJson11ProtocolGenerator() ) val contents = getFileContents( context.manifest, @@ -22,30 +22,24 @@ class EventStreamsInitialResponseTests { ) contents.shouldSyntacticSanityCheck() val expectedContents = """ -extension TestStreamOperationWithInitialRequestResponseOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { - if case let .stream(stream) = httpResponse.body, let responseDecoder = decoder { - let messageDecoder: ClientRuntime.MessageDecoder? = nil - let decoderStream = ClientRuntime.EventStream.DefaultMessageDecoderStream(stream: stream, messageDecoder: messageDecoder, unmarshalClosure: jsonUnmarshalClosure(responseDecoder: responseDecoder)) - self.value = decoderStream.toAsyncStream() +extension TestStreamOperationWithInitialRequestResponseOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> TestStreamOperationWithInitialRequestResponseOutput { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let reader = responseReader + var value = TestStreamOperationWithInitialRequestResponseOutput() + if case let .stream(stream) = httpResponse.body { + let messageDecoder = ClientRuntime.MessageDecoder() + let decoderStream = ClientRuntime.EventStream.DefaultMessageDecoderStream(stream: stream, messageDecoder: messageDecoder, unmarshalClosure: InitialMessageEventStreamsClientTypes.TestStream.unmarshal) + value.value = decoderStream.toAsyncStream() if let initialDataWithoutHttp = await messageDecoder.awaitInitialResponse() { - let decoder = JSONDecoder() - do { - let response = try decoder.decode([String: String].self, from: initialDataWithoutHttp) - self.initial1 = response["initial1"] - self.initial2 = response["initial2"] - } catch { - print("Error decoding JSON: \(error)") - self.initial1 = nil - self.initial2 = nil - } - } else { - self.initial1 = nil - self.initial2 = nil + let payloadReader = try Reader.from(data: initialDataWithoutHttp) + value.initial1 = try payloadReader["initial1"].readIfPresent() + value.initial2 = try payloadReader["initial2"].readIfPresent() } - } else { - self.value = nil } + return value } } """ @@ -55,7 +49,7 @@ extension TestStreamOperationWithInitialRequestResponseOutput: ClientRuntime.Htt private fun setupInitialMessageTests( smithyFile: String, serviceShapeId: String, - protocolGenerator: HttpBindingProtocolGenerator + protocolGenerator: HTTPBindingProtocolGenerator ): TestContext { val context = TestContext.initContextFrom(smithyFile, serviceShapeId, protocolGenerator) { model -> model.defaultSettings(serviceShapeId, "InitialMessageEventStreams", "123", "InitialMessageEventStreams") diff --git a/smithy-swift-codegen/src/test/kotlin/HttpBindingProtocolGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/HTTPBindingProtocolGeneratorTests.kt similarity index 57% rename from smithy-swift-codegen/src/test/kotlin/HttpBindingProtocolGeneratorTests.kt rename to smithy-swift-codegen/src/test/kotlin/HTTPBindingProtocolGeneratorTests.kt index 7e559ea7b..3d9ffc543 100644 --- a/smithy-swift-codegen/src/test/kotlin/HttpBindingProtocolGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/HTTPBindingProtocolGeneratorTests.kt @@ -7,14 +7,11 @@ import io.kotest.matchers.comparables.shouldBeEqualComparingTo import io.kotest.matchers.string.shouldContainOnlyOnce import org.junit.jupiter.api.Test import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ClientProperty -import software.amazon.smithy.swift.codegen.integration.DefaultRequestEncoder -import software.amazon.smithy.swift.codegen.integration.DefaultResponseDecoder import software.amazon.smithy.swift.codegen.integration.DefaultServiceConfig +import software.amazon.smithy.swift.codegen.integration.HTTPProtocolCustomizable import software.amazon.smithy.swift.codegen.integration.HttpBindingResolver import software.amazon.smithy.swift.codegen.integration.HttpProtocolClientGenerator import software.amazon.smithy.swift.codegen.integration.HttpProtocolClientGeneratorFactory -import software.amazon.smithy.swift.codegen.integration.HttpProtocolCustomizable import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.ServiceConfig import software.amazon.smithy.swift.codegen.middleware.OperationMiddleware @@ -27,7 +24,7 @@ class TestHttpProtocolClientGeneratorFactory : HttpProtocolClientGeneratorFactor writer: SwiftWriter, serviceName: String, defaultContentType: String, - httpProtocolCustomizable: HttpProtocolCustomizable, + httpProtocolCustomizable: HTTPProtocolCustomizable, operationMiddleware: OperationMiddleware, ): HttpProtocolClientGenerator { val serviceSymbol = ctx.symbolProvider.toSymbol(ctx.service) @@ -35,20 +32,13 @@ class TestHttpProtocolClientGeneratorFactory : HttpProtocolClientGeneratorFactor return HttpProtocolClientGenerator(ctx, writer, config, httpBindingResolver, defaultContentType, httpProtocolCustomizable, operationMiddleware) } - private fun getClientProperties(ctx: ProtocolGenerator.GenerationContext): List { - return mutableListOf( - DefaultRequestEncoder(), - DefaultResponseDecoder(), - ) - } - private fun getConfigClass(writer: SwiftWriter, serviceName: String): ServiceConfig { return DefaultServiceConfig(writer, serviceName) } } // NOTE: protocol conformance is mostly handled by the protocol tests suite -class HttpBindingProtocolGeneratorTests { +class HTTPBindingProtocolGeneratorTests { private var model = javaClass.getResource("http-binding-protocol-generator-test.smithy").asSmithy() private fun newTestContext(): TestContext { val settings = model.defaultSettings() @@ -68,19 +58,19 @@ class HttpBindingProtocolGeneratorTests { fun `it creates correct init for explicit struct payloads`() { val contents = getModelFileContents("example", "ExplicitStructOutput+HttpResponseBinding.swift", newTestContext.manifest) contents.shouldSyntacticSanityCheck() - val expectedContents = - """ -extension ExplicitStructOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { - if let data = try await httpResponse.body.readData(), let responseDecoder = decoder { - let output: Nested2 = try responseDecoder.decode(responseBody: data) - self.payload1 = output - } else { - self.payload1 = nil - } + val expectedContents = """ +extension ExplicitStructOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> ExplicitStructOutput { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let reader = responseReader + var value = ExplicitStructOutput() + value.payload1 = try reader.readIfPresent(with: Nested2.read(from:)) + return value } } - """.trimIndent() +""" contents.shouldContainOnlyOnce(expectedContents) } @@ -95,14 +85,16 @@ extension ExplicitStructOutput: ClientRuntime.HttpResponseBinding { fun `httpResponseCodeOutput response init content`() { val contents = getModelFileContents("example", "HttpResponseCodeOutput+HttpResponseBinding.swift", newTestContext.manifest) contents.shouldSyntacticSanityCheck() - val expectedContents = - """ -extension HttpResponseCodeOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { - self.status = httpResponse.statusCode.rawValue + val expectedContents = """ +extension HttpResponseCodeOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> HttpResponseCodeOutput { + var value = HttpResponseCodeOutput() + value.status = httpResponse.statusCode.rawValue + return value } } - """.trimIndent() +""" contents.shouldContainOnlyOnce(expectedContents) } @@ -110,49 +102,51 @@ extension HttpResponseCodeOutput: ClientRuntime.HttpResponseBinding { fun `decode the document type in HttpResponseBinding`() { val contents = getModelFileContents("example", "InlineDocumentAsPayloadOutput+HttpResponseBinding.swift", newTestContext.manifest) contents.shouldSyntacticSanityCheck() - val expectedContents = - """ -extension InlineDocumentAsPayloadOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { - if let data = try await httpResponse.body.readData(), let responseDecoder = decoder { - let output: ClientRuntime.Document = try responseDecoder.decode(responseBody: data) - self.documentValue = output - } else { - self.documentValue = nil + val expectedContents = """ +extension InlineDocumentAsPayloadOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> InlineDocumentAsPayloadOutput { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let reader = responseReader + var value = InlineDocumentAsPayloadOutput() + if let data = try await httpResponse.body.readData() { + value.documentValue = try SmithyReadWrite.Document.make(from: data) } + return value } } - """.trimIndent() +""" contents.shouldContainOnlyOnce(expectedContents) } @Test fun `default fooMap to an empty map if keysForFooMap is empty`() { val contents = getModelFileContents("example", "HttpPrefixHeadersOutput+HttpResponseBinding.swift", newTestContext.manifest) - val expectedContents = - """ - extension HttpPrefixHeadersOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { - if let fooHeaderValue = httpResponse.headers.value(for: "X-Foo") { - self.foo = fooHeaderValue - } else { - self.foo = nil - } - let keysForFooMap = httpResponse.headers.dictionary.keys.filter({ ${'$'}0.starts(with: "X-Foo-") }) - if (!keysForFooMap.isEmpty) { - var mapMember = [Swift.String: String]() - for headerKey in keysForFooMap { - let mapMemberValue = httpResponse.headers.dictionary[headerKey]?[0] - let mapMemberKey = headerKey.removePrefix("X-Foo-") - mapMember[mapMemberKey] = mapMemberValue - } - self.fooMap = mapMember - } else { - self.fooMap = [:] - } - } + val expectedContents = """ +extension HttpPrefixHeadersOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> HttpPrefixHeadersOutput { + var value = HttpPrefixHeadersOutput() + if let fooHeaderValue = httpResponse.headers.value(for: "X-Foo") { + value.foo = fooHeaderValue + } + let keysForFooMap = httpResponse.headers.dictionary.keys.filter({ ${'$'}0.starts(with: "X-Foo-") }) + if (!keysForFooMap.isEmpty) { + var mapMember = [Swift.String: String]() + for headerKey in keysForFooMap { + let mapMemberValue = httpResponse.headers.dictionary[headerKey]?[0] + let mapMemberKey = headerKey.removePrefix("X-Foo-") + mapMember[mapMemberKey] = mapMemberValue } - """.trimIndent() + value.fooMap = mapMember + } else { + value.fooMap = [:] + } + return value + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } } diff --git a/smithy-swift-codegen/src/test/kotlin/HttpProtocolClientGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/HttpProtocolClientGeneratorTests.kt index e2da97388..73f7dd1ae 100644 --- a/smithy-swift-codegen/src/test/kotlin/HttpProtocolClientGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/HttpProtocolClientGeneratorTests.kt @@ -14,102 +14,91 @@ class HttpProtocolClientGeneratorTests { val context = setupTests("service-generator-test-operations.smithy", "com.test#Example") val contents = getFileContents(context.manifest, "/RestJson/RestJsonProtocolClient.swift") contents.shouldSyntacticSanityCheck() - contents.shouldContainOnlyOnce( - """ - public class RestJsonProtocolClient: Client { - public static let clientName = "RestJsonProtocolClient" - let client: ClientRuntime.SdkHttpClient - let config: RestJsonProtocolClient.RestJsonProtocolClientConfiguration - let serviceName = "Rest Json Protocol" - let encoder: ClientRuntime.RequestEncoder - let decoder: ClientRuntime.ResponseDecoder - - public required init(config: RestJsonProtocolClient.RestJsonProtocolClientConfiguration) { - client = ClientRuntime.SdkHttpClient(engine: config.httpClientEngine, config: config.httpClientConfiguration) - let encoder = ClientRuntime.JSONEncoder() - encoder.dateEncodingStrategy = .secondsSince1970 - encoder.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - self.encoder = encoder - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - self.decoder = decoder - self.config = config - } - - public convenience required init() throws { - let config = try RestJsonProtocolClient.RestJsonProtocolClientConfiguration() - self.init(config: config) - } - - } - - extension RestJsonProtocolClient { - public class RestJsonProtocolClientConfiguration: DefaultClientConfiguration & DefaultHttpClientConfiguration { - public var telemetryProvider: ClientRuntime.TelemetryProvider - - public var retryStrategyOptions: ClientRuntime.RetryStrategyOptions - - public var clientLogMode: ClientRuntime.ClientLogMode - - public var endpoint: Swift.String? - - public var idempotencyTokenGenerator: ClientRuntime.IdempotencyTokenGenerator - - public var httpClientEngine: ClientRuntime.HTTPClient - - public var httpClientConfiguration: ClientRuntime.HttpClientConfiguration - - public var authSchemes: [ClientRuntime.AuthScheme]? - - public var authSchemeResolver: ClientRuntime.AuthSchemeResolver - - private init(_ telemetryProvider: ClientRuntime.TelemetryProvider, _ retryStrategyOptions: ClientRuntime.RetryStrategyOptions, _ clientLogMode: ClientRuntime.ClientLogMode, _ endpoint: Swift.String?, _ idempotencyTokenGenerator: ClientRuntime.IdempotencyTokenGenerator, _ httpClientEngine: ClientRuntime.HTTPClient, _ httpClientConfiguration: ClientRuntime.HttpClientConfiguration, _ authSchemes: [ClientRuntime.AuthScheme]?, _ authSchemeResolver: ClientRuntime.AuthSchemeResolver) { - self.telemetryProvider = telemetryProvider - self.retryStrategyOptions = retryStrategyOptions - self.clientLogMode = clientLogMode - self.endpoint = endpoint - self.idempotencyTokenGenerator = idempotencyTokenGenerator - self.httpClientEngine = httpClientEngine - self.httpClientConfiguration = httpClientConfiguration - self.authSchemes = authSchemes - self.authSchemeResolver = authSchemeResolver - } - - public convenience init(telemetryProvider: ClientRuntime.TelemetryProvider? = nil, retryStrategyOptions: ClientRuntime.RetryStrategyOptions? = nil, clientLogMode: ClientRuntime.ClientLogMode? = nil, endpoint: Swift.String? = nil, idempotencyTokenGenerator: ClientRuntime.IdempotencyTokenGenerator? = nil, httpClientEngine: ClientRuntime.HTTPClient? = nil, httpClientConfiguration: ClientRuntime.HttpClientConfiguration? = nil, authSchemes: [ClientRuntime.AuthScheme]? = nil, authSchemeResolver: ClientRuntime.AuthSchemeResolver? = nil) throws { - self.init(telemetryProvider ?? ClientRuntime.DefaultTelemetry.provider, retryStrategyOptions ?? ClientConfigurationDefaults.defaultRetryStrategyOptions, clientLogMode ?? ClientConfigurationDefaults.defaultClientLogMode, endpoint, idempotencyTokenGenerator ?? ClientConfigurationDefaults.defaultIdempotencyTokenGenerator, httpClientEngine ?? ClientConfigurationDefaults.makeClient(httpClientConfiguration: httpClientConfiguration ?? ClientConfigurationDefaults.defaultHttpClientConfiguration), httpClientConfiguration ?? ClientConfigurationDefaults.defaultHttpClientConfiguration, authSchemes, authSchemeResolver ?? ClientConfigurationDefaults.defaultAuthSchemeResolver) - } - - public convenience required init() async throws { - try await self.init(telemetryProvider: nil, retryStrategyOptions: nil, clientLogMode: nil, endpoint: nil, idempotencyTokenGenerator: nil, httpClientEngine: nil, httpClientConfiguration: nil, authSchemes: nil, authSchemeResolver: nil) - } - - public var partitionID: String? { - return "" - } - } - - public static func builder() -> ClientBuilder { - return ClientBuilder(defaultPlugins: [ - ClientRuntime.DefaultClientPlugin() - ]) - } - } - - public struct RestJsonProtocolClientLogHandlerFactory: ClientRuntime.SDKLogHandlerFactory { - public var label = "RestJsonProtocolClient" - let logLevel: ClientRuntime.SDKLogLevel - public func construct(label: String) -> LogHandler { - var handler = StreamLogHandler.standardOutput(label: label) - handler.logLevel = logLevel.toLoggerType() - return handler - } - public init(logLevel: ClientRuntime.SDKLogLevel) { - self.logLevel = logLevel - } - } - """.trimIndent() - ) + val expected = """ +public class RestJsonProtocolClient: Client { + public static let clientName = "RestJsonProtocolClient" + let client: ClientRuntime.SdkHttpClient + let config: RestJsonProtocolClient.RestJsonProtocolClientConfiguration + let serviceName = "Rest Json Protocol" + + public required init(config: RestJsonProtocolClient.RestJsonProtocolClientConfiguration) { + client = ClientRuntime.SdkHttpClient(engine: config.httpClientEngine, config: config.httpClientConfiguration) + self.config = config + } + + public convenience required init() throws { + let config = try RestJsonProtocolClient.RestJsonProtocolClientConfiguration() + self.init(config: config) + } + +} + +extension RestJsonProtocolClient { + public class RestJsonProtocolClientConfiguration: DefaultClientConfiguration & DefaultHttpClientConfiguration { + public var telemetryProvider: ClientRuntime.TelemetryProvider + + public var retryStrategyOptions: ClientRuntime.RetryStrategyOptions + + public var clientLogMode: ClientRuntime.ClientLogMode + + public var endpoint: Swift.String? + + public var idempotencyTokenGenerator: ClientRuntime.IdempotencyTokenGenerator + + public var httpClientEngine: ClientRuntime.HTTPClient + + public var httpClientConfiguration: ClientRuntime.HttpClientConfiguration + + public var authSchemes: [ClientRuntime.AuthScheme]? + + public var authSchemeResolver: ClientRuntime.AuthSchemeResolver + + private init(_ telemetryProvider: ClientRuntime.TelemetryProvider, _ retryStrategyOptions: ClientRuntime.RetryStrategyOptions, _ clientLogMode: ClientRuntime.ClientLogMode, _ endpoint: Swift.String?, _ idempotencyTokenGenerator: ClientRuntime.IdempotencyTokenGenerator, _ httpClientEngine: ClientRuntime.HTTPClient, _ httpClientConfiguration: ClientRuntime.HttpClientConfiguration, _ authSchemes: [ClientRuntime.AuthScheme]?, _ authSchemeResolver: ClientRuntime.AuthSchemeResolver) { + self.telemetryProvider = telemetryProvider + self.retryStrategyOptions = retryStrategyOptions + self.clientLogMode = clientLogMode + self.endpoint = endpoint + self.idempotencyTokenGenerator = idempotencyTokenGenerator + self.httpClientEngine = httpClientEngine + self.httpClientConfiguration = httpClientConfiguration + self.authSchemes = authSchemes + self.authSchemeResolver = authSchemeResolver + } + + public convenience init(telemetryProvider: ClientRuntime.TelemetryProvider? = nil, retryStrategyOptions: ClientRuntime.RetryStrategyOptions? = nil, clientLogMode: ClientRuntime.ClientLogMode? = nil, endpoint: Swift.String? = nil, idempotencyTokenGenerator: ClientRuntime.IdempotencyTokenGenerator? = nil, httpClientEngine: ClientRuntime.HTTPClient? = nil, httpClientConfiguration: ClientRuntime.HttpClientConfiguration? = nil, authSchemes: [ClientRuntime.AuthScheme]? = nil, authSchemeResolver: ClientRuntime.AuthSchemeResolver? = nil) throws { + self.init(telemetryProvider ?? ClientRuntime.DefaultTelemetry.provider, retryStrategyOptions ?? ClientConfigurationDefaults.defaultRetryStrategyOptions, clientLogMode ?? ClientConfigurationDefaults.defaultClientLogMode, endpoint, idempotencyTokenGenerator ?? ClientConfigurationDefaults.defaultIdempotencyTokenGenerator, httpClientEngine ?? ClientConfigurationDefaults.makeClient(httpClientConfiguration: httpClientConfiguration ?? ClientConfigurationDefaults.defaultHttpClientConfiguration), httpClientConfiguration ?? ClientConfigurationDefaults.defaultHttpClientConfiguration, authSchemes, authSchemeResolver ?? ClientConfigurationDefaults.defaultAuthSchemeResolver) + } + + public convenience required init() async throws { + try await self.init(telemetryProvider: nil, retryStrategyOptions: nil, clientLogMode: nil, endpoint: nil, idempotencyTokenGenerator: nil, httpClientEngine: nil, httpClientConfiguration: nil, authSchemes: nil, authSchemeResolver: nil) + } + + public var partitionID: String? { + return "" + } + } + + public static func builder() -> ClientBuilder { + return ClientBuilder(defaultPlugins: [ + ClientRuntime.DefaultClientPlugin() + ]) + } +} + +public struct RestJsonProtocolClientLogHandlerFactory: ClientRuntime.SDKLogHandlerFactory { + public var label = "RestJsonProtocolClient" + let logLevel: ClientRuntime.SDKLogLevel + public func construct(label: String) -> LogHandler { + var handler = StreamLogHandler.standardOutput(label: label) + handler.logLevel = logLevel.toLoggerType() + return handler + } + public init(logLevel: ClientRuntime.SDKLogLevel) { + self.logLevel = logLevel + } +} +""" + contents.shouldContainOnlyOnce(expected) } @Test fun `it renders host prefix with label in context correctly`() { @@ -118,14 +107,18 @@ class HttpProtocolClientGeneratorTests { contents.shouldSyntacticSanityCheck() val expectedFragment = """ let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .post) .withServiceName(value: serviceName) .withOperation(value: "getStatus") .withIdempotencyTokenGenerator(value: config.idempotencyTokenGenerator) .withLogger(value: config.logger) - """ + .withPartitionID(value: config.partitionID) + .withAuthSchemes(value: config.authSchemes ?? []) + .withAuthSchemeResolver(value: config.authSchemeResolver) + .withUnsignedPayloadTrait(value: false) + .withSocketTimeout(value: config.httpClientConfiguration.socketTimeout) + .build() +""" contents.shouldContainOnlyOnce(expectedFragment) } @Test @@ -143,8 +136,6 @@ class HttpProtocolClientGeneratorTests { val expected = """ public func allocateWidget(input: AllocateWidgetInput) async throws -> AllocateWidgetOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .post) .withServiceName(value: serviceName) .withOperation(value: "allocateWidget") @@ -162,11 +153,11 @@ class HttpProtocolClientGeneratorTests { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) operation.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/json")) - operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(documentWritingClosure: ClientRuntime.JSONReadWrite.documentWritingClosure(encoder: encoder), inputWritingClosure: JSONReadWrite.writingClosure())) + operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(rootNodeInfo: "", inputWritingClosure: AllocateWidgetInput.write(value:to:))) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(AllocateWidgetOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(AllocateWidgetOutput.httpOutput(from:), AllocateWidgetOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -183,8 +174,6 @@ class HttpProtocolClientGeneratorTests { val expected = """ public func unsignedFooBlobStream(input: UnsignedFooBlobStreamInput) async throws -> UnsignedFooBlobStreamOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .post) .withServiceName(value: serviceName) .withOperation(value: "unsignedFooBlobStream") @@ -201,11 +190,11 @@ class HttpProtocolClientGeneratorTests { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) operation.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/json")) - operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(documentWritingClosure: ClientRuntime.JSONReadWrite.documentWritingClosure(encoder: encoder), inputWritingClosure: JSONReadWrite.writingClosure())) + operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(rootNodeInfo: "", inputWritingClosure: UnsignedFooBlobStreamInput.write(value:to:))) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware(requiresLength: false, unsignedPayload: true)) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(UnsignedFooBlobStreamOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(UnsignedFooBlobStreamOutput.httpOutput(from:), UnsignedFooBlobStreamOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -222,8 +211,6 @@ class HttpProtocolClientGeneratorTests { val expected = """ public func explicitBlobStreamWithLength(input: ExplicitBlobStreamWithLengthInput) async throws -> ExplicitBlobStreamWithLengthOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .post) .withServiceName(value: serviceName) .withOperation(value: "explicitBlobStreamWithLength") @@ -244,7 +231,7 @@ class HttpProtocolClientGeneratorTests { operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware(requiresLength: true, unsignedPayload: false)) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(ExplicitBlobStreamWithLengthOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(ExplicitBlobStreamWithLengthOutput.httpOutput(from:), ExplicitBlobStreamWithLengthOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -261,8 +248,6 @@ class HttpProtocolClientGeneratorTests { val expected = """ public func unsignedFooBlobStreamWithLength(input: UnsignedFooBlobStreamWithLengthInput) async throws -> UnsignedFooBlobStreamWithLengthOutput { let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .post) .withServiceName(value: serviceName) .withOperation(value: "unsignedFooBlobStreamWithLength") @@ -283,7 +268,7 @@ class HttpProtocolClientGeneratorTests { operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware(requiresLength: true, unsignedPayload: true)) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(UnsignedFooBlobStreamWithLengthOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(UnsignedFooBlobStreamWithLengthOutput.httpOutput(from:), UnsignedFooBlobStreamWithLengthOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result @@ -295,7 +280,7 @@ class HttpProtocolClientGeneratorTests { val context = TestContext.initContextFrom( listOf(smithyFile), serviceShapeId, - MockHttpRestJsonProtocolGenerator(), + MockHTTPRestJsonProtocolGenerator(), { model -> model.defaultSettings(serviceShapeId, "RestJson", "2019-12-16", "Rest Json Protocol") }, listOf(DefaultClientConfigurationIntegration()) ) diff --git a/smithy-swift-codegen/src/test/kotlin/HttpProtocolUnitTestErrorGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/HttpProtocolUnitTestErrorGeneratorTests.kt index 5d692e829..4828195ce 100644 --- a/smithy-swift-codegen/src/test/kotlin/HttpProtocolUnitTestErrorGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/HttpProtocolUnitTestErrorGeneratorTests.kt @@ -13,8 +13,6 @@ class HttpProtocolUnitTestErrorGeneratorTests : HttpProtocolUnitTestResponseGene val contents = getTestFileContents("example", "GreetingWithErrorsErrorTest.swift", ctx.manifest) contents.shouldSyntacticSanityCheck() val expectedContents = """ -class GreetingWithErrorsComplexErrorTest: HttpResponseTestBase { - /// Serializes a complex error with no message member func testRestJsonComplexErrorWithNoMessage() async throws { do { guard let httpResponse = buildHttpResponse( @@ -37,10 +35,7 @@ class GreetingWithErrorsComplexErrorTest: HttpResponseTestBase { return } - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let greetingWithErrorsOutputError = try await responseErrorClosure(GreetingWithErrorsOutputError.self, decoder: decoder)(httpResponse) + let greetingWithErrorsOutputError = try await GreetingWithErrorsOutputError.httpError(from:)(httpResponse) if let actual = greetingWithErrorsOutputError as? ComplexError { @@ -61,7 +56,6 @@ class GreetingWithErrorsComplexErrorTest: HttpResponseTestBase { XCTFail(error.localizedDescription) } } -} """ contents.shouldContainOnlyOnce(expectedContents) } @@ -71,8 +65,6 @@ class GreetingWithErrorsComplexErrorTest: HttpResponseTestBase { val contents = getTestFileContents("example", "GreetingWithErrorsErrorTest.swift", ctx.manifest) contents.shouldSyntacticSanityCheck() val expectedContents = """ -class GreetingWithErrorsComplexErrorTest: HttpResponseTestBase { - /// Serializes a complex error with no message member func testRestJsonComplexErrorWithNoMessage() async throws { do { guard let httpResponse = buildHttpResponse( @@ -95,10 +87,7 @@ class GreetingWithErrorsComplexErrorTest: HttpResponseTestBase { return } - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let greetingWithErrorsOutputError = try await responseErrorClosure(GreetingWithErrorsOutputError.self, decoder: decoder)(httpResponse) + let greetingWithErrorsOutputError = try await GreetingWithErrorsOutputError.httpError(from:)(httpResponse) if let actual = greetingWithErrorsOutputError as? ComplexError { @@ -119,7 +108,6 @@ class GreetingWithErrorsComplexErrorTest: HttpResponseTestBase { XCTFail(error.localizedDescription) } } -} """ contents.shouldContainOnlyOnce(expectedContents) } diff --git a/smithy-swift-codegen/src/test/kotlin/HttpProtocolUnitTestRequestGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/HttpProtocolUnitTestRequestGeneratorTests.kt index 66c09db5c..e7ac82e4a 100644 --- a/smithy-swift-codegen/src/test/kotlin/HttpProtocolUnitTestRequestGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/HttpProtocolUnitTestRequestGeneratorTests.kt @@ -58,10 +58,6 @@ class HttpProtocolUnitTestRequestGeneratorTests { resolvedHost: "" ) - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let input = SmokeTestInput( header1: "Foo", header2: "Bar", @@ -74,11 +70,7 @@ class HttpProtocolUnitTestRequestGeneratorTests { ), query1: "Query 1" ) - let encoder = ClientRuntime.JSONEncoder() - encoder.dateEncodingStrategy = .secondsSince1970 - encoder.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") let context = HttpContextBuilder() - .withEncoder(value: encoder) .withMethod(value: .post) .build() var operationStack = OperationStack(id: "SmokeTest") @@ -94,13 +86,13 @@ class HttpProtocolUnitTestRequestGeneratorTests { operationStack.serializeStep.intercept(position: .after, middleware: ClientRuntime.HeaderMiddleware(SmokeTestInput.headerProvider(_:))) operationStack.serializeStep.intercept(position: .after, middleware: ClientRuntime.QueryItemMiddleware(SmokeTestInput.queryItemProvider(_:))) operationStack.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/json")) - operationStack.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(documentWritingClosure: ClientRuntime.JSONReadWrite.documentWritingClosure(encoder: encoder), inputWritingClosure: JSONReadWrite.writingClosure())) + operationStack.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(rootNodeInfo: "", inputWritingClosure: SmokeTestInput.write(value:to:))) operationStack.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) operationStack.deserializeStep.intercept( position: .after, middleware: MockDeserializeMiddleware( id: "TestDeserializeMiddleware", - responseClosure: responseClosure(decoder: decoder), + responseClosure: SmokeTestOutput.httpOutput(from:), callback: { context, actual in try await self.assertEqual(expected, actual, { (expectedHttpBody, actualHttpBody) -> Void in XCTAssertNotNil(actualHttpBody, "The actual ByteStream is nil") @@ -143,18 +135,10 @@ class HttpProtocolUnitTestRequestGeneratorTests { resolvedHost: "" ) - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let input = ExplicitStringInput( payload1: "explicit string" ) - let encoder = ClientRuntime.JSONEncoder() - encoder.dateEncodingStrategy = .secondsSince1970 - encoder.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") let context = HttpContextBuilder() - .withEncoder(value: encoder) .withMethod(value: .post) .build() var operationStack = OperationStack(id: "ExplicitString") @@ -174,7 +158,7 @@ class HttpProtocolUnitTestRequestGeneratorTests { position: .after, middleware: MockDeserializeMiddleware( id: "TestDeserializeMiddleware", - responseClosure: responseClosure(decoder: decoder), + responseClosure: ExplicitStringOutput.httpOutput(from:), callback: { context, actual in try await self.assertEqual(expected, actual, { (expectedHttpBody, actualHttpBody) -> Void in XCTAssertNotNil(actualHttpBody, "The actual ByteStream is nil") @@ -210,17 +194,9 @@ class HttpProtocolUnitTestRequestGeneratorTests { resolvedHost: "" ) - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let input = EmptyInputAndEmptyOutputInput( ) - let encoder = ClientRuntime.JSONEncoder() - encoder.dateEncodingStrategy = .secondsSince1970 - encoder.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") let context = HttpContextBuilder() - .withEncoder(value: encoder) .withMethod(value: .post) .build() var operationStack = OperationStack(id: "RestJsonEmptyInputAndEmptyOutput") @@ -237,7 +213,7 @@ class HttpProtocolUnitTestRequestGeneratorTests { position: .after, middleware: MockDeserializeMiddleware( id: "TestDeserializeMiddleware", - responseClosure: responseClosure(decoder: decoder), + responseClosure: EmptyInputAndEmptyOutputOutput.httpOutput(from:), callback: { context, actual in try await self.assertEqual(expected, actual) return OperationOutput(httpResponse: HttpResponse(body: ByteStream.noStream, statusCode: .ok), output: EmptyInputAndEmptyOutputOutput()) @@ -274,18 +250,10 @@ class HttpProtocolUnitTestRequestGeneratorTests { resolvedHost: "" ) - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let input = SimpleScalarPropertiesInput( stringValue: nil ) - let encoder = ClientRuntime.JSONEncoder() - encoder.dateEncodingStrategy = .secondsSince1970 - encoder.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") let context = HttpContextBuilder() - .withEncoder(value: encoder) .withMethod(value: .put) .build() var operationStack = OperationStack(id: "RestJsonDoesntSerializeNullStructureValues") @@ -300,13 +268,13 @@ class HttpProtocolUnitTestRequestGeneratorTests { } operationStack.serializeStep.intercept(position: .after, middleware: ClientRuntime.HeaderMiddleware(SimpleScalarPropertiesInput.headerProvider(_:))) operationStack.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/json")) - operationStack.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(documentWritingClosure: ClientRuntime.JSONReadWrite.documentWritingClosure(encoder: encoder), inputWritingClosure: JSONReadWrite.writingClosure())) + operationStack.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(rootNodeInfo: "", inputWritingClosure: SimpleScalarPropertiesInput.write(value:to:))) operationStack.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) operationStack.deserializeStep.intercept( position: .after, middleware: MockDeserializeMiddleware( id: "TestDeserializeMiddleware", - responseClosure: responseClosure(decoder: decoder), + responseClosure: SimpleScalarPropertiesOutput.httpOutput(from:), callback: { context, actual in try await self.assertEqual(expected, actual, { (expectedHttpBody, actualHttpBody) -> Void in XCTAssertNotNil(actualHttpBody, "The actual ByteStream is nil") @@ -348,19 +316,11 @@ class HttpProtocolUnitTestRequestGeneratorTests { resolvedHost: "" ) - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let input = StreamingTraitsInput( blob: .stream(BufferedStream(data: "blobby blob blob".data(using: .utf8)!, isClosed: true)), foo: "Foo" ) - let encoder = ClientRuntime.JSONEncoder() - encoder.dateEncodingStrategy = .secondsSince1970 - encoder.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") let context = HttpContextBuilder() - .withEncoder(value: encoder) .withMethod(value: .post) .build() var operationStack = OperationStack(id: "RestJsonStreamingTraitsWithBlob") @@ -381,7 +341,7 @@ class HttpProtocolUnitTestRequestGeneratorTests { position: .after, middleware: MockDeserializeMiddleware( id: "TestDeserializeMiddleware", - responseClosure: responseClosure(decoder: decoder), + responseClosure: StreamingTraitsOutput.httpOutput(from:), callback: { context, actual in try await self.assertEqual(expected, actual, { (expectedHttpBody, actualHttpBody) -> Void in XCTAssertNotNil(actualHttpBody, "The actual ByteStream is nil") @@ -420,20 +380,12 @@ class HttpProtocolUnitTestRequestGeneratorTests { resolvedHost: "" ) - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let input = HttpPrefixHeadersInput( foo: "Foo", fooMap: [:] ) - let encoder = ClientRuntime.JSONEncoder() - encoder.dateEncodingStrategy = .secondsSince1970 - encoder.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") let context = HttpContextBuilder() - .withEncoder(value: encoder) .withMethod(value: .get) .build() var operationStack = OperationStack(id: "RestJsonHttpPrefixHeadersAreNotPresent") @@ -451,7 +403,7 @@ class HttpProtocolUnitTestRequestGeneratorTests { position: .after, middleware: MockDeserializeMiddleware( id: "TestDeserializeMiddleware", - responseClosure: responseClosure(decoder: decoder), + responseClosure: HttpPrefixHeadersOutput.httpOutput(from:), callback: { context, actual in try await self.assertEqual(expected, actual) return OperationOutput(httpResponse: HttpResponse(body: ByteStream.noStream, statusCode: .ok), output: HttpPrefixHeadersOutput()) @@ -492,19 +444,11 @@ class HttpProtocolUnitTestRequestGeneratorTests { resolvedHost: "" ) - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let input = JsonUnionsInput( contents: MyUnion.stringvalue("foo") ) - let encoder = ClientRuntime.JSONEncoder() - encoder.dateEncodingStrategy = .secondsSince1970 - encoder.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") let context = HttpContextBuilder() - .withEncoder(value: encoder) .withMethod(value: .put) .build() var operationStack = OperationStack(id: "RestJsonSerializeStringUnionValue") @@ -518,13 +462,13 @@ class HttpProtocolUnitTestRequestGeneratorTests { return try await next.handle(context: context, input: input) } operationStack.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/json")) - operationStack.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(documentWritingClosure: ClientRuntime.JSONReadWrite.documentWritingClosure(encoder: encoder), inputWritingClosure: JSONReadWrite.writingClosure())) + operationStack.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(rootNodeInfo: "", inputWritingClosure: JsonUnionsInput.write(value:to:))) operationStack.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) operationStack.deserializeStep.intercept( position: .after, middleware: MockDeserializeMiddleware( id: "TestDeserializeMiddleware", - responseClosure: responseClosure(decoder: decoder), + responseClosure: JsonUnionsOutput.httpOutput(from:), callback: { context, actual in try await self.assertEqual(expected, actual, { (expectedHttpBody, actualHttpBody) -> Void in XCTAssertNotNil(actualHttpBody, "The actual ByteStream is nil") @@ -578,10 +522,6 @@ class HttpProtocolUnitTestRequestGeneratorTests { resolvedHost: "" ) - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let input = RecursiveShapesInput( nested: RecursiveShapesInputOutputNested1( foo: "Foo1", @@ -596,11 +536,7 @@ class HttpProtocolUnitTestRequestGeneratorTests { ) ) ) - let encoder = ClientRuntime.JSONEncoder() - encoder.dateEncodingStrategy = .secondsSince1970 - encoder.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") let context = HttpContextBuilder() - .withEncoder(value: encoder) .withMethod(value: .put) .build() var operationStack = OperationStack(id: "RestJsonRecursiveShapes") @@ -614,13 +550,13 @@ class HttpProtocolUnitTestRequestGeneratorTests { return try await next.handle(context: context, input: input) } operationStack.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/json")) - operationStack.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(documentWritingClosure: ClientRuntime.JSONReadWrite.documentWritingClosure(encoder: encoder), inputWritingClosure: JSONReadWrite.writingClosure())) + operationStack.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(rootNodeInfo: "", inputWritingClosure: RecursiveShapesInput.write(value:to:))) operationStack.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) operationStack.deserializeStep.intercept( position: .after, middleware: MockDeserializeMiddleware( id: "TestDeserializeMiddleware", - responseClosure: responseClosure(decoder: decoder), + responseClosure: RecursiveShapesOutput.httpOutput(from:), callback: { context, actual in try await self.assertEqual(expected, actual, { (expectedHttpBody, actualHttpBody) -> Void in XCTAssertNotNil(actualHttpBody, "The actual ByteStream is nil") @@ -666,60 +602,51 @@ class HttpProtocolUnitTestRequestGeneratorTests { resolvedHost: "" ) - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let input = InlineDocumentInput( - documentValue: try decoder.decode(Document.self, from: - ""${'"'} + documentValue: try SmithyReadWrite.Document.make(from: Data(""${'"'} { "foo": "bar" } - ""${'"'}.data(using: .utf8)!) - , - stringValue: "string" - ) - let encoder = ClientRuntime.JSONEncoder() - encoder.dateEncodingStrategy = .secondsSince1970 - encoder.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let context = HttpContextBuilder() - .withEncoder(value: encoder) - .withMethod(value: .put) - .build() - var operationStack = OperationStack(id: "InlineDocumentInput") - operationStack.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(urlPrefix: urlPrefix, InlineDocumentInput.urlPathProvider(_:))) - operationStack.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware(host: hostOnly)) - operationStack.buildStep.intercept(position: .after, id: "RequestTestEndpointResolver") { (context, input, next) -> ClientRuntime.OperationOutput in - input.withMethod(context.getMethod()) - input.withPath(context.getPath()) - let host = "\(context.getHostPrefix() ?? "")\(context.getHost() ?? "")" - input.withHost(host) - return try await next.handle(context: context, input: input) - } - operationStack.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/json")) - operationStack.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(documentWritingClosure: ClientRuntime.JSONReadWrite.documentWritingClosure(encoder: encoder), inputWritingClosure: JSONReadWrite.writingClosure())) - operationStack.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) - operationStack.deserializeStep.intercept( - position: .after, - middleware: MockDeserializeMiddleware( - id: "TestDeserializeMiddleware", - responseClosure: responseClosure(decoder: decoder), - callback: { context, actual in - try await self.assertEqual(expected, actual, { (expectedHttpBody, actualHttpBody) -> Void in - XCTAssertNotNil(actualHttpBody, "The actual ByteStream is nil") - XCTAssertNotNil(expectedHttpBody, "The expected ByteStream is nil") - try await self.genericAssertEqualHttpBodyData(expected: expectedHttpBody!, actual: actualHttpBody!, contentType: .json) - }) - return OperationOutput(httpResponse: HttpResponse(body: ByteStream.noStream, statusCode: .ok), output: InlineDocumentOutput()) - } - ) - ) - _ = try await operationStack.handleMiddleware(context: context, input: input, next: MockHandler() { (context, request) in - XCTFail("Deserialize was mocked out, this should fail") - throw SmithyTestUtilError("Mock handler unexpectedly failed") - }) + ""${'"'}.utf8)) + , + stringValue: "string" + ) + let context = HttpContextBuilder() + .withMethod(value: .put) + .build() + var operationStack = OperationStack(id: "InlineDocumentInput") + operationStack.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(urlPrefix: urlPrefix, InlineDocumentInput.urlPathProvider(_:))) + operationStack.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware(host: hostOnly)) + operationStack.buildStep.intercept(position: .after, id: "RequestTestEndpointResolver") { (context, input, next) -> ClientRuntime.OperationOutput in + input.withMethod(context.getMethod()) + input.withPath(context.getPath()) + let host = "\(context.getHostPrefix() ?? "")\(context.getHost() ?? "")" + input.withHost(host) + return try await next.handle(context: context, input: input) } + operationStack.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/json")) + operationStack.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(rootNodeInfo: "", inputWritingClosure: InlineDocumentInput.write(value:to:))) + operationStack.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) + operationStack.deserializeStep.intercept( + position: .after, + middleware: MockDeserializeMiddleware( + id: "TestDeserializeMiddleware", + responseClosure: InlineDocumentOutput.httpOutput(from:), + callback: { context, actual in + try await self.assertEqual(expected, actual, { (expectedHttpBody, actualHttpBody) -> Void in + XCTAssertNotNil(actualHttpBody, "The actual ByteStream is nil") + XCTAssertNotNil(expectedHttpBody, "The expected ByteStream is nil") + try await self.genericAssertEqualHttpBodyData(expected: expectedHttpBody!, actual: actualHttpBody!, contentType: .json) + }) + return OperationOutput(httpResponse: HttpResponse(body: ByteStream.noStream, statusCode: .ok), output: InlineDocumentOutput()) + } + ) + ) + _ = try await operationStack.handleMiddleware(context: context, input: input, next: MockHandler() { (context, request) in + XCTFail("Deserialize was mocked out, this should fail") + throw SmithyTestUtilError("Mock handler unexpectedly failed") + }) + } """ contents.shouldContainOnlyOnce(expectedContents) } @@ -747,59 +674,49 @@ class HttpProtocolUnitTestRequestGeneratorTests { resolvedHost: "" ) - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let input = InlineDocumentAsPayloadInput( - documentValue: try decoder.decode(Document.self, from: - ""${'"'} + documentValue: try SmithyReadWrite.Document.make(from: Data(""${'"'} { "foo": "bar" } - ""${'"'}.data(using: .utf8)!) + ""${'"'}.utf8)) - ) - let encoder = ClientRuntime.JSONEncoder() - encoder.dateEncodingStrategy = .secondsSince1970 - encoder.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let context = HttpContextBuilder() - .withEncoder(value: encoder) - .withMethod(value: .put) - .build() - var operationStack = OperationStack(id: "InlineDocumentAsPayloadInput") - operationStack.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(urlPrefix: urlPrefix, InlineDocumentAsPayloadInput.urlPathProvider(_:))) - operationStack.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware(host: hostOnly)) - operationStack.buildStep.intercept(position: .after, id: "RequestTestEndpointResolver") { (context, input, next) -> ClientRuntime.OperationOutput in - input.withMethod(context.getMethod()) - input.withPath(context.getPath()) - let host = "\(context.getHostPrefix() ?? "")\(context.getHost() ?? "")" - input.withHost(host) - return try await next.handle(context: context, input: input) - } - operationStack.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/json")) - operationStack.serializeStep.intercept(position: .after, middleware: ClientRuntime.PayloadBodyMiddleware(documentWritingClosure: ClientRuntime.JSONReadWrite.documentWritingClosure(encoder: encoder), inputWritingClosure: JSONReadWrite.writingClosure(), keyPath: \.documentValue, defaultBody: "{}")) - operationStack.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) - operationStack.deserializeStep.intercept( - position: .after, - middleware: MockDeserializeMiddleware( - id: "TestDeserializeMiddleware", - responseClosure: responseClosure(decoder: decoder), - callback: { context, actual in - try await self.assertEqual(expected, actual, { (expectedHttpBody, actualHttpBody) -> Void in - XCTAssertNotNil(actualHttpBody, "The actual ByteStream is nil") - XCTAssertNotNil(expectedHttpBody, "The expected ByteStream is nil") - try await self.genericAssertEqualHttpBodyData(expected: expectedHttpBody!, actual: actualHttpBody!, contentType: .json) - }) - return OperationOutput(httpResponse: HttpResponse(body: ByteStream.noStream, statusCode: .ok), output: InlineDocumentAsPayloadOutput()) - } - ) - ) - _ = try await operationStack.handleMiddleware(context: context, input: input, next: MockHandler() { (context, request) in - XCTFail("Deserialize was mocked out, this should fail") - throw SmithyTestUtilError("Mock handler unexpectedly failed") - }) + ) + let context = HttpContextBuilder() + .withMethod(value: .put) + .build() + var operationStack = OperationStack(id: "InlineDocumentAsPayloadInput") + operationStack.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(urlPrefix: urlPrefix, InlineDocumentAsPayloadInput.urlPathProvider(_:))) + operationStack.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware(host: hostOnly)) + operationStack.buildStep.intercept(position: .after, id: "RequestTestEndpointResolver") { (context, input, next) -> ClientRuntime.OperationOutput in + input.withMethod(context.getMethod()) + input.withPath(context.getPath()) + let host = "\(context.getHostPrefix() ?? "")\(context.getHost() ?? "")" + input.withHost(host) + return try await next.handle(context: context, input: input) } + operationStack.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/json")) + operationStack.serializeStep.intercept(position: .after, middleware: ClientRuntime.PayloadBodyMiddleware(rootNodeInfo: "", inputWritingClosure: SmithyReadWrite.Document.write(value:to:), keyPath: \.documentValue, defaultBody: "{}")) + operationStack.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) + operationStack.deserializeStep.intercept( + position: .after, + middleware: MockDeserializeMiddleware( + id: "TestDeserializeMiddleware", + responseClosure: InlineDocumentAsPayloadOutput.httpOutput(from:), + callback: { context, actual in + try await self.assertEqual(expected, actual, { (expectedHttpBody, actualHttpBody) -> Void in + XCTAssertNotNil(actualHttpBody, "The actual ByteStream is nil") + XCTAssertNotNil(expectedHttpBody, "The expected ByteStream is nil") + try await self.genericAssertEqualHttpBodyData(expected: expectedHttpBody!, actual: actualHttpBody!, contentType: .json) + }) + return OperationOutput(httpResponse: HttpResponse(body: ByteStream.noStream, statusCode: .ok), output: InlineDocumentAsPayloadOutput()) + } + ) + ) + _ = try await operationStack.handleMiddleware(context: context, input: input, next: MockHandler() { (context, request) in + XCTFail("Deserialize was mocked out, this should fail") + throw SmithyTestUtilError("Mock handler unexpectedly failed") + }) } """ contents.shouldContainOnlyOnce(expectedContents) diff --git a/smithy-swift-codegen/src/test/kotlin/HttpProtocolUnitTestResponseGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/HttpProtocolUnitTestResponseGeneratorTests.kt index 8759e775a..13c1c85b6 100644 --- a/smithy-swift-codegen/src/test/kotlin/HttpProtocolUnitTestResponseGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/HttpProtocolUnitTestResponseGeneratorTests.kt @@ -51,10 +51,7 @@ open class HttpProtocolUnitTestResponseGeneratorTests { return } - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let actual: SmokeTestOutput = try await responseClosure(decoder: decoder)(httpResponse) + let actual: SmokeTestOutput = try await SmokeTestOutput.httpOutput(from:)(httpResponse) let expected = SmokeTestOutput( boolHeader: false, @@ -94,10 +91,7 @@ open class HttpProtocolUnitTestResponseGeneratorTests { return } - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let actual: HttpPrefixHeadersOutput = try await responseClosure(decoder: decoder)(httpResponse) + let actual: HttpPrefixHeadersOutput = try await HttpPrefixHeadersOutput.httpOutput(from:)(httpResponse) let expected = HttpPrefixHeadersOutput( foo: "Foo", @@ -131,10 +125,7 @@ open class HttpProtocolUnitTestResponseGeneratorTests { return } - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let actual: HttpPrefixHeadersOutput = try await responseClosure(decoder: decoder)(httpResponse) + let actual: HttpPrefixHeadersOutput = try await HttpPrefixHeadersOutput.httpOutput(from:)(httpResponse) let expected = HttpPrefixHeadersOutput( foo: "Foo" @@ -170,10 +161,7 @@ open class HttpProtocolUnitTestResponseGeneratorTests { return } - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let actual: JsonUnionsOutput = try await responseClosure(decoder: decoder)(httpResponse) + let actual: JsonUnionsOutput = try await JsonUnionsOutput.httpOutput(from:)(httpResponse) let expected = JsonUnionsOutput( contents: MyUnion.stringvalue("foo") @@ -219,10 +207,7 @@ open class HttpProtocolUnitTestResponseGeneratorTests { return } - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let actual: RecursiveShapesOutput = try await responseClosure(decoder: decoder)(httpResponse) + let actual: RecursiveShapesOutput = try await RecursiveShapesOutput.httpOutput(from:)(httpResponse) let expected = RecursiveShapesOutput( nested: RecursiveShapesInputOutputNested1( @@ -251,8 +236,6 @@ open class HttpProtocolUnitTestResponseGeneratorTests { val contents = getTestFileContents("example", "InlineDocumentResponseTest.swift", ctx.manifest) contents.shouldSyntacticSanityCheck() val expectedContents = """ -class InlineDocumentResponseTest: HttpResponseTestBase { - /// Serializes inline documents as part of the JSON response payload with no escaping. func testInlineDocumentOutput() async throws { guard let httpResponse = buildHttpResponse( code: 200, @@ -272,25 +255,20 @@ class InlineDocumentResponseTest: HttpResponseTestBase { return } - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let actual: InlineDocumentOutput = try await responseClosure(decoder: decoder)(httpResponse) + let actual: InlineDocumentOutput = try await InlineDocumentOutput.httpOutput(from:)(httpResponse) let expected = InlineDocumentOutput( - documentValue: try decoder.decode(Document.self, from: - ""${'"'} + documentValue: try SmithyReadWrite.Document.make(from: Data(""${'"'} { "foo": "bar" } - ""${'"'}.data(using: .utf8)!) - , - stringValue: "string" - ) + ""${'"'}.utf8)) + , + stringValue: "string" + ) - XCTAssertEqual(actual, expected) + XCTAssertEqual(actual, expected) - } } """ contents.shouldContainOnlyOnce(expectedContents) @@ -317,24 +295,19 @@ class InlineDocumentResponseTest: HttpResponseTestBase { return } - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let actual: InlineDocumentAsPayloadOutput = try await responseClosure(decoder: decoder)(httpResponse) + let actual: InlineDocumentAsPayloadOutput = try await InlineDocumentAsPayloadOutput.httpOutput(from:)(httpResponse) let expected = InlineDocumentAsPayloadOutput( - documentValue: try decoder.decode(Document.self, from: - ""${'"'} + documentValue: try SmithyReadWrite.Document.make(from: Data(""${'"'} { "foo": "bar" } - ""${'"'}.data(using: .utf8)!) + ""${'"'}.utf8)) - ) + ) - XCTAssertEqual(actual, expected) + XCTAssertEqual(actual, expected) - } } """ contents.shouldContainOnlyOnce(expectedContents) diff --git a/smithy-swift-codegen/src/test/kotlin/IdempotencyTokenTraitTests.kt b/smithy-swift-codegen/src/test/kotlin/IdempotencyTokenTraitTests.kt index 4fd575f54..73c14d77d 100644 --- a/smithy-swift-codegen/src/test/kotlin/IdempotencyTokenTraitTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/IdempotencyTokenTraitTests.kt @@ -6,39 +6,12 @@ class IdempotencyTokenTraitTests { val context = setupTests("Isolated/idempotencyToken.smithy", "aws.protocoltests.restxml#RestXml") val contents = getFileContents(context.manifest, "/RestXml/RestXmlProtocolClient.swift") val expectedContents = """ - public func idempotencyTokenWithStructure(input: IdempotencyTokenWithStructureInput) async throws -> IdempotencyTokenWithStructureOutput { - let context = ClientRuntime.HttpContextBuilder() - .withMethod(value: .put) - .withServiceName(value: serviceName) - .withOperation(value: "idempotencyTokenWithStructure") - .withIdempotencyTokenGenerator(value: config.idempotencyTokenGenerator) - .withLogger(value: config.logger) - .withPartitionID(value: config.partitionID) - .withAuthSchemes(value: config.authSchemes ?? []) - .withAuthSchemeResolver(value: config.authSchemeResolver) - .withUnsignedPayloadTrait(value: false) - .withSocketTimeout(value: config.httpClientConfiguration.socketTimeout) - .build() - var operation = ClientRuntime.OperationStack(id: "idempotencyTokenWithStructure") operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.IdempotencyTokenMiddleware(keyPath: \.token)) - operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(IdempotencyTokenWithStructureInput.urlPathProvider(_:))) - operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) - operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) - operation.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/xml")) - operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(documentWritingClosure: SmithyXML.XMLReadWrite.documentWritingClosure(rootNodeInfo: "IdempotencyToken"), inputWritingClosure: IdempotencyTokenWithStructureInput.writingClosure(_:to:))) - operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) - operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(IdempotencyTokenWithStructureOutput.httpBinding, responseDocumentBinding), responseErrorClosure(IdempotencyTokenWithStructureOutputError.httpBinding, responseDocumentBinding))) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) - let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) - return result - } """ contents.shouldContainOnlyOnce(expectedContents) } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.initializeMiddleware(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/IntEnumGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/IntEnumGeneratorTests.kt index 19ad111a4..0f226a3fe 100644 --- a/smithy-swift-codegen/src/test/kotlin/IntEnumGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/IntEnumGeneratorTests.kt @@ -15,38 +15,37 @@ class IntEnumGeneratorTests { val enumShape = manifest .getFileString("example/models/Abcs.swift").get() Assertions.assertNotNull(enumShape) + var expectedGeneratedEnum = """ +public enum Abcs: Swift.Equatable, Swift.RawRepresentable, Swift.CaseIterable, Swift.Hashable { + case a + case b + case c + case sdkUnknown(Swift.Int) - var expectedGeneratedEnum = - """ - public enum Abcs: Swift.Equatable, Swift.RawRepresentable, Swift.CaseIterable, Swift.Codable, Swift.Hashable { - case a - case b - case c - case sdkUnknown(Swift.Int) + public static var allCases: [Abcs] { + return [ + .a, + .b, + .c, + .sdkUnknown(0) + ] + } - public static var allCases: [Abcs] { - return [ - .a, - .b, - .c, - .sdkUnknown(0) - ] - } - public init(rawValue: Swift.Int) { - let value = Self.allCases.first(where: { ${'$'}0.rawValue == rawValue }) - self = value ?? Self.sdkUnknown(rawValue) - } - public var rawValue: Swift.Int { - switch self { - case .a: return 1 - case .b: return 2 - case .c: return 3 - case let .sdkUnknown(s): return s - } - } - } - """.trimIndent() + public init(rawValue: Swift.Int) { + let value = Self.allCases.first(where: { ${'$'}0.rawValue == rawValue }) + self = value ?? Self.sdkUnknown(rawValue) + } + public var rawValue: Swift.Int { + switch self { + case .a: return 1 + case .b: return 2 + case .c: return 3 + case let .sdkUnknown(s): return s + } + } +} +""" enumShape.shouldContain(expectedGeneratedEnum) } } diff --git a/smithy-swift-codegen/src/test/kotlin/IsolatedHttpProtocolUnitTestRequestGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/IsolatedHttpProtocolUnitTestRequestGeneratorTests.kt index 77b24d5d3..ee01541f9 100644 --- a/smithy-swift-codegen/src/test/kotlin/IsolatedHttpProtocolUnitTestRequestGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/IsolatedHttpProtocolUnitTestRequestGeneratorTests.kt @@ -13,8 +13,6 @@ class IsolatedHttpProtocolUnitTestRequestGeneratorTests { val contents = getFileContents(context.manifest, "/RestJsonTests/HttpRequestWithFloatLabelsRequestTest.swift") val expectedContents = """ -class HttpRequestWithFloatLabelsRequestTest: HttpRequestTestBase { - /// Supports handling NaN float label values. func testRestJsonSupportsNaNFloatLabels() async throws { let urlPrefix = urlPrefixFromHost(host: "") let hostOnly = hostOnlyFromHost(host: "") @@ -26,15 +24,40 @@ class HttpRequestWithFloatLabelsRequestTest: HttpRequestTestBase { resolvedHost: "" ) - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let input = HttpRequestWithFloatLabelsInput( double: Swift.Double.nan, float: Swift.Float.nan ) - """.trimIndent() + let context = HttpContextBuilder() + .withMethod(value: .get) + .build() + var operationStack = OperationStack(id: "RestJsonSupportsNaNFloatLabels") + operationStack.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(urlPrefix: urlPrefix, HttpRequestWithFloatLabelsInput.urlPathProvider(_:))) + operationStack.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware(host: hostOnly)) + operationStack.buildStep.intercept(position: .after, id: "RequestTestEndpointResolver") { (context, input, next) -> ClientRuntime.OperationOutput in + input.withMethod(context.getMethod()) + input.withPath(context.getPath()) + let host = "\(context.getHostPrefix() ?? "")\(context.getHost() ?? "")" + input.withHost(host) + return try await next.handle(context: context, input: input) + } + operationStack.deserializeStep.intercept( + position: .after, + middleware: MockDeserializeMiddleware( + id: "TestDeserializeMiddleware", + responseClosure: HttpRequestWithFloatLabelsOutput.httpOutput(from:), + callback: { context, actual in + try await self.assertEqual(expected, actual) + return OperationOutput(httpResponse: HttpResponse(body: ByteStream.noStream, statusCode: .ok), output: HttpRequestWithFloatLabelsOutput()) + } + ) + ) + _ = try await operationStack.handleMiddleware(context: context, input: input, next: MockHandler() { (context, request) in + XCTFail("Deserialize was mocked out, this should fail") + throw SmithyTestUtilError("Mock handler unexpectedly failed") + }) + } +""" contents.shouldContainOnlyOnce(expectedContents) } @@ -44,28 +67,52 @@ class HttpRequestWithFloatLabelsRequestTest: HttpRequestTestBase { val context = setupTests("Isolated/number-type-test.smithy", "aws.protocoltests.restjson#RestJson") val contents = getFileContents(context.manifest, "/RestJsonTests/HttpRequestWithFloatLabelsRequestTest.swift") - val expectedContents = - """ + val expectedContents = """ func testRestJsonSupportsInfinityFloatLabels() async throws { - let urlPrefix = urlPrefixFromHost(host: "") - let hostOnly = hostOnlyFromHost(host: "") - let expected = buildExpectedHttpRequest( - method: .get, - path: "/FloatHttpLabels/Infinity/Infinity", - body: nil, - host: "", - resolvedHost: "" - ) - - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") + let urlPrefix = urlPrefixFromHost(host: "") + let hostOnly = hostOnlyFromHost(host: "") + let expected = buildExpectedHttpRequest( + method: .get, + path: "/FloatHttpLabels/Infinity/Infinity", + body: nil, + host: "", + resolvedHost: "" + ) - let input = HttpRequestWithFloatLabelsInput( - double: Swift.Double.infinity, - float: Swift.Float.infinity + let input = HttpRequestWithFloatLabelsInput( + double: Swift.Double.infinity, + float: Swift.Float.infinity + ) + let context = HttpContextBuilder() + .withMethod(value: .get) + .build() + var operationStack = OperationStack(id: "RestJsonSupportsInfinityFloatLabels") + operationStack.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(urlPrefix: urlPrefix, HttpRequestWithFloatLabelsInput.urlPathProvider(_:))) + operationStack.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware(host: hostOnly)) + operationStack.buildStep.intercept(position: .after, id: "RequestTestEndpointResolver") { (context, input, next) -> ClientRuntime.OperationOutput in + input.withMethod(context.getMethod()) + input.withPath(context.getPath()) + let host = "\(context.getHostPrefix() ?? "")\(context.getHost() ?? "")" + input.withHost(host) + return try await next.handle(context: context, input: input) + } + operationStack.deserializeStep.intercept( + position: .after, + middleware: MockDeserializeMiddleware( + id: "TestDeserializeMiddleware", + responseClosure: HttpRequestWithFloatLabelsOutput.httpOutput(from:), + callback: { context, actual in + try await self.assertEqual(expected, actual) + return OperationOutput(httpResponse: HttpResponse(body: ByteStream.noStream, statusCode: .ok), output: HttpRequestWithFloatLabelsOutput()) + } ) - """.trimIndent() + ) + _ = try await operationStack.handleMiddleware(context: context, input: input, next: MockHandler() { (context, request) in + XCTFail("Deserialize was mocked out, this should fail") + throw SmithyTestUtilError("Mock handler unexpectedly failed") + }) + } +""" contents.shouldContainOnlyOnce(expectedContents) } @@ -74,28 +121,52 @@ class HttpRequestWithFloatLabelsRequestTest: HttpRequestTestBase { val context = setupTests("Isolated/number-type-test.smithy", "aws.protocoltests.restjson#RestJson") val contents = getFileContents(context.manifest, "/RestJsonTests/HttpRequestWithFloatLabelsRequestTest.swift") - val expectedContents = - """ + val expectedContents = """ func testRestJsonSupportsNegativeInfinityFloatLabels() async throws { - let urlPrefix = urlPrefixFromHost(host: "") - let hostOnly = hostOnlyFromHost(host: "") - let expected = buildExpectedHttpRequest( - method: .get, - path: "/FloatHttpLabels/-Infinity/-Infinity", - body: nil, - host: "", - resolvedHost: "" - ) - - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - - let input = HttpRequestWithFloatLabelsInput( - double: -Swift.Double.infinity, - float: -Swift.Float.infinity + let urlPrefix = urlPrefixFromHost(host: "") + let hostOnly = hostOnlyFromHost(host: "") + let expected = buildExpectedHttpRequest( + method: .get, + path: "/FloatHttpLabels/-Infinity/-Infinity", + body: nil, + host: "", + resolvedHost: "" + ) + + let input = HttpRequestWithFloatLabelsInput( + double: -Swift.Double.infinity, + float: -Swift.Float.infinity + ) + let context = HttpContextBuilder() + .withMethod(value: .get) + .build() + var operationStack = OperationStack(id: "RestJsonSupportsNegativeInfinityFloatLabels") + operationStack.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(urlPrefix: urlPrefix, HttpRequestWithFloatLabelsInput.urlPathProvider(_:))) + operationStack.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware(host: hostOnly)) + operationStack.buildStep.intercept(position: .after, id: "RequestTestEndpointResolver") { (context, input, next) -> ClientRuntime.OperationOutput in + input.withMethod(context.getMethod()) + input.withPath(context.getPath()) + let host = "\(context.getHostPrefix() ?? "")\(context.getHost() ?? "")" + input.withHost(host) + return try await next.handle(context: context, input: input) + } + operationStack.deserializeStep.intercept( + position: .after, + middleware: MockDeserializeMiddleware( + id: "TestDeserializeMiddleware", + responseClosure: HttpRequestWithFloatLabelsOutput.httpOutput(from:), + callback: { context, actual in + try await self.assertEqual(expected, actual) + return OperationOutput(httpResponse: HttpResponse(body: ByteStream.noStream, statusCode: .ok), output: HttpRequestWithFloatLabelsOutput()) + } ) - """.trimIndent() + ) + _ = try await operationStack.handleMiddleware(context: context, input: input, next: MockHandler() { (context, request) in + XCTFail("Deserialize was mocked out, this should fail") + throw SmithyTestUtilError("Mock handler unexpectedly failed") + }) + } +""" contents.shouldContainOnlyOnce(expectedContents) } @@ -120,10 +191,7 @@ class InputAndOutputWithHeadersResponseTest: HttpResponseTestBase { return } - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let actual: InputAndOutputWithHeadersOutput = try await responseClosure(decoder: decoder)(httpResponse) + let actual: InputAndOutputWithHeadersOutput = try await InputAndOutputWithHeadersOutput.httpOutput(from:)(httpResponse) let expected = InputAndOutputWithHeadersOutput( headerDouble: Swift.Double.nan, @@ -180,13 +248,8 @@ class DocumentTypeRequestTest: HttpRequestTestBase { resolvedHost: "" ) - let decoder = ClientRuntime.JSONDecoder() - decoder.dateDecodingStrategy = .secondsSince1970 - decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") - let input = DocumentTypeInput( - documentValue: try decoder.decode(Document.self, from: - ""${'"'} + documentValue: try SmithyReadWrite.Document.make(from: Data(""${'"'} [ true, "hi", @@ -203,16 +266,50 @@ class DocumentTypeRequestTest: HttpRequestTestBase { } } ] - ""${'"'}.data(using: .utf8)!) - , - stringValue: "string" + ""${'"'}.utf8)) + , + stringValue: "string" + ) + let context = HttpContextBuilder() + .withMethod(value: .put) + .build() + var operationStack = OperationStack(id: "DocumentInputWithList") + operationStack.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(urlPrefix: urlPrefix, DocumentTypeInput.urlPathProvider(_:))) + operationStack.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware(host: hostOnly)) + operationStack.buildStep.intercept(position: .after, id: "RequestTestEndpointResolver") { (context, input, next) -> ClientRuntime.OperationOutput in + input.withMethod(context.getMethod()) + input.withPath(context.getPath()) + let host = "\(context.getHostPrefix() ?? "")\(context.getHost() ?? "")" + input.withHost(host) + return try await next.handle(context: context, input: input) + } + operationStack.deserializeStep.intercept( + position: .after, + middleware: MockDeserializeMiddleware( + id: "TestDeserializeMiddleware", + responseClosure: DocumentTypeOutput.httpOutput(from:), + callback: { context, actual in + try await self.assertEqual(expected, actual, { (expectedHttpBody, actualHttpBody) -> Void in + XCTAssertNotNil(actualHttpBody, "The actual ByteStream is nil") + XCTAssertNotNil(expectedHttpBody, "The expected ByteStream is nil") + try await self.genericAssertEqualHttpBodyData(expected: expectedHttpBody!, actual: actualHttpBody!, contentType: .json) + }) + return OperationOutput(httpResponse: HttpResponse(body: ByteStream.noStream, statusCode: .ok), output: DocumentTypeOutput()) + } ) + ) + _ = try await operationStack.handleMiddleware(context: context, input: input, next: MockHandler() { (context, request) in + XCTFail("Deserialize was mocked out, this should fail") + throw SmithyTestUtilError("Mock handler unexpectedly failed") + }) + } +} """ contents.shouldContainOnlyOnce(expectedContents) } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestJsonProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestJsonProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestJson", "2019-12-16", "Rest Json Protocol") } context.generator.generateProtocolUnitTests(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/PackageManifestGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/PackageManifestGeneratorTests.kt index 27314812b..06568df5c 100644 --- a/smithy-swift-codegen/src/test/kotlin/PackageManifestGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/PackageManifestGeneratorTests.kt @@ -37,7 +37,7 @@ class PackageManifestGeneratorTests { url: "https://github.com/apple/swift-numerics", from: "0.0.5" ), - """ +""" packageManifest.shouldContain(expectedContents) } @@ -70,32 +70,33 @@ class PackageManifestGeneratorTests { writePackageManifest(settings, manifest, mockDependencies, true) val packageManifest = manifest.getFileString("Package.swift").get() assertNotNull(packageManifest) - packageManifest.shouldContain( - "targets: [\n" + - " .target(\n" + - " name: \"MockSDK\",\n" + - " dependencies: [\n" + - " .product(\n" + - " name: \"ComplexModule\",\n" + - " package: \"swift-numerics\"\n" + - " ),\n" + - " .product(\n" + - " name: \"ClientRuntime\",\n" + - " package: \"smithy-swift\"\n" + - " ),\n" + - " ],\n" + - " path: \"./MockSDK\"\n" + - " ),\n" + - " .testTarget(\n" + - " name: \"MockSDKTests\",\n" + - " dependencies: [\n" + - " \"MockSDK\",\n" + - " .product(name: \"SmithyTestUtil\", package: \"smithy-swift\")\n" + - " ],\n" + - " path: \"./MockSDKTests\"\n" + - " )\n" + - " ]" + val expected = """ + targets: [ + .target( + name: "MockSDK", + dependencies: [ + .product( + name: "ComplexModule", + package: "swift-numerics" + ), + .product( + name: "SmithyReadWrite", + package: "smithy-swift" + ), + ], + path: "./MockSDK" + ), + .testTarget( + name: "MockSDKTests", + dependencies: [ + "MockSDK", + .product(name: "SmithyTestUtil", package: "smithy-swift") + ], + path: "./MockSDKTests" ) + ] +""" + packageManifest.shouldContain(expected) } @Test @@ -103,8 +104,7 @@ class PackageManifestGeneratorTests { writePackageManifest(settings, manifest, mockDependencies, false) val packageManifest = manifest.getFileString("Package.swift").get() assertNotNull(packageManifest) - packageManifest.shouldContain( -""" + val expected = """ targets: [ .target( name: "MockSDK", @@ -114,7 +114,7 @@ class PackageManifestGeneratorTests { package: "swift-numerics" ), .product( - name: "ClientRuntime", + name: "SmithyReadWrite", package: "smithy-swift" ), ], @@ -122,7 +122,7 @@ class PackageManifestGeneratorTests { ), ] """ - ) + packageManifest.shouldContain(expected) } fun getMockDependenciesFromModel(model: Model, symbolProvider: SymbolProvider): MutableList { diff --git a/smithy-swift-codegen/src/test/kotlin/PaginatorGeneratorTest.kt b/smithy-swift-codegen/src/test/kotlin/PaginatorGeneratorTest.kt index 9fd2c8928..d106a959a 100644 --- a/smithy-swift-codegen/src/test/kotlin/PaginatorGeneratorTest.kt +++ b/smithy-swift-codegen/src/test/kotlin/PaginatorGeneratorTest.kt @@ -175,7 +175,7 @@ class PaginatorGeneratorTest { } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestJsonProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestJsonProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "Test", "2019-12-16", "Test") } context.generator.generateProtocolClient(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/ReservedWordsGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/ReservedWordsGeneratorTests.kt index dbd4bc802..9e606c91e 100644 --- a/smithy-swift-codegen/src/test/kotlin/ReservedWordsGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/ReservedWordsGeneratorTests.kt @@ -11,47 +11,43 @@ class ReservedWordsGeneratorTests { fun `test enum`() { val context = setupTests("reserved-name-enum-test.smithy", "com.test#Example") val contents = getFileContents(context.manifest, "/example/models/ReservedWordsEnum.swift") - val expectedContents = - """ - extension ExampleClientTypes { - public enum ReservedWordsEnum: Swift.Equatable, Swift.RawRepresentable, Swift.CaseIterable, Swift.Codable, Swift.Hashable { - case any - case `open` - case `self` - case `protocol` - case sdkUnknown(Swift.String) - - public static var allCases: [ReservedWordsEnum] { - return [ - .any, - .open, - .self, - .protocol, - .sdkUnknown("") - ] - } - public init?(rawValue: Swift.String) { - let value = Self.allCases.first(where: { ${'$'}0.rawValue == rawValue }) - self = value ?? Self.sdkUnknown(rawValue) - } - public var rawValue: Swift.String { - switch self { - case .any: return "Any" - case .open: return "OPEN" - case .self: return "Self" - case .protocol: return "PROTOCOL" - case let .sdkUnknown(s): return s - } - } - public init(from decoder: Swift.Decoder) throws { - let container = try decoder.singleValueContainer() - let rawValue = try container.decode(RawValue.self) - self = ReservedWordsEnum(rawValue: rawValue) ?? ReservedWordsEnum.sdkUnknown(rawValue) - } - } + val expectedContents = """ +extension ExampleClientTypes { + + public enum ReservedWordsEnum: Swift.Equatable, Swift.RawRepresentable, Swift.CaseIterable, Swift.Hashable { + case any + case `open` + case `self` + case `protocol` + case sdkUnknown(Swift.String) + + public static var allCases: [ReservedWordsEnum] { + return [ + .any, + .open, + .self, + .protocol, + .sdkUnknown("") + ] } - """.trimIndent() + public init?(rawValue: Swift.String) { + let value = Self.allCases.first(where: { ${'$'}0.rawValue == rawValue }) + self = value ?? Self.sdkUnknown(rawValue) + } + + public var rawValue: Swift.String { + switch self { + case .any: return "Any" + case .open: return "OPEN" + case .self: return "Self" + case .protocol: return "PROTOCOL" + case let .sdkUnknown(s): return s + } + } + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } @@ -59,40 +55,37 @@ class ReservedWordsGeneratorTests { fun `it handles type name that conflicts with swift metatype`() { val context = setupTests("reserved-name-enum-test.smithy", "com.test#Example") val contents = getFileContents(context.manifest, "/example/models/Type.swift") - val expectedContents = - """ - extension ExampleClientTypes { - public enum ModelType: Swift.Equatable, Swift.RawRepresentable, Swift.CaseIterable, Swift.Codable, Swift.Hashable { - case foo - case test - case sdkUnknown(Swift.String) - - public static var allCases: [ModelType] { - return [ - .foo, - .test, - .sdkUnknown("") - ] - } - public init?(rawValue: Swift.String) { - let value = Self.allCases.first(where: { ${'$'}0.rawValue == rawValue }) - self = value ?? Self.sdkUnknown(rawValue) - } - public var rawValue: Swift.String { - switch self { - case .foo: return "foo" - case .test: return "test" - case let .sdkUnknown(s): return s - } - } - public init(from decoder: Swift.Decoder) throws { - let container = try decoder.singleValueContainer() - let rawValue = try container.decode(RawValue.self) - self = ModelType(rawValue: rawValue) ?? ModelType.sdkUnknown(rawValue) - } + val expectedContents = """ +extension ExampleClientTypes { + + public enum ModelType: Swift.Equatable, Swift.RawRepresentable, Swift.CaseIterable, Swift.Hashable { + case foo + case test + case sdkUnknown(Swift.String) + + public static var allCases: [ModelType] { + return [ + .foo, + .test, + .sdkUnknown("") + ] + } + + public init?(rawValue: Swift.String) { + let value = Self.allCases.first(where: { ${'$'}0.rawValue == rawValue }) + self = value ?? Self.sdkUnknown(rawValue) + } + + public var rawValue: Swift.String { + switch self { + case .foo: return "foo" + case .test: return "test" + case let .sdkUnknown(s): return s } } - """.trimIndent() + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } @@ -100,40 +93,37 @@ class ReservedWordsGeneratorTests { fun `it handles protocol name that conflicts with swift metatype`() { val context = setupTests("reserved-name-enum-test.smithy", "com.test#Example") val contents = getFileContents(context.manifest, "/example/models/Protocol.swift") - val expectedContents = - """ - extension ExampleClientTypes { - public enum ModelProtocol: Swift.Equatable, Swift.RawRepresentable, Swift.CaseIterable, Swift.Codable, Swift.Hashable { - case bar - case foo - case sdkUnknown(Swift.String) - - public static var allCases: [ModelProtocol] { - return [ - .bar, - .foo, - .sdkUnknown("") - ] - } - public init?(rawValue: Swift.String) { - let value = Self.allCases.first(where: { ${'$'}0.rawValue == rawValue }) - self = value ?? Self.sdkUnknown(rawValue) - } - public var rawValue: Swift.String { - switch self { - case .bar: return "bar" - case .foo: return "foo" - case let .sdkUnknown(s): return s - } - } - public init(from decoder: Swift.Decoder) throws { - let container = try decoder.singleValueContainer() - let rawValue = try container.decode(RawValue.self) - self = ModelProtocol(rawValue: rawValue) ?? ModelProtocol.sdkUnknown(rawValue) - } - } + val expectedContents = """ +extension ExampleClientTypes { + + public enum ModelProtocol: Swift.Equatable, Swift.RawRepresentable, Swift.CaseIterable, Swift.Hashable { + case bar + case foo + case sdkUnknown(Swift.String) + + public static var allCases: [ModelProtocol] { + return [ + .bar, + .foo, + .sdkUnknown("") + ] + } + + public init?(rawValue: Swift.String) { + let value = Self.allCases.first(where: { ${'$'}0.rawValue == rawValue }) + self = value ?? Self.sdkUnknown(rawValue) + } + + public var rawValue: Swift.String { + switch self { + case .bar: return "bar" + case .foo: return "foo" + case let .sdkUnknown(s): return s } - """.trimIndent() + } + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } diff --git a/smithy-swift-codegen/src/test/kotlin/RetryMiddlewareTests.kt b/smithy-swift-codegen/src/test/kotlin/RetryMiddlewareTests.kt index 95f6d17be..e405571b0 100644 --- a/smithy-swift-codegen/src/test/kotlin/RetryMiddlewareTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/RetryMiddlewareTests.kt @@ -13,7 +13,7 @@ class RetryMiddlewareTests { contents.shouldContainOnlyOnce(expectedContents) } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.initializeMiddleware(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/ServiceRenamesTests.kt b/smithy-swift-codegen/src/test/kotlin/ServiceRenamesTests.kt index 704a45d4a..e93d1ab40 100644 --- a/smithy-swift-codegen/src/test/kotlin/ServiceRenamesTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/ServiceRenamesTests.kt @@ -104,7 +104,6 @@ class ServiceRenamesTests { ), "aws.protocoltests.restjson#RestJson" ) - print(listFilesFromManifest(context.manifest)) val contents = getFileContents(context.manifest, "/RestJson/models/RenamedGreeting.swift") contents.shouldSyntacticSanityCheck() val expectedContents = @@ -136,30 +135,24 @@ class ServiceRenamesTests { ), "aws.protocoltests.restjson#RestJson" ) - print(listFilesFromManifest(context.manifest)) - val contents = getFileContents(context.manifest, "/RestJson/models/RenamedGreeting+Codable.swift") + val contents = getFileContents(context.manifest, "/RestJson/models/RenamedGreeting+ReadWrite.swift") contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension RestJsonProtocolClientTypes.RenamedGreeting: Swift.Codable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case salutation - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let salutation = self.salutation { - try encodeContainer.encode(salutation, forKey: .salutation) - } - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let salutationDecoded = try containerValues.decodeIfPresent(Swift.String.self, forKey: .salutation) - salutation = salutationDecoded - } - } - """.trimIndent() + val expectedContents = """ +extension RestJsonProtocolClientTypes.RenamedGreeting { + + static func write(value: RestJsonProtocolClientTypes.RenamedGreeting?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["salutation"].write(value.salutation) + } + + static func read(from reader: SmithyJSON.Reader) throws -> RestJsonProtocolClientTypes.RenamedGreeting { + guard reader.hasContent else { throw SmithyReadWrite.ReaderError.requiredValueNotPresent } + var value = RestJsonProtocolClientTypes.RenamedGreeting() + value.salutation = try reader["salutation"].readIfPresent() + return value + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } diff --git a/smithy-swift-codegen/src/test/kotlin/StructDecodeGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/StructDecodeGenerationTests.kt index b3a24ee92..e147b2927 100644 --- a/smithy-swift-codegen/src/test/kotlin/StructDecodeGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/StructDecodeGenerationTests.kt @@ -27,494 +27,98 @@ class StructDecodeGenerationTests { newTestContext.generationCtx.delegator.flushWriters() } - @Test - fun `it creates decodable conformance in correct file`() { - Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/SmokeTestOutputBody+Decodable.swift")) - } - @Test fun `it creates decodable conformance for nested structures`() { - Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/Nested+Codable.swift")) - Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/Nested2+Codable.swift")) - Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/Nested3+Codable.swift")) - Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/Nested4+Codable.swift")) - } - - @Test - fun `it creates smoke test request decodable conformance`() { - val contents = getModelFileContents("example", "SmokeTestOutputBody+Decodable.swift", newTestContext.manifest) - contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - struct SmokeTestOutputBody { - let payload1: Swift.String? - let payload2: Swift.Int? - let payload3: ExampleClientTypes.Nested? - } - - extension SmokeTestOutputBody: Swift.Decodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case payload1 - case payload2 - case payload3 - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let payload1Decoded = try containerValues.decodeIfPresent(Swift.String.self, forKey: .payload1) - payload1 = payload1Decoded - let payload2Decoded = try containerValues.decodeIfPresent(Swift.Int.self, forKey: .payload2) - payload2 = payload2Decoded - let payload3Decoded = try containerValues.decodeIfPresent(ExampleClientTypes.Nested.self, forKey: .payload3) - payload3 = payload3Decoded - } - } - """.trimIndent() - contents.shouldContainOnlyOnce(expectedContents) + Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/Nested+ReadWrite.swift")) + Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/Nested2+ReadWrite.swift")) + Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/Nested3+ReadWrite.swift")) + Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/Nested4+ReadWrite.swift")) } @Test fun `it decodes nested documents with aggregate shapes`() { - val contents = getModelFileContents("example", "Nested4+Codable.swift", newTestContext.manifest) + val contents = getModelFileContents("example", "Nested4+ReadWrite.swift", newTestContext.manifest) contents.shouldSyntacticSanityCheck() val expectedContents = """ - extension ExampleClientTypes.Nested4: Swift.Codable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case intList - case intMap - case member1 - case stringMap - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let intList = intList { - var intListContainer = encodeContainer.nestedUnkeyedContainer(forKey: .intList) - for integer0 in intList { - try intListContainer.encode(integer0) - } - } - if let intMap = intMap { - var intMapContainer = encodeContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: .intMap) - for (dictKey0, intMap0) in intMap { - try intMapContainer.encode(intMap0, forKey: ClientRuntime.Key(stringValue: dictKey0)) - } - } - if let member1 = self.member1 { - try encodeContainer.encode(member1, forKey: .member1) - } - if let stringMap = stringMap { - var stringMapContainer = encodeContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: .stringMap) - for (dictKey0, nestedStringMap0) in stringMap { - var nestedStringMap0Container = stringMapContainer.nestedUnkeyedContainer(forKey: ClientRuntime.Key(stringValue: dictKey0)) - for string1 in nestedStringMap0 { - try nestedStringMap0Container.encode(string1) - } - } - } - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let member1Decoded = try containerValues.decodeIfPresent(Swift.Int.self, forKey: .member1) - member1 = member1Decoded - let intListContainer = try containerValues.decodeIfPresent([Swift.Int?].self, forKey: .intList) - var intListDecoded0:[Swift.Int]? = nil - if let intListContainer = intListContainer { - intListDecoded0 = [Swift.Int]() - for integer0 in intListContainer { - if let integer0 = integer0 { - intListDecoded0?.append(integer0) - } - } - } - intList = intListDecoded0 - let intMapContainer = try containerValues.decodeIfPresent([Swift.String: Swift.Int?].self, forKey: .intMap) - var intMapDecoded0: [Swift.String:Swift.Int]? = nil - if let intMapContainer = intMapContainer { - intMapDecoded0 = [Swift.String:Swift.Int]() - for (key0, integer0) in intMapContainer { - if let integer0 = integer0 { - intMapDecoded0?[key0] = integer0 - } - } - } - intMap = intMapDecoded0 - let stringMapContainer = try containerValues.decodeIfPresent([Swift.String: [Swift.String?]?].self, forKey: .stringMap) - var stringMapDecoded0: [Swift.String:[Swift.String]]? = nil - if let stringMapContainer = stringMapContainer { - stringMapDecoded0 = [Swift.String:[Swift.String]]() - for (key0, stringlist0) in stringMapContainer { - var stringlist0Decoded0: [Swift.String]? = nil - if let stringlist0 = stringlist0 { - stringlist0Decoded0 = [Swift.String]() - for string1 in stringlist0 { - if let string1 = string1 { - stringlist0Decoded0?.append(string1) - } - } - } - stringMapDecoded0?[key0] = stringlist0Decoded0 - } - } - stringMap = stringMapDecoded0 - } - } - """.trimIndent() - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `it provides decodable conformance to operation outputs with timestamps`() { - val contents = - getModelFileContents("example", "TimestampInputOutputBody+Decodable.swift", newTestContext.manifest) - contents.shouldSyntacticSanityCheck() - val expectedContents = - """ -struct TimestampInputOutputBody { - let normal: ClientRuntime.Date? - let dateTime: ClientRuntime.Date? - let epochSeconds: ClientRuntime.Date? - let httpDate: ClientRuntime.Date? - let inheritedTimestamp: ClientRuntime.Date? - let nestedTimestampList: [[ClientRuntime.Date]]? - let timestampList: [ClientRuntime.Date]? -} - -extension TimestampInputOutputBody: Swift.Decodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case dateTime - case epochSeconds - case httpDate - case inheritedTimestamp - case nestedTimestampList - case normal - case timestampList - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let normalDecoded = try containerValues.decodeTimestampIfPresent(.dateTime, forKey: .normal) - normal = normalDecoded - let dateTimeDecoded = try containerValues.decodeTimestampIfPresent(.dateTime, forKey: .dateTime) - dateTime = dateTimeDecoded - let epochSecondsDecoded = try containerValues.decodeTimestampIfPresent(.epochSeconds, forKey: .epochSeconds) - epochSeconds = epochSecondsDecoded - let httpDateDecoded = try containerValues.decodeTimestampIfPresent(.httpDate, forKey: .httpDate) - httpDate = httpDateDecoded - let inheritedTimestampDecoded = try containerValues.decodeTimestampIfPresent(.httpDate, forKey: .inheritedTimestamp) - inheritedTimestamp = inheritedTimestampDecoded - let nestedTimestampListContainer = try containerValues.decodeIfPresent([[Swift.String?]?].self, forKey: .nestedTimestampList) - var nestedTimestampListDecoded0:[[ClientRuntime.Date]]? = nil - if let nestedTimestampListContainer = nestedTimestampListContainer { - nestedTimestampListDecoded0 = [[ClientRuntime.Date]]() - for list0 in nestedTimestampListContainer { - var list0Decoded0: [Swift.String]? = nil - if let list0 = list0 { - list0Decoded0 = [Swift.String]() - for timestamp1 in list0 { - if let timestamp1 = timestamp1 { - let date1 = try containerValues.timestampStringAsDate(timestamp1, format: .dateTime, forKey: .nestedTimestampList) - list0Decoded0?.append(date1) - } - } - } - if let list0Decoded0 = list0Decoded0 { - nestedTimestampListDecoded0?.append(list0Decoded0) - } - } - } - nestedTimestampList = nestedTimestampListDecoded0 - let timestampListContainer = try containerValues.decodeIfPresent([Swift.String?].self, forKey: .timestampList) - var timestampListDecoded0:[ClientRuntime.Date]? = nil - if let timestampListContainer = timestampListContainer { - timestampListDecoded0 = [ClientRuntime.Date]() - for timestamp0 in timestampListContainer { - if let timestamp0 = timestamp0 { - let date0 = try containerValues.timestampStringAsDate(timestamp0, format: .dateTime, forKey: .timestampList) - timestampListDecoded0?.append(date0) - } - } - } - timestampList = timestampListDecoded0 - } -} - """.trimIndent() - contents.shouldContainOnlyOnce(expectedContents) - } - - @Test - fun `it decodes maps correctly`() { - val contents = getModelFileContents("example", "MapInputOutputBody+Decodable.swift", newTestContext.manifest) - contents.shouldSyntacticSanityCheck() - val expectedContents = - """ -struct MapInputOutputBody { - let intMap: [Swift.String:Swift.Int]? - let structMap: [Swift.String:ExampleClientTypes.ReachableOnlyThroughMap]? - let enumMap: [Swift.String:ExampleClientTypes.MyEnum]? - let blobMap: [Swift.String:ClientRuntime.Data]? - let nestedMap: [Swift.String:[Swift.String:Swift.Int]]? - let dateMap: [Swift.String:ClientRuntime.Date]? -} +extension ExampleClientTypes.Nested4 { -extension MapInputOutputBody: Swift.Decodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case blobMap - case dateMap - case enumMap - case intMap - case nestedMap - case structMap + static func write(value: ExampleClientTypes.Nested4?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["intList"].writeList(value.intList, memberWritingClosure: Swift.Int.write(value:to:), memberNodeInfo: "member", isFlattened: false) + try writer["intMap"].writeMap(value.intMap, valueWritingClosure: Swift.Int.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + try writer["member1"].write(value.member1) + try writer["stringMap"].writeMap(value.stringMap, valueWritingClosure: listWritingClosure(memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) } - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let intMapContainer = try containerValues.decodeIfPresent([Swift.String: Swift.Int?].self, forKey: .intMap) - var intMapDecoded0: [Swift.String:Swift.Int]? = nil - if let intMapContainer = intMapContainer { - intMapDecoded0 = [Swift.String:Swift.Int]() - for (key0, integer0) in intMapContainer { - if let integer0 = integer0 { - intMapDecoded0?[key0] = integer0 - } - } - } - intMap = intMapDecoded0 - let structMapContainer = try containerValues.decodeIfPresent([Swift.String: ExampleClientTypes.ReachableOnlyThroughMap?].self, forKey: .structMap) - var structMapDecoded0: [Swift.String:ExampleClientTypes.ReachableOnlyThroughMap]? = nil - if let structMapContainer = structMapContainer { - structMapDecoded0 = [Swift.String:ExampleClientTypes.ReachableOnlyThroughMap]() - for (key0, reachableonlythroughmap0) in structMapContainer { - if let reachableonlythroughmap0 = reachableonlythroughmap0 { - structMapDecoded0?[key0] = reachableonlythroughmap0 - } - } - } - structMap = structMapDecoded0 - let enumMapContainer = try containerValues.decodeIfPresent([Swift.String: ExampleClientTypes.MyEnum?].self, forKey: .enumMap) - var enumMapDecoded0: [Swift.String:ExampleClientTypes.MyEnum]? = nil - if let enumMapContainer = enumMapContainer { - enumMapDecoded0 = [Swift.String:ExampleClientTypes.MyEnum]() - for (key0, myenum0) in enumMapContainer { - if let myenum0 = myenum0 { - enumMapDecoded0?[key0] = myenum0 - } - } - } - enumMap = enumMapDecoded0 - let blobMapContainer = try containerValues.decodeIfPresent([Swift.String: ClientRuntime.Data?].self, forKey: .blobMap) - var blobMapDecoded0: [Swift.String:ClientRuntime.Data]? = nil - if let blobMapContainer = blobMapContainer { - blobMapDecoded0 = [Swift.String:ClientRuntime.Data]() - for (key0, blob0) in blobMapContainer { - if let blob0 = blob0 { - blobMapDecoded0?[key0] = blob0 - } - } - } - blobMap = blobMapDecoded0 - let nestedMapContainer = try containerValues.decodeIfPresent([Swift.String: [Swift.String: Swift.Int?]?].self, forKey: .nestedMap) - var nestedMapDecoded0: [Swift.String:[Swift.String:Swift.Int]]? = nil - if let nestedMapContainer = nestedMapContainer { - nestedMapDecoded0 = [Swift.String:[Swift.String:Swift.Int]]() - for (key0, intmap0) in nestedMapContainer { - var intmap0Decoded0: [Swift.String: Swift.Int]? = nil - if let intmap0 = intmap0 { - intmap0Decoded0 = [Swift.String: Swift.Int]() - for (key1, integer1) in intmap0 { - if let integer1 = integer1 { - intmap0Decoded0?[key1] = integer1 - } - } - } - nestedMapDecoded0?[key0] = intmap0Decoded0 - } - } - nestedMap = nestedMapDecoded0 - let dateMapContainer = try containerValues.decodeIfPresent([Swift.String: Swift.String?].self, forKey: .dateMap) - var dateMapDecoded0: [Swift.String:ClientRuntime.Date]? = nil - if let dateMapContainer = dateMapContainer { - dateMapDecoded0 = [Swift.String:ClientRuntime.Date]() - for (key0, timestamp0) in dateMapContainer { - let date0 = try containerValues.timestampStringAsDate(timestamp0, format: .dateTime, forKey: .dateMap) - dateMapDecoded0?[key0] = date0 - } - } - dateMap = dateMapDecoded0 + static func read(from reader: SmithyJSON.Reader) throws -> ExampleClientTypes.Nested4 { + guard reader.hasContent else { throw SmithyReadWrite.ReaderError.requiredValueNotPresent } + var value = ExampleClientTypes.Nested4() + value.member1 = try reader["member1"].readIfPresent() + value.intList = try reader["intList"].readListIfPresent(memberReadingClosure: Swift.Int.read(from:), memberNodeInfo: "member", isFlattened: false) + value.intMap = try reader["intMap"].readMapIfPresent(valueReadingClosure: Swift.Int.read(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + value.stringMap = try reader["stringMap"].readMapIfPresent(valueReadingClosure: listReadingClosure(memberReadingClosure: Swift.String.read(from:), memberNodeInfo: "member", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + return value } } - """.trimIndent() +""" contents.shouldContainOnlyOnce(expectedContents) } @Test - fun `it decodes nested diverse shapes correctly`() { - val contents = - getModelFileContents("example", "NestedShapesOutputBody+Decodable.swift", newTestContext.manifest) + fun `it decodes recursive boxed types correctly`() { + val contents = getModelFileContents( + "example", + "RecursiveShapesInputOutputNested1+ReadWrite.swift", + newTestContext.manifest + ) contents.shouldSyntacticSanityCheck() - val expectedContents = - """ -struct NestedShapesOutputBody { - let nestedListInDict: [Swift.String:[ClientRuntime.Date]]? - let nestedDictInList: [[Swift.String:Swift.String]]? - let nestedListOfListInDict: [Swift.String:[[Swift.Int]]]? -} + val expectedContents = """ +extension ExampleClientTypes.RecursiveShapesInputOutputNested1 { -extension NestedShapesOutputBody: Swift.Decodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case nestedDictInList - case nestedListInDict - case nestedListOfListInDict + static func write(value: ExampleClientTypes.RecursiveShapesInputOutputNested1?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["foo"].write(value.foo) + try writer["nested"].write(value.nested, with: ExampleClientTypes.RecursiveShapesInputOutputNested2.write(value:to:)) } - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let nestedListInDictContainer = try containerValues.decodeIfPresent([Swift.String: [Swift.String?]?].self, forKey: .nestedListInDict) - var nestedListInDictDecoded0: [Swift.String:[ClientRuntime.Date]]? = nil - if let nestedListInDictContainer = nestedListInDictContainer { - nestedListInDictDecoded0 = [Swift.String:[ClientRuntime.Date]]() - for (key0, timestamplist0) in nestedListInDictContainer { - var timestamplist0Decoded0: [Swift.String]? = nil - if let timestamplist0 = timestamplist0 { - timestamplist0Decoded0 = [Swift.String]() - for timestamp1 in timestamplist0 { - if let timestamp1 = timestamp1 { - let date1 = try containerValues.timestampStringAsDate(timestamp1, format: .dateTime, forKey: .nestedListInDict) - timestamplist0Decoded0?.append(date1) - } - } - } - nestedListInDictDecoded0?[key0] = timestamplist0Decoded0 - } - } - nestedListInDict = nestedListInDictDecoded0 - let nestedDictInListContainer = try containerValues.decodeIfPresent([[Swift.String: Swift.String?]?].self, forKey: .nestedDictInList) - var nestedDictInListDecoded0:[[Swift.String:Swift.String]]? = nil - if let nestedDictInListContainer = nestedDictInListContainer { - nestedDictInListDecoded0 = [[Swift.String:Swift.String]]() - for map0 in nestedDictInListContainer { - var nestedDictInListContainerDecoded0: [Swift.String: Swift.String]? = nil - if let map0 = map0 { - nestedDictInListContainerDecoded0 = [Swift.String: Swift.String]() - for (key1, string1) in map0 { - if let string1 = string1 { - nestedDictInListContainerDecoded0?[key1] = string1 - } - } - } - if let nestedDictInListContainerDecoded0 = nestedDictInListContainerDecoded0 { - nestedDictInListDecoded0?.append(nestedDictInListContainerDecoded0) - } - } - } - nestedDictInList = nestedDictInListDecoded0 - let nestedListOfListInDictContainer = try containerValues.decodeIfPresent([Swift.String: [[Swift.Int?]?]?].self, forKey: .nestedListOfListInDict) - var nestedListOfListInDictDecoded0: [Swift.String:[[Swift.Int]]]? = nil - if let nestedListOfListInDictContainer = nestedListOfListInDictContainer { - nestedListOfListInDictDecoded0 = [Swift.String:[[Swift.Int]]]() - for (key0, nestedlonglist0) in nestedListOfListInDictContainer { - var nestedlonglist0Decoded0: [[Swift.Int]]? = nil - if let nestedlonglist0 = nestedlonglist0 { - nestedlonglist0Decoded0 = [[Swift.Int]]() - for list1 in nestedlonglist0 { - var list1Decoded1: [Swift.Int]? = nil - if let list1 = list1 { - list1Decoded1 = [Swift.Int]() - for long2 in list1 { - if let long2 = long2 { - list1Decoded1?.append(long2) - } - } - } - if let list1Decoded1 = list1Decoded1 { - nestedlonglist0Decoded0?.append(list1Decoded1) - } - } - } - nestedListOfListInDictDecoded0?[key0] = nestedlonglist0Decoded0 - } - } - nestedListOfListInDict = nestedListOfListInDictDecoded0 + static func read(from reader: SmithyJSON.Reader) throws -> ExampleClientTypes.RecursiveShapesInputOutputNested1 { + guard reader.hasContent else { throw SmithyReadWrite.ReaderError.requiredValueNotPresent } + var value = ExampleClientTypes.RecursiveShapesInputOutputNested1() + value.foo = try reader["foo"].readIfPresent() + value.nested = try reader["nested"].readIfPresent(with: ExampleClientTypes.RecursiveShapesInputOutputNested2.read(from:)) + return value } } - """.trimIndent() +""" contents.shouldContainOnlyOnce(expectedContents) } @Test - fun `it decodes recursive boxed types correctly`() { + fun `it encodes one side of the recursive shape`() { val contents = getModelFileContents( "example", - "RecursiveShapesInputOutputNested1+Codable.swift", + "RecursiveShapesInputOutputNested2+ReadWrite.swift", newTestContext.manifest ) contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension ExampleClientTypes.RecursiveShapesInputOutputNested1: Swift.Codable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case foo - case nested - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let foo = self.foo { - try encodeContainer.encode(foo, forKey: .foo) - } - if let nested = self.nested { - try encodeContainer.encode(nested, forKey: .nested) - } - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let fooDecoded = try containerValues.decodeIfPresent(Swift.String.self, forKey: .foo) - foo = fooDecoded - let nestedDecoded = try containerValues.decodeIfPresent(ExampleClientTypes.RecursiveShapesInputOutputNested2.self, forKey: .nested) - nested = nestedDecoded - } - } - """.trimIndent() - contents.shouldContainOnlyOnce(expectedContents) + val expectedContents = """ +extension ExampleClientTypes.RecursiveShapesInputOutputNested2 { + + static func write(value: ExampleClientTypes.RecursiveShapesInputOutputNested2?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["bar"].write(value.bar) + try writer["recursiveMember"].write(value.recursiveMember, with: ExampleClientTypes.RecursiveShapesInputOutputNested1.write(value:to:)) } - @Test - fun `it encodes one side of the recursive shape`() { - val contents = getModelFileContents( - "example", - "RecursiveShapesInputOutputNested2+Codable.swift", - newTestContext.manifest - ) - contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension ExampleClientTypes.RecursiveShapesInputOutputNested2: Swift.Codable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case bar - case recursiveMember - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let bar = self.bar { - try encodeContainer.encode(bar, forKey: .bar) - } - if let recursiveMember = self.recursiveMember { - try encodeContainer.encode(recursiveMember, forKey: .recursiveMember) - } - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let barDecoded = try containerValues.decodeIfPresent(Swift.String.self, forKey: .bar) - bar = barDecoded - let recursiveMemberDecoded = try containerValues.decodeIfPresent(ExampleClientTypes.RecursiveShapesInputOutputNested1.self, forKey: .recursiveMember) - recursiveMember = recursiveMemberDecoded - } - } - """.trimIndent() + static func read(from reader: SmithyJSON.Reader) throws -> ExampleClientTypes.RecursiveShapesInputOutputNested2 { + guard reader.hasContent else { throw SmithyReadWrite.ReaderError.requiredValueNotPresent } + var value = ExampleClientTypes.RecursiveShapesInputOutputNested2() + value.bar = try reader["bar"].readIfPresent() + value.recursiveMember = try reader["recursiveMember"].readIfPresent(with: ExampleClientTypes.RecursiveShapesInputOutputNested1.read(from:)) + return value + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } } diff --git a/smithy-swift-codegen/src/test/kotlin/StructEncodeGenerationIsolatedTests.kt b/smithy-swift-codegen/src/test/kotlin/StructEncodeGenerationIsolatedTests.kt index f87052464..54f03a0f4 100644 --- a/smithy-swift-codegen/src/test/kotlin/StructEncodeGenerationIsolatedTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/StructEncodeGenerationIsolatedTests.kt @@ -11,20 +11,20 @@ class StructEncodeGenerationIsolatedTests { @Test fun `BlobInput`() { val context = setupTests("Isolated/BlobInput.smithy", "com.test#Example") - Assertions.assertTrue(context.manifest.hasFile("/example/models/BlobInputInput+Encodable.swift")) + Assertions.assertTrue(context.manifest.hasFile("/example/models/BlobInputInput+Write.swift")) } @Test fun `BlobInput Contents`() { val context = setupTests("Isolated/BlobInput.smithy", "com.test#Example") - val contents = getModelFileContents("example", "BlobInputInput+Encodable.swift", context.manifest) + val contents = getModelFileContents("example", "BlobInputInput+Write.swift", context.manifest) contents.shouldSyntacticSanityCheck() } @Test fun `EnumInput`() { val testContext = setupTests("Isolated/EnumInput.smithy", "com.test#Example") - Assertions.assertTrue(testContext.manifest.hasFile("/example/models/EnumInputInput+Encodable.swift")) + Assertions.assertTrue(testContext.manifest.hasFile("/example/models/EnumInputInput+Write.swift")) } @Test @@ -40,96 +40,24 @@ class StructEncodeGenerationIsolatedTests { """.trimIndent() contents.shouldContainOnlyOnce(expectedContents) } - @Test - fun `NestedNested Contents`() { - val context = setupTests("Isolated/NestedNested-List.smithy", "com.test#Example") - val contents = getFileContents(context.manifest, "/example/models/NestedNestedJsonListInputBody+Decodable.swift") - contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension NestedNestedJsonListInputBody: Swift.Decodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case nestedNestedStringList - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let nestedNestedStringListContainer = try containerValues.decodeIfPresent([[[Swift.String?]?]?].self, forKey: .nestedNestedStringList) - var nestedNestedStringListDecoded0:[[[Swift.String]]]? = nil - if let nestedNestedStringListContainer = nestedNestedStringListContainer { - nestedNestedStringListDecoded0 = [[[Swift.String]]]() - for list0 in nestedNestedStringListContainer { - var list0Decoded0: [[Swift.String]]? = nil - if let list0 = list0 { - list0Decoded0 = [[Swift.String]]() - for list1 in list0 { - var list1Decoded1: [Swift.String]? = nil - if let list1 = list1 { - list1Decoded1 = [Swift.String]() - for string2 in list1 { - if let string2 = string2 { - list1Decoded1?.append(string2) - } - } - } - if let list1Decoded1 = list1Decoded1 { - list0Decoded0?.append(list1Decoded1) - } - } - } - if let list0Decoded0 = list0Decoded0 { - nestedNestedStringListDecoded0?.append(list0Decoded0) - } - } - } - nestedNestedStringList = nestedNestedStringListDecoded0 - } - } - """.trimIndent() - contents.shouldContainOnlyOnce(expectedContents) - } - @Test fun `it can handle nested string lists`() { val context = setupTests("Isolated/NestedStringList.smithy", "com.test#Example") - - val contents = getFileContents(context.manifest, "/example/models/JsonListsInput+Encodable.swift") + print(context.manifest.files) + val contents = getFileContents(context.manifest, "/example/models/JsonListsInput+Write.swift") contents.shouldSyntacticSanityCheck() val expectedContents = """ - extension JsonListsInput: Swift.Encodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case nestedStringList - case stringList - case stringSet - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let nestedStringList = nestedStringList { - var nestedStringListContainer = encodeContainer.nestedUnkeyedContainer(forKey: .nestedStringList) - for stringlist0 in nestedStringList { - var stringlist0Container = nestedStringListContainer.nestedUnkeyedContainer() - for string1 in stringlist0 { - try stringlist0Container.encode(string1) - } - } - } - if let stringList = stringList { - var stringListContainer = encodeContainer.nestedUnkeyedContainer(forKey: .stringList) - for string0 in stringList { - try stringListContainer.encode(string0) - } - } - if let stringSet = stringSet { - var stringSetContainer = encodeContainer.nestedUnkeyedContainer(forKey: .stringSet) - for string0 in stringSet { - try stringSetContainer.encode(string0) - } - } - } - } - """.trimIndent() +extension JsonListsInput { + + static func write(value: JsonListsInput?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["nestedStringList"].writeList(value.nestedStringList, memberWritingClosure: listWritingClosure(memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) + try writer["stringList"].writeList(value.stringList, memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: false) + try writer["stringSet"].writeList(value.stringSet, memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: false) + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } diff --git a/smithy-swift-codegen/src/test/kotlin/StructEncodeGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/StructEncodeGenerationTests.kt index 63f76dda5..2d8f4ebc9 100644 --- a/smithy-swift-codegen/src/test/kotlin/StructEncodeGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/StructEncodeGenerationTests.kt @@ -26,278 +26,138 @@ class StructEncodeGenerationTests { @Test fun `it creates encodable conformance in correct file`() { - Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/SmokeTestInput+Encodable.swift")) + Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/SmokeTestInput+Write.swift")) } @Test fun `it creates encodable conformance for nested structures`() { - Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/Nested+Codable.swift")) - Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/Nested2+Codable.swift")) - Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/Nested3+Codable.swift")) - Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/Nested4+Codable.swift")) + Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/Nested+ReadWrite.swift")) + Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/Nested2+ReadWrite.swift")) + Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/Nested3+ReadWrite.swift")) + Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/Nested4+ReadWrite.swift")) } @Test fun `it creates smoke test request encodable conformance`() { - val contents = getModelFileContents("example", "SmokeTestInput+Encodable.swift", newTestContext.manifest) + val contents = getModelFileContents("example", "SmokeTestInput+Write.swift", newTestContext.manifest) contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension SmokeTestInput: Swift.Encodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case payload1 - case payload2 - case payload3 - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let payload1 = self.payload1 { - try encodeContainer.encode(payload1, forKey: .payload1) - } - if let payload2 = self.payload2 { - try encodeContainer.encode(payload2, forKey: .payload2) - } - if let payload3 = self.payload3 { - try encodeContainer.encode(payload3, forKey: .payload3) - } - } - } - """.trimIndent() + val expectedContents = """ +extension SmokeTestInput { + + static func write(value: SmokeTestInput?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["payload1"].write(value.payload1) + try writer["payload2"].write(value.payload2) + try writer["payload3"].write(value.payload3, with: Nested.write(value:to:)) + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } @Test fun `it encodes nested documents with aggregate shapes`() { - val contents = getModelFileContents("example", "Nested4+Codable.swift", newTestContext.manifest) + val contents = getModelFileContents("example", "Nested4+ReadWrite.swift", newTestContext.manifest) contents.shouldSyntacticSanityCheck() val expectedContents = """ - extension Nested4: Swift.Codable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case intList - case intMap - case member1 - case stringMap - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let intList = intList { - var intListContainer = encodeContainer.nestedUnkeyedContainer(forKey: .intList) - for integer0 in intList { - try intListContainer.encode(integer0) - } - } - if let intMap = intMap { - var intMapContainer = encodeContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: .intMap) - for (dictKey0, intMap0) in intMap { - try intMapContainer.encode(intMap0, forKey: ClientRuntime.Key(stringValue: dictKey0)) - } - } - if let member1 = self.member1 { - try encodeContainer.encode(member1, forKey: .member1) - } - if let stringMap = stringMap { - var stringMapContainer = encodeContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: .stringMap) - for (dictKey0, nestedStringMap0) in stringMap { - var nestedStringMap0Container = stringMapContainer.nestedUnkeyedContainer(forKey: ClientRuntime.Key(stringValue: dictKey0)) - for string1 in nestedStringMap0 { - try nestedStringMap0Container.encode(string1) - } - } - } - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let member1Decoded = try containerValues.decodeIfPresent(Swift.Int.self, forKey: .member1) - member1 = member1Decoded - let intListContainer = try containerValues.decodeIfPresent([Swift.Int?].self, forKey: .intList) - var intListDecoded0:[Swift.Int]? = nil - if let intListContainer = intListContainer { - intListDecoded0 = [Swift.Int]() - for integer0 in intListContainer { - if let integer0 = integer0 { - intListDecoded0?.append(integer0) - } - } - } - intList = intListDecoded0 - let intMapContainer = try containerValues.decodeIfPresent([Swift.String: Swift.Int?].self, forKey: .intMap) - var intMapDecoded0: [Swift.String:Swift.Int]? = nil - if let intMapContainer = intMapContainer { - intMapDecoded0 = [Swift.String:Swift.Int]() - for (key0, integer0) in intMapContainer { - if let integer0 = integer0 { - intMapDecoded0?[key0] = integer0 - } - } - } - intMap = intMapDecoded0 - let stringMapContainer = try containerValues.decodeIfPresent([Swift.String: [Swift.String?]?].self, forKey: .stringMap) - var stringMapDecoded0: [Swift.String:[Swift.String]]? = nil - if let stringMapContainer = stringMapContainer { - stringMapDecoded0 = [Swift.String:[Swift.String]]() - for (key0, stringlist0) in stringMapContainer { - var stringlist0Decoded0: [Swift.String]? = nil - if let stringlist0 = stringlist0 { - stringlist0Decoded0 = [Swift.String]() - for string1 in stringlist0 { - if let string1 = string1 { - stringlist0Decoded0?.append(string1) - } - } - } - stringMapDecoded0?[key0] = stringlist0Decoded0 - } - } - stringMap = stringMapDecoded0 - } - } - """.trimIndent() +extension Nested4 { + + static func write(value: Nested4?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["intList"].writeList(value.intList, memberWritingClosure: Swift.Int.write(value:to:), memberNodeInfo: "member", isFlattened: false) + try writer["intMap"].writeMap(value.intMap, valueWritingClosure: Swift.Int.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + try writer["member1"].write(value.member1) + try writer["stringMap"].writeMap(value.stringMap, valueWritingClosure: listWritingClosure(memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + } + + static func read(from reader: SmithyJSON.Reader) throws -> Nested4 { + guard reader.hasContent else { throw SmithyReadWrite.ReaderError.requiredValueNotPresent } + var value = Nested4() + value.member1 = try reader["member1"].readIfPresent() + value.intList = try reader["intList"].readListIfPresent(memberReadingClosure: Swift.Int.read(from:), memberNodeInfo: "member", isFlattened: false) + value.intMap = try reader["intMap"].readMapIfPresent(valueReadingClosure: Swift.Int.read(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + value.stringMap = try reader["stringMap"].readMapIfPresent(valueReadingClosure: listReadingClosure(memberReadingClosure: Swift.String.read(from:), memberNodeInfo: "member", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + return value + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } @Test fun `it provides encodable conformance to operation inputs with timestamps`() { - val contents = getModelFileContents("example", "TimestampInputInput+Encodable.swift", newTestContext.manifest) + val contents = getModelFileContents("example", "TimestampInputInput+Write.swift", newTestContext.manifest) contents.shouldSyntacticSanityCheck() val expectedContents = """ - extension TimestampInputInput: Swift.Encodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case dateTime - case epochSeconds - case httpDate - case inheritedTimestamp - case normal - case timestampList - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let dateTime = self.dateTime { - try encodeContainer.encodeTimestamp(dateTime, format: .dateTime, forKey: .dateTime) - } - if let epochSeconds = self.epochSeconds { - try encodeContainer.encodeTimestamp(epochSeconds, format: .epochSeconds, forKey: .epochSeconds) - } - if let httpDate = self.httpDate { - try encodeContainer.encodeTimestamp(httpDate, format: .httpDate, forKey: .httpDate) - } - if let inheritedTimestamp = self.inheritedTimestamp { - try encodeContainer.encodeTimestamp(inheritedTimestamp, format: .httpDate, forKey: .inheritedTimestamp) - } - if let normal = self.normal { - try encodeContainer.encodeTimestamp(normal, format: .dateTime, forKey: .normal) - } - if let timestampList = timestampList { - var timestampListContainer = encodeContainer.nestedUnkeyedContainer(forKey: .timestampList) - for timestamp0 in timestampList { - try timestampListContainer.encodeTimestamp(timestamp0, format: .dateTime) - } - } - } - } - """.trimIndent() +extension TimestampInputInput { + + static func write(value: TimestampInputInput?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["dateTime"].writeTimestamp(value.dateTime, format: .dateTime) + try writer["epochSeconds"].writeTimestamp(value.epochSeconds, format: .epochSeconds) + try writer["httpDate"].writeTimestamp(value.httpDate, format: .httpDate) + try writer["inheritedTimestamp"].writeTimestamp(value.inheritedTimestamp, format: .httpDate) + try writer["normal"].writeTimestamp(value.normal, format: .epochSeconds) + try writer["timestampList"].writeList(value.timestampList, memberWritingClosure: timestampWritingClosure(format: .dateTime), memberNodeInfo: "member", isFlattened: false) + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } @Test fun `it encodes maps correctly`() { - val contents = getModelFileContents("example", "MapInputInput+Encodable.swift", newTestContext.manifest) + val contents = getModelFileContents("example", "MapInputInput+Write.swift", newTestContext.manifest) contents.shouldSyntacticSanityCheck() val expectedContents = """ - extension MapInputInput: Swift.Encodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case blobMap - case dateMap - case enumMap - case intMap - case structMap - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let blobMap = blobMap { - var blobMapContainer = encodeContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: .blobMap) - for (dictKey0, blobMap0) in blobMap { - try blobMapContainer.encode(blobMap0.base64EncodedString(), forKey: ClientRuntime.Key(stringValue: dictKey0)) - } - } - if let dateMap = dateMap { - var dateMapContainer = encodeContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: .dateMap) - for (dictKey0, dateMap0) in dateMap { - try dateMapContainer.encodeTimestamp(dateMap0, format: .dateTime, forKey: ClientRuntime.Key(stringValue: dictKey0)) - } - } - if let enumMap = enumMap { - var enumMapContainer = encodeContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: .enumMap) - for (dictKey0, enumMap0) in enumMap { - try enumMapContainer.encode(enumMap0.rawValue, forKey: ClientRuntime.Key(stringValue: dictKey0)) - } - } - if let intMap = intMap { - var intMapContainer = encodeContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: .intMap) - for (dictKey0, intMap0) in intMap { - try intMapContainer.encode(intMap0, forKey: ClientRuntime.Key(stringValue: dictKey0)) - } - } - if let structMap = structMap { - var structMapContainer = encodeContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: .structMap) - for (dictKey0, structMap0) in structMap { - try structMapContainer.encode(structMap0, forKey: ClientRuntime.Key(stringValue: dictKey0)) - } - } - } - } - """.trimIndent() +extension MapInputInput { + + static func write(value: MapInputInput?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["blobMap"].writeMap(value.blobMap, valueWritingClosure: ClientRuntime.Data.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + try writer["dateMap"].writeMap(value.dateMap, valueWritingClosure: timestampWritingClosure(format: .httpDate), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + try writer["enumMap"].writeMap(value.enumMap, valueWritingClosure: MyEnum.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + try writer["intMap"].writeMap(value.intMap, valueWritingClosure: Swift.Int.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + try writer["structMap"].writeMap(value.structMap, valueWritingClosure: ReachableOnlyThroughMap.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } @Test fun `it encodes nested enums correctly`() { - val contents = getModelFileContents("example", "EnumInputInput+Encodable.swift", newTestContext.manifest) + val contents = getModelFileContents("example", "EnumInputInput+Write.swift", newTestContext.manifest) contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension EnumInputInput: Swift.Encodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case nestedWithEnum - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let nestedWithEnum = self.nestedWithEnum { - try encodeContainer.encode(nestedWithEnum, forKey: .nestedWithEnum) - } - } - } - """.trimIndent() + val expectedContents = """ +extension EnumInputInput { + + static func write(value: EnumInputInput?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["nestedWithEnum"].write(value.nestedWithEnum, with: NestedEnum.write(value:to:)) + } +} +""" contents.shouldContainOnlyOnce(expectedContents) - val contents2 = getModelFileContents("example", "NestedEnum+Codable.swift", newTestContext.manifest) + val contents2 = getModelFileContents("example", "NestedEnum+ReadWrite.swift", newTestContext.manifest) contents.shouldSyntacticSanityCheck() - val expectedContents2 = - """ - extension NestedEnum: Swift.Codable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case myEnum - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let myEnum = self.myEnum { - try encodeContainer.encode(myEnum.rawValue, forKey: .myEnum) - } - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let myEnumDecoded = try containerValues.decodeIfPresent(MyEnum.self, forKey: .myEnum) - myEnum = myEnumDecoded - } - } - """.trimIndent() + val expectedContents2 = """ +extension NestedEnum { + + static func write(value: NestedEnum?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["myEnum"].write(value.myEnum) + } + + static func read(from reader: SmithyJSON.Reader) throws -> NestedEnum { + guard reader.hasContent else { throw SmithyReadWrite.ReaderError.requiredValueNotPresent } + var value = NestedEnum() + value.myEnum = try reader["myEnum"].readIfPresent() + return value + } +} +""" contents2.shouldContainOnlyOnce(expectedContents2) } @@ -305,37 +165,28 @@ class StructEncodeGenerationTests { fun `it encodes recursive boxed types correctly`() { val contents = getModelFileContents( "example", - "RecursiveShapesInputOutputNested1+Codable.swift", + "RecursiveShapesInputOutputNested1+ReadWrite.swift", newTestContext.manifest ) contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension RecursiveShapesInputOutputNested1: Swift.Codable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case foo - case nested - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let foo = self.foo { - try encodeContainer.encode(foo, forKey: .foo) - } - if let nested = self.nested { - try encodeContainer.encode(nested, forKey: .nested) - } - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let fooDecoded = try containerValues.decodeIfPresent(Swift.String.self, forKey: .foo) - foo = fooDecoded - let nestedDecoded = try containerValues.decodeIfPresent(RecursiveShapesInputOutputNested2.self, forKey: .nested) - nested = nestedDecoded - } - } - """.trimIndent() + val expectedContents = """ +extension RecursiveShapesInputOutputNested1 { + + static func write(value: RecursiveShapesInputOutputNested1?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["foo"].write(value.foo) + try writer["nested"].write(value.nested, with: RecursiveShapesInputOutputNested2.write(value:to:)) + } + + static func read(from reader: SmithyJSON.Reader) throws -> RecursiveShapesInputOutputNested1 { + guard reader.hasContent else { throw SmithyReadWrite.ReaderError.requiredValueNotPresent } + var value = RecursiveShapesInputOutputNested1() + value.foo = try reader["foo"].readIfPresent() + value.nested = try reader["nested"].readIfPresent(with: RecursiveShapesInputOutputNested2.read(from:)) + return value + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } @@ -343,277 +194,103 @@ class StructEncodeGenerationTests { fun `it encodes one side of the recursive shape`() { val contents = getModelFileContents( "example", - "RecursiveShapesInputOutputNested2+Codable.swift", + "RecursiveShapesInputOutputNested2+ReadWrite.swift", newTestContext.manifest ) contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension RecursiveShapesInputOutputNested2: Swift.Codable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case bar - case recursiveMember - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let bar = self.bar { - try encodeContainer.encode(bar, forKey: .bar) - } - if let recursiveMember = self.recursiveMember { - try encodeContainer.encode(recursiveMember, forKey: .recursiveMember) - } - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let barDecoded = try containerValues.decodeIfPresent(Swift.String.self, forKey: .bar) - bar = barDecoded - let recursiveMemberDecoded = try containerValues.decodeIfPresent(RecursiveShapesInputOutputNested1.self, forKey: .recursiveMember) - recursiveMember = recursiveMemberDecoded - } - } - """.trimIndent() + val expectedContents = """ +extension RecursiveShapesInputOutputNested2 { + + static func write(value: RecursiveShapesInputOutputNested2?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["bar"].write(value.bar) + try writer["recursiveMember"].write(value.recursiveMember, with: RecursiveShapesInputOutputNested1.write(value:to:)) + } + + static func read(from reader: SmithyJSON.Reader) throws -> RecursiveShapesInputOutputNested2 { + guard reader.hasContent else { throw SmithyReadWrite.ReaderError.requiredValueNotPresent } + var value = RecursiveShapesInputOutputNested2() + value.bar = try reader["bar"].readIfPresent() + value.recursiveMember = try reader["recursiveMember"].readIfPresent(with: RecursiveShapesInputOutputNested1.read(from:)) + return value + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } @Test fun `it encodes structure with sparse list`() { - val contents = getModelFileContents("example", "JsonListsInput+Encodable.swift", newTestContext.manifest) + val contents = getModelFileContents("example", "JsonListsInput+Write.swift", newTestContext.manifest) contents.shouldSyntacticSanityCheck() val expectedContents = """ -extension JsonListsInput: Swift.Encodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case booleanList - case integerList - case nestedStringList - case sparseStringList - case stringList - case stringSet - case timestampList - } +extension JsonListsInput { - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let booleanList = booleanList { - var booleanListContainer = encodeContainer.nestedUnkeyedContainer(forKey: .booleanList) - for boolean0 in booleanList { - try booleanListContainer.encode(boolean0) - } - } - if let integerList = integerList { - var integerListContainer = encodeContainer.nestedUnkeyedContainer(forKey: .integerList) - for integer0 in integerList { - try integerListContainer.encode(integer0) - } - } - if let nestedStringList = nestedStringList { - var nestedStringListContainer = encodeContainer.nestedUnkeyedContainer(forKey: .nestedStringList) - for stringlist0 in nestedStringList { - var stringlist0Container = nestedStringListContainer.nestedUnkeyedContainer() - for string1 in stringlist0 { - try stringlist0Container.encode(string1) - } - } - } - if let sparseStringList = sparseStringList { - var sparseStringListContainer = encodeContainer.nestedUnkeyedContainer(forKey: .sparseStringList) - for string0 in sparseStringList { - guard let string0 = string0 else { - try sparseStringListContainer.encodeNil() - continue - } - try sparseStringListContainer.encode(string0) - } - } - if let stringList = stringList { - var stringListContainer = encodeContainer.nestedUnkeyedContainer(forKey: .stringList) - for string0 in stringList { - try stringListContainer.encode(string0) - } - } - if let stringSet = stringSet { - var stringSetContainer = encodeContainer.nestedUnkeyedContainer(forKey: .stringSet) - for string0 in stringSet { - try stringSetContainer.encode(string0) - } - } - if let timestampList = timestampList { - var timestampListContainer = encodeContainer.nestedUnkeyedContainer(forKey: .timestampList) - for timestamp0 in timestampList { - try timestampListContainer.encodeTimestamp(timestamp0, format: .dateTime) - } - } + static func write(value: JsonListsInput?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["booleanList"].writeList(value.booleanList, memberWritingClosure: Swift.Bool.write(value:to:), memberNodeInfo: "member", isFlattened: false) + try writer["integerList"].writeList(value.integerList, memberWritingClosure: Swift.Int.write(value:to:), memberNodeInfo: "member", isFlattened: false) + try writer["nestedStringList"].writeList(value.nestedStringList, memberWritingClosure: listWritingClosure(memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) + try writer["sparseStringList"].writeList(value.sparseStringList, memberWritingClosure: sparseFormOf(writingClosure: Swift.String.write(value:to:)), memberNodeInfo: "member", isFlattened: false) + try writer["stringList"].writeList(value.stringList, memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: false) + try writer["stringSet"].writeList(value.stringSet, memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: false) + try writer["timestampList"].writeList(value.timestampList, memberWritingClosure: timestampWritingClosure(format: .dateTime), memberNodeInfo: "member", isFlattened: false) } } - """.trimIndent() +""" contents.shouldContainOnlyOnce(expectedContents) } @Test fun `it encodes structure with sparse map`() { - val contents = getModelFileContents("example", "JsonMapsInput+Encodable.swift", newTestContext.manifest) + val contents = getModelFileContents("example", "JsonMapsInput+Write.swift", newTestContext.manifest) contents.shouldSyntacticSanityCheck() val expectedContents = """ -extension JsonMapsInput: Swift.Encodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case denseBooleanMap - case denseNumberMap - case denseStringMap - case denseStructMap - case sparseBooleanMap - case sparseNumberMap - case sparseStringMap - case sparseStructMap - } +extension JsonMapsInput { - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let denseBooleanMap = denseBooleanMap { - var denseBooleanMapContainer = encodeContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: .denseBooleanMap) - for (dictKey0, denseBooleanMap0) in denseBooleanMap { - try denseBooleanMapContainer.encode(denseBooleanMap0, forKey: ClientRuntime.Key(stringValue: dictKey0)) - } - } - if let denseNumberMap = denseNumberMap { - var denseNumberMapContainer = encodeContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: .denseNumberMap) - for (dictKey0, denseNumberMap0) in denseNumberMap { - try denseNumberMapContainer.encode(denseNumberMap0, forKey: ClientRuntime.Key(stringValue: dictKey0)) - } - } - if let denseStringMap = denseStringMap { - var denseStringMapContainer = encodeContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: .denseStringMap) - for (dictKey0, denseStringMap0) in denseStringMap { - try denseStringMapContainer.encode(denseStringMap0, forKey: ClientRuntime.Key(stringValue: dictKey0)) - } - } - if let denseStructMap = denseStructMap { - var denseStructMapContainer = encodeContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: .denseStructMap) - for (dictKey0, denseStructMap0) in denseStructMap { - try denseStructMapContainer.encode(denseStructMap0, forKey: ClientRuntime.Key(stringValue: dictKey0)) - } - } - if let sparseBooleanMap = sparseBooleanMap { - var sparseBooleanMapContainer = encodeContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: .sparseBooleanMap) - for (dictKey0, sparseBooleanMap0) in sparseBooleanMap { - guard let sparseBooleanMap0 = sparseBooleanMap0 else { - try sparseBooleanMapContainer.encodeNil(forKey: ClientRuntime.Key(stringValue: dictKey0)) - continue - } - try sparseBooleanMapContainer.encode(sparseBooleanMap0, forKey: ClientRuntime.Key(stringValue: dictKey0)) - } - } - if let sparseNumberMap = sparseNumberMap { - var sparseNumberMapContainer = encodeContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: .sparseNumberMap) - for (dictKey0, sparseNumberMap0) in sparseNumberMap { - guard let sparseNumberMap0 = sparseNumberMap0 else { - try sparseNumberMapContainer.encodeNil(forKey: ClientRuntime.Key(stringValue: dictKey0)) - continue - } - try sparseNumberMapContainer.encode(sparseNumberMap0, forKey: ClientRuntime.Key(stringValue: dictKey0)) - } - } - if let sparseStringMap = sparseStringMap { - var sparseStringMapContainer = encodeContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: .sparseStringMap) - for (dictKey0, sparseStringMap0) in sparseStringMap { - guard let sparseStringMap0 = sparseStringMap0 else { - try sparseStringMapContainer.encodeNil(forKey: ClientRuntime.Key(stringValue: dictKey0)) - continue - } - try sparseStringMapContainer.encode(sparseStringMap0, forKey: ClientRuntime.Key(stringValue: dictKey0)) - } - } - if let sparseStructMap = sparseStructMap { - var sparseStructMapContainer = encodeContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: .sparseStructMap) - for (dictKey0, sparseStructMap0) in sparseStructMap { - guard let sparseStructMap0 = sparseStructMap0 else { - try sparseStructMapContainer.encodeNil(forKey: ClientRuntime.Key(stringValue: dictKey0)) - continue - } - try sparseStructMapContainer.encode(sparseStructMap0, forKey: ClientRuntime.Key(stringValue: dictKey0)) - } - } + static func write(value: JsonMapsInput?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["denseBooleanMap"].writeMap(value.denseBooleanMap, valueWritingClosure: Swift.Bool.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + try writer["denseNumberMap"].writeMap(value.denseNumberMap, valueWritingClosure: Swift.Int.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + try writer["denseStringMap"].writeMap(value.denseStringMap, valueWritingClosure: Swift.String.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + try writer["denseStructMap"].writeMap(value.denseStructMap, valueWritingClosure: GreetingStruct.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + try writer["sparseBooleanMap"].writeMap(value.sparseBooleanMap, valueWritingClosure: sparseFormOf(writingClosure: Swift.Bool.write(value:to:)), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + try writer["sparseNumberMap"].writeMap(value.sparseNumberMap, valueWritingClosure: sparseFormOf(writingClosure: Swift.Int.write(value:to:)), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + try writer["sparseStringMap"].writeMap(value.sparseStringMap, valueWritingClosure: sparseFormOf(writingClosure: Swift.String.write(value:to:)), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + try writer["sparseStructMap"].writeMap(value.sparseStructMap, valueWritingClosure: sparseFormOf(writingClosure: GreetingStruct.write(value:to:)), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) } } - """.trimIndent() +""" contents.shouldContainOnlyOnce(expectedContents) } @Test fun `encode checks for 0 or false for primitive types`() { - val contents = getModelFileContents("example", "PrimitiveTypesInput+Encodable.swift", newTestContext.manifest) + val contents = getModelFileContents("example", "PrimitiveTypesInput+Write.swift", newTestContext.manifest) contents.shouldSyntacticSanityCheck() - val expectedContents = - """ -extension PrimitiveTypesInput: Swift.Encodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case booleanVal - case byteVal - case doubleVal - case floatVal - case intVal - case longVal - case primitiveBooleanVal - case primitiveByteVal - case primitiveDoubleVal - case primitiveFloatVal - case primitiveIntVal - case primitiveLongVal - case primitiveShortVal - case shortVal - case str - } + val expectedContents = """ +extension PrimitiveTypesInput { - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let booleanVal = self.booleanVal { - try encodeContainer.encode(booleanVal, forKey: .booleanVal) - } - if let byteVal = self.byteVal { - try encodeContainer.encode(byteVal, forKey: .byteVal) - } - if let doubleVal = self.doubleVal { - try encodeContainer.encode(doubleVal, forKey: .doubleVal) - } - if let floatVal = self.floatVal { - try encodeContainer.encode(floatVal, forKey: .floatVal) - } - if let intVal = self.intVal { - try encodeContainer.encode(intVal, forKey: .intVal) - } - if let longVal = self.longVal { - try encodeContainer.encode(longVal, forKey: .longVal) - } - if primitiveBooleanVal != false { - try encodeContainer.encode(primitiveBooleanVal, forKey: .primitiveBooleanVal) - } - if primitiveByteVal != 0 { - try encodeContainer.encode(primitiveByteVal, forKey: .primitiveByteVal) - } - if primitiveDoubleVal != 0.0 { - try encodeContainer.encode(primitiveDoubleVal, forKey: .primitiveDoubleVal) - } - if primitiveFloatVal != 0.0 { - try encodeContainer.encode(primitiveFloatVal, forKey: .primitiveFloatVal) - } - if primitiveIntVal != 0 { - try encodeContainer.encode(primitiveIntVal, forKey: .primitiveIntVal) - } - if primitiveLongVal != 0 { - try encodeContainer.encode(primitiveLongVal, forKey: .primitiveLongVal) - } - if primitiveShortVal != 0 { - try encodeContainer.encode(primitiveShortVal, forKey: .primitiveShortVal) - } - if let shortVal = self.shortVal { - try encodeContainer.encode(shortVal, forKey: .shortVal) - } - if let str = self.str { - try encodeContainer.encode(str, forKey: .str) - } + static func write(value: PrimitiveTypesInput?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["booleanVal"].write(value.booleanVal) + try writer["byteVal"].write(value.byteVal) + try writer["doubleVal"].write(value.doubleVal) + try writer["floatVal"].write(value.floatVal) + try writer["intVal"].write(value.intVal) + try writer["longVal"].write(value.longVal) + try writer["primitiveBooleanVal"].write(value.primitiveBooleanVal) + try writer["primitiveByteVal"].write(value.primitiveByteVal) + try writer["primitiveDoubleVal"].write(value.primitiveDoubleVal) + try writer["primitiveFloatVal"].write(value.primitiveFloatVal) + try writer["primitiveIntVal"].write(value.primitiveIntVal) + try writer["primitiveLongVal"].write(value.primitiveLongVal) + try writer["primitiveShortVal"].write(value.primitiveShortVal) + try writer["shortVal"].write(value.shortVal) + try writer["str"].write(value.str) } } - """.trimIndent() +""" contents.shouldContainOnlyOnce(expectedContents) } } diff --git a/smithy-swift-codegen/src/test/kotlin/StructureGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/StructureGeneratorTests.kt index fc508fe04..377bc37b0 100644 --- a/smithy-swift-codegen/src/test/kotlin/StructureGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/StructureGeneratorTests.kt @@ -241,7 +241,7 @@ public struct RecursiveShapesInputOutputLists { } @Test - fun `it renders error structures along with proper import statement`() { + fun `it renders error structures`() { val struct: StructureShape = createStructureWithOptionalErrorMessage() val model: Model = createModelWithStructureShape(struct) @@ -254,38 +254,34 @@ public struct RecursiveShapesInputOutputLists { val contents = writer.toString() contents.shouldContain(SwiftWriter.GENERATED_FILE_HEADER) - val expectedGeneratedStructure = - """ - import ClientRuntime - - /// This is documentation about the shape. - public struct MyError: ClientRuntime.ModeledError, ClientRuntime.ServiceError, ClientRuntime.HTTPError, Swift.Error { - - public struct Properties { - /// This is documentation about the member. - public internal(set) var baz: Swift.Int? = nil - public internal(set) var message: Swift.String? = nil - } - - public internal(set) var properties = Properties() - public static var typeName: Swift.String { "MyError" } - public static var fault: ErrorFault { .client } - public static var isRetryable: Swift.Bool { true } - public static var isThrottling: Swift.Bool { false } - public internal(set) var httpResponse = HttpResponse() - public internal(set) var message: Swift.String? - public internal(set) var requestID: Swift.String? - - public init( - baz: Swift.Int? = nil, - message: Swift.String? = nil - ) - { - self.properties.baz = baz - self.properties.message = message - } - } - """.trimIndent() + val expectedGeneratedStructure = """ +public struct MyError: ClientRuntime.ModeledError, ClientRuntime.ServiceError, ClientRuntime.HTTPError, Swift.Error { + + public struct Properties { + /// This is documentation about the member. + public internal(set) var baz: Swift.Int? = nil + public internal(set) var message: Swift.String? = nil + } + + public internal(set) var properties = Properties() + public static var typeName: Swift.String { "MyError" } + public static var fault: ErrorFault { .client } + public static var isRetryable: Swift.Bool { true } + public static var isThrottling: Swift.Bool { false } + public internal(set) var httpResponse = HttpResponse() + public internal(set) var message: Swift.String? + public internal(set) var requestID: Swift.String? + + public init( + baz: Swift.Int? = nil, + message: Swift.String? = nil + ) + { + self.properties.baz = baz + self.properties.message = message + } +} +""" contents.shouldContain(expectedGeneratedStructure) } diff --git a/smithy-swift-codegen/src/test/kotlin/SymbolProviderTest.kt b/smithy-swift-codegen/src/test/kotlin/SymbolProviderTest.kt index 31884f1fd..571c14af7 100644 --- a/smithy-swift-codegen/src/test/kotlin/SymbolProviderTest.kt +++ b/smithy-swift-codegen/src/test/kotlin/SymbolProviderTest.kt @@ -59,7 +59,7 @@ class SymbolProviderTest { "PrimitiveDouble, Double, 0.0, false, Swift", "Boolean, Bool, nil, true, Swift", "PrimitiveBoolean, Bool, false, false, Swift", - "Document, Document, nil, true, ClientRuntime" + "Document, Document, nil, true, SmithyReadWrite" ) fun `creates primitives`(primitiveType: String, swiftType: String, expectedDefault: String, boxed: Boolean, namespace: String?) { val model = """ diff --git a/smithy-swift-codegen/src/test/kotlin/TestUtils.kt b/smithy-swift-codegen/src/test/kotlin/TestUtils.kt index bcbac6c31..133414ad9 100644 --- a/smithy-swift-codegen/src/test/kotlin/TestUtils.kt +++ b/smithy-swift-codegen/src/test/kotlin/TestUtils.kt @@ -25,7 +25,7 @@ import software.amazon.smithy.swift.codegen.SwiftCodegenPlugin import software.amazon.smithy.swift.codegen.SwiftDelegator import software.amazon.smithy.swift.codegen.SwiftSettings import software.amazon.smithy.swift.codegen.customtraits.SwiftBoxTrait -import software.amazon.smithy.swift.codegen.integration.HttpBindingProtocolGenerator +import software.amazon.smithy.swift.codegen.integration.HTTPBindingProtocolGenerator import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.SwiftIntegration import software.amazon.smithy.swift.codegen.model.AddOperationShapes @@ -261,7 +261,7 @@ class TestContext( fun initContextFrom( smithyFile: String, serviceShapeId: String, - httpBindingProtocolGenerator: HttpBindingProtocolGenerator? = null, + httpBindingProtocolGenerator: HTTPBindingProtocolGenerator? = null, swiftSettingCallback: ((model: Model) -> SwiftSettings)? = null ): TestContext { return initContextFrom(listOf(smithyFile), serviceShapeId, httpBindingProtocolGenerator, swiftSettingCallback) @@ -269,7 +269,7 @@ class TestContext( fun initContextFrom( smithyFiles: List, serviceShapeId: String, - httpBindingProtocolGenerator: HttpBindingProtocolGenerator? = null, + httpBindingProtocolGenerator: HTTPBindingProtocolGenerator? = null, swiftSettingCallback: ((model: Model) -> SwiftSettings)? = null, integrations: List = emptyList() ): TestContext { @@ -292,7 +292,7 @@ class TestContext( model = AddOperationShapes.execute(model, swiftSettings.getService(model), swiftSettings.moduleName) model = RecursiveShapeBoxer.transform(model) model = NestedShapeTransformer.transform(model, swiftSettings.getService(model)) - val protocolGenerator = httpBindingProtocolGenerator ?: MockHttpRestJsonProtocolGenerator() + val protocolGenerator = httpBindingProtocolGenerator ?: MockHTTPRestJsonProtocolGenerator() return model.newTestContext(manifest, serviceShapeId, swiftSettings, protocolGenerator, integrations) } } @@ -305,7 +305,7 @@ fun TestContext.expectShape(shapeId: String): Shape = fun Model.newTestContext( serviceShapeId: String = "com.test#Example", settings: SwiftSettings = this.defaultSettings(), - generator: ProtocolGenerator = MockHttpRestJsonProtocolGenerator() + generator: ProtocolGenerator = MockHTTPRestJsonProtocolGenerator() ): TestContext { return newTestContext(MockManifest(), serviceShapeId, settings, generator) } diff --git a/smithy-swift-codegen/src/test/kotlin/UnionDecodeGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/UnionDecodeGeneratorTests.kt index a8598e572..1813c3f1a 100644 --- a/smithy-swift-codegen/src/test/kotlin/UnionDecodeGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/UnionDecodeGeneratorTests.kt @@ -25,169 +25,76 @@ class UnionDecodeGeneratorTests { newTestContext.generationCtx.delegator.flushWriters() } - @Test - fun `it creates decodable conformance in correct file`() { - Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/JsonUnionsOutputBody+Decodable.swift")) - } - @Test fun `it creates decodable conformance for nested structures`() { - Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/MyUnion+Codable.swift")) - } - - @Test - fun `it decodes a union member in an operation body`() { - val contents = getModelFileContents("example", "JsonUnionsOutputBody+Decodable.swift", newTestContext.manifest) - contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - struct JsonUnionsOutputBody { - let contents: ExampleClientTypes.MyUnion? - } - - extension JsonUnionsOutputBody: Swift.Decodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case contents - } - - public init(from decoder: Swift.Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let contentsDecoded = try containerValues.decodeIfPresent(ExampleClientTypes.MyUnion.self, forKey: .contents) - contents = contentsDecoded - } - } - """.trimIndent() - contents.shouldContainOnlyOnce(expectedContents) + Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/MyUnion+ReadWrite.swift")) } @Test fun `it decodes a union with various member shape types`() { - val contents = getModelFileContents("example", "MyUnion+Codable.swift", newTestContext.manifest) + val contents = getModelFileContents("example", "MyUnion+ReadWrite.swift", newTestContext.manifest) contents.shouldSyntacticSanityCheck() val expectedContents = """ - extension ExampleClientTypes.MyUnion: Swift.Codable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case blobvalue = "blobValue" - case booleanvalue = "booleanValue" - case enumvalue = "enumValue" - case inheritedtimestamp = "inheritedTimestamp" - case listvalue = "listValue" - case mapvalue = "mapValue" - case numbervalue = "numberValue" - case sdkUnknown - case stringvalue = "stringValue" - case structurevalue = "structureValue" - case timestampvalue = "timestampValue" - } - - public func encode(to encoder: Swift.Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - switch self { - case let .blobvalue(blobvalue): - try container.encode(blobvalue.base64EncodedString(), forKey: .blobvalue) - case let .booleanvalue(booleanvalue): - try container.encode(booleanvalue, forKey: .booleanvalue) - case let .enumvalue(enumvalue): - try container.encode(enumvalue.rawValue, forKey: .enumvalue) - case let .inheritedtimestamp(inheritedtimestamp): - try container.encodeTimestamp(inheritedtimestamp, format: .httpDate, forKey: .inheritedtimestamp) - case let .listvalue(listvalue): - var listvalueContainer = container.nestedUnkeyedContainer(forKey: .listvalue) - for string0 in listvalue { - try listvalueContainer.encode(string0) - } - case let .mapvalue(mapvalue): - var mapvalueContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: .mapvalue) - for (dictKey0, stringMap0) in mapvalue { - try mapvalueContainer.encode(stringMap0, forKey: ClientRuntime.Key(stringValue: dictKey0)) - } - case let .numbervalue(numbervalue): - try container.encode(numbervalue, forKey: .numbervalue) - case let .stringvalue(stringvalue): - try container.encode(stringvalue, forKey: .stringvalue) - case let .structurevalue(structurevalue): - try container.encode(structurevalue, forKey: .structurevalue) - case let .timestampvalue(timestampvalue): - try container.encodeTimestamp(timestampvalue, format: .dateTime, forKey: .timestampvalue) - case let .sdkUnknown(sdkUnknown): - try container.encode(sdkUnknown, forKey: .sdkUnknown) - } - } - - public init(from decoder: Swift.Decoder) throws { - let values = try decoder.container(keyedBy: CodingKeys.self) - let stringvalueDecoded = try values.decodeIfPresent(Swift.String.self, forKey: .stringvalue) - if let stringvalue = stringvalueDecoded { - self = .stringvalue(stringvalue) - return - } - let booleanvalueDecoded = try values.decodeIfPresent(Swift.Bool.self, forKey: .booleanvalue) - if let booleanvalue = booleanvalueDecoded { - self = .booleanvalue(booleanvalue) - return - } - let numbervalueDecoded = try values.decodeIfPresent(Swift.Int.self, forKey: .numbervalue) - if let numbervalue = numbervalueDecoded { - self = .numbervalue(numbervalue) - return - } - let blobvalueDecoded = try values.decodeIfPresent(ClientRuntime.Data.self, forKey: .blobvalue) - if let blobvalue = blobvalueDecoded { - self = .blobvalue(blobvalue) - return - } - let timestampvalueDecoded = try values.decodeTimestampIfPresent(.dateTime, forKey: .timestampvalue) - if let timestampvalue = timestampvalueDecoded { - self = .timestampvalue(timestampvalue) - return - } - let inheritedtimestampDecoded = try values.decodeTimestampIfPresent(.httpDate, forKey: .inheritedtimestamp) - if let inheritedtimestamp = inheritedtimestampDecoded { - self = .inheritedtimestamp(inheritedtimestamp) - return - } - let enumvalueDecoded = try values.decodeIfPresent(ExampleClientTypes.FooEnum.self, forKey: .enumvalue) - if let enumvalue = enumvalueDecoded { - self = .enumvalue(enumvalue) - return - } - let listvalueContainer = try values.decodeIfPresent([Swift.String?].self, forKey: .listvalue) - var listvalueDecoded0:[Swift.String]? = nil - if let listvalueContainer = listvalueContainer { - listvalueDecoded0 = [Swift.String]() - for string0 in listvalueContainer { - if let string0 = string0 { - listvalueDecoded0?.append(string0) - } - } - } - if let listvalue = listvalueDecoded0 { - self = .listvalue(listvalue) - return - } - let mapvalueContainer = try values.decodeIfPresent([Swift.String: Swift.String?].self, forKey: .mapvalue) - var mapvalueDecoded0: [Swift.String:Swift.String]? = nil - if let mapvalueContainer = mapvalueContainer { - mapvalueDecoded0 = [Swift.String:Swift.String]() - for (key0, string0) in mapvalueContainer { - if let string0 = string0 { - mapvalueDecoded0?[key0] = string0 - } - } - } - if let mapvalue = mapvalueDecoded0 { - self = .mapvalue(mapvalue) - return - } - let structurevalueDecoded = try values.decodeIfPresent(ExampleClientTypes.GreetingWithErrorsOutput.self, forKey: .structurevalue) - if let structurevalue = structurevalueDecoded { - self = .structurevalue(structurevalue) - return - } - self = .sdkUnknown("") - } - } - """.trimIndent() +extension ExampleClientTypes.MyUnion { + + static func write(value: ExampleClientTypes.MyUnion?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + switch value { + case let .blobvalue(blobvalue): + try writer["blobValue"].write(blobvalue) + case let .booleanvalue(booleanvalue): + try writer["booleanValue"].write(booleanvalue) + case let .enumvalue(enumvalue): + try writer["enumValue"].write(enumvalue) + case let .inheritedtimestamp(inheritedtimestamp): + try writer["inheritedTimestamp"].writeTimestamp(inheritedtimestamp, format: .httpDate) + case let .listvalue(listvalue): + try writer["listValue"].writeList(listvalue, memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: false) + case let .mapvalue(mapvalue): + try writer["mapValue"].writeMap(mapvalue, valueWritingClosure: Swift.String.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + case let .numbervalue(numbervalue): + try writer["numberValue"].write(numbervalue) + case let .stringvalue(stringvalue): + try writer["stringValue"].write(stringvalue) + case let .structurevalue(structurevalue): + try writer["structureValue"].write(structurevalue, with: ExampleClientTypes.GreetingWithErrorsOutput.write(value:to:)) + case let .timestampvalue(timestampvalue): + try writer["timestampValue"].writeTimestamp(timestampvalue, format: .epochSeconds) + case let .sdkUnknown(sdkUnknown): + try writer["sdkUnknown"].write(sdkUnknown) + } + } + + static func read(from reader: SmithyJSON.Reader) throws -> ExampleClientTypes.MyUnion { + 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 "stringValue": + return .stringvalue(try reader["stringValue"].read()) + case "booleanValue": + return .booleanvalue(try reader["booleanValue"].read()) + case "numberValue": + return .numbervalue(try reader["numberValue"].read()) + case "blobValue": + return .blobvalue(try reader["blobValue"].read()) + case "timestampValue": + return .timestampvalue(try reader["timestampValue"].readTimestamp(format: .epochSeconds)) + case "inheritedTimestamp": + return .inheritedtimestamp(try reader["inheritedTimestamp"].readTimestamp(format: .httpDate)) + case "enumValue": + return .enumvalue(try reader["enumValue"].read()) + case "listValue": + return .listvalue(try reader["listValue"].readList(memberReadingClosure: Swift.String.read(from:), memberNodeInfo: "member", isFlattened: false)) + case "mapValue": + return .mapvalue(try reader["mapValue"].readMap(valueReadingClosure: Swift.String.read(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false)) + case "structureValue": + return .structurevalue(try reader["structureValue"].read(with: ExampleClientTypes.GreetingWithErrorsOutput.read(from:))) + default: + return .sdkUnknown(name ?? "") + } + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } } diff --git a/smithy-swift-codegen/src/test/kotlin/UnionEncodeGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/UnionEncodeGeneratorTests.kt index 7d9e2832a..b61958c7e 100644 --- a/smithy-swift-codegen/src/test/kotlin/UnionEncodeGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/UnionEncodeGeneratorTests.kt @@ -34,208 +34,131 @@ class UnionEncodeGeneratorTests { @Test fun `it creates encodable conformance in correct file`() { - Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/JsonUnionsInput+Encodable.swift")) + Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/JsonUnionsInput+Write.swift")) } @Test fun `it creates encodable conformance for nested structures`() { - Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/MyUnion+Codable.swift")) + Assertions.assertTrue(newTestContext.manifest.hasFile("/example/models/MyUnion+ReadWrite.swift")) } @Test fun `it encodes a union member in an operation`() { - val contents = getModelFileContents("example", "JsonUnionsInput+Encodable.swift", newTestContext.manifest) + val contents = getModelFileContents("example", "JsonUnionsInput+Write.swift", newTestContext.manifest) contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension JsonUnionsInput: Swift.Encodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case contents - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let contents = self.contents { - try encodeContainer.encode(contents, forKey: .contents) - } - } - } - """.trimIndent() + val expectedContents = """ +extension JsonUnionsInput { + + static func write(value: JsonUnionsInput?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["contents"].write(value.contents, with: ExampleClientTypes.MyUnion.write(value:to:)) + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } @Test fun `it encodes a union with various member shape types`() { - val contents = getModelFileContents("example", "MyUnion+Codable.swift", newTestContext.manifest) + val contents = getModelFileContents("example", "MyUnion+ReadWrite.swift", newTestContext.manifest) contents.shouldSyntacticSanityCheck() val expectedContents = """ - extension ExampleClientTypes.MyUnion: Swift.Codable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case blobvalue = "blobValue" - case booleanvalue = "booleanValue" - case enumvalue = "enumValue" - case inheritedtimestamp = "inheritedTimestamp" - case listvalue = "listValue" - case mapvalue = "mapValue" - case numbervalue = "numberValue" - case sdkUnknown - case stringvalue = "stringValue" - case structurevalue = "structureValue" - case timestampvalue = "timestampValue" - } - - public func encode(to encoder: Swift.Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - switch self { - case let .blobvalue(blobvalue): - try container.encode(blobvalue.base64EncodedString(), forKey: .blobvalue) - case let .booleanvalue(booleanvalue): - try container.encode(booleanvalue, forKey: .booleanvalue) - case let .enumvalue(enumvalue): - try container.encode(enumvalue.rawValue, forKey: .enumvalue) - case let .inheritedtimestamp(inheritedtimestamp): - try container.encodeTimestamp(inheritedtimestamp, format: .httpDate, forKey: .inheritedtimestamp) - case let .listvalue(listvalue): - var listvalueContainer = container.nestedUnkeyedContainer(forKey: .listvalue) - for string0 in listvalue { - try listvalueContainer.encode(string0) - } - case let .mapvalue(mapvalue): - var mapvalueContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: .mapvalue) - for (dictKey0, stringMap0) in mapvalue { - try mapvalueContainer.encode(stringMap0, forKey: ClientRuntime.Key(stringValue: dictKey0)) - } - case let .numbervalue(numbervalue): - try container.encode(numbervalue, forKey: .numbervalue) - case let .stringvalue(stringvalue): - try container.encode(stringvalue, forKey: .stringvalue) - case let .structurevalue(structurevalue): - try container.encode(structurevalue, forKey: .structurevalue) - case let .timestampvalue(timestampvalue): - try container.encodeTimestamp(timestampvalue, format: .dateTime, forKey: .timestampvalue) - case let .sdkUnknown(sdkUnknown): - try container.encode(sdkUnknown, forKey: .sdkUnknown) - } - } - - public init(from decoder: Swift.Decoder) throws { - let values = try decoder.container(keyedBy: CodingKeys.self) - let stringvalueDecoded = try values.decodeIfPresent(Swift.String.self, forKey: .stringvalue) - if let stringvalue = stringvalueDecoded { - self = .stringvalue(stringvalue) - return - } - let booleanvalueDecoded = try values.decodeIfPresent(Swift.Bool.self, forKey: .booleanvalue) - if let booleanvalue = booleanvalueDecoded { - self = .booleanvalue(booleanvalue) - return - } - let numbervalueDecoded = try values.decodeIfPresent(Swift.Int.self, forKey: .numbervalue) - if let numbervalue = numbervalueDecoded { - self = .numbervalue(numbervalue) - return - } - let blobvalueDecoded = try values.decodeIfPresent(ClientRuntime.Data.self, forKey: .blobvalue) - if let blobvalue = blobvalueDecoded { - self = .blobvalue(blobvalue) - return - } - let timestampvalueDecoded = try values.decodeTimestampIfPresent(.dateTime, forKey: .timestampvalue) - if let timestampvalue = timestampvalueDecoded { - self = .timestampvalue(timestampvalue) - return - } - let inheritedtimestampDecoded = try values.decodeTimestampIfPresent(.httpDate, forKey: .inheritedtimestamp) - if let inheritedtimestamp = inheritedtimestampDecoded { - self = .inheritedtimestamp(inheritedtimestamp) - return - } - let enumvalueDecoded = try values.decodeIfPresent(ExampleClientTypes.FooEnum.self, forKey: .enumvalue) - if let enumvalue = enumvalueDecoded { - self = .enumvalue(enumvalue) - return - } - let listvalueContainer = try values.decodeIfPresent([Swift.String?].self, forKey: .listvalue) - var listvalueDecoded0:[Swift.String]? = nil - if let listvalueContainer = listvalueContainer { - listvalueDecoded0 = [Swift.String]() - for string0 in listvalueContainer { - if let string0 = string0 { - listvalueDecoded0?.append(string0) - } - } - } - if let listvalue = listvalueDecoded0 { - self = .listvalue(listvalue) - return - } - let mapvalueContainer = try values.decodeIfPresent([Swift.String: Swift.String?].self, forKey: .mapvalue) - var mapvalueDecoded0: [Swift.String:Swift.String]? = nil - if let mapvalueContainer = mapvalueContainer { - mapvalueDecoded0 = [Swift.String:Swift.String]() - for (key0, string0) in mapvalueContainer { - if let string0 = string0 { - mapvalueDecoded0?[key0] = string0 - } - } - } - if let mapvalue = mapvalueDecoded0 { - self = .mapvalue(mapvalue) - return - } - let structurevalueDecoded = try values.decodeIfPresent(ExampleClientTypes.GreetingWithErrorsOutput.self, forKey: .structurevalue) - if let structurevalue = structurevalueDecoded { - self = .structurevalue(structurevalue) - return - } - self = .sdkUnknown("") - } - } - """.trimIndent() +extension ExampleClientTypes.MyUnion { + + static func write(value: ExampleClientTypes.MyUnion?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + switch value { + case let .blobvalue(blobvalue): + try writer["blobValue"].write(blobvalue) + case let .booleanvalue(booleanvalue): + try writer["booleanValue"].write(booleanvalue) + case let .enumvalue(enumvalue): + try writer["enumValue"].write(enumvalue) + case let .inheritedtimestamp(inheritedtimestamp): + try writer["inheritedTimestamp"].writeTimestamp(inheritedtimestamp, format: .httpDate) + case let .listvalue(listvalue): + try writer["listValue"].writeList(listvalue, memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: false) + case let .mapvalue(mapvalue): + try writer["mapValue"].writeMap(mapvalue, valueWritingClosure: Swift.String.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + case let .numbervalue(numbervalue): + try writer["numberValue"].write(numbervalue) + case let .stringvalue(stringvalue): + try writer["stringValue"].write(stringvalue) + case let .structurevalue(structurevalue): + try writer["structureValue"].write(structurevalue, with: ExampleClientTypes.GreetingWithErrorsOutput.write(value:to:)) + case let .timestampvalue(timestampvalue): + try writer["timestampValue"].writeTimestamp(timestampvalue, format: .epochSeconds) + case let .sdkUnknown(sdkUnknown): + try writer["sdkUnknown"].write(sdkUnknown) + } + } + + static func read(from reader: SmithyJSON.Reader) throws -> ExampleClientTypes.MyUnion { + 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 "stringValue": + return .stringvalue(try reader["stringValue"].read()) + case "booleanValue": + return .booleanvalue(try reader["booleanValue"].read()) + case "numberValue": + return .numbervalue(try reader["numberValue"].read()) + case "blobValue": + return .blobvalue(try reader["blobValue"].read()) + case "timestampValue": + return .timestampvalue(try reader["timestampValue"].readTimestamp(format: .epochSeconds)) + case "inheritedTimestamp": + return .inheritedtimestamp(try reader["inheritedTimestamp"].readTimestamp(format: .httpDate)) + case "enumValue": + return .enumvalue(try reader["enumValue"].read()) + case "listValue": + return .listvalue(try reader["listValue"].readList(memberReadingClosure: Swift.String.read(from:), memberNodeInfo: "member", isFlattened: false)) + case "mapValue": + return .mapvalue(try reader["mapValue"].readMap(valueReadingClosure: Swift.String.read(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false)) + case "structureValue": + return .structurevalue(try reader["structureValue"].read(with: ExampleClientTypes.GreetingWithErrorsOutput.read(from:))) + default: + return .sdkUnknown(name ?? "") + } + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } @Test fun `it generates codable conformance for a recursive union`() { - val contents = getModelFileContents("example", "IndirectEnum+Codable.swift", newTestContext.manifest) + val contents = getModelFileContents("example", "IndirectEnum+ReadWrite.swift", newTestContext.manifest) contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension ExampleClientTypes.IndirectEnum: Swift.Codable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case other - case sdkUnknown - case some - } + val expectedContents = """ +extension ExampleClientTypes.IndirectEnum { - public func encode(to encoder: Swift.Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - switch self { - case let .other(other): - try container.encode(other, forKey: .other) - case let .some(some): - try container.encode(some, forKey: .some) - case let .sdkUnknown(sdkUnknown): - try container.encode(sdkUnknown, forKey: .sdkUnknown) - } - } + static func write(value: ExampleClientTypes.IndirectEnum?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + switch value { + case let .other(other): + try writer["other"].write(other) + case let .some(some): + try writer["some"].write(some, with: ExampleClientTypes.IndirectEnum.write(value:to:)) + case let .sdkUnknown(sdkUnknown): + try writer["sdkUnknown"].write(sdkUnknown) + } + } - public init(from decoder: Swift.Decoder) throws { - let values = try decoder.container(keyedBy: CodingKeys.self) - let someDecoded = try values.decodeIfPresent(ExampleClientTypes.IndirectEnum.self, forKey: .some) - if let some = someDecoded { - self = .some(some) - return - } - let otherDecoded = try values.decodeIfPresent(Swift.String.self, forKey: .other) - if let other = otherDecoded { - self = .other(other) - return - } - self = .sdkUnknown("") - } - } - """.trimIndent() + static func read(from reader: SmithyJSON.Reader) throws -> ExampleClientTypes.IndirectEnum { + 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 "some": + return .some(try reader["some"].read(with: ExampleClientTypes.IndirectEnum.read(from:))) + case "other": + return .other(try reader["other"].read()) + default: + return .sdkUnknown(name ?? "") + } + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } } diff --git a/smithy-swift-codegen/src/test/kotlin/httpResponse/HttpResponseBindingIgnoreQuery.kt b/smithy-swift-codegen/src/test/kotlin/httpResponse/HttpResponseBindingIgnoreQuery.kt index 4903fadd1..7a06ba218 100644 --- a/smithy-swift-codegen/src/test/kotlin/httpResponse/HttpResponseBindingIgnoreQuery.kt +++ b/smithy-swift-codegen/src/test/kotlin/httpResponse/HttpResponseBindingIgnoreQuery.kt @@ -5,7 +5,7 @@ package httpResponse -import MockHttpRestJsonProtocolGenerator +import MockHTTPRestJsonProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -20,19 +20,24 @@ class HttpResponseBindingIgnoreQuery { val context = setupTests("http-query-payload.smithy", "aws.protocoltests.restjson#RestJson") val contents = getFileContents(context.manifest, "/RestJson/models/IgnoreQueryParamsInResponseOutput+HttpResponseBinding.swift") contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension IgnoreQueryParamsInResponseOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { - self.baz = nil - } - } - """.trimIndent() + val expectedContents = """ +extension IgnoreQueryParamsInResponseOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> IgnoreQueryParamsInResponseOutput { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let reader = responseReader + var value = IgnoreQueryParamsInResponseOutput() + value.baz = nil + return value + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestJsonProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestJsonProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestJson", "2019-12-16", "Rest Json Protocol") } context.generator.generateDeserializers(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/mocks/MockHttpAWSJson11ProtocolGenerator.kt b/smithy-swift-codegen/src/test/kotlin/mocks/MockHTTPAWSJson11ProtocolGenerator.kt similarity index 67% rename from smithy-swift-codegen/src/test/kotlin/mocks/MockHttpAWSJson11ProtocolGenerator.kt rename to smithy-swift-codegen/src/test/kotlin/mocks/MockHTTPAWSJson11ProtocolGenerator.kt index 6fd1ef452..0961c1f9f 100644 --- a/smithy-swift-codegen/src/test/kotlin/mocks/MockHttpAWSJson11ProtocolGenerator.kt +++ b/smithy-swift-codegen/src/test/kotlin/mocks/MockHTTPAWSJson11ProtocolGenerator.kt @@ -14,23 +14,17 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.model.traits.TimestampFormatTrait 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.DefaultHTTPProtocolCustomizations +import software.amazon.smithy.swift.codegen.integration.HTTPBindingProtocolGenerator import software.amazon.smithy.swift.codegen.integration.HttpBindingResolver 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.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.codingKeys.CodingKeysCustomizationJsonName -import software.amazon.smithy.swift.codegen.integration.codingKeys.CodingKeysGenerator -import software.amazon.smithy.swift.codegen.integration.codingKeys.DefaultCodingKeysGenerator -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingOutputGenerator -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseGeneratable -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseGenerator import software.amazon.smithy.swift.codegen.integration.protocols.core.StaticHttpBindingResolver -import software.amazon.smithy.swift.codegen.integration.serde.json.StructDecodeGenerator -import software.amazon.smithy.swift.codegen.integration.serde.json.StructEncodeGenerator +import software.amazon.smithy.swift.codegen.integration.serde.struct.StructDecodeGenerator +import software.amazon.smithy.swift.codegen.integration.serde.struct.StructEncodeGenerator import software.amazon.smithy.swift.codegen.model.ShapeMetadata class MockJsonHttpBindingResolver( @@ -47,7 +41,7 @@ class MockJsonHttpBindingResolver( .build() } } -class MockAWSJson11HttpProtocolCustomizations() : DefaultHttpProtocolCustomizations() { +class MockAWSJson11Customizations() : DefaultHTTPProtocolCustomizations() { override fun renderEventStreamAttributes( ctx: ProtocolGenerator.GenerationContext, writer: SwiftWriter, @@ -58,9 +52,8 @@ class MockAWSJson11HttpProtocolCustomizations() : DefaultHttpProtocolCustomizati } } -class MockHttpAWSJson11ProtocolGenerator : HttpBindingProtocolGenerator() { +class MockHTTPAWSJson11ProtocolGenerator() : HTTPBindingProtocolGenerator(MockAWSJson11Customizations()) { override val defaultContentType: String = "application/json" - override val defaultTimestampFormat: TimestampFormatTrait.Format = TimestampFormatTrait.Format.DATE_TIME override val protocol: ShapeId = AwsJson1_1Trait.ID override fun generateProtocolUnitTests(ctx: ProtocolGenerator.GenerationContext): Int { val requestTestBuilder = HttpProtocolUnitTestRequestGenerator.Builder() @@ -71,24 +64,13 @@ class MockHttpAWSJson11ProtocolGenerator : HttpBindingProtocolGenerator() { requestTestBuilder, responseTestBuilder, errorTestBuilder, - httpProtocolCustomizable, + customizations, operationMiddleware, getProtocolHttpBindingResolver(ctx, defaultContentType), -// HttpProtocolUnitTestGenerator.SerdeContext("JSONEncoder()", "JSONDecoder()", ".secondsSince1970") ).generateProtocolTests() } override val httpProtocolClientGeneratorFactory = TestHttpProtocolClientGeneratorFactory() - override val httpProtocolCustomizable = MockAWSJson11HttpProtocolCustomizations() - override val codingKeysGenerator: CodingKeysGenerator = DefaultCodingKeysGenerator(CodingKeysCustomizationJsonName()) - override val httpResponseGenerator: HttpResponseGeneratable = HttpResponseGenerator( - unknownServiceErrorSymbol, - defaultTimestampFormat, - HttpResponseBindingOutputGenerator(), - MockHttpResponseBindingErrorGenerator() - ) - override val shouldRenderDecodableBodyStructForInputShapes = true - override val shouldRenderCodingKeysForEncodable = true override val shouldRenderEncodableConformance = false override fun renderStructEncode( @@ -100,7 +82,7 @@ class MockHttpAWSJson11ProtocolGenerator : HttpBindingProtocolGenerator() { defaultTimestampFormat: TimestampFormatTrait.Format, path: String? ) { - val encodeGenerator = StructEncodeGenerator(ctx, members, writer, defaultTimestampFormat, path) + val encodeGenerator = StructEncodeGenerator(ctx, shapeContainingMembers, members, shapeMetadata, writer) encodeGenerator.render() } override fun renderStructDecode( @@ -112,8 +94,7 @@ class MockHttpAWSJson11ProtocolGenerator : HttpBindingProtocolGenerator() { defaultTimestampFormat: TimestampFormatTrait.Format, path: String ) { - val decodeGenerator = StructDecodeGenerator(ctx, members, writer, defaultTimestampFormat, path) - decodeGenerator.render() + StructDecodeGenerator(ctx, shapeContainingMembers, members, shapeMetadata, writer).render() } override fun addProtocolSpecificMiddleware(ctx: ProtocolGenerator.GenerationContext, operation: OperationShape) { diff --git a/smithy-swift-codegen/src/test/kotlin/mocks/MockHttpEC2QueryProtocolGenerator.kt b/smithy-swift-codegen/src/test/kotlin/mocks/MockHTTPEC2QueryProtocolGenerator.kt similarity index 62% rename from smithy-swift-codegen/src/test/kotlin/mocks/MockHttpEC2QueryProtocolGenerator.kt rename to smithy-swift-codegen/src/test/kotlin/mocks/MockHTTPEC2QueryProtocolGenerator.kt index 34afdf142..0129f5c53 100644 --- a/smithy-swift-codegen/src/test/kotlin/mocks/MockHttpEC2QueryProtocolGenerator.kt +++ b/smithy-swift-codegen/src/test/kotlin/mocks/MockHTTPEC2QueryProtocolGenerator.kt @@ -15,25 +15,20 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.model.traits.TimestampFormatTrait 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.DefaultHTTPProtocolCustomizations +import software.amazon.smithy.swift.codegen.integration.HTTPBindingProtocolGenerator import software.amazon.smithy.swift.codegen.integration.HttpBindingResolver 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.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingOutputGenerator -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseGeneratable -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseGenerator import software.amazon.smithy.swift.codegen.integration.protocols.core.StaticHttpBindingResolver -import software.amazon.smithy.swift.codegen.integration.serde.formurl.FormURLEncodeCustomizable -import software.amazon.smithy.swift.codegen.integration.serde.formurl.StructEncodeFormURLGenerator -import software.amazon.smithy.swift.codegen.integration.serde.formurl.trait.Ec2QueryNameTraitGenerator -import software.amazon.smithy.swift.codegen.integration.serde.xml.StructDecodeXMLGenerator +import software.amazon.smithy.swift.codegen.integration.serde.struct.StructDecodeGenerator +import software.amazon.smithy.swift.codegen.integration.serde.struct.StructEncodeGenerator import software.amazon.smithy.swift.codegen.model.ShapeMetadata -class MockEC2QueryHttpProtocolCustomizations() : DefaultHttpProtocolCustomizations() +class MockEC2QueryHTTPProtocolCustomizations() : DefaultHTTPProtocolCustomizations() class MockEC2QueryHttpBindingResolver( private val context: ProtocolGenerator.GenerationContext, @@ -50,34 +45,10 @@ class MockEC2QueryHttpBindingResolver( } } -class MockEc2QueryFormURLEncodeCustomizations : FormURLEncodeCustomizable { - override fun alwaysUsesFlattenedCollections(): Boolean { - return true - } - override fun customNameTraitGenerator(shape: Shape, defaultName: String): String { - return Ec2QueryNameTraitGenerator.construct(shape, defaultName).toString() - } - - override fun shouldSerializeEmptyLists(): Boolean { - return true - } -} - -class MockHttpEC2QueryProtocolGenerator : HttpBindingProtocolGenerator() { +class MockHTTPEC2QueryProtocolGenerator : HTTPBindingProtocolGenerator(MockEC2QueryHTTPProtocolCustomizations()) { override val defaultContentType: String = "application/x-www-form-urlencoded" - override val defaultTimestampFormat: TimestampFormatTrait.Format = TimestampFormatTrait.Format.DATE_TIME override val protocol: ShapeId = Ec2QueryTrait.ID override val httpProtocolClientGeneratorFactory = TestHttpProtocolClientGeneratorFactory() - override val httpProtocolCustomizable = MockEC2QueryHttpProtocolCustomizations() - override val codingKeysGenerator = null - override val httpResponseGenerator: HttpResponseGeneratable = HttpResponseGenerator( - unknownServiceErrorSymbol, - defaultTimestampFormat, - HttpResponseBindingOutputGenerator(), - MockHttpResponseBindingErrorGenerator() - ) - override val shouldRenderDecodableBodyStructForInputShapes = false - override val shouldRenderCodingKeysForEncodable = false override val shouldRenderEncodableConformance = true override fun renderStructEncode( ctx: ProtocolGenerator.GenerationContext, @@ -88,9 +59,7 @@ class MockHttpEC2QueryProtocolGenerator : HttpBindingProtocolGenerator() { defaultTimestampFormat: TimestampFormatTrait.Format, path: String? ) { - val customizations = MockEc2QueryFormURLEncodeCustomizations() - val encodeGenerator = StructEncodeFormURLGenerator(ctx, customizations, shapeContainingMembers, shapeMetadata, members, writer, defaultTimestampFormat) - encodeGenerator.render() + StructEncodeGenerator(ctx, shapeContainingMembers, members, shapeMetadata, writer).render() } override fun renderStructDecode( ctx: ProtocolGenerator.GenerationContext, @@ -101,7 +70,7 @@ class MockHttpEC2QueryProtocolGenerator : HttpBindingProtocolGenerator() { defaultTimestampFormat: TimestampFormatTrait.Format, path: String ) { - val decodeGenerator = StructDecodeXMLGenerator(ctx, shapeContainingMembers, members, mapOf(), writer) + val decodeGenerator = StructDecodeGenerator(ctx, shapeContainingMembers, members, mapOf(), writer) decodeGenerator.render() } @@ -130,10 +99,9 @@ class MockHttpEC2QueryProtocolGenerator : HttpBindingProtocolGenerator() { requestTestBuilder, responseTestBuilder, errorTestBuilder, - httpProtocolCustomizable, + customizations, operationMiddleware, getProtocolHttpBindingResolver(ctx, defaultContentType), -// HttpProtocolUnitTestGenerator.SerdeContext("FormURLEncoder()", null, ".secondsSince1970") ).generateProtocolTests() } } diff --git a/smithy-swift-codegen/src/test/kotlin/mocks/MockHTTPRestJsonProtocolGenerator.kt b/smithy-swift-codegen/src/test/kotlin/mocks/MockHTTPRestJsonProtocolGenerator.kt new file mode 100644 index 000000000..58f618c31 --- /dev/null +++ b/smithy-swift-codegen/src/test/kotlin/mocks/MockHTTPRestJsonProtocolGenerator.kt @@ -0,0 +1,81 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +import software.amazon.smithy.aws.traits.protocols.RestJson1Trait +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.model.traits.TimestampFormatTrait +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.ProtocolGenerator +import software.amazon.smithy.swift.codegen.integration.serde.struct.StructDecodeGenerator +import software.amazon.smithy.swift.codegen.integration.serde.struct.StructEncodeGenerator +import software.amazon.smithy.swift.codegen.model.ShapeMetadata + +class MockRestJsonHTTPProtocolCustomizations() : DefaultHTTPProtocolCustomizations() +class MockHTTPRestJsonProtocolGenerator : HTTPBindingProtocolGenerator(MockRestJsonHTTPProtocolCustomizations()) { + override val defaultContentType: String = "application/json" + override val protocol: ShapeId = RestJson1Trait.ID + override val httpProtocolClientGeneratorFactory = TestHttpProtocolClientGeneratorFactory() + override val shouldRenderEncodableConformance = false + + override fun renderStructEncode( + ctx: ProtocolGenerator.GenerationContext, + shapeContainingMembers: Shape, + shapeMetadata: Map, + members: List, + writer: SwiftWriter, + defaultTimestampFormat: TimestampFormatTrait.Format, + path: String? + ) { + StructEncodeGenerator(ctx, shapeContainingMembers, members, shapeMetadata, writer).render() + } + override fun renderStructDecode( + ctx: ProtocolGenerator.GenerationContext, + shapeContainingMembers: Shape, + shapeMetadata: Map, + members: List, + writer: SwiftWriter, + defaultTimestampFormat: TimestampFormatTrait.Format, + path: String + ) { + StructDecodeGenerator(ctx, shapeContainingMembers, members, shapeMetadata, writer).render() + } + + override fun addProtocolSpecificMiddleware(ctx: ProtocolGenerator.GenerationContext, operation: OperationShape) { + // Intentionally empty + } + + override fun generateMessageMarshallable(ctx: ProtocolGenerator.GenerationContext) { + TODO("Not yet implemented") + } + + override fun generateMessageUnmarshallable(ctx: ProtocolGenerator.GenerationContext) { + TODO("Not yet implemented") + } + + override fun generateProtocolUnitTests(ctx: ProtocolGenerator.GenerationContext): Int { + val requestTestBuilder = HttpProtocolUnitTestRequestGenerator.Builder() + val responseTestBuilder = HttpProtocolUnitTestResponseGenerator.Builder() + val errorTestBuilder = HttpProtocolUnitTestErrorGenerator.Builder() + + return HttpProtocolTestGenerator( + ctx, + requestTestBuilder, + responseTestBuilder, + errorTestBuilder, + customizations, + operationMiddleware, + getProtocolHttpBindingResolver(ctx, defaultContentType), + ).generateProtocolTests() + } +} diff --git a/smithy-swift-codegen/src/test/kotlin/mocks/MockHttpRestXMLProtocolGenerator.kt b/smithy-swift-codegen/src/test/kotlin/mocks/MockHTTPRestXMLProtocolGenerator.kt similarity index 64% rename from smithy-swift-codegen/src/test/kotlin/mocks/MockHttpRestXMLProtocolGenerator.kt rename to smithy-swift-codegen/src/test/kotlin/mocks/MockHTTPRestXMLProtocolGenerator.kt index 428db64ba..eb6c0a6d2 100644 --- a/smithy-swift-codegen/src/test/kotlin/mocks/MockHttpRestXMLProtocolGenerator.kt +++ b/smithy-swift-codegen/src/test/kotlin/mocks/MockHTTPRestXMLProtocolGenerator.kt @@ -3,7 +3,6 @@ * SPDX-License-Identifier: Apache-2.0. */ -import mocks.MockHttpResponseBindingErrorGenerator import software.amazon.smithy.aws.traits.protocols.RestXmlTrait import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.OperationShape @@ -11,43 +10,25 @@ import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.traits.TimestampFormatTrait 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.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.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingOutputGenerator -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseGeneratable -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseGenerator -import software.amazon.smithy.swift.codegen.integration.serde.json.StructEncodeXMLGenerator -import software.amazon.smithy.swift.codegen.integration.serde.xml.StructDecodeXMLGenerator +import software.amazon.smithy.swift.codegen.integration.serde.struct.StructDecodeGenerator +import software.amazon.smithy.swift.codegen.integration.serde.struct.StructEncodeGenerator import software.amazon.smithy.swift.codegen.model.ShapeMetadata -class MockRestXMLHttpProtocolCustomizations() : DefaultHttpProtocolCustomizations() +class MockRestXMLHTTPProtocolCustomizations() : DefaultHTTPProtocolCustomizations() -class MockHttpRestXMLProtocolGenerator : HttpBindingProtocolGenerator() { +class MockHTTPRestXMLProtocolGenerator : HTTPBindingProtocolGenerator(MockRestXMLHTTPProtocolCustomizations()) { override val defaultContentType: String = "application/xml" - override val defaultTimestampFormat: TimestampFormatTrait.Format = TimestampFormatTrait.Format.DATE_TIME override val protocol: ShapeId = RestXmlTrait.ID override val httpProtocolClientGeneratorFactory = TestHttpProtocolClientGeneratorFactory() - override val httpProtocolCustomizable = MockRestXMLHttpProtocolCustomizations() - override val codingKeysGenerator = null - override val httpResponseGenerator: HttpResponseGeneratable = HttpResponseGenerator( - unknownServiceErrorSymbol, - defaultTimestampFormat, - HttpResponseBindingOutputGenerator(), - MockHttpResponseBindingErrorGenerator() - ) - override val shouldRenderDecodableBodyStructForInputShapes = false - override val shouldRenderCodingKeysForEncodable = false override val shouldRenderEncodableConformance = false - override val codableProtocol = null - override val encodableProtocol = null - override val decodableProtocol = null - override fun renderStructEncode( ctx: ProtocolGenerator.GenerationContext, shapeContainingMembers: Shape, @@ -57,7 +38,7 @@ class MockHttpRestXMLProtocolGenerator : HttpBindingProtocolGenerator() { defaultTimestampFormat: TimestampFormatTrait.Format, path: String? ) { - val encoder = StructEncodeXMLGenerator(ctx, shapeContainingMembers, members, writer) + val encoder = StructEncodeGenerator(ctx, shapeContainingMembers, members, shapeMetadata, writer) encoder.render() } override fun renderStructDecode( @@ -69,7 +50,7 @@ class MockHttpRestXMLProtocolGenerator : HttpBindingProtocolGenerator() { defaultTimestampFormat: TimestampFormatTrait.Format, path: String ) { - val decodeGenerator = StructDecodeXMLGenerator(ctx, shapeContainingMembers, members, shapeMetadata, writer) + val decodeGenerator = StructDecodeGenerator(ctx, shapeContainingMembers, members, shapeMetadata, writer) decodeGenerator.render() } @@ -95,7 +76,7 @@ class MockHttpRestXMLProtocolGenerator : HttpBindingProtocolGenerator() { requestTestBuilder, responseTestBuilder, errorTestBuilder, - httpProtocolCustomizable, + customizations, operationMiddleware, getProtocolHttpBindingResolver(ctx, defaultContentType), ).generateProtocolTests() diff --git a/smithy-swift-codegen/src/test/kotlin/mocks/MockHttpResponseBindingErrorGenerator.kt b/smithy-swift-codegen/src/test/kotlin/mocks/MockHttpResponseBindingErrorGenerator.kt deleted file mode 100644 index aba1cb16a..000000000 --- a/smithy-swift-codegen/src/test/kotlin/mocks/MockHttpResponseBindingErrorGenerator.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package mocks - -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.swift.codegen.SwiftDependency -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingErrorGeneratable -import software.amazon.smithy.swift.codegen.integration.middlewares.handlers.MiddlewareShapeUtils - -class MockHttpResponseBindingErrorGenerator : HttpResponseBindingErrorGeneratable { - override fun renderOperationError( - ctx: ProtocolGenerator.GenerationContext, - op: OperationShape, - unknownServiceErrorSymbol: Symbol - ) { - val operationErrorName = MiddlewareShapeUtils.outputErrorSymbolName(op) - val rootNamespace = ctx.settings.moduleName - val httpBindingSymbol = Symbol.builder() - .definitionFile("./$rootNamespace/models/$operationErrorName+HttpResponseBinding.swift") - .name(operationErrorName) - .build() - - ctx.delegator.useShapeWriter(httpBindingSymbol) { writer -> - writer.addImport(SwiftDependency.CLIENT_RUNTIME.target) - writer.openBlock("extension \$L {", "}", operationErrorName) { - writer.openBlock("public init(httpResponse: HttpResponse, decoder: ResponseDecoder? = nil, messageDecoder: MessageDecoder? = nil) throws {", "}") { - writer.write("throw ClientError.deserializationFailed(ClientError.dataNotFound(\"Invalid information in current codegen context to resolve the ErrorType\"))") - } - } - } - } - - override fun renderServiceError(ctx: ProtocolGenerator.GenerationContext) { - // not yet implemented - return - } -} diff --git a/smithy-swift-codegen/src/test/kotlin/mocks/MockHttpRestJsonProtocolGenerator.kt b/smithy-swift-codegen/src/test/kotlin/mocks/MockHttpRestJsonProtocolGenerator.kt deleted file mode 100644 index ea97e490f..000000000 --- a/smithy-swift-codegen/src/test/kotlin/mocks/MockHttpRestJsonProtocolGenerator.kt +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -import mocks.MockHttpResponseBindingErrorGenerator -import software.amazon.smithy.aws.traits.protocols.RestJson1Trait -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.model.traits.TimestampFormatTrait -import software.amazon.smithy.swift.codegen.ClientRuntimeTypes -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ClientProperty -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.HttpRequestEncoder -import software.amazon.smithy.swift.codegen.integration.HttpResponseDecoder -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.codingKeys.CodingKeysCustomizationJsonName -import software.amazon.smithy.swift.codegen.integration.codingKeys.CodingKeysGenerator -import software.amazon.smithy.swift.codegen.integration.codingKeys.DefaultCodingKeysGenerator -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingOutputGenerator -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseGeneratable -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseGenerator -import software.amazon.smithy.swift.codegen.integration.serde.json.StructDecodeGenerator -import software.amazon.smithy.swift.codegen.integration.serde.json.StructEncodeGenerator -import software.amazon.smithy.swift.codegen.model.ShapeMetadata - -class MockHttpRequestJsonEncoder( - requestEncoderOptions: MutableMap = mutableMapOf() -) : HttpRequestEncoder(ClientRuntimeTypes.Serde.JSONEncoder, requestEncoderOptions) - -class MockHttpRequestJsonDecoder( - requestDecoderOptions: MutableMap = mutableMapOf() -) : HttpResponseDecoder(ClientRuntimeTypes.Serde.JSONDecoder, requestDecoderOptions) - -class MockRestJsonHttpProtocolCustomizations() : DefaultHttpProtocolCustomizations() { - override fun getClientProperties(): List { - val properties = mutableListOf() - val requestEncoderOptions = mutableMapOf() - val responseDecoderOptions = mutableMapOf() - requestEncoderOptions["dateEncodingStrategy"] = ".secondsSince1970" - requestEncoderOptions["nonConformingFloatEncodingStrategy"] = ".convertToString(positiveInfinity: \"Infinity\", negativeInfinity: \"-Infinity\", nan: \"NaN\")" - responseDecoderOptions["dateDecodingStrategy"] = ".secondsSince1970" - responseDecoderOptions["nonConformingFloatDecodingStrategy"] = ".convertFromString(positiveInfinity: \"Infinity\", negativeInfinity: \"-Infinity\", nan: \"NaN\")" - properties.add(MockHttpRequestJsonEncoder(requestEncoderOptions)) - properties.add(MockHttpRequestJsonDecoder(responseDecoderOptions)) - return properties - } -} -class MockHttpRestJsonProtocolGenerator : HttpBindingProtocolGenerator() { - override val defaultContentType: String = "application/json" - override val defaultTimestampFormat: TimestampFormatTrait.Format = TimestampFormatTrait.Format.DATE_TIME - override val protocol: ShapeId = RestJson1Trait.ID - override val httpProtocolClientGeneratorFactory = TestHttpProtocolClientGeneratorFactory() - override val httpProtocolCustomizable = MockRestJsonHttpProtocolCustomizations() - override val codingKeysGenerator: CodingKeysGenerator = DefaultCodingKeysGenerator(CodingKeysCustomizationJsonName()) - override val httpResponseGenerator: HttpResponseGeneratable = HttpResponseGenerator( - unknownServiceErrorSymbol, - defaultTimestampFormat, - HttpResponseBindingOutputGenerator(), - MockHttpResponseBindingErrorGenerator() - ) - override val shouldRenderDecodableBodyStructForInputShapes = true - override val shouldRenderCodingKeysForEncodable = true - override val shouldRenderEncodableConformance = false - - override fun renderStructEncode( - ctx: ProtocolGenerator.GenerationContext, - shapeContainingMembers: Shape, - shapeMetadata: Map, - members: List, - writer: SwiftWriter, - defaultTimestampFormat: TimestampFormatTrait.Format, - path: String? - ) { - val encodeGenerator = StructEncodeGenerator(ctx, members, writer, defaultTimestampFormat, path) - encodeGenerator.render() - } - override fun renderStructDecode( - ctx: ProtocolGenerator.GenerationContext, - shapeContainingMembers: Shape, - shapeMetadata: Map, - members: List, - writer: SwiftWriter, - defaultTimestampFormat: TimestampFormatTrait.Format, - path: String - ) { - val decodeGenerator = StructDecodeGenerator(ctx, members, writer, defaultTimestampFormat, path) - decodeGenerator.render() - } - - override fun addProtocolSpecificMiddleware(ctx: ProtocolGenerator.GenerationContext, operation: OperationShape) { - // Intentionally empty - } - - override fun generateMessageMarshallable(ctx: ProtocolGenerator.GenerationContext) { - TODO("Not yet implemented") - } - - override fun generateMessageUnmarshallable(ctx: ProtocolGenerator.GenerationContext) { - TODO("Not yet implemented") - } - - override fun generateProtocolUnitTests(ctx: ProtocolGenerator.GenerationContext): Int { - val requestTestBuilder = HttpProtocolUnitTestRequestGenerator.Builder() - val responseTestBuilder = HttpProtocolUnitTestResponseGenerator.Builder() - val errorTestBuilder = HttpProtocolUnitTestErrorGenerator.Builder() - - return HttpProtocolTestGenerator( - ctx, - requestTestBuilder, - responseTestBuilder, - errorTestBuilder, - httpProtocolCustomizable, - operationMiddleware, - getProtocolHttpBindingResolver(ctx, defaultContentType), -// HttpProtocolUnitTestGenerator.SerdeContext("JSONEncoder()", "JSONDecoder()", ".secondsSince1970") - ).generateProtocolTests() - } -} diff --git a/smithy-swift-codegen/src/test/kotlin/serde/awsjson11/NestedListEncodeJSONGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/awsjson11/NestedListEncodeJSONGenerationTests.kt index e99a1c7ac..b41349c6e 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/awsjson11/NestedListEncodeJSONGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/awsjson11/NestedListEncodeJSONGenerationTests.kt @@ -4,7 +4,7 @@ import TestContext import defaultSettings import getFileContents import io.kotest.matchers.string.shouldContainOnlyOnce -import mocks.MockHttpAWSJson11ProtocolGenerator +import mocks.MockHTTPAWSJson11ProtocolGenerator import org.junit.jupiter.api.Test import shouldSyntacticSanityCheck @@ -34,35 +34,21 @@ class NestedListEncodeJSONGenerationTests { @Test fun `encode list of maps of lists`() { val context = setupTests("Isolated/json11/lists-of-maps-of-lists.smithy", "aws.protocoltests.json#JsonProtocol") - val contents = getFileContents(context.manifest, "/Example/models/ListOfMapsOperationInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/Example/models/ListOfMapsOperationInput+Write.swift") contents.shouldSyntacticSanityCheck() val expectedContents = """ - extension ListOfMapsOperationInput: Swift.Encodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case targetMaps - } - - public func encode(to encoder: Swift.Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let targetMaps = targetMaps { - var targetMapsContainer = encodeContainer.nestedUnkeyedContainer(forKey: .targetMaps) - for targetmap0 in targetMaps { - var targetmap0Container = targetMapsContainer.nestedContainer(keyedBy: ClientRuntime.Key.self) - for (dictKey1, targetMap1) in targetmap0 { - var targetMap1Container = targetmap0Container.nestedUnkeyedContainer(forKey: ClientRuntime.Key(stringValue: dictKey1)) - for string2 in targetMap1 { - try targetMap1Container.encode(string2) - } - } - } - } - } - } - """.trimIndent() +extension ListOfMapsOperationInput { + + static func write(value: ListOfMapsOperationInput?, to writer: SmithyJSON.Writer) throws { + guard let value else { return } + try writer["targetMaps"].writeList(value.targetMaps, memberWritingClosure: mapWritingClosure(valueWritingClosure: listWritingClosure(memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), memberNodeInfo: "member", isFlattened: false) + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpAWSJson11ProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPAWSJson11ProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "Example", "2014-11-06", "aws json 11") } context.generator.generateCodableConformanceForNestedTypes(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/serde/awsjson11/OutputResponseDeserializerTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/awsjson11/OutputResponseDeserializerTests.kt index 88c2f5b32..d90ab61ff 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/awsjson11/OutputResponseDeserializerTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/awsjson11/OutputResponseDeserializerTests.kt @@ -37,22 +37,20 @@ class OutputDeserializerTests { newTestContext.manifest ) contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension SimpleStructureOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { - if let data = try await httpResponse.body.readData(), - let responseDecoder = decoder { - let output: SimpleStructureOutputBody = try responseDecoder.decode(responseBody: data) - self.name = output.name - self.number = output.number - } else { - self.name = nil - self.number = nil - } - } - } - """.trimIndent() + val expectedContents = """ +extension SimpleStructureOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> SimpleStructureOutput { + let data = try await httpResponse.data() + let responseReader = try SmithyJSON.Reader.from(data: data) + let reader = responseReader + var value = SimpleStructureOutput() + value.name = try reader["name"].readIfPresent() + value.number = try reader["number"].readIfPresent() + return value + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } @@ -64,21 +62,23 @@ class OutputDeserializerTests { newTestContext.manifest ) contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension DataStreamingOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { - switch httpResponse.body { - case .data(let data): - self.streamingData = .data(data) - case .stream(let stream): - self.streamingData = .stream(stream) - case .noStream: - self.streamingData = nil - } - } - } - """.trimIndent() + val expectedContents = """ +extension DataStreamingOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> DataStreamingOutput { + var value = DataStreamingOutput() + switch httpResponse.body { + case .data(let data): + value.streamingData = .data(data) + case .stream(let stream): + value.streamingData = .stream(stream) + case .noStream: + value.streamingData = nil + } + return value + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } @@ -91,15 +91,16 @@ class OutputDeserializerTests { ) contents.shouldSyntacticSanityCheck() val expectedContents = """ -extension EventStreamingOutput: ClientRuntime.HttpResponseBinding { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws { - if case let .stream(stream) = httpResponse.body, let responseDecoder = decoder { - let messageDecoder: ClientRuntime.MessageDecoder? = nil - let decoderStream = ClientRuntime.EventStream.DefaultMessageDecoderStream(stream: stream, messageDecoder: messageDecoder, unmarshalClosure: jsonUnmarshalClosure(responseDecoder: responseDecoder)) - self.eventStream = decoderStream.toAsyncStream() - } else { - self.eventStream = nil +extension EventStreamingOutput { + + static func httpOutput(from httpResponse: ClientRuntime.HttpResponse) async throws -> EventStreamingOutput { + var value = EventStreamingOutput() + if case let .stream(stream) = httpResponse.body { + let messageDecoder = ClientRuntime.MessageDecoder() + let decoderStream = ClientRuntime.EventStream.DefaultMessageDecoderStream(stream: stream, messageDecoder: messageDecoder, unmarshalClosure: EventStream.unmarshal) + value.eventStream = decoderStream.toAsyncStream() } + return value } } """ diff --git a/smithy-swift-codegen/src/test/kotlin/serde/ec2/Ec2QueryNameTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/ec2/Ec2QueryNameTests.kt index b683ad315..6aa9ed621 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/ec2/Ec2QueryNameTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/ec2/Ec2QueryNameTests.kt @@ -9,7 +9,7 @@ import TestContext import defaultSettings import getFileContents import io.kotest.matchers.string.shouldContainOnlyOnce -import mocks.MockHttpEC2QueryProtocolGenerator +import mocks.MockHTTPEC2QueryProtocolGenerator import org.junit.jupiter.api.Test import shouldSyntacticSanityCheck @@ -17,49 +17,31 @@ class Ec2QueryNameTests { @Test fun `001 encode simple types`() { val context = setupTests("Isolated/ec2/query-simple.smithy", "aws.protocoltests.ec2#AwsEc2") - val contents = getFileContents(context.manifest, "/Example/models/Ec2SimpleInputParamsInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/Example/models/Ec2SimpleInputParamsInput+Write.swift") contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension Ec2SimpleInputParamsInput: Swift.Encodable { - public func encode(to encoder: Swift.Encoder) throws { - var container = encoder.container(keyedBy: ClientRuntime.Key.self) - if let bamInt = bamInt { - try container.encode(bamInt, forKey: ClientRuntime.Key("BamInt")) - } - if let barString = barString { - try container.encode(barString, forKey: ClientRuntime.Key("BarString")) - } - if let bazBoolean = bazBoolean { - try container.encode(bazBoolean, forKey: ClientRuntime.Key("BazBoolean")) - } - if let booDouble = booDouble { - try container.encode(booDouble, forKey: ClientRuntime.Key("BooDouble")) - } - if let fzzEnum = fzzEnum { - try container.encode(fzzEnum, forKey: ClientRuntime.Key("FzzEnum")) - } - if let hasQueryAndXmlNameString = hasQueryAndXmlNameString { - try container.encode(hasQueryAndXmlNameString, forKey: ClientRuntime.Key("B")) - } - if let hasQueryNameString = hasQueryNameString { - try container.encode(hasQueryNameString, forKey: ClientRuntime.Key("A")) - } - if let quxBlob = quxBlob { - try container.encode(quxBlob.base64EncodedString(), forKey: ClientRuntime.Key("QuxBlob")) - } - if let usesXmlNameString = usesXmlNameString { - try container.encode(usesXmlNameString, forKey: ClientRuntime.Key("C")) - } - try container.encode("Ec2SimpleInputParams", forKey:ClientRuntime.Key("Action")) - try container.encode("2020-01-08", forKey:ClientRuntime.Key("Version")) - } - } - """.trimIndent() + val expectedContents = """ +extension Ec2SimpleInputParamsInput { + + static func write(value: Ec2SimpleInputParamsInput?, to writer: SmithyFormURL.Writer) throws { + guard let value else { return } + try writer["BamInt"].write(value.bamInt) + try writer["BarString"].write(value.barString) + try writer["BazBoolean"].write(value.bazBoolean) + try writer["BooDouble"].write(value.booDouble) + try writer["FzzEnum"].write(value.fzzEnum) + try writer["B"].write(value.hasQueryAndXmlNameString) + try writer["A"].write(value.hasQueryNameString) + try writer["QuxBlob"].write(value.quxBlob) + try writer["C"].write(value.usesXmlNameString) + try writer["Action"].write("Ec2SimpleInputParams") + try writer["Version"].write("2020-01-08") + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpEC2QueryProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPEC2QueryProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "Example", "2020-01-08", "Ec2 query protocol") } context.generator.generateCodableConformanceForNestedTypes(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/serde/ec2/OnlyFlattenedListEncodeFormURLGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/ec2/OnlyFlattenedListEncodeFormURLGeneratorTests.kt index 4af777eab..df28c1848 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/ec2/OnlyFlattenedListEncodeFormURLGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/ec2/OnlyFlattenedListEncodeFormURLGeneratorTests.kt @@ -9,7 +9,7 @@ import TestContext import defaultSettings import getFileContents import io.kotest.matchers.string.shouldContainOnlyOnce -import mocks.MockHttpEC2QueryProtocolGenerator +import mocks.MockHTTPEC2QueryProtocolGenerator import org.junit.jupiter.api.Test import shouldSyntacticSanityCheck @@ -18,70 +18,26 @@ class OnlyFlattenedListEncodeFormURLGeneratorTests { @Test fun `001 encode different types of lists`() { val context = setupTests("Isolated/ec2/query-lists.smithy", "aws.protocoltests.ec2#AwsEc2") - val contents = getFileContents(context.manifest, "/Example/models/Ec2QueryListsInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/Example/models/Ec2QueryListsInput+Write.swift") contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension Ec2QueryListsInput: Swift.Encodable { - public func encode(to encoder: Swift.Encoder) throws { - var container = encoder.container(keyedBy: ClientRuntime.Key.self) - if let complexListArg = complexListArg { - if !complexListArg.isEmpty { - for (index0, greetingstruct0) in complexListArg.enumerated() { - var complexListArgContainer0 = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("ComplexListArg.\(index0.advanced(by: 1))")) - try complexListArgContainer0.encode(greetingstruct0, forKey: ClientRuntime.Key("")) - } - } - else { - var complexListArgContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("ComplexListArg")) - try complexListArgContainer.encode("", forKey: ClientRuntime.Key("")) - } - } - if let listArg = listArg { - if !listArg.isEmpty { - for (index0, string0) in listArg.enumerated() { - var listArgContainer0 = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("ListArg.\(index0.advanced(by: 1))")) - try listArgContainer0.encode(string0, forKey: ClientRuntime.Key("")) - } - } - else { - var listArgContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("ListArg")) - try listArgContainer.encode("", forKey: ClientRuntime.Key("")) - } - } - if let listArgWithXmlName = listArgWithXmlName { - if !listArgWithXmlName.isEmpty { - for (index0, string0) in listArgWithXmlName.enumerated() { - var listArgWithXmlNameContainer0 = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("Hi.\(index0.advanced(by: 1))")) - try listArgWithXmlNameContainer0.encode(string0, forKey: ClientRuntime.Key("")) - } - } - else { - var listArgWithXmlNameContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("Hi")) - try listArgWithXmlNameContainer.encode("", forKey: ClientRuntime.Key("")) - } - } - if let listArgWithXmlNameMember = listArgWithXmlNameMember { - if !listArgWithXmlNameMember.isEmpty { - for (index0, string0) in listArgWithXmlNameMember.enumerated() { - var listArgWithXmlNameMemberContainer0 = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("ListArgWithXmlNameMember.\(index0.advanced(by: 1))")) - try listArgWithXmlNameMemberContainer0.encode(string0, forKey: ClientRuntime.Key("")) - } - } - else { - var listArgWithXmlNameMemberContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("ListArgWithXmlNameMember")) - try listArgWithXmlNameMemberContainer.encode("", forKey: ClientRuntime.Key("")) - } - } - try container.encode("Ec2QueryLists", forKey:ClientRuntime.Key("Action")) - try container.encode("2020-01-08", forKey:ClientRuntime.Key("Version")) - } - } - """.trimIndent() + val expectedContents = """ +extension Ec2QueryListsInput { + + static func write(value: Ec2QueryListsInput?, to writer: SmithyFormURL.Writer) throws { + guard let value else { return } + try writer["ComplexListArg"].writeList(value.complexListArg, memberWritingClosure: Ec2queryprotocolClientTypes.GreetingStruct.write(value:to:), memberNodeInfo: "Member", isFlattened: true) + try writer["ListArg"].writeList(value.listArg, memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "Member", isFlattened: true) + try writer["Hi"].writeList(value.listArgWithXmlName, memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "Item", isFlattened: true) + try writer["ListArgWithXmlNameMember"].writeList(value.listArgWithXmlNameMember, memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "Item", isFlattened: true) + try writer["Action"].write("Ec2QueryLists") + try writer["Version"].write("2020-01-08") + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpEC2QueryProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPEC2QueryProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "Example", "2020-01-08", "Ec2 query protocol") } context.generator.generateCodableConformanceForNestedTypes(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/serde/xml/BlobDecodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/xml/BlobDecodeXMLGenerationTests.kt index c3f450ae3..923b45c76 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/xml/BlobDecodeXMLGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/xml/BlobDecodeXMLGenerationTests.kt @@ -5,7 +5,7 @@ package serde.xml -import MockHttpRestXMLProtocolGenerator +import MockHTTPRestXMLProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -17,17 +17,17 @@ class BlobDecodeXMLGenerationTests { @Test fun `decode blob`() { val context = setupTests("Isolated/Restxml/xml-blobs.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlBlobsOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlBlobsOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlBlobsOutputBody { +extension XmlBlobsOutput { - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlBlobsOutput() - value.data = try reader["data"].readIfPresent() - return value - } + static func httpOutput(from httpResponse: ClientRuntime.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 } } """ @@ -37,24 +37,24 @@ extension XmlBlobsOutputBody { @Test fun `decode blob nested`() { val context = setupTests("Isolated/Restxml/xml-blobs.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlBlobsNestedOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlBlobsNestedOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlBlobsNestedOutputBody { +extension XmlBlobsNestedOutput { - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlBlobsNestedOutput() - value.nestedBlobList = try reader["nestedBlobList"].readListIfPresent(memberReadingClosure: SmithyXML.listReadingClosure(memberReadingClosure: ClientRuntime.Data.readingClosure, memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - return value - } + static func httpOutput(from httpResponse: ClientRuntime.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: listReadingClosure(memberReadingClosure: ClientRuntime.Data.read(from:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) + return value } } """ contents.shouldContainOnlyOnce(expectedContents) } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.generateDeserializers(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/serde/xml/BlobEncodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/xml/BlobEncodeXMLGenerationTests.kt index f4b0af14f..1affdb576 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/xml/BlobEncodeXMLGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/xml/BlobEncodeXMLGenerationTests.kt @@ -5,7 +5,7 @@ package serde.xml -import MockHttpRestXMLProtocolGenerator +import MockHTTPRestXMLProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -16,11 +16,12 @@ class BlobEncodeXMLGenerationTests { @Test fun `encode blob`() { val context = setupTests("Isolated/Restxml/xml-blobs.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlBlobsInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlBlobsInput+Write.swift") val expectedContents = """ extension XmlBlobsInput { - static func writingClosure(_ value: XmlBlobsInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } + + static func write(value: XmlBlobsInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } try writer["data"].write(value.data) } } @@ -31,19 +32,20 @@ extension XmlBlobsInput { @Test fun `encode nested blob`() { val context = setupTests("Isolated/Restxml/xml-blobs.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlBlobsNestedInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlBlobsNestedInput+Write.swift") val expectedContents = """ extension XmlBlobsNestedInput { - static func writingClosure(_ value: XmlBlobsNestedInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["nestedBlobList"].writeList(value.nestedBlobList, memberWritingClosure: SmithyXML.listWritingClosure(memberWritingClosure: ClientRuntime.Data.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) + + static func write(value: XmlBlobsNestedInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["nestedBlobList"].writeList(value.nestedBlobList, memberWritingClosure: listWritingClosure(memberWritingClosure: ClientRuntime.Data.write(value:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) } } """ contents.shouldContainOnlyOnce(expectedContents) } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.generateSerializers(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/serde/xml/EnumDecodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/xml/EnumDecodeXMLGenerationTests.kt index 606c8b400..d212832eb 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/xml/EnumDecodeXMLGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/xml/EnumDecodeXMLGenerationTests.kt @@ -5,7 +5,7 @@ package serde.xml -import MockHttpRestXMLProtocolGenerator +import MockHTTPRestXMLProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -17,27 +17,20 @@ class EnumDecodeXMLGenerationTests { @Test fun `decode enum`() { val context = setupTests("Isolated/Restxml/xml-enums.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlEnumsOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlEnumsOutput+HttpResponseBinding.swift") val expectedContents = """ -struct XmlEnumsOutputBody { - let fooEnum1: RestXmlProtocolClientTypes.FooEnum? - let fooEnum2: RestXmlProtocolClientTypes.FooEnum? - let fooEnum3: RestXmlProtocolClientTypes.FooEnum? - let fooEnumList: [RestXmlProtocolClientTypes.FooEnum]? -} - -extension XmlEnumsOutputBody { +extension XmlEnumsOutput { - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - 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: RestXmlProtocolClientTypes.FooEnum.readingClosure, memberNodeInfo: "member", isFlattened: false) - return value - } + static func httpOutput(from httpResponse: ClientRuntime.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: RestXmlProtocolClientTypes.FooEnum.read(from:), memberNodeInfo: "member", isFlattened: false) + return value } } """ @@ -47,28 +40,24 @@ extension XmlEnumsOutputBody { @Test fun `decode enum nested`() { val context = setupTests("Isolated/Restxml/xml-enums.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlEnumsNestedOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlEnumsNestedOutput+HttpResponseBinding.swift") val expectedContents = """ -struct XmlEnumsNestedOutputBody { - let nestedEnumsList: [[RestXmlProtocolClientTypes.FooEnum]]? -} - -extension XmlEnumsNestedOutputBody { +extension XmlEnumsNestedOutput { - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlEnumsNestedOutput() - value.nestedEnumsList = try reader["nestedEnumsList"].readListIfPresent(memberReadingClosure: SmithyXML.listReadingClosure(memberReadingClosure: RestXmlProtocolClientTypes.FooEnum.readingClosure, memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - return value - } + static func httpOutput(from httpResponse: ClientRuntime.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: listReadingClosure(memberReadingClosure: RestXmlProtocolClientTypes.FooEnum.read(from:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) + return value } } """ contents.shouldContainOnlyOnce(expectedContents) } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.generateDeserializers(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/serde/xml/EnumEncodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/xml/EnumEncodeXMLGenerationTests.kt index fce39fe89..5a673078f 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/xml/EnumEncodeXMLGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/xml/EnumEncodeXMLGenerationTests.kt @@ -5,7 +5,7 @@ package serde.xml -import MockHttpRestXMLProtocolGenerator +import MockHTTPRestXMLProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -16,15 +16,16 @@ class EnumEncodeXMLGenerationTests { @Test fun `001 encode enum`() { val context = setupTests("Isolated/Restxml/xml-enums.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlEnumsInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlEnumsInput+Write.swift") val expectedContents = """ extension XmlEnumsInput { - static func writingClosure(_ value: XmlEnumsInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } + + 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: RestXmlProtocolClientTypes.FooEnum.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: false) + try writer["fooEnumList"].writeList(value.fooEnumList, memberWritingClosure: RestXmlProtocolClientTypes.FooEnum.write(value:to:), memberNodeInfo: "member", isFlattened: false) } } """ @@ -34,12 +35,13 @@ extension XmlEnumsInput { @Test fun `002 encode nested enum`() { val context = setupTests("Isolated/Restxml/xml-enums.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlEnumsNestedInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlEnumsNestedInput+Write.swift") val expectedContents = """ extension XmlEnumsNestedInput { - static func writingClosure(_ value: XmlEnumsNestedInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["nestedEnumsList"].writeList(value.nestedEnumsList, memberWritingClosure: SmithyXML.listWritingClosure(memberWritingClosure: RestXmlProtocolClientTypes.FooEnum.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) + + static func write(value: XmlEnumsNestedInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["nestedEnumsList"].writeList(value.nestedEnumsList, memberWritingClosure: listWritingClosure(memberWritingClosure: RestXmlProtocolClientTypes.FooEnum.write(value:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) } } """ @@ -47,7 +49,7 @@ extension XmlEnumsNestedInput { } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.generateSerializers(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/serde/xml/ListDecodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/xml/ListDecodeXMLGenerationTests.kt index 5ebc8e65e..6e99788fb 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/xml/ListDecodeXMLGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/xml/ListDecodeXMLGenerationTests.kt @@ -5,7 +5,7 @@ package serde.xml -import MockHttpRestXMLProtocolGenerator +import MockHTTPRestXMLProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -16,17 +16,17 @@ 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, "/RestXml/models/XmlListXmlNameOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlListXmlNameOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlListXmlNameOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlListXmlNameOutput() - value.renamedListMembers = try reader["renamed"].readListIfPresent(memberReadingClosure: Swift.String.readingClosure, memberNodeInfo: "item", isFlattened: false) - return value - } +extension XmlListXmlNameOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: Swift.String.read(from:), memberNodeInfo: "item", isFlattened: false) + return value } } """ @@ -36,17 +36,17 @@ extension XmlListXmlNameOutputBody { @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, "/RestXml/models/XmlListXmlNameNestedOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlListXmlNameNestedOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlListXmlNameNestedOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlListXmlNameNestedOutput() - value.renamedListMembers = try reader["renamed"].readListIfPresent(memberReadingClosure: SmithyXML.listReadingClosure(memberReadingClosure: Swift.String.readingClosure, memberNodeInfo: "subItem", isFlattened: false), memberNodeInfo: "item", isFlattened: false) - return value - } +extension XmlListXmlNameNestedOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: listReadingClosure(memberReadingClosure: Swift.String.read(from:), memberNodeInfo: "subItem", isFlattened: false), memberNodeInfo: "item", isFlattened: false) + return value } } """ @@ -57,17 +57,17 @@ extension XmlListXmlNameNestedOutputBody { fun `003 decode flattened list`() { val context = setupTests("Isolated/Restxml/xml-lists-flattened.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlFlattenedListOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlFlattenedListOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlFlattenedListOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlFlattenedListOutput() - value.myGroceryList = try reader["myGroceryList"].readListIfPresent(memberReadingClosure: Swift.String.readingClosure, memberNodeInfo: "member", isFlattened: true) - return value - } +extension XmlFlattenedListOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: Swift.String.read(from:), memberNodeInfo: "member", isFlattened: true) + return value } } """ @@ -77,20 +77,20 @@ extension XmlFlattenedListOutputBody { @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, "/RestXml/models/XmlEmptyFlattenedListsOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlEmptyFlattenedListsOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlEmptyFlattenedListsOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlEmptyFlattenedListsOutput() - value.stringList = try reader["stringList"].readListIfPresent(memberReadingClosure: Swift.String.readingClosure, memberNodeInfo: "member", isFlattened: true) - value.stringSet = try reader["stringSet"].readListIfPresent(memberReadingClosure: Swift.String.readingClosure, memberNodeInfo: "member", isFlattened: true) - value.integerList = try reader["integerList"].readListIfPresent(memberReadingClosure: Swift.Int.readingClosure, memberNodeInfo: "member", isFlattened: false) - value.booleanList = try reader["booleanList"].readListIfPresent(memberReadingClosure: Swift.Bool.readingClosure, memberNodeInfo: "member", isFlattened: false) - return value - } +extension XmlEmptyFlattenedListsOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: Swift.Bool.read(from:), memberNodeInfo: "member", isFlattened: false) + value.integerList = try reader["integerList"].readListIfPresent(memberReadingClosure: Swift.Int.read(from:), memberNodeInfo: "member", isFlattened: false) + value.stringList = try reader["stringList"].readListIfPresent(memberReadingClosure: Swift.String.read(from:), memberNodeInfo: "member", isFlattened: true) + value.stringSet = try reader["stringSet"].readListIfPresent(memberReadingClosure: Swift.String.read(from:), memberNodeInfo: "member", isFlattened: true) + return value } } """ @@ -100,17 +100,17 @@ extension XmlEmptyFlattenedListsOutputBody { @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, "/RestXml/models/XmlNestedNestedFlattenedListOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlNestedNestedFlattenedListOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlNestedNestedFlattenedListOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlNestedNestedFlattenedListOutput() - value.nestedNestedStringList = try reader["nestedNestedStringList"].readListIfPresent(memberReadingClosure: SmithyXML.listReadingClosure(memberReadingClosure: SmithyXML.listReadingClosure(memberReadingClosure: Swift.String.readingClosure, memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: true) - return value - } +extension XmlNestedNestedFlattenedListOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: listReadingClosure(memberReadingClosure: listReadingClosure(memberReadingClosure: Swift.String.read(from:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: true) + return value } } """ @@ -120,17 +120,17 @@ extension XmlNestedNestedFlattenedListOutputBody { @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, "/RestXml/models/XmlListContainMapOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlListContainMapOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlListContainMapOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlListContainMapOutput() - value.myList = try reader["myList"].readListIfPresent(memberReadingClosure: SmithyXML.mapReadingClosure(valueReadingClosure: Swift.String.readingClosure, keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - return value - } +extension XmlListContainMapOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: mapReadingClosure(valueReadingClosure: Swift.String.read(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), memberNodeInfo: "member", isFlattened: false) + return value } } """ @@ -140,24 +140,24 @@ extension XmlListContainMapOutputBody { @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, "/RestXml/models/XmlListFlattenedContainMapOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlListFlattenedContainMapOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlListFlattenedContainMapOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlListFlattenedContainMapOutput() - value.myList = try reader["myList"].readListIfPresent(memberReadingClosure: SmithyXML.mapReadingClosure(valueReadingClosure: Swift.String.readingClosure, keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), memberNodeInfo: "member", isFlattened: true) - return value - } +extension XmlListFlattenedContainMapOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: mapReadingClosure(valueReadingClosure: Swift.String.read(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), memberNodeInfo: "member", isFlattened: true) + return value } } """ contents.shouldContainOnlyOnce(expectedContents) } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.generateDeserializers(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/serde/xml/ListEncodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/xml/ListEncodeXMLGenerationTests.kt index 2f5a6cf3c..67f00cb6f 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/xml/ListEncodeXMLGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/xml/ListEncodeXMLGenerationTests.kt @@ -5,7 +5,7 @@ package serde.xml -import MockHttpRestXMLProtocolGenerator +import MockHTTPRestXMLProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -16,12 +16,13 @@ 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, "/RestXml/models/XmlListXmlNameInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlListXmlNameInput+Write.swift") val expectedContents = """ extension XmlListXmlNameInput { - static func writingClosure(_ value: XmlListXmlNameInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["renamed"].writeList(value.renamedListMembers, memberWritingClosure: Swift.String.writingClosure(_:to:), memberNodeInfo: "item", isFlattened: false) + + static func write(value: XmlListXmlNameInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["renamed"].writeList(value.renamedListMembers, memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "item", isFlattened: false) } } """ @@ -31,12 +32,13 @@ extension XmlListXmlNameInput { @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, "/RestXml/models/XmlListXmlNameNestedInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlListXmlNameNestedInput+Write.swift") val expectedContents = """ extension XmlListXmlNameNestedInput { - static func writingClosure(_ value: XmlListXmlNameNestedInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["renamed"].writeList(value.renamedListMembers, memberWritingClosure: SmithyXML.listWritingClosure(memberWritingClosure: Swift.String.writingClosure(_:to:), memberNodeInfo: "subItem", isFlattened: false), memberNodeInfo: "item", isFlattened: false) + + static func write(value: XmlListXmlNameNestedInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["renamed"].writeList(value.renamedListMembers, memberWritingClosure: listWritingClosure(memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "subItem", isFlattened: false), memberNodeInfo: "item", isFlattened: false) } } """ @@ -46,12 +48,13 @@ extension XmlListXmlNameNestedInput { @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, "/RestXml/models/XmlNestedWrappedListInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlNestedWrappedListInput+Write.swift") val expectedContents = """ extension XmlNestedWrappedListInput { - static func writingClosure(_ value: XmlNestedWrappedListInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["nestedStringList"].writeList(value.nestedStringList, memberWritingClosure: SmithyXML.listWritingClosure(memberWritingClosure: Swift.String.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) + + static func write(value: XmlNestedWrappedListInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["nestedStringList"].writeList(value.nestedStringList, memberWritingClosure: listWritingClosure(memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) } } """ @@ -61,12 +64,13 @@ extension XmlNestedWrappedListInput { @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, "/RestXml/models/XmlNestedNestedWrappedListInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlNestedNestedWrappedListInput+Write.swift") val expectedContents = """ extension XmlNestedNestedWrappedListInput { - static func writingClosure(_ value: XmlNestedNestedWrappedListInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["nestedNestedStringList"].writeList(value.nestedNestedStringList, memberWritingClosure: SmithyXML.listWritingClosure(memberWritingClosure: SmithyXML.listWritingClosure(memberWritingClosure: Swift.String.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) + + static func write(value: XmlNestedNestedWrappedListInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["nestedNestedStringList"].writeList(value.nestedNestedStringList, memberWritingClosure: listWritingClosure(memberWritingClosure: listWritingClosure(memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) } } """ @@ -76,12 +80,13 @@ extension XmlNestedNestedWrappedListInput { @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, "/RestXml/models/XmlNestedNestedFlattenedListInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlNestedNestedFlattenedListInput+Write.swift") val expectedContents = """ extension XmlNestedNestedFlattenedListInput { - static func writingClosure(_ value: XmlNestedNestedFlattenedListInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["nestedNestedStringList"].writeList(value.nestedNestedStringList, memberWritingClosure: SmithyXML.listWritingClosure(memberWritingClosure: SmithyXML.listWritingClosure(memberWritingClosure: Swift.String.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: true) + + static func write(value: XmlNestedNestedFlattenedListInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["nestedNestedStringList"].writeList(value.nestedNestedStringList, memberWritingClosure: listWritingClosure(memberWritingClosure: listWritingClosure(memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: true) } } """ @@ -91,15 +96,16 @@ extension XmlNestedNestedFlattenedListInput { @Test fun `006 empty lists`() { val context = setupTests("Isolated/Restxml/xml-lists-empty.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlEmptyListsInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlEmptyListsInput+Write.swift") val expectedContents = """ extension XmlEmptyListsInput { - static func writingClosure(_ value: XmlEmptyListsInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["booleanList"].writeList(value.booleanList, memberWritingClosure: Swift.Bool.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: false) - try writer["integerList"].writeList(value.integerList, memberWritingClosure: Swift.Int.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: false) - try writer["stringList"].writeList(value.stringList, memberWritingClosure: Swift.String.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: false) - try writer["stringSet"].writeList(value.stringSet, memberWritingClosure: Swift.String.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: false) + + static func write(value: XmlEmptyListsInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["booleanList"].writeList(value.booleanList, memberWritingClosure: Swift.Bool.write(value:to:), memberNodeInfo: "member", isFlattened: false) + try writer["integerList"].writeList(value.integerList, memberWritingClosure: Swift.Int.write(value:to:), memberNodeInfo: "member", isFlattened: false) + try writer["stringList"].writeList(value.stringList, memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: false) + try writer["stringSet"].writeList(value.stringSet, memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: false) } } """ @@ -109,12 +115,13 @@ extension XmlEmptyListsInput { @Test fun `007 wrapped list serialization`() { val context = setupTests("Isolated/Restxml/xml-lists-wrapped.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlWrappedListInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlWrappedListInput+Write.swift") val expectedContents = """ extension XmlWrappedListInput { - static func writingClosure(_ value: XmlWrappedListInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["myGroceryList"].writeList(value.myGroceryList, memberWritingClosure: Swift.String.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: false) + + static func write(value: XmlWrappedListInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["myGroceryList"].writeList(value.myGroceryList, memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: false) } } """ @@ -124,12 +131,13 @@ extension XmlWrappedListInput { @Test fun `008 flattened list serialization`() { val context = setupTests("Isolated/Restxml/xml-lists-flattened.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlFlattenedListInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlFlattenedListInput+Write.swift") val expectedContents = """ extension XmlFlattenedListInput { - static func writingClosure(_ value: XmlFlattenedListInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["myGroceryList"].writeList(value.myGroceryList, memberWritingClosure: Swift.String.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: true) + + static func write(value: XmlFlattenedListInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["myGroceryList"].writeList(value.myGroceryList, memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: true) } } """ @@ -139,12 +147,13 @@ extension XmlFlattenedListInput { @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, "/RestXml/models/XmlTimestampsNestedFlattenedInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlTimestampsNestedFlattenedInput+Write.swift") val expectedContents = """ extension XmlTimestampsNestedFlattenedInput { - static func writingClosure(_ value: XmlTimestampsNestedFlattenedInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["nestedTimestampList"].writeList(value.nestedTimestampList, memberWritingClosure: SmithyXML.listWritingClosure(memberWritingClosure: SmithyXML.timestampWritingClosure(format: .epochSeconds), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: .init("nestedMember", namespaceDef: .init(prefix: "baz", uri: "http://baz.com")), isFlattened: true) + + static func write(value: XmlTimestampsNestedFlattenedInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["nestedTimestampList"].writeList(value.nestedTimestampList, memberWritingClosure: listWritingClosure(memberWritingClosure: timestampWritingClosure(format: .epochSeconds), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: .init("nestedMember", namespaceDef: .init(prefix: "baz", uri: "http://baz.com")), isFlattened: true) } } """ @@ -153,15 +162,16 @@ extension XmlTimestampsNestedFlattenedInput { @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, "/RestXml/models/XmlEmptyFlattenedListsInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlEmptyFlattenedListsInput+Write.swift") val expectedContents = """ extension XmlEmptyFlattenedListsInput { - static func writingClosure(_ value: XmlEmptyFlattenedListsInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["booleanList"].writeList(value.booleanList, memberWritingClosure: Swift.Bool.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: false) - try writer["integerList"].writeList(value.integerList, memberWritingClosure: Swift.Int.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: false) - try writer["stringList"].writeList(value.stringList, memberWritingClosure: Swift.String.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: true) - try writer["stringSet"].writeList(value.stringSet, memberWritingClosure: Swift.String.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: true) + + static func write(value: XmlEmptyFlattenedListsInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["booleanList"].writeList(value.booleanList, memberWritingClosure: Swift.Bool.write(value:to:), memberNodeInfo: "member", isFlattened: false) + try writer["integerList"].writeList(value.integerList, memberWritingClosure: Swift.Int.write(value:to:), memberNodeInfo: "member", isFlattened: false) + try writer["stringList"].writeList(value.stringList, memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: true) + try writer["stringSet"].writeList(value.stringSet, memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: true) } } """ @@ -171,12 +181,13 @@ extension XmlEmptyFlattenedListsInput { @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, "/RestXml/models/XmlListNestedFlattenedXmlNameInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlListNestedFlattenedXmlNameInput+Write.swift") val expectedContents = """ extension XmlListNestedFlattenedXmlNameInput { - static func writingClosure(_ value: XmlListNestedFlattenedXmlNameInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["listOfNestedStrings"].writeList(value.nestedList, memberWritingClosure: SmithyXML.listWritingClosure(memberWritingClosure: Swift.String.writingClosure(_:to:), memberNodeInfo: "nestedNestedMember", isFlattened: false), memberNodeInfo: "nestedMember", isFlattened: true) + + static func write(value: XmlListNestedFlattenedXmlNameInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["listOfNestedStrings"].writeList(value.nestedList, memberWritingClosure: listWritingClosure(memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "nestedNestedMember", isFlattened: false), memberNodeInfo: "nestedMember", isFlattened: true) } } """ @@ -186,12 +197,13 @@ extension XmlListNestedFlattenedXmlNameInput { @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, "/RestXml/models/XmlListContainMapInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlListContainMapInput+Write.swift") val expectedContents = """ extension XmlListContainMapInput { - static func writingClosure(_ value: XmlListContainMapInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["myList"].writeList(value.myList, memberWritingClosure: SmithyXML.mapWritingClosure(valueWritingClosure: Swift.String.writingClosure(_:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), memberNodeInfo: "member", isFlattened: false) + + static func write(value: XmlListContainMapInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["myList"].writeList(value.myList, memberWritingClosure: mapWritingClosure(valueWritingClosure: Swift.String.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), memberNodeInfo: "member", isFlattened: false) } } """ @@ -200,19 +212,20 @@ extension XmlListContainMapInput { @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, "/RestXml/models/XmlListFlattenedContainMapInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlListFlattenedContainMapInput+Write.swift") val expectedContents = """ extension XmlListFlattenedContainMapInput { - static func writingClosure(_ value: XmlListFlattenedContainMapInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["myList"].writeList(value.myList, memberWritingClosure: SmithyXML.mapWritingClosure(valueWritingClosure: Swift.String.writingClosure(_:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), memberNodeInfo: "member", isFlattened: true) + + static func write(value: XmlListFlattenedContainMapInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["myList"].writeList(value.myList, memberWritingClosure: mapWritingClosure(valueWritingClosure: Swift.String.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), memberNodeInfo: "member", isFlattened: true) } } """ contents.shouldContainOnlyOnce(expectedContents) } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.generateCodableConformanceForNestedTypes(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/serde/xml/MapDecodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/xml/MapDecodeXMLGenerationTests.kt index 582c91f03..16d3a892d 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/xml/MapDecodeXMLGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/xml/MapDecodeXMLGenerationTests.kt @@ -5,7 +5,7 @@ package serde.xml -import MockHttpRestXMLProtocolGenerator +import MockHTTPRestXMLProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -17,17 +17,17 @@ 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, "/RestXml/models/XmlMapsOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlMapsOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlMapsOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.readingClosure, keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - return value - } +extension XmlMapsOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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 } } """ @@ -37,17 +37,17 @@ extension XmlMapsOutputBody { @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, "/RestXml/models/XmlMapsWithNameProtocolOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsWithNameProtocolOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlMapsWithNameProtocolOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlMapsWithNameProtocolOutput() - value.`protocol` = try reader["protocol"].readMapIfPresent(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.readingClosure, keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - return value - } +extension XmlMapsWithNameProtocolOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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 } } """ @@ -57,17 +57,17 @@ extension XmlMapsWithNameProtocolOutputBody { @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, "/RestXml/models/XmlMapsNestedOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsNestedOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlMapsNestedOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlMapsNestedOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: SmithyXML.mapReadingClosure(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.readingClosure, keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - return value - } +extension XmlMapsNestedOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: mapReadingClosure(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.read(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + return value } } """ @@ -77,17 +77,17 @@ extension XmlMapsNestedOutputBody { @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, "/RestXml/models/XmlMapsNestedNestedOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsNestedNestedOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlMapsNestedNestedOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlMapsNestedNestedOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: SmithyXML.mapReadingClosure(valueReadingClosure: SmithyXML.mapReadingClosure(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.readingClosure, keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - return value - } +extension XmlMapsNestedNestedOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: mapReadingClosure(valueReadingClosure: 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 } } """ @@ -97,17 +97,17 @@ extension XmlMapsNestedNestedOutputBody { @Test fun `005 decode flattened map`() { val context = setupTests("Isolated/Restxml/xml-maps-flattened.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlFlattenedMapsOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlFlattenedMapsOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlFlattenedMapsOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlFlattenedMapsOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.readingClosure, keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) - return value - } +extension XmlFlattenedMapsOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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 } } """ @@ -117,17 +117,17 @@ extension XmlFlattenedMapsOutputBody { @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, "/RestXml/models/XmlMapsFlattenedNestedOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsFlattenedNestedOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlMapsFlattenedNestedOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlMapsFlattenedNestedOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: SmithyXML.mapReadingClosure(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.readingClosure, keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) - return value - } +extension XmlMapsFlattenedNestedOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: mapReadingClosure(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.read(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) + return value } } """ @@ -137,17 +137,17 @@ extension XmlMapsFlattenedNestedOutputBody { @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, "/RestXml/models/XmlMapsXmlNameOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsXmlNameOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlMapsXmlNameOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlMapsXmlNameOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.readingClosure, keyNodeInfo: "Attribute", valueNodeInfo: "Setting", isFlattened: false) - return value - } +extension XmlMapsXmlNameOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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 } } """ @@ -157,17 +157,17 @@ extension XmlMapsXmlNameOutputBody { @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, "/RestXml/models/XmlMapsXmlNameFlattenedOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsXmlNameFlattenedOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlMapsXmlNameFlattenedOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlMapsXmlNameFlattenedOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.readingClosure, keyNodeInfo: "SomeCustomKey", valueNodeInfo: "SomeCustomValue", isFlattened: true) - return value - } +extension XmlMapsXmlNameFlattenedOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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 } } """ @@ -177,17 +177,17 @@ extension XmlMapsXmlNameFlattenedOutputBody { @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, "/RestXml/models/XmlMapsXmlNameNestedOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsXmlNameNestedOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlMapsXmlNameNestedOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlMapsXmlNameNestedOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: SmithyXML.mapReadingClosure(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.readingClosure, keyNodeInfo: "CustomKey2", valueNodeInfo: "CustomValue2", isFlattened: false), keyNodeInfo: "CustomKey1", valueNodeInfo: "CustomValue1", isFlattened: false) - return value - } +extension XmlMapsXmlNameNestedOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: mapReadingClosure(valueReadingClosure: RestXmlProtocolClientTypes.GreetingStruct.read(from:), keyNodeInfo: "CustomKey2", valueNodeInfo: "CustomValue2", isFlattened: false), keyNodeInfo: "CustomKey1", valueNodeInfo: "CustomValue1", isFlattened: false) + return value } } """ @@ -196,17 +196,17 @@ extension XmlMapsXmlNameNestedOutputBody { @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, "/RestXml/models/XmlMapsFlattenedNestedXmlNameOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsFlattenedNestedXmlNameOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlMapsFlattenedNestedXmlNameOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlMapsFlattenedNestedXmlNameOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: SmithyXML.mapReadingClosure(valueReadingClosure: Swift.String.readingClosure, keyNodeInfo: "K", valueNodeInfo: "V", isFlattened: false), keyNodeInfo: "yek", valueNodeInfo: "eulav", isFlattened: true) - return value - } +extension XmlMapsFlattenedNestedXmlNameOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: mapReadingClosure(valueReadingClosure: Swift.String.read(from:), keyNodeInfo: "K", valueNodeInfo: "V", isFlattened: false), keyNodeInfo: "yek", valueNodeInfo: "eulav", isFlattened: true) + return value } } """ @@ -216,17 +216,17 @@ extension XmlMapsFlattenedNestedXmlNameOutputBody { @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, "/RestXml/models/XmlMapsXmlNamespaceOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsXmlNamespaceOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlMapsXmlNamespaceOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlMapsXmlNamespaceOutput() - value.myMap = try reader[.init("myMap", namespaceDef: .init(prefix: "", uri: "http://boo.com"))].readMapIfPresent(valueReadingClosure: Swift.String.readingClosure, keyNodeInfo: .init("Quality", namespaceDef: .init(prefix: "", uri: "http://doo.com")), valueNodeInfo: .init("Degree", namespaceDef: .init(prefix: "", uri: "http://eoo.com")), isFlattened: false) - return value - } +extension XmlMapsXmlNamespaceOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: Swift.String.read(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 } } """ @@ -236,17 +236,17 @@ extension XmlMapsXmlNamespaceOutputBody { @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, "/RestXml/models/XmlMapsFlattenedXmlNamespaceOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsFlattenedXmlNamespaceOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlMapsFlattenedXmlNamespaceOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlMapsFlattenedXmlNamespaceOutput() - value.myMap = try reader[.init("myMap", namespaceDef: .init(prefix: "", uri: "http://boo.com"))].readMapIfPresent(valueReadingClosure: Swift.String.readingClosure, keyNodeInfo: .init("Uid", namespaceDef: .init(prefix: "", uri: "http://doo.com")), valueNodeInfo: .init("Val", namespaceDef: .init(prefix: "", uri: "http://eoo.com")), isFlattened: true) - return value - } +extension XmlMapsFlattenedXmlNamespaceOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: Swift.String.read(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 } } """ @@ -256,17 +256,17 @@ extension XmlMapsFlattenedXmlNamespaceOutputBody { @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, "/RestXml/models/XmlMapsNestedXmlNamespaceOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsNestedXmlNamespaceOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlMapsNestedXmlNamespaceOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlMapsNestedXmlNamespaceOutput() - value.myMap = try reader[.init("myMap", namespaceDef: .init(prefix: "", uri: "http://boo.com"))].readMapIfPresent(valueReadingClosure: SmithyXML.mapReadingClosure(valueReadingClosure: Swift.String.readingClosure, 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 - } +extension XmlMapsNestedXmlNamespaceOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: mapReadingClosure(valueReadingClosure: Swift.String.read(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 } } """ @@ -276,17 +276,17 @@ extension XmlMapsNestedXmlNamespaceOutputBody { @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, "/RestXml/models/XmlMapsFlattenedNestedXmlNamespaceOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsFlattenedNestedXmlNamespaceOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlMapsFlattenedNestedXmlNamespaceOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlMapsFlattenedNestedXmlNamespaceOutput() - value.myMap = try reader[.init("myMap", namespaceDef: .init(prefix: "", uri: "http://boo.com"))].readMapIfPresent(valueReadingClosure: SmithyXML.mapReadingClosure(valueReadingClosure: Swift.String.readingClosure, 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 - } +extension XmlMapsFlattenedNestedXmlNamespaceOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: mapReadingClosure(valueReadingClosure: Swift.String.read(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 } } """ @@ -295,17 +295,17 @@ extension XmlMapsFlattenedNestedXmlNamespaceOutputBody { @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, "/RestXml/models/XmlMapsContainListOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsContainListOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlMapsContainListOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlMapsContainListOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: SmithyXML.listReadingClosure(memberReadingClosure: Swift.String.readingClosure, memberNodeInfo: "member", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - return value - } +extension XmlMapsContainListOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: listReadingClosure(memberReadingClosure: Swift.String.read(from:), memberNodeInfo: "member", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + return value } } """ @@ -314,17 +314,17 @@ extension XmlMapsContainListOutputBody { @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, "/RestXml/models/XmlMapsFlattenedContainListOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsFlattenedContainListOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlMapsFlattenedContainListOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlMapsFlattenedContainListOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: SmithyXML.listReadingClosure(memberReadingClosure: Swift.String.readingClosure, memberNodeInfo: "member", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) - return value - } +extension XmlMapsFlattenedContainListOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: listReadingClosure(memberReadingClosure: Swift.String.read(from:), memberNodeInfo: "member", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) + return value } } """ @@ -334,17 +334,17 @@ extension XmlMapsFlattenedContainListOutputBody { @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, "/RestXml/models/XmlMapsTimestampsOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsTimestampsOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlMapsTimestampsOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlMapsTimestampsOutput() - value.timestampMap = try reader["timestampMap"].readMapIfPresent(valueReadingClosure: SmithyXML.timestampReadingClosure(format: .epochSeconds), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - return value - } +extension XmlMapsTimestampsOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: timestampReadingClosure(format: .epochSeconds), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + return value } } """ @@ -354,17 +354,17 @@ extension XmlMapsTimestampsOutputBody { @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, "/RestXml/models/XmlMapsFlattenedTimestampsOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsFlattenedTimestampsOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlMapsFlattenedTimestampsOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlMapsFlattenedTimestampsOutput() - value.timestampMap = try reader["timestampMap"].readMapIfPresent(valueReadingClosure: SmithyXML.timestampReadingClosure(format: .epochSeconds), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) - return value - } +extension XmlMapsFlattenedTimestampsOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: timestampReadingClosure(format: .epochSeconds), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) + return value } } """ @@ -373,25 +373,25 @@ extension XmlMapsFlattenedTimestampsOutputBody { @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, "/RestXml/models/XmlMapsTwoOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsTwoOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlMapsTwoOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlMapsTwoOutput() - value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: Swift.String.readingClosure, keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - value.mySecondMap = try reader["mySecondMap"].readMapIfPresent(valueReadingClosure: Swift.String.readingClosure, keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) - return value - } +extension XmlMapsTwoOutput { + + static func httpOutput(from httpResponse: ClientRuntime.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: Swift.String.read(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + value.mySecondMap = try reader["mySecondMap"].readMapIfPresent(valueReadingClosure: Swift.String.read(from:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + return value } } """ contents.shouldContainOnlyOnce(expectedContents) } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.generateDeserializers(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/serde/xml/MapEncodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/xml/MapEncodeXMLGenerationTests.kt index f53db61e3..422352059 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/xml/MapEncodeXMLGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/xml/MapEncodeXMLGenerationTests.kt @@ -5,7 +5,7 @@ package serde.xml -import MockHttpRestXMLProtocolGenerator +import MockHTTPRestXMLProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -16,12 +16,13 @@ class MapEncodeXMLGenerationTests { @Test fun `001 encode map`() { val context = setupTests("Isolated/Restxml/xml-maps.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsInput+Write.swift") val expectedContents = """ extension XmlMapsInput { - static func writingClosure(_ value: XmlMapsInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.writingClosure(_:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + + 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) } } """ @@ -31,12 +32,13 @@ extension XmlMapsInput { @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, "/RestXml/models/XmlMapsWithNameProtocolInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsWithNameProtocolInput+Write.swift") val expectedContents = """ extension XmlMapsWithNameProtocolInput { - static func writingClosure(_ value: XmlMapsWithNameProtocolInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["protocol"].writeMap(value.`protocol`, valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.writingClosure(_:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + + 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) } } """ @@ -46,12 +48,13 @@ extension XmlMapsWithNameProtocolInput { @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, "/RestXml/models/XmlMapsNestedInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsNestedInput+Write.swift") val expectedContents = """ extension XmlMapsNestedInput { - static func writingClosure(_ value: XmlMapsNestedInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: SmithyXML.mapWritingClosure(valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.writingClosure(_:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + + static func write(value: XmlMapsNestedInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["myMap"].writeMap(value.myMap, valueWritingClosure: mapWritingClosure(valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) } } """ @@ -61,12 +64,13 @@ extension XmlMapsNestedInput { @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, "/RestXml/models/XmlMapsNestedNestedInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsNestedNestedInput+Write.swift") val expectedContents = """ extension XmlMapsNestedNestedInput { - static func writingClosure(_ value: XmlMapsNestedNestedInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: SmithyXML.mapWritingClosure(valueWritingClosure: SmithyXML.mapWritingClosure(valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.writingClosure(_:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + + static func write(value: XmlMapsNestedNestedInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["myMap"].writeMap(value.myMap, valueWritingClosure: mapWritingClosure(valueWritingClosure: 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) } } """ @@ -76,12 +80,13 @@ extension XmlMapsNestedNestedInput { @Test fun `005 encode flattened map`() { val context = setupTests("Isolated/Restxml/xml-maps-flattened.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlFlattenedMapsInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlFlattenedMapsInput+Write.swift") val expectedContents = """ extension XmlFlattenedMapsInput { - static func writingClosure(_ value: XmlFlattenedMapsInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.writingClosure(_:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) + + 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) } } """ @@ -91,12 +96,13 @@ extension XmlFlattenedMapsInput { @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, "/RestXml/models/XmlMapsFlattenedNestedInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsFlattenedNestedInput+Write.swift") val expectedContents = """ extension XmlMapsFlattenedNestedInput { - static func writingClosure(_ value: XmlMapsFlattenedNestedInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: SmithyXML.mapWritingClosure(valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.writingClosure(_:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) + + static func write(value: XmlMapsFlattenedNestedInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["myMap"].writeMap(value.myMap, valueWritingClosure: mapWritingClosure(valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) } } """ @@ -106,12 +112,13 @@ extension XmlMapsFlattenedNestedInput { @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, "/RestXml/models/XmlMapsXmlNameInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsXmlNameInput+Write.swift") val expectedContents = """ extension XmlMapsXmlNameInput { - static func writingClosure(_ value: XmlMapsXmlNameInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.writingClosure(_:to:), keyNodeInfo: "Attribute", valueNodeInfo: "Setting", isFlattened: false) + + 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) } } """ @@ -121,12 +128,13 @@ extension XmlMapsXmlNameInput { @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, "/RestXml/models/XmlMapsXmlNameFlattenedInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsXmlNameFlattenedInput+Write.swift") val expectedContents = """ extension XmlMapsXmlNameFlattenedInput { - static func writingClosure(_ value: XmlMapsXmlNameFlattenedInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.writingClosure(_:to:), keyNodeInfo: "SomeCustomKey", valueNodeInfo: "SomeCustomValue", isFlattened: true) + + 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) } } """ @@ -136,12 +144,13 @@ extension XmlMapsXmlNameFlattenedInput { @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, "/RestXml/models/XmlMapsXmlNameNestedInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsXmlNameNestedInput+Write.swift") val expectedContents = """ extension XmlMapsXmlNameNestedInput { - static func writingClosure(_ value: XmlMapsXmlNameNestedInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: SmithyXML.mapWritingClosure(valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.writingClosure(_:to:), keyNodeInfo: "CustomKey2", valueNodeInfo: "CustomValue2", isFlattened: false), keyNodeInfo: "CustomKey1", valueNodeInfo: "CustomValue1", isFlattened: false) + + static func write(value: XmlMapsXmlNameNestedInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["myMap"].writeMap(value.myMap, valueWritingClosure: mapWritingClosure(valueWritingClosure: RestXmlProtocolClientTypes.GreetingStruct.write(value:to:), keyNodeInfo: "CustomKey2", valueNodeInfo: "CustomValue2", isFlattened: false), keyNodeInfo: "CustomKey1", valueNodeInfo: "CustomValue1", isFlattened: false) } } """ @@ -151,12 +160,13 @@ extension XmlMapsXmlNameNestedInput { @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, "/RestXml/models/XmlMapsFlattenedNestedXmlNameInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsFlattenedNestedXmlNameInput+Write.swift") val expectedContents = """ extension XmlMapsFlattenedNestedXmlNameInput { - static func writingClosure(_ value: XmlMapsFlattenedNestedXmlNameInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: SmithyXML.mapWritingClosure(valueWritingClosure: Swift.String.writingClosure(_:to:), keyNodeInfo: "K", valueNodeInfo: "V", isFlattened: false), keyNodeInfo: "yek", valueNodeInfo: "eulav", isFlattened: true) + + static func write(value: XmlMapsFlattenedNestedXmlNameInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["myMap"].writeMap(value.myMap, valueWritingClosure: mapWritingClosure(valueWritingClosure: Swift.String.write(value:to:), keyNodeInfo: "K", valueNodeInfo: "V", isFlattened: false), keyNodeInfo: "yek", valueNodeInfo: "eulav", isFlattened: true) } } """ @@ -166,12 +176,13 @@ extension XmlMapsFlattenedNestedXmlNameInput { @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, "/RestXml/models/XmlMapsXmlNamespaceInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsXmlNamespaceInput+Write.swift") val expectedContents = """ extension XmlMapsXmlNamespaceInput { - static func writingClosure(_ value: XmlMapsXmlNamespaceInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer[.init("myMap", namespaceDef: .init(prefix: "", uri: "http://boo.com"))].writeMap(value.myMap, valueWritingClosure: Swift.String.writingClosure(_:to:), keyNodeInfo: .init("Quality", namespaceDef: .init(prefix: "", uri: "http://doo.com")), valueNodeInfo: .init("Degree", namespaceDef: .init(prefix: "", uri: "http://eoo.com")), isFlattened: false) + + 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: Swift.String.write(value:to:), keyNodeInfo: .init("Quality", namespaceDef: .init(prefix: "", uri: "http://doo.com")), valueNodeInfo: .init("Degree", namespaceDef: .init(prefix: "", uri: "http://eoo.com")), isFlattened: false) } } """ @@ -180,12 +191,13 @@ extension XmlMapsXmlNamespaceInput { @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, "/RestXml/models/XmlMapsFlattenedXmlNamespaceInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsFlattenedXmlNamespaceInput+Write.swift") val expectedContents = """ extension XmlMapsFlattenedXmlNamespaceInput { - static func writingClosure(_ value: XmlMapsFlattenedXmlNamespaceInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer[.init("myMap", namespaceDef: .init(prefix: "", uri: "http://boo.com"))].writeMap(value.myMap, valueWritingClosure: Swift.String.writingClosure(_:to:), keyNodeInfo: .init("Uid", namespaceDef: .init(prefix: "", uri: "http://doo.com")), valueNodeInfo: .init("Val", namespaceDef: .init(prefix: "", uri: "http://eoo.com")), isFlattened: true) + + 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: Swift.String.write(value:to:), keyNodeInfo: .init("Uid", namespaceDef: .init(prefix: "", uri: "http://doo.com")), valueNodeInfo: .init("Val", namespaceDef: .init(prefix: "", uri: "http://eoo.com")), isFlattened: true) } } """ @@ -195,12 +207,13 @@ extension XmlMapsFlattenedXmlNamespaceInput { @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, "/RestXml/models/XmlMapsNestedXmlNamespaceInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsNestedXmlNamespaceInput+Write.swift") val expectedContents = """ extension XmlMapsNestedXmlNamespaceInput { - static func writingClosure(_ value: XmlMapsNestedXmlNamespaceInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer[.init("myMap", namespaceDef: .init(prefix: "", uri: "http://boo.com"))].writeMap(value.myMap, valueWritingClosure: SmithyXML.mapWritingClosure(valueWritingClosure: Swift.String.writingClosure(_: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) + + 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: mapWritingClosure(valueWritingClosure: Swift.String.write(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) } } """ @@ -209,12 +222,13 @@ extension XmlMapsNestedXmlNamespaceInput { @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, "/RestXml/models/XmlMapsFlattenedNestedXmlNamespaceInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsFlattenedNestedXmlNamespaceInput+Write.swift") val expectedContents = """ extension XmlMapsFlattenedNestedXmlNamespaceInput { - static func writingClosure(_ value: XmlMapsFlattenedNestedXmlNamespaceInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer[.init("myMap", namespaceDef: .init(prefix: "", uri: "http://boo.com"))].writeMap(value.myMap, valueWritingClosure: SmithyXML.mapWritingClosure(valueWritingClosure: Swift.String.writingClosure(_: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) + + 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: mapWritingClosure(valueWritingClosure: Swift.String.write(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) } } """ @@ -223,12 +237,13 @@ extension XmlMapsFlattenedNestedXmlNamespaceInput { @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, "/RestXml/models/XmlMapsContainListInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsContainListInput+Write.swift") val expectedContents = """ extension XmlMapsContainListInput { - static func writingClosure(_ value: XmlMapsContainListInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: SmithyXML.listWritingClosure(memberWritingClosure: Swift.String.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + + static func write(value: XmlMapsContainListInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["myMap"].writeMap(value.myMap, valueWritingClosure: listWritingClosure(memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) } } """ @@ -237,12 +252,13 @@ extension XmlMapsContainListInput { @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, "/RestXml/models/XmlMapsFlattenedContainListInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsFlattenedContainListInput+Write.swift") val expectedContents = """ extension XmlMapsFlattenedContainListInput { - static func writingClosure(_ value: XmlMapsFlattenedContainListInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["myMap"].writeMap(value.myMap, valueWritingClosure: SmithyXML.listWritingClosure(memberWritingClosure: Swift.String.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) + + static func write(value: XmlMapsFlattenedContainListInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["myMap"].writeMap(value.myMap, valueWritingClosure: listWritingClosure(memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) } } """ @@ -251,12 +267,13 @@ extension XmlMapsFlattenedContainListInput { @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, "/RestXml/models/XmlMapsTimestampsInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsTimestampsInput+Write.swift") val expectedContents = """ extension XmlMapsTimestampsInput { - static func writingClosure(_ value: XmlMapsTimestampsInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["timestampMap"].writeMap(value.timestampMap, valueWritingClosure: SmithyXML.timestampWritingClosure(format: .epochSeconds), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + + static func write(value: XmlMapsTimestampsInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["timestampMap"].writeMap(value.timestampMap, valueWritingClosure: timestampWritingClosure(format: .epochSeconds), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) } } """ @@ -266,12 +283,13 @@ extension XmlMapsTimestampsInput { @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, "/RestXml/models/XmlMapsFlattenedTimestampsInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlMapsFlattenedTimestampsInput+Write.swift") val expectedContents = """ extension XmlMapsFlattenedTimestampsInput { - static func writingClosure(_ value: XmlMapsFlattenedTimestampsInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["timestampMap"].writeMap(value.timestampMap, valueWritingClosure: SmithyXML.timestampWritingClosure(format: .epochSeconds), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) + + static func write(value: XmlMapsFlattenedTimestampsInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["timestampMap"].writeMap(value.timestampMap, valueWritingClosure: timestampWritingClosure(format: .epochSeconds), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) } } """ @@ -281,13 +299,14 @@ extension XmlMapsFlattenedTimestampsInput { @Test fun `018 encode fooenumMap`() { val context = setupTests("Isolated/Restxml/xml-maps-nested-fooenum.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/NestedXmlMapsInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/NestedXmlMapsInput+Write.swift") val expectedContents = """ extension NestedXmlMapsInput { - static func writingClosure(_ value: NestedXmlMapsInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["flatNestedMap"].writeMap(value.flatNestedMap, valueWritingClosure: SmithyXML.mapWritingClosure(valueWritingClosure: RestXmlProtocolClientTypes.FooEnum.writingClosure(_:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) - try writer["nestedMap"].writeMap(value.nestedMap, valueWritingClosure: SmithyXML.mapWritingClosure(valueWritingClosure: RestXmlProtocolClientTypes.FooEnum.writingClosure(_:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) + + static func write(value: NestedXmlMapsInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["flatNestedMap"].writeMap(value.flatNestedMap, valueWritingClosure: mapWritingClosure(valueWritingClosure: RestXmlProtocolClientTypes.FooEnum.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) + try writer["nestedMap"].writeMap(value.nestedMap, valueWritingClosure: mapWritingClosure(valueWritingClosure: RestXmlProtocolClientTypes.FooEnum.write(value:to:), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false), keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: false) } } """ @@ -295,7 +314,7 @@ extension NestedXmlMapsInput { } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.generateSerializers(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/serde/xml/MemberShapeDecodeXMLGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/xml/MemberShapeDecodeXMLGeneratorTests.kt deleted file mode 100644 index c87574fc8..000000000 --- a/smithy-swift-codegen/src/test/kotlin/serde/xml/MemberShapeDecodeXMLGeneratorTests.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package serde.xml - -import MockHttpRestXMLProtocolGenerator -import TestContext -import defaultSettings -import getFileContents -import io.kotest.matchers.string.shouldContainOnlyOnce -import org.junit.jupiter.api.Test - -class MemberShapeDecodeXMLGeneratorTests { - - @Test - fun `001 set default value for a missing value of a scalar member`() { - val context = setupTests("Isolated/Restxml/xml-scalarmember-default-value.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/SimpleScalarPropertiesOutputBody+Decodable.swift") - val expectedContents = """ -extension SimpleScalarPropertiesOutputBody { - - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = SimpleScalarPropertiesOutput() - value.stringValue = try reader["stringValue"].readIfPresent() ?? test - value.trueBooleanValue = try reader["trueBooleanValue"].readIfPresent() ?? false - value.falseBooleanValue = try reader["falseBooleanValue"].readIfPresent() - value.byteValue = try reader["byteValue"].readIfPresent() - value.shortValue = try reader["shortValue"].readIfPresent() - value.integerValue = try reader["integerValue"].readIfPresent() ?? 5 - value.longValue = try reader["longValue"].readIfPresent() - value.floatValue = try reader["floatValue"].readIfPresent() ?? 2.4 - value.`protocol` = try reader["protocol"].readIfPresent() - value.doubleValue = try reader["DoubleDribble"].readIfPresent() - return value - } - } -} -""" - contents.shouldContainOnlyOnce(expectedContents) - } - private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> - model.defaultSettings(serviceShapeId, "RestXml", "2023-08-08", "Rest Xml Protocol") - } - context.generator.generateDeserializers(context.generationCtx) - context.generationCtx.delegator.flushWriters() - return context - } -} diff --git a/smithy-swift-codegen/src/test/kotlin/serde/xml/NamespaceEncodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/xml/NamespaceEncodeXMLGenerationTests.kt index a0acda270..9d59ad64b 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/xml/NamespaceEncodeXMLGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/xml/NamespaceEncodeXMLGenerationTests.kt @@ -5,7 +5,7 @@ package serde.xml -import MockHttpRestXMLProtocolGenerator +import MockHTTPRestXMLProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -16,12 +16,13 @@ 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, "/RestXml/models/XmlNamespacesInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlNamespacesInput+Write.swift") val expectedContents = """ extension XmlNamespacesInput { - static func writingClosure(_ value: XmlNamespacesInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer[.init("nested", namespaceDef: .init(prefix: "", uri: "http://boo.com"))].write(value.nested, writingClosure: RestXmlProtocolClientTypes.XmlNamespaceNested.writingClosure(_:to:)) + + 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:)) } } """ @@ -31,24 +32,22 @@ extension XmlNamespacesInput { @Test fun `003 xmlnamespace, XmlNamespaceNested`() { val context = setupTests("Isolated/Restxml/xml-namespace.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlNamespaceNested+Codable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlNamespaceNested+ReadWrite.swift") val expectedContents = """ extension RestXmlProtocolClientTypes.XmlNamespaceNested { - static func writingClosure(_ value: RestXmlProtocolClientTypes.XmlNamespaceNested?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } + 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: Swift.String.writingClosure(_:to:), memberNodeInfo: .init("member", namespaceDef: .init(prefix: "", uri: "http://bux.com")), isFlattened: false) + try writer[.init("values", namespaceDef: .init(prefix: "", uri: "http://qux.com"))].writeList(value.values, memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: .init("member", namespaceDef: .init(prefix: "", uri: "http://bux.com")), isFlattened: false) } - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - 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: Swift.String.readingClosure, memberNodeInfo: .init("member", namespaceDef: .init(prefix: "", uri: "http://bux.com")), isFlattened: false) - return value - } + 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: Swift.String.read(from:), memberNodeInfo: .init("member", namespaceDef: .init(prefix: "", uri: "http://bux.com")), isFlattened: false) + return value } } """ @@ -58,12 +57,13 @@ extension RestXmlProtocolClientTypes.XmlNamespaceNested { @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, "/RestXml/models/XmlNamespaceNestedListInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlNamespaceNestedListInput+Write.swift") val expectedContents = """ extension XmlNamespaceNestedListInput { - static func writingClosure(_ value: XmlNamespaceNestedListInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer[.init("nested", namespaceDef: .init(prefix: "", uri: "http://aux.com"))].writeList(value.nested, memberWritingClosure: SmithyXML.listWritingClosure(memberWritingClosure: Swift.String.writingClosure(_: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) + + 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: listWritingClosure(memberWritingClosure: Swift.String.write(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) } } """ @@ -73,12 +73,13 @@ extension XmlNamespaceNestedListInput { @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, "/RestXml/models/XmlNamespaceFlattenedListInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlNamespaceFlattenedListInput+Write.swift") val expectedContents = """ extension XmlNamespaceFlattenedListInput { - static func writingClosure(_ value: XmlNamespaceFlattenedListInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer[.init("nested", namespaceDef: .init(prefix: "baz", uri: "http://aux.com"))].writeList(value.nested, memberWritingClosure: Swift.String.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: true) + + 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: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: true) } } """ @@ -88,13 +89,14 @@ extension XmlNamespaceFlattenedListInput { @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, "/RestXml/models/XmlNamespacesOnServiceInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlNamespacesOnServiceInput+Write.swift") val expectedContents = """ extension XmlNamespacesOnServiceInput { - static func writingClosure(_ value: XmlNamespacesOnServiceInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } + + 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, writingClosure: RestXmlProtocolClientTypes.NestedWithNamespace.writingClosure(_:to:)) + try writer[.init("nested", namespaceDef: .init(prefix: "xsi", uri: "https://example.com"))].write(value.nested, with: RestXmlProtocolClientTypes.NestedWithNamespace.write(value:to:)) } } """ @@ -104,20 +106,21 @@ extension XmlNamespacesOnServiceInput { @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, "/RestXml/models/XmlNamespacesOnServiceOverridableInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlNamespacesOnServiceOverridableInput+Write.swift") val expectedContents = """ extension XmlNamespacesOnServiceOverridableInput { - static func writingClosure(_ value: XmlNamespacesOnServiceOverridableInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } + + 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, writingClosure: RestXmlProtocolClientTypes.NestedWithNamespace.writingClosure(_:to:)) + try writer[.init("nested", namespaceDef: .init(prefix: "xsi", uri: "https://example.com"))].write(value.nested, with: RestXmlProtocolClientTypes.NestedWithNamespace.write(value:to:)) } } """ contents.shouldContainOnlyOnce(expectedContents) } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.generateCodableConformanceForNestedTypes(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/serde/xml/RecursiveShapesDecodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/xml/RecursiveShapesDecodeXMLGenerationTests.kt index 90f179138..425956611 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/xml/RecursiveShapesDecodeXMLGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/xml/RecursiveShapesDecodeXMLGenerationTests.kt @@ -5,7 +5,7 @@ package serde.xml -import MockHttpRestXMLProtocolGenerator +import MockHTTPRestXMLProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -16,17 +16,17 @@ class RecursiveShapesDecodeXMLGenerationTests { @Test fun `decode recursive shape`() { val context = setupTests("Isolated/Restxml/xml-recursive.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlRecursiveShapesOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlRecursiveShapesOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlRecursiveShapesOutputBody { +extension XmlRecursiveShapesOutput { - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlRecursiveShapesOutput() - value.nested = try reader["nested"].readIfPresent(readingClosure: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1.readingClosure) - return value - } + static func httpOutput(from httpResponse: ClientRuntime.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 } } """ @@ -36,17 +36,17 @@ extension XmlRecursiveShapesOutputBody { @Test fun `decode recursive nested shape`() { val context = setupTests("Isolated/Restxml/xml-recursive-nested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlNestedRecursiveShapesOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlNestedRecursiveShapesOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlNestedRecursiveShapesOutputBody { +extension XmlNestedRecursiveShapesOutput { - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlNestedRecursiveShapesOutput() - value.nestedRecursiveList = try reader["nestedRecursiveList"].readListIfPresent(memberReadingClosure: SmithyXML.listReadingClosure(memberReadingClosure: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1.readingClosure, memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - return value - } + static func httpOutput(from httpResponse: ClientRuntime.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: listReadingClosure(memberReadingClosure: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1.read(from:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) + return value } } """ @@ -54,7 +54,7 @@ extension XmlNestedRecursiveShapesOutputBody { } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.generateDeserializers(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/serde/xml/RecursiveShapesEncodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/xml/RecursiveShapesEncodeXMLGenerationTests.kt index 424df6a3b..c3d7bd66a 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/xml/RecursiveShapesEncodeXMLGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/xml/RecursiveShapesEncodeXMLGenerationTests.kt @@ -5,7 +5,7 @@ package serde.xml -import MockHttpRestXMLProtocolGenerator +import MockHTTPRestXMLProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -16,24 +16,22 @@ 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, "/RestXml/models/RecursiveShapesInputOutputNested1+Codable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/RecursiveShapesInputOutputNested1+ReadWrite.swift") val expectedContents = """ extension RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1 { - static func writingClosure(_ value: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } + 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, writingClosure: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested2.writingClosure(_:to:)) + try writer["nested"].write(value.nested, with: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested2.write(value:to:)) } - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1() - value.foo = try reader["foo"].readIfPresent() - value.nested = try reader["nested"].readIfPresent(readingClosure: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested2.readingClosure) - return value - } + 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 } } """ @@ -43,24 +41,22 @@ extension RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1 { @Test fun `encode recursive shape Nested2`() { val context = setupTests("Isolated/Restxml/xml-recursive.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/RecursiveShapesInputOutputNested2+Codable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/RecursiveShapesInputOutputNested2+ReadWrite.swift") val expectedContents = """ extension RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested2 { - static func writingClosure(_ value: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested2?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } + 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, writingClosure: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1.writingClosure(_:to:)) + try writer["recursiveMember"].write(value.recursiveMember, with: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1.write(value:to:)) } - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested2() - value.bar = try reader["bar"].readIfPresent() - value.recursiveMember = try reader["recursiveMember"].readIfPresent(readingClosure: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1.readingClosure) - return value - } + 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 } } """ @@ -69,19 +65,20 @@ extension RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested2 { @Test fun `encode recursive nested shape`() { val context = setupTests("Isolated/Restxml/xml-recursive-nested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlNestedRecursiveShapesInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlNestedRecursiveShapesInput+Write.swift") val expectedContents = """ extension XmlNestedRecursiveShapesInput { - static func writingClosure(_ value: XmlNestedRecursiveShapesInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["nestedRecursiveList"].writeList(value.nestedRecursiveList, memberWritingClosure: SmithyXML.listWritingClosure(memberWritingClosure: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) + + static func write(value: XmlNestedRecursiveShapesInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["nestedRecursiveList"].writeList(value.nestedRecursiveList, memberWritingClosure: listWritingClosure(memberWritingClosure: RestXmlProtocolClientTypes.RecursiveShapesInputOutputNested1.write(value:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) } } """ contents.shouldContainOnlyOnce(expectedContents) } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.generateSerializers(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/serde/xml/SetDecodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/xml/SetDecodeXMLGenerationTests.kt index 056c02912..5960c4e2a 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/xml/SetDecodeXMLGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/xml/SetDecodeXMLGenerationTests.kt @@ -5,7 +5,7 @@ package serde.xml -import MockHttpRestXMLProtocolGenerator +import MockHTTPRestXMLProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -16,17 +16,17 @@ class SetDecodeXMLGenerationTests { @Test fun `XmlEnumSetOutputBody decodable`() { val context = setupTests("Isolated/Restxml/xml-sets.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlEnumSetOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlEnumSetOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlEnumSetOutputBody { +extension XmlEnumSetOutput { - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlEnumSetOutput() - value.fooEnumSet = try reader["fooEnumSet"].readListIfPresent(memberReadingClosure: RestXmlProtocolClientTypes.FooEnum.readingClosure, memberNodeInfo: "member", isFlattened: false) - return value - } + static func httpOutput(from httpResponse: ClientRuntime.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: RestXmlProtocolClientTypes.FooEnum.read(from:), memberNodeInfo: "member", isFlattened: false) + return value } } """ @@ -36,17 +36,17 @@ extension XmlEnumSetOutputBody { @Test fun `XmlEnumNestedSetOutputBody nested decodable`() { val context = setupTests("Isolated/Restxml/xml-sets-nested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlEnumNestedSetOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlEnumNestedSetOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlEnumNestedSetOutputBody { +extension XmlEnumNestedSetOutput { - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlEnumNestedSetOutput() - value.fooEnumSet = try reader["fooEnumSet"].readListIfPresent(memberReadingClosure: SmithyXML.listReadingClosure(memberReadingClosure: RestXmlProtocolClientTypes.FooEnum.readingClosure, memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - return value - } + static func httpOutput(from httpResponse: ClientRuntime.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: listReadingClosure(memberReadingClosure: RestXmlProtocolClientTypes.FooEnum.read(from:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) + return value } } """ @@ -54,7 +54,7 @@ extension XmlEnumNestedSetOutputBody { } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.generateDeserializers(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/serde/xml/SetEncodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/xml/SetEncodeXMLGenerationTests.kt index ceb6d880a..8a008e78d 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/xml/SetEncodeXMLGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/xml/SetEncodeXMLGenerationTests.kt @@ -5,7 +5,7 @@ package serde.xml -import MockHttpRestXMLProtocolGenerator +import MockHTTPRestXMLProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -16,12 +16,13 @@ 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, "/RestXml/models/XmlEnumSetInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlEnumSetInput+Write.swift") val expectedContents = """ extension XmlEnumSetInput { - static func writingClosure(_ value: XmlEnumSetInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["fooEnumSet"].writeList(value.fooEnumSet, memberWritingClosure: RestXmlProtocolClientTypes.FooEnum.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: false) + + static func write(value: XmlEnumSetInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["fooEnumSet"].writeList(value.fooEnumSet, memberWritingClosure: RestXmlProtocolClientTypes.FooEnum.write(value:to:), memberNodeInfo: "member", isFlattened: false) } } """ @@ -31,12 +32,13 @@ extension XmlEnumSetInput { @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, "/RestXml/models/XmlEnumNestedSetInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlEnumNestedSetInput+Write.swift") val expectedContents = """ extension XmlEnumNestedSetInput { - static func writingClosure(_ value: XmlEnumNestedSetInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["fooEnumSet"].writeList(value.fooEnumSet, memberWritingClosure: SmithyXML.listWritingClosure(memberWritingClosure: RestXmlProtocolClientTypes.FooEnum.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) + + static func write(value: XmlEnumNestedSetInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["fooEnumSet"].writeList(value.fooEnumSet, memberWritingClosure: listWritingClosure(memberWritingClosure: RestXmlProtocolClientTypes.FooEnum.write(value:to:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) } } """ @@ -44,7 +46,7 @@ extension XmlEnumNestedSetInput { } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.generateCodableConformanceForNestedTypes(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/serde/xml/StructDecodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/xml/StructDecodeXMLGenerationTests.kt index f9a9458dd..0ec1f2162 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/xml/StructDecodeXMLGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/xml/StructDecodeXMLGenerationTests.kt @@ -5,7 +5,7 @@ package serde.xml -import MockHttpRestXMLProtocolGenerator +import MockHTTPRestXMLProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -17,17 +17,17 @@ class StructDecodeXMLGenerationTests { fun `XmlWrappedListOutputBody decodable`() { val context = setupTests("Isolated/Restxml/xml-lists-wrapped.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlWrappedListOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlWrappedListOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlWrappedListOutputBody { +extension XmlWrappedListOutput { - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlWrappedListOutput() - value.myGroceryList = try reader["myGroceryList"].readListIfPresent(memberReadingClosure: Swift.String.readingClosure, memberNodeInfo: "member", isFlattened: false) - return value - } + static func httpOutput(from httpResponse: ClientRuntime.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: Swift.String.read(from:), memberNodeInfo: "member", isFlattened: false) + return value } } """ @@ -38,39 +38,29 @@ extension XmlWrappedListOutputBody { fun `SimpleScalarPropertiesOutputBody decodable`() { val context = setupTests("Isolated/Restxml/xml-scalar.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/SimpleScalarPropertiesOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/SimpleScalarPropertiesOutput+HttpResponseBinding.swift") val expectedContents = """ -struct SimpleScalarPropertiesOutputBody { - let stringValue: Swift.String? - let trueBooleanValue: Swift.Bool? - let falseBooleanValue: Swift.Bool? - let byteValue: Swift.Int8? - let shortValue: Swift.Int16? - let integerValue: Swift.Int? - let longValue: Swift.Int? - let floatValue: Swift.Float? - let `protocol`: Swift.String? - let doubleValue: Swift.Double? -} - -extension SimpleScalarPropertiesOutputBody { +extension SimpleScalarPropertiesOutput { - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = SimpleScalarPropertiesOutput() - value.stringValue = try reader["stringValue"].readIfPresent() - value.trueBooleanValue = try reader["trueBooleanValue"].readIfPresent() - value.falseBooleanValue = try reader["falseBooleanValue"].readIfPresent() - value.byteValue = try reader["byteValue"].readIfPresent() - value.shortValue = try reader["shortValue"].readIfPresent() - value.integerValue = try reader["integerValue"].readIfPresent() - value.longValue = try reader["longValue"].readIfPresent() - value.floatValue = try reader["floatValue"].readIfPresent() - value.`protocol` = try reader["protocol"].readIfPresent() - value.doubleValue = try reader["DoubleDribble"].readIfPresent() - return value + static func httpOutput(from httpResponse: ClientRuntime.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 } } """ @@ -80,17 +70,17 @@ extension SimpleScalarPropertiesOutputBody { @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, "/RestXml/models/XmlNestedNestedWrappedListOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlNestedNestedWrappedListOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlNestedNestedWrappedListOutputBody { +extension XmlNestedNestedWrappedListOutput { - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlNestedNestedWrappedListOutput() - value.nestedNestedStringList = try reader["nestedNestedStringList"].readListIfPresent(memberReadingClosure: SmithyXML.listReadingClosure(memberReadingClosure: SmithyXML.listReadingClosure(memberReadingClosure: Swift.String.readingClosure, memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - return value - } + static func httpOutput(from httpResponse: ClientRuntime.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: listReadingClosure(memberReadingClosure: listReadingClosure(memberReadingClosure: Swift.String.read(from:), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) + return value } } """ @@ -100,20 +90,20 @@ extension XmlNestedNestedWrappedListOutputBody { @Test fun `empty lists decode`() { val context = setupTests("Isolated/Restxml/xml-lists-empty.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlEmptyListsOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlEmptyListsOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlEmptyListsOutputBody { +extension XmlEmptyListsOutput { - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlEmptyListsOutput() - value.stringList = try reader["stringList"].readListIfPresent(memberReadingClosure: Swift.String.readingClosure, memberNodeInfo: "member", isFlattened: false) - value.stringSet = try reader["stringSet"].readListIfPresent(memberReadingClosure: Swift.String.readingClosure, memberNodeInfo: "member", isFlattened: false) - value.integerList = try reader["integerList"].readListIfPresent(memberReadingClosure: Swift.Int.readingClosure, memberNodeInfo: "member", isFlattened: false) - value.booleanList = try reader["booleanList"].readListIfPresent(memberReadingClosure: Swift.Bool.readingClosure, memberNodeInfo: "member", isFlattened: false) - return value - } + static func httpOutput(from httpResponse: ClientRuntime.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: Swift.Bool.read(from:), memberNodeInfo: "member", isFlattened: false) + value.integerList = try reader["integerList"].readListIfPresent(memberReadingClosure: Swift.Int.read(from:), memberNodeInfo: "member", isFlattened: false) + value.stringList = try reader["stringList"].readListIfPresent(memberReadingClosure: Swift.String.read(from:), memberNodeInfo: "member", isFlattened: false) + value.stringSet = try reader["stringSet"].readListIfPresent(memberReadingClosure: Swift.String.read(from:), memberNodeInfo: "member", isFlattened: false) + return value } } """ @@ -121,7 +111,7 @@ extension XmlEmptyListsOutputBody { } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.generateDeserializers(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/serde/xml/StructEncodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/xml/StructEncodeXMLGenerationTests.kt index bf265108e..d4309cd27 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/xml/StructEncodeXMLGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/xml/StructEncodeXMLGenerationTests.kt @@ -5,7 +5,7 @@ package serde.xml -import MockHttpRestXMLProtocolGenerator +import MockHTTPRestXMLProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -16,11 +16,12 @@ class StructEncodeXMLGenerationTests { @Test fun `simpleScalar serialization`() { val context = setupTests("Isolated/Restxml/xml-scalar.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/SimpleScalarPropertiesInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/SimpleScalarPropertiesInput+Write.swift") val expectedContents = """ extension SimpleScalarPropertiesInput { - static func writingClosure(_ value: SimpleScalarPropertiesInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } + + 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) @@ -40,24 +41,22 @@ extension SimpleScalarPropertiesInput { @Test fun `008 structure xmlName`() { val context = setupTests("Isolated/Restxml/xml-lists-structure.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/StructureListMember+Codable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/StructureListMember+ReadWrite.swift") val expectedContents = """ extension RestXmlProtocolClientTypes.StructureListMember { - static func writingClosure(_ value: RestXmlProtocolClientTypes.StructureListMember?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } + 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 var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = RestXmlProtocolClientTypes.StructureListMember() - value.a = try reader["value"].readIfPresent() - value.b = try reader["other"].readIfPresent() - return value - } + 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 } } """ @@ -65,7 +64,7 @@ extension RestXmlProtocolClientTypes.StructureListMember { } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.generateCodableConformanceForNestedTypes(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/serde/xml/TimeStampDecodeGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/xml/TimeStampDecodeGenerationTests.kt index f4eff893b..296dbfa07 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/xml/TimeStampDecodeGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/xml/TimeStampDecodeGenerationTests.kt @@ -5,7 +5,7 @@ package serde.xml -import MockHttpRestXMLProtocolGenerator +import MockHTTPRestXMLProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -16,20 +16,20 @@ 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, "/RestXml/models/XmlTimestampsOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlTimestampsOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlTimestampsOutputBody { +extension XmlTimestampsOutput { - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlTimestampsOutput() - value.normal = try reader["normal"].readTimestampIfPresent(format: .dateTime) - value.dateTime = try reader["dateTime"].readTimestampIfPresent(format: .dateTime) - value.epochSeconds = try reader["epochSeconds"].readTimestampIfPresent(format: .epochSeconds) - value.httpDate = try reader["httpDate"].readTimestampIfPresent(format: .httpDate) - return value - } + static func httpOutput(from httpResponse: ClientRuntime.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: .dateTime) + value.epochSeconds = try reader["epochSeconds"].readTimestampIfPresent(format: .epochSeconds) + value.httpDate = try reader["httpDate"].readTimestampIfPresent(format: .httpDate) + value.normal = try reader["normal"].readTimestampIfPresent(format: .dateTime) + return value } } """ @@ -39,17 +39,17 @@ extension XmlTimestampsOutputBody { @Test fun `002 decode nested timestamps`() { val context = setupTests("Isolated/Restxml/xml-timestamp-nested.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlTimestampsNestedOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlTimestampsNestedOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlTimestampsNestedOutputBody { +extension XmlTimestampsNestedOutput { - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlTimestampsNestedOutput() - value.nestedTimestampList = try reader["nestedTimestampList"].readListIfPresent(memberReadingClosure: SmithyXML.listReadingClosure(memberReadingClosure: SmithyXML.timestampReadingClosure(format: .epochSeconds), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - return value - } + static func httpOutput(from httpResponse: ClientRuntime.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: listReadingClosure(memberReadingClosure: timestampReadingClosure(format: .epochSeconds), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) + return value } } """ @@ -59,17 +59,17 @@ extension XmlTimestampsNestedOutputBody { @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, "/RestXml/models/XmlTimestampsNestedHTTPDateOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlTimestampsNestedHTTPDateOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlTimestampsNestedHTTPDateOutputBody { +extension XmlTimestampsNestedHTTPDateOutput { - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlTimestampsNestedHTTPDateOutput() - value.nestedTimestampList = try reader["nestedTimestampList"].readListIfPresent(memberReadingClosure: SmithyXML.listReadingClosure(memberReadingClosure: SmithyXML.timestampReadingClosure(format: .httpDate), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) - return value - } + static func httpOutput(from httpResponse: ClientRuntime.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: listReadingClosure(memberReadingClosure: timestampReadingClosure(format: .httpDate), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) + return value } } """ @@ -79,24 +79,24 @@ extension XmlTimestampsNestedHTTPDateOutputBody { @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, "/RestXml/models/XmlTimestampsNestedXmlNameOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlTimestampsNestedXmlNameOutput+HttpResponseBinding.swift") val expectedContents = """ -extension XmlTimestampsNestedXmlNameOutputBody { +extension XmlTimestampsNestedXmlNameOutput { - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - var value = XmlTimestampsNestedXmlNameOutput() - value.nestedTimestampList = try reader["nestedTimestampList"].readListIfPresent(memberReadingClosure: SmithyXML.listReadingClosure(memberReadingClosure: SmithyXML.timestampReadingClosure(format: .epochSeconds), memberNodeInfo: "nestedTag2", isFlattened: false), memberNodeInfo: "nestedTag1", isFlattened: false) - return value - } + static func httpOutput(from httpResponse: ClientRuntime.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: listReadingClosure(memberReadingClosure: timestampReadingClosure(format: .epochSeconds), memberNodeInfo: "nestedTag2", isFlattened: false), memberNodeInfo: "nestedTag1", isFlattened: false) + return value } } """ contents.shouldContainOnlyOnce(expectedContents) } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.generateDeserializers(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/serde/xml/TimeStampEncodeGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/xml/TimeStampEncodeGenerationTests.kt index 01ecd69a4..a004500c5 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/xml/TimeStampEncodeGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/xml/TimeStampEncodeGenerationTests.kt @@ -5,7 +5,7 @@ package serde.xml -import MockHttpRestXMLProtocolGenerator +import MockHTTPRestXMLProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -16,11 +16,12 @@ 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, "/RestXml/models/XmlTimestampsInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlTimestampsInput+Write.swift") val expectedContents = """ extension XmlTimestampsInput { - static func writingClosure(_ value: XmlTimestampsInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } + + static func write(value: XmlTimestampsInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } try writer["dateTime"].writeTimestamp(value.dateTime, format: .dateTime) try writer["epochSeconds"].writeTimestamp(value.epochSeconds, format: .epochSeconds) try writer["httpDate"].writeTimestamp(value.httpDate, format: .httpDate) @@ -34,12 +35,13 @@ extension XmlTimestampsInput { @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, "/RestXml/models/XmlTimestampsNestedInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlTimestampsNestedInput+Write.swift") val expectedContents = """ extension XmlTimestampsNestedInput { - static func writingClosure(_ value: XmlTimestampsNestedInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["nestedTimestampList"].writeList(value.nestedTimestampList, memberWritingClosure: SmithyXML.listWritingClosure(memberWritingClosure: SmithyXML.timestampWritingClosure(format: .epochSeconds), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) + + static func write(value: XmlTimestampsNestedInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["nestedTimestampList"].writeList(value.nestedTimestampList, memberWritingClosure: listWritingClosure(memberWritingClosure: timestampWritingClosure(format: .epochSeconds), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) } } """ @@ -49,12 +51,13 @@ extension XmlTimestampsNestedInput { @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, "/RestXml/models/XmlTimestampsNestedHTTPDateInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlTimestampsNestedHTTPDateInput+Write.swift") val expectedContents = """ extension XmlTimestampsNestedHTTPDateInput { - static func writingClosure(_ value: XmlTimestampsNestedHTTPDateInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["nestedTimestampList"].writeList(value.nestedTimestampList, memberWritingClosure: SmithyXML.listWritingClosure(memberWritingClosure: SmithyXML.timestampWritingClosure(format: .httpDate), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) + + static func write(value: XmlTimestampsNestedHTTPDateInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["nestedTimestampList"].writeList(value.nestedTimestampList, memberWritingClosure: listWritingClosure(memberWritingClosure: timestampWritingClosure(format: .httpDate), memberNodeInfo: "member", isFlattened: false), memberNodeInfo: "member", isFlattened: false) } } """ @@ -64,12 +67,13 @@ extension XmlTimestampsNestedHTTPDateInput { @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, "/RestXml/models/XmlTimestampsNestedXmlNameInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlTimestampsNestedXmlNameInput+Write.swift") val expectedContents = """ extension XmlTimestampsNestedXmlNameInput { - static func writingClosure(_ value: XmlTimestampsNestedXmlNameInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } - try writer["nestedTimestampList"].writeList(value.nestedTimestampList, memberWritingClosure: SmithyXML.listWritingClosure(memberWritingClosure: SmithyXML.timestampWritingClosure(format: .epochSeconds), memberNodeInfo: "nestedTag2", isFlattened: false), memberNodeInfo: "nestedTag1", isFlattened: false) + + static func write(value: XmlTimestampsNestedXmlNameInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } + try writer["nestedTimestampList"].writeList(value.nestedTimestampList, memberWritingClosure: listWritingClosure(memberWritingClosure: timestampWritingClosure(format: .epochSeconds), memberNodeInfo: "nestedTag2", isFlattened: false), memberNodeInfo: "nestedTag1", isFlattened: false) } } """ @@ -79,11 +83,12 @@ extension XmlTimestampsNestedXmlNameInput { @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, "/RestXml/models/XmlTimestampsXmlNameInput+Encodable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlTimestampsXmlNameInput+Write.swift") val expectedContents = """ extension XmlTimestampsXmlNameInput { - static func writingClosure(_ value: XmlTimestampsXmlNameInput?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } + + static func write(value: XmlTimestampsXmlNameInput?, to writer: SmithyXML.Writer) throws { + guard let value else { return } try writer["dateTime"].writeTimestamp(value.dateTime, format: .dateTime) try writer["notNormalName"].writeTimestamp(value.normal, format: .dateTime) } @@ -92,7 +97,7 @@ extension XmlTimestampsXmlNameInput { contents.shouldContainOnlyOnce(expectedContents) } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.generateCodableConformanceForNestedTypes(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/serde/xml/UnionEncodeXMLGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/serde/xml/UnionEncodeXMLGenerationTests.kt index 9b5203baa..f710bd459 100644 --- a/smithy-swift-codegen/src/test/kotlin/serde/xml/UnionEncodeXMLGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/serde/xml/UnionEncodeXMLGenerationTests.kt @@ -5,7 +5,7 @@ package serde.xml -import MockHttpRestXMLProtocolGenerator +import MockHTTPRestXMLProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -16,54 +16,52 @@ class UnionEncodeXMLGenerationTests { @Test fun `001 XmlUnionShape+Codable`() { val context = setupTests("Isolated/Restxml/xml-unions.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/RestXml/models/XmlUnionShape+Codable.swift") + val contents = getFileContents(context.manifest, "/RestXml/models/XmlUnionShape+ReadWrite.swift") val expectedContents = """ extension RestXmlProtocolClientTypes.XmlUnionShape { - static func writingClosure(_ value: RestXmlProtocolClientTypes.XmlUnionShape?, to writer: SmithyXML.Writer) throws { - guard let value else { writer.detach(); return } + 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: Swift.String.writingClosure(_:to:), keyNodeInfo: "K", valueNodeInfo: "V", isFlattened: false) + try writer["mapValue"].writeMap(mapvalue, valueWritingClosure: Swift.String.write(value:to:), keyNodeInfo: "K", valueNodeInfo: "V", isFlattened: false) case let .stringlist(stringlist): - try writer["stringList"].writeList(stringlist, memberWritingClosure: Swift.String.writingClosure(_:to:), memberNodeInfo: "member", isFlattened: false) + try writer["stringList"].writeList(stringlist, memberWritingClosure: Swift.String.write(value:to:), memberNodeInfo: "member", isFlattened: false) case let .structvalue(structvalue): - try writer["structValue"].write(structvalue, writingClosure: RestXmlProtocolClientTypes.XmlNestedUnionStruct.writingClosure(_:to:)) + try writer["structValue"].write(structvalue, with: RestXmlProtocolClientTypes.XmlNestedUnionStruct.write(value:to:)) case let .timestampvalue(timestampvalue): try writer["timeStampValue"].writeTimestamp(timestampvalue, format: .dateTime) case let .unionvalue(unionvalue): - try writer["unionValue"].write(unionvalue, writingClosure: RestXmlProtocolClientTypes.XmlUnionShape.writingClosure(_:to:)) + try writer["unionValue"].write(unionvalue, with: RestXmlProtocolClientTypes.XmlUnionShape.write(value:to:)) case let .sdkUnknown(sdkUnknown): - try writer[.init("sdkUnknown")].write(sdkUnknown) + try writer["sdkUnknown"].write(sdkUnknown) } } - static var readingClosure: SmithyReadWrite.ReadingClosure { - return { reader in - guard reader.content != nil else { return nil } - let name = reader.children.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(readingClosure: RestXmlProtocolClientTypes.XmlUnionShape.readingClosure)) - case "structValue": - return .structvalue(try reader["structValue"].read(readingClosure: RestXmlProtocolClientTypes.XmlNestedUnionStruct.readingClosure)) - case "mapValue": - return .mapvalue(try reader["mapValue"].readMap(valueReadingClosure: Swift.String.readingClosure, keyNodeInfo: "K", valueNodeInfo: "V", isFlattened: false)) - case "stringList": - return .stringlist(try reader["stringList"].readList(memberReadingClosure: Swift.String.readingClosure, memberNodeInfo: "member", isFlattened: false)) - case "timeStampValue": - return .timestampvalue(try reader["timeStampValue"].readTimestamp(format: .dateTime)) - default: - return .sdkUnknown(name ?? "") - } + 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: Swift.String.read(from:), keyNodeInfo: "K", valueNodeInfo: "V", isFlattened: false)) + case "stringList": + return .stringlist(try reader["stringList"].readList(memberReadingClosure: Swift.String.read(from:), memberNodeInfo: "member", isFlattened: false)) + case "timeStampValue": + return .timestampvalue(try reader["timeStampValue"].readTimestamp(format: .dateTime)) + default: + return .sdkUnknown(name ?? "") } } } @@ -96,7 +94,7 @@ extension RestXmlProtocolClientTypes.XmlUnionShape { } private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { - val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestXMLProtocolGenerator()) { model -> + val context = TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestXMLProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "RestXml", "2019-12-16", "Rest Xml Protocol") } context.generator.generateCodableConformanceForNestedTypes(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/waiters/WaiterAcceptorGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/waiters/WaiterAcceptorGeneratorTests.kt index 3ee664167..6a029d249 100644 --- a/smithy-swift-codegen/src/test/kotlin/waiters/WaiterAcceptorGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/waiters/WaiterAcceptorGeneratorTests.kt @@ -5,7 +5,7 @@ package waiters -import MockHttpRestJsonProtocolGenerator +import MockHTTPRestJsonProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -98,7 +98,7 @@ class WaiterAcceptorGeneratorTests { private fun setupTests(smithyFile: String, serviceShapeId: String, index: Int): TestContext { val context = - TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestJsonProtocolGenerator()) { model -> + TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestJsonProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "Test", "2019-12-16", "Test") } context.generator.generateProtocolClient(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/waiters/WaiterConfigGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/waiters/WaiterConfigGeneratorTests.kt index 25de7e0b0..063b4bad6 100644 --- a/smithy-swift-codegen/src/test/kotlin/waiters/WaiterConfigGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/waiters/WaiterConfigGeneratorTests.kt @@ -5,7 +5,7 @@ package waiters -import MockHttpRestJsonProtocolGenerator +import MockHTTPRestJsonProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -61,7 +61,7 @@ class WaiterConfigGeneratorTests { private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { val context = - TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestJsonProtocolGenerator()) { model -> + TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestJsonProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "Test", "2019-12-16", "Test") } context.generator.generateProtocolClient(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/waiters/WaiterGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/waiters/WaiterGeneratorTests.kt index 9d1905c97..8f01356ec 100644 --- a/smithy-swift-codegen/src/test/kotlin/waiters/WaiterGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/waiters/WaiterGeneratorTests.kt @@ -5,7 +5,7 @@ package waiters -import MockHttpRestJsonProtocolGenerator +import MockHTTPRestJsonProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -57,7 +57,7 @@ extension TestClient { private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { val context = - TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestJsonProtocolGenerator()) { model -> + TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestJsonProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "Test", "2019-12-16", "Test") } context.generator.generateProtocolClient(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/waiters/WaiterIntegrationTests.kt b/smithy-swift-codegen/src/test/kotlin/waiters/WaiterIntegrationTests.kt index da2499937..7c866a74d 100644 --- a/smithy-swift-codegen/src/test/kotlin/waiters/WaiterIntegrationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/waiters/WaiterIntegrationTests.kt @@ -5,7 +5,7 @@ package waiters -import MockHttpRestJsonProtocolGenerator +import MockHTTPRestJsonProtocolGenerator import TestContext import defaultSettings import io.kotest.matchers.booleans.shouldBeFalse @@ -56,7 +56,7 @@ class WaiterIntegrationTests { private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { val context = - TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestJsonProtocolGenerator()) { model -> + TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestJsonProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "Test", "2019-12-16", "Test") } context.generator.generateProtocolClient(context.generationCtx) diff --git a/smithy-swift-codegen/src/test/kotlin/waiters/WaiterMethodGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/waiters/WaiterMethodGeneratorTests.kt index e348e32e4..94c62219b 100644 --- a/smithy-swift-codegen/src/test/kotlin/waiters/WaiterMethodGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/waiters/WaiterMethodGeneratorTests.kt @@ -5,7 +5,7 @@ package waiters -import MockHttpRestJsonProtocolGenerator +import MockHTTPRestJsonProtocolGenerator import TestContext import defaultSettings import getFileContents @@ -55,7 +55,7 @@ class WaiterMethodGeneratorTests { private fun setupTests(smithyFile: String, serviceShapeId: String): TestContext { val context = - TestContext.initContextFrom(smithyFile, serviceShapeId, MockHttpRestJsonProtocolGenerator()) { model -> + TestContext.initContextFrom(smithyFile, serviceShapeId, MockHTTPRestJsonProtocolGenerator()) { model -> model.defaultSettings(serviceShapeId, "Test", "2019-12-16", "Test") } context.generator.generateProtocolClient(context.generationCtx)