From 368026eb507d6a9ec6f4c9330a2af5a8f6f29a98 Mon Sep 17 00:00:00 2001 From: Josh Elkins Date: Sat, 10 Feb 2024 11:58:24 -0600 Subject: [PATCH] feat!: XML response deserialization (#1299) --- .../AWSS3IntegrationTests/S3StreamTests.swift | 6 +- .../AWSS3IntegrationTests/S3XCTestCase.swift | 2 +- .../Errors/RestXMLError+AWS.swift | 27 +-- .../Protocols/Ec2Query/Ec2Error.swift | 19 +- .../Protocols/Ec2Query/Ec2Errors.swift | 15 +- .../Protocols/Ec2Query/Ec2QueryError.swift | 24 +-- .../Protocols/Ec2Query/Ec2Response.swift | 30 ++- .../Protocols/RestXML/RestXMLError.swift | 48 ++--- .../AWSMessageDecoderStreamTests.swift | 8 +- .../Sha256TreeHashMiddlewareTests.swift | 2 +- .../Ec2Query/Ec2ErrorRequestIdTests.swift | 37 ++-- .../Errors/ComplexXMLError+ResponseInit.swift | 33 --- .../Models/Errors/ComplexXMLError.swift | 31 --- .../ComplexXMLErrorBody+Decodable.swift | 28 --- .../ComplexXMLErrorNoErrorWrapping+Init.swift | 32 --- .../ComplexXMLErrorNoErrorWrapping.swift | 30 --- ...MLErrorNoErrorWrappingBody+Decodable.swift | 30 --- .../ComplexXMLNestedErrorData+Codable.swift | 29 --- .../Errors/ComplexXMLNestedErrorData.swift | 19 -- ...ppingOutputError+HttpResponseBinding.swift | 21 -- ...ngWithErrorsOutputError+ResponseInit.swift | 21 -- .../Errors/InvalidGreeting+ResponseInit.swift | 26 --- .../Models/Errors/InvalidGreeting.swift | 25 --- .../InvalidGreetingBody+Decodable.swift | 28 --- .../Protocols/RestXML/RestXMLErrorTests.swift | 202 ----------------- .../AWSHttpBindingProtocolGenerator.kt | 7 +- .../swift/codegen/AWSHttpRequestXMLEncoder.kt | 13 -- .../codegen/AWSHttpResponseXMLDecoder.kt | 13 -- .../swift/codegen/EndpointParamsGenerator.kt | 3 +- .../codegen/MessageUnmarshallableGenerator.kt | 1 + .../XMLMessageUnmarshallableGenerator.kt | 204 ++++++++++++++++++ .../awsjson/AwsJson1_0_ProtocolGenerator.kt | 5 +- .../awsjson/AwsJson1_1_ProtocolGenerator.kt | 5 +- .../AWSHttpProtocolAwsQueryCustomizations.kt | 3 - .../awsquery/AwsQueryProtocolGenerator.kt | 54 ++++- .../AwsQueryStructDecodeXMLGenerator.kt | 44 ---- ...SQueryHttpResponseBindingErrorGenerator.kt | 121 +++++++++++ .../FlexibleChecksumsRequestIntegration.kt | 9 +- .../FlexibleChecksumsResponseIntegration.kt | 11 +- .../glacier/GlacierAcccountIdDefault.kt | 3 +- .../Route53InvalidBatchErrorIntegration.kt | 57 +++-- .../customization/s3/S3ErrorIntegration.kt | 50 ++++- .../AWSHttpProtocolEc2QueryCustomizations.kt | 3 - .../ec2query/Ec2QueryProtocolGenerator.kt | 32 ++- ...2QueryHttpResponseBindingErrorGenerator.kt | 65 +++--- ...esponseBindingErrorInitGeneratorFactory.kt | 8 +- ...Ec2QueryHttpResponseTraitWithoutPayload.kt | 54 +---- .../AWSClientContextParamsTransformer.kt | 5 +- .../codegen/model/AWSHttpTraitTransformer.kt | 3 +- .../restjson/AWSRestJson1ProtocolGenerator.kt | 5 +- .../AWSHttpProtocolRestXMLCustomizations.kt | 13 +- ...estXMLHttpResponseBindingErrorGenerator.kt | 77 ++++--- .../restxml/RestXmlProtocolGenerator.kt | 61 ++++-- .../RestXmlStructDecodeXMLGenerator.kt | 68 ------ ...esponseBindingErrorInitGeneratorFactory.kt | 4 +- .../AWSXMLHttpResponseTraitWithoutPayload.kt | 73 +------ .../swift/codegen/PresignerGeneratorTests.kt | 6 +- .../awsquery/AWSQueryOperationStackTest.kt | 3 +- .../awsquery/BlobEncodeGeneratorTests.kt | 125 ++++++----- .../ListEncodeFormURLGeneratorTests.kt | 193 +++++++++-------- .../MapEncodeFormURLGeneratorTests.kt | 201 ++++++++--------- ...yIdempotencyTokenAutoFillGeneratorTests.kt | 29 +-- .../StructDecodeWrappedXMLGeneratorTests.kt | 48 ++--- .../awsquery/TimestampGeneratorTests.kt | 43 ++-- ...oute53InvalidBatchErrorIntegrationTests.kt | 131 +++++------ ...yHttpResponseBindingErrorGeneratorTests.kt | 66 +++--- ...LHttpResponseBindingErrorGeneratorTests.kt | 134 ++++++------ .../serde/S3UnwrappedXMLOutputTraitTests.kt | 29 ++- sdk.properties | 9 + 69 files changed, 1267 insertions(+), 1597 deletions(-) delete mode 100644 Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLError+ResponseInit.swift delete mode 100644 Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLError.swift delete mode 100644 Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLErrorBody+Decodable.swift delete mode 100644 Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLErrorNoErrorWrapping+Init.swift delete mode 100644 Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLErrorNoErrorWrapping.swift delete mode 100644 Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLErrorNoErrorWrappingBody+Decodable.swift delete mode 100644 Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLNestedErrorData+Codable.swift delete mode 100644 Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLNestedErrorData.swift delete mode 100644 Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/GreetingWithErrorsNoErrorWrappingOutputError+HttpResponseBinding.swift delete mode 100644 Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/GreetingWithErrorsOutputError+ResponseInit.swift delete mode 100644 Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/InvalidGreeting+ResponseInit.swift delete mode 100644 Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/InvalidGreeting.swift delete mode 100644 Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/InvalidGreetingBody+Decodable.swift delete mode 100644 Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/RestXMLErrorTests.swift delete mode 100644 codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHttpRequestXMLEncoder.kt delete mode 100644 codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHttpResponseXMLDecoder.kt create mode 100644 codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/XMLMessageUnmarshallableGenerator.kt delete mode 100644 codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/AwsQueryStructDecodeXMLGenerator.kt create mode 100644 codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/httpResponse/AWSQueryHttpResponseBindingErrorGenerator.kt delete mode 100644 codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/RestXmlStructDecodeXMLGenerator.kt diff --git a/IntegrationTests/Services/AWSS3IntegrationTests/S3StreamTests.swift b/IntegrationTests/Services/AWSS3IntegrationTests/S3StreamTests.swift index 83864750e18..1d0a0c43de4 100644 --- a/IntegrationTests/Services/AWSS3IntegrationTests/S3StreamTests.swift +++ b/IntegrationTests/Services/AWSS3IntegrationTests/S3StreamTests.swift @@ -22,10 +22,10 @@ final class S3StreamTests: S3XCTestCase { case .data(let dataOrNil): let data = try XCTUnwrap(dataOrNil) let actual = String(data: data, encoding: .utf8) - XCTAssertEqual(actual, expected) + XCTAssertEqual(expected, actual) case .stream(let stream): - let actual = String(data: try await stream.readToEndAsync()!, encoding: .utf8) - XCTAssertEqual(actual, expected) + let actual = String(data: try await stream.readToEndAsync() ?? Data(), encoding: .utf8) + XCTAssertEqual(expected, actual) case .noStream: XCTFail("Expected stream") } diff --git a/IntegrationTests/Services/AWSS3IntegrationTests/S3XCTestCase.swift b/IntegrationTests/Services/AWSS3IntegrationTests/S3XCTestCase.swift index 3205c3ca7ab..f296e7573de 100644 --- a/IntegrationTests/Services/AWSS3IntegrationTests/S3XCTestCase.swift +++ b/IntegrationTests/Services/AWSS3IntegrationTests/S3XCTestCase.swift @@ -92,7 +92,7 @@ class S3XCTestCase: XCTestCase { let data = try XCTUnwrap(dataOrNil) return String(data: data, encoding: .utf8) case .stream(let stream): - return String(data: try await stream.readToEndAsync()!, encoding: .utf8) + return String(data: try await stream.readToEndAsync() ?? Data(), encoding: .utf8) case .noStream: return nil } diff --git a/Sources/Core/AWSClientRuntime/Errors/RestXMLError+AWS.swift b/Sources/Core/AWSClientRuntime/Errors/RestXMLError+AWS.swift index 7f57293f777..70a2011277f 100644 --- a/Sources/Core/AWSClientRuntime/Errors/RestXMLError+AWS.swift +++ b/Sources/Core/AWSClientRuntime/Errors/RestXMLError+AWS.swift @@ -4,24 +4,25 @@ */ import ClientRuntime +import class SmithyXML.Reader extension RestXMLError { + /// Makes a `RestXMLError` from the provided `HttpResponse`. /// If the response body is empty and the status code is "not-found" aka 404, then this returns a `RestXMLError` instance with an error code of "NotFound". /// Otherwise, it creates an instance of `RestXMLError` by calling ``RestXMLError.init(httpResponse: HttpResponse)``. - /// - /// - Parameter response: The HTTP response - /// - /// - Returns: A`RestXMLError` instance with an error code of "NotFound" if the response body is empty and the status code is 404. Otherwise returns a `RestXMLError` by calling ``RestXMLError.init(httpResponse: HttpResponse)``. - /// + /// - Parameter httpResponse: The HTTP response from the server. + /// - Parameter responseReader: The Reader created from the XML response body. + /// - Parameter noErrorWrapping: `true` if the error is wrapped in a XML `ErrorResponse` element, `false` otherwise. + /// - Returns: A`RestXMLError` instance with an error code of "NotFound" if the response body is empty and the status code is 404, else returns a `RestXMLError` by calling the `RestXMLError` initializer. /// - Throws: An error if it fails to decode the response body. - public static func makeError(from response: HttpResponse) async throws -> RestXMLError { - response.statusCodeIsNotFoundAndBodyIsEmpty - ? .makeNotFoundError(requestID: response.requestId) - : try await .init(httpResponse: response) - } - - static func makeNotFoundError(requestID: String?) -> RestXMLError { - return RestXMLError(errorCode: "NotFound", requestId: requestID) + public static func makeError( + from httpResponse: HttpResponse, + responseReader: SmithyXML.Reader, + noErrorWrapping: Bool + ) async throws -> RestXMLError { + return httpResponse.statusCodeIsNotFoundAndBodyIsEmpty + ? .init(code: "NotFound", message: "404 Not Found", requestID: httpResponse.requestId) + : try .init(responseReader: responseReader, noErrorWrapping: noErrorWrapping) } } diff --git a/Sources/Core/AWSClientRuntime/Protocols/Ec2Query/Ec2Error.swift b/Sources/Core/AWSClientRuntime/Protocols/Ec2Query/Ec2Error.swift index 55140be3ef1..412346f7019 100644 --- a/Sources/Core/AWSClientRuntime/Protocols/Ec2Query/Ec2Error.swift +++ b/Sources/Core/AWSClientRuntime/Protocols/Ec2Query/Ec2Error.swift @@ -5,12 +5,19 @@ // SPDX-License-Identifier: Apache-2.0 // -public struct Ec2Error: Decodable { - public let code: String - public let message: String +import SmithyReadWrite +import SmithyXML - enum CodingKeys: String, CodingKey { - case code = "Code" - case message = "Message" +public struct Ec2Error { + public var code: String? + public var message: String? + + static var readingClosure: ReadingClosure { + return { reader in + var value = Ec2Error() + value.code = try reader["Code"].readIfPresent() + value.message = try reader["Message"].readIfPresent() + return value + } } } diff --git a/Sources/Core/AWSClientRuntime/Protocols/Ec2Query/Ec2Errors.swift b/Sources/Core/AWSClientRuntime/Protocols/Ec2Query/Ec2Errors.swift index 7db4112a13a..55710cf4898 100644 --- a/Sources/Core/AWSClientRuntime/Protocols/Ec2Query/Ec2Errors.swift +++ b/Sources/Core/AWSClientRuntime/Protocols/Ec2Query/Ec2Errors.swift @@ -5,10 +5,17 @@ // SPDX-License-Identifier: Apache-2.0 // -public struct Ec2Errors: Decodable { - public let error: Ec2Error +import SmithyReadWrite +import SmithyXML - enum CodingKeys: String, CodingKey { - case error = "Error" +public struct Ec2Errors { + public var error: Ec2Error? + + static var readingClosure: ReadingClosure { + return { reader in + var value = Ec2Errors() + value.error = try reader["Error"].readIfPresent(readingClosure: Ec2Error.readingClosure) + return value + } } } diff --git a/Sources/Core/AWSClientRuntime/Protocols/Ec2Query/Ec2QueryError.swift b/Sources/Core/AWSClientRuntime/Protocols/Ec2Query/Ec2QueryError.swift index 11b07226524..19d2c419c11 100644 --- a/Sources/Core/AWSClientRuntime/Protocols/Ec2Query/Ec2QueryError.swift +++ b/Sources/Core/AWSClientRuntime/Protocols/Ec2Query/Ec2QueryError.swift @@ -5,23 +5,19 @@ // SPDX-License-Identifier: Apache-2.0 // -import ClientRuntime +import class ClientRuntime.HttpResponse +import class SmithyXML.Reader +import var ClientRuntime.responseDocumentBinding public struct Ec2QueryError { - public let errorCode: String? - public let requestId: String? - public let message: String? + public var errorCode: String? + public var requestId: String? + public var message: String? public init(httpResponse: HttpResponse) async throws { - guard let data = try await httpResponse.body.readData() else { - errorCode = nil - requestId = nil - message = nil - return - } - let decoded: Ec2Response = try XMLDecoder().decode(responseBody: data) - self.errorCode = decoded.errors.error.code - self.message = decoded.errors.error.message - self.requestId = decoded.requestId + let response = try await Ec2Response.httpBinding(httpResponse, responseDocumentBinding) + self.errorCode = response.errors?.error?.code + self.message = response.errors?.error?.message + self.requestId = response.requestId } } diff --git a/Sources/Core/AWSClientRuntime/Protocols/Ec2Query/Ec2Response.swift b/Sources/Core/AWSClientRuntime/Protocols/Ec2Query/Ec2Response.swift index 464343c0c4f..0105ae2fd9c 100644 --- a/Sources/Core/AWSClientRuntime/Protocols/Ec2Query/Ec2Response.swift +++ b/Sources/Core/AWSClientRuntime/Protocols/Ec2Query/Ec2Response.swift @@ -5,23 +5,21 @@ // SPDX-License-Identifier: Apache-2.0 // -public struct Ec2Response: Decodable { - public let errors: Ec2Errors - public let requestId: String +import SmithyReadWrite +import SmithyXML +@testable import ClientRuntime - enum CodingKeys: String, CodingKey { - case errors = "Errors" - case requestId = "RequestId" - case requestID = "RequestID" - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.errors = try container.decode(Ec2Errors.self, forKey: .errors) +public struct Ec2Response { + public var errors: Ec2Errors? + public var requestId: String? - // Attempt to decode the requestId with the key "RequestID" - // if that is not present, then fallback to the key "RequestId" - self.requestId = try container.decodeIfPresent(String.self, forKey: .requestID) - ?? container.decode(String.self, forKey: .requestId) + public static var httpBinding: HTTPResponseOutputBinding { + return { httpResponse, responseDocumentBinding in + let reader = try await responseDocumentBinding(httpResponse) + var value = Ec2Response() + value.errors = try reader["Errors"].readIfPresent(readingClosure: Ec2Errors.readingClosure) + value.requestId = try reader["RequestId"].readIfPresent() ?? reader["RequestID"].readIfPresent() + return value + } } } diff --git a/Sources/Core/AWSClientRuntime/Protocols/RestXML/RestXMLError.swift b/Sources/Core/AWSClientRuntime/Protocols/RestXML/RestXMLError.swift index a1469dc6efb..1dbd6aaa4e8 100644 --- a/Sources/Core/AWSClientRuntime/Protocols/RestXML/RestXMLError.swift +++ b/Sources/Core/AWSClientRuntime/Protocols/RestXML/RestXMLError.swift @@ -5,37 +5,37 @@ // SPDX-License-Identifier: Apache-2.0 // -import ClientRuntime +import class SmithyXML.Reader public struct RestXMLError { - public let errorCode: String? - public let requestId: String? + public let code: String public let message: String? + public let requestID: String? - public init(httpResponse: HttpResponse) async throws { - guard let data = try await httpResponse.body.readData() else { - errorCode = nil - requestId = nil - message = nil - return - } - do { - let decoded: ErrorResponseContainer - decoded = try XMLDecoder().decode(responseBody: data) - self.errorCode = decoded.error.errorCode - self.message = decoded.error.message - self.requestId = decoded.requestId - } catch { - let decoded: RestXMLErrorNoErrorWrappingPayload = try XMLDecoder().decode(responseBody: data) - self.errorCode = decoded.errorCode - self.message = decoded.message - self.requestId = decoded.requestId + public static func errorBodyReader(responseReader: Reader, noErrorWrapping: Bool) -> Reader { + noErrorWrapping ? responseReader : responseReader["Error"] + } + + public init(responseReader: Reader, noErrorWrapping: Bool) throws { + let reader = Self.errorBodyReader(responseReader: responseReader, noErrorWrapping: noErrorWrapping) + let code: String? = try reader["Code"].readIfPresent() + let message: String? = try reader["Message"].readIfPresent() + let requestID: String? = try responseReader["RequestId"].readIfPresent() + guard let code else { + throw RestXMLDecodeError.missingRequiredData } + self.code = code + self.message = message + self.requestID = requestID } - public init(errorCode: String? = nil, requestId: String? = nil, message: String? = nil) { - self.errorCode = errorCode - self.requestId = requestId + public init(code: String, message: String?, requestID: String?) { + self.code = code self.message = message + self.requestID = requestID } } + +public enum RestXMLDecodeError: Error { + case missingRequiredData +} diff --git a/Tests/Core/AWSClientRuntimeTests/EventStream/AWSMessageDecoderStreamTests.swift b/Tests/Core/AWSClientRuntimeTests/EventStream/AWSMessageDecoderStreamTests.swift index 27d21d715b2..dc9321aad97 100644 --- a/Tests/Core/AWSClientRuntimeTests/EventStream/AWSMessageDecoderStreamTests.swift +++ b/Tests/Core/AWSClientRuntimeTests/EventStream/AWSMessageDecoderStreamTests.swift @@ -14,9 +14,11 @@ final class AWSMessageDecoderStreamTests: XCTestCase { let bufferedStream = BufferedStream(data: validMessageDataWithAllHeaders + validMessageDataEmptyPayload + validMessageDataNoHeaders, isClosed: true) let messageDecoder = AWSEventStream.AWSMessageDecoder() - let sut = EventStream.DefaultMessageDecoderStream(stream: bufferedStream, - messageDecoder: messageDecoder, - responseDecoder: JSONDecoder()) + let sut = EventStream.DefaultMessageDecoderStream( + stream: bufferedStream, + messageDecoder: messageDecoder, + unmarshalClosure: jsonUnmarshalClosure(responseDecoder: JSONDecoder()) + ) var events: [TestEvent] = [] for try await evnt in sut { diff --git a/Tests/Core/AWSClientRuntimeTests/Middlewares/Sha256TreeHashMiddlewareTests.swift b/Tests/Core/AWSClientRuntimeTests/Middlewares/Sha256TreeHashMiddlewareTests.swift index 7c8d812cf49..4b3186e8eb0 100644 --- a/Tests/Core/AWSClientRuntimeTests/Middlewares/Sha256TreeHashMiddlewareTests.swift +++ b/Tests/Core/AWSClientRuntimeTests/Middlewares/Sha256TreeHashMiddlewareTests.swift @@ -23,7 +23,7 @@ class Sha256TreeHashMiddlewareTests: XCTestCase { var stack = OperationStack(id: "TreeHashMiddlewareTestStack") stack.serializeStep.intercept(position: .before, middleware: MockSerializeStreamMiddleware()) let mockHttpResponse = HttpResponse(body: .noStream, statusCode: .accepted) - let mockOutput = try MockOutput(httpResponse: mockHttpResponse, decoder: nil) + let mockOutput = MockOutput() let output = OperationOutput(httpResponse: mockHttpResponse, output: mockOutput) stack.finalizeStep.intercept(position: .after, middleware: Sha256TreeHashMiddleware()) _ = try await stack.handleMiddleware(context: context, input: streamInput, next: MockHandler(handleCallback: { context, input in diff --git a/Tests/Core/AWSClientRuntimeTests/Protocols/Ec2Query/Ec2ErrorRequestIdTests.swift b/Tests/Core/AWSClientRuntimeTests/Protocols/Ec2Query/Ec2ErrorRequestIdTests.swift index 22f1792ea64..7c8a817e7d1 100644 --- a/Tests/Core/AWSClientRuntimeTests/Protocols/Ec2Query/Ec2ErrorRequestIdTests.swift +++ b/Tests/Core/AWSClientRuntimeTests/Protocols/Ec2Query/Ec2ErrorRequestIdTests.swift @@ -9,12 +9,13 @@ import ClientRuntime import SmithyTestUtil import XCTest +import SmithyXML @testable import AWSClientRuntime class Ec2ErrorRequestIdTests: XCTestCase { - func testEc2ResponseDecodesRequestID() throws { - let data = """ + func testEc2ResponseDecodesRequestID() async throws { + let data = Data(""" @@ -24,13 +25,14 @@ class Ec2ErrorRequestIdTests: XCTestCase { abcdefg12345 - """.data(using: .utf8)! - let response = try XMLDecoder().decode(Ec2Response.self, from: data) + """.utf8) + let httpResponse = HttpResponse(body: .data(data), statusCode: .ok) + let response = try await responseClosure(Ec2Response.httpBinding, responseDocumentBinding)(httpResponse) XCTAssertEqual(response.requestId, "abcdefg12345") } - func testEc2ResponseDecodesRequestId() throws { - let data = """ + func testEc2ResponseDecodesRequestId() async throws { + let data = Data(""" @@ -40,34 +42,37 @@ class Ec2ErrorRequestIdTests: XCTestCase { abcdefg12345 - """.data(using: .utf8)! - let response = try XMLDecoder().decode(Ec2Response.self, from: data) + """.utf8) + let httpResponse = HttpResponse(body: .data(data), statusCode: .ok) + let response = try await responseClosure(Ec2Response.httpBinding, responseDocumentBinding)(httpResponse) XCTAssertEqual(response.requestId, "abcdefg12345") } - func testEc2NarrowedResponseDecodesRequestID() throws { - let data = """ + func testEc2NarrowedResponseDecodesRequestID() async throws { + let data = Data(""" Sample Error abcdefg12345 - """.data(using: .utf8)! - let response = try XMLDecoder().decode(Ec2NarrowedResponse.self, from: data) + """.utf8) + let httpResponse = HttpResponse(body: .data(data), statusCode: .ok) + let response = try await responseClosure(Ec2Response.httpBinding, responseDocumentBinding)(httpResponse) XCTAssertEqual(response.requestId, "abcdefg12345") } - func testEc2NarrowResponseDecodesRequestId() throws { - let data = """ + func testEc2NarrowResponseDecodesRequestId() async throws { + let data = Data(""" Sample Error abcdefg12345 - """.data(using: .utf8)! - let response = try XMLDecoder().decode(Ec2NarrowedResponse.self, from: data) + """.utf8) + let httpResponse = HttpResponse(body: .data(data), statusCode: .ok) + let response = try await responseClosure(Ec2Response.httpBinding, responseDocumentBinding)(httpResponse) XCTAssertEqual(response.requestId, "abcdefg12345") } } diff --git a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLError+ResponseInit.swift b/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLError+ResponseInit.swift deleted file mode 100644 index 7230ce6e722..00000000000 --- a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLError+ResponseInit.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import AWSClientRuntime -import ClientRuntime - -extension ComplexXMLError { - public init (httpResponse: HttpResponse, decoder: ResponseDecoder? = nil, message: String? = nil, requestID: String? = nil) async throws { - if let headerHeaderValue = httpResponse.headers.value(for: "X-Header") { - self.header = headerHeaderValue - } else { - self.header = nil - } - - if let data = try await httpResponse.body.readData(), let responseDecoder = decoder { - let output: ErrorResponseContainer = try responseDecoder.decode(responseBody: data) - self.nested = output.error.nested - self.topLevel = output.error.topLevel - } else { - self.nested = nil - self.topLevel = nil - } - self.httpResponse = httpResponse - self.requestID = requestID - self.message = message - } -} diff --git a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLError.swift b/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLError.swift deleted file mode 100644 index 1f0ef96c1ef..00000000000 --- a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLError.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -// Code generated by smithy-swift-codegen. DO NOT EDIT! -import AWSClientRuntime -import ClientRuntime - -public struct ComplexXMLError: AWSServiceError, HTTPError, Error { - public var typeName: String? - public var message: String? - public var requestID: String? - public var header: String? - public var nested: ComplexXMLNestedErrorData? - public var topLevel: String? - public var httpResponse = HttpResponse() - - public init ( - header: String? = nil, - nested: ComplexXMLNestedErrorData? = nil, - topLevel: String? = nil - ) - { - self.header = header - self.nested = nested - self.topLevel = topLevel - } -} diff --git a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLErrorBody+Decodable.swift b/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLErrorBody+Decodable.swift deleted file mode 100644 index 11fc83101a7..00000000000 --- a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLErrorBody+Decodable.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -// Code generated by smithy-swift-codegen. DO NOT EDIT! -import ClientRuntime - -struct ComplexXMLErrorBody: Equatable { - public let topLevel: String? - public let nested: ComplexXMLNestedErrorData? -} -extension ComplexXMLErrorBody: Decodable { - enum CodingKeys: String, CodingKey { - case nested = "Nested" - case topLevel = "TopLevel" - } - - public init (from decoder: Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let topLevelDecoded = try containerValues.decodeIfPresent(String.self, forKey: .topLevel) - topLevel = topLevelDecoded - let nestedDecoded = try containerValues.decodeIfPresent(ComplexXMLNestedErrorData.self, forKey: .nested) - nested = nestedDecoded - } -} diff --git a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLErrorNoErrorWrapping+Init.swift b/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLErrorNoErrorWrapping+Init.swift deleted file mode 100644 index dce5308ed90..00000000000 --- a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLErrorNoErrorWrapping+Init.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import AWSClientRuntime -import ClientRuntime - -extension ComplexXMLErrorNoErrorWrapping { - public init (httpResponse: HttpResponse, decoder: ResponseDecoder? = nil, message: String? = nil, requestID: String? = nil) async throws { - if let headerHeaderValue = httpResponse.headers.value(for: "X-Header") { - self.header = headerHeaderValue - } else { - self.header = nil - } - if let data = try await httpResponse.body.readData(), let responseDecoder = decoder { - let output: ComplexXMLErrorNoErrorWrappingBody = try responseDecoder.decode(responseBody: data) - self.nested = output.nested - self.topLevel = output.topLevel - } else { - self.nested = nil - self.topLevel = nil - } - self.httpResponse = httpResponse - self.requestID = requestID - self.message = message - } -} diff --git a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLErrorNoErrorWrapping.swift b/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLErrorNoErrorWrapping.swift deleted file mode 100644 index 5612f0553ff..00000000000 --- a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLErrorNoErrorWrapping.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -import AWSClientRuntime -import ClientRuntime - -public struct ComplexXMLErrorNoErrorWrapping: AWSServiceError, HTTPError, Error { - public var typeName: String? - public var message: String? - public var httpResponse = HttpResponse() - public var requestID: String? - public var header: String? - public var nested: ComplexXMLNestedErrorData? - public var topLevel: String? - - public init ( - header: String? = nil, - nested: ComplexXMLNestedErrorData? = nil, - topLevel: String? = nil - ) - { - self.header = header - self.nested = nested - self.topLevel = topLevel - } -} diff --git a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLErrorNoErrorWrappingBody+Decodable.swift b/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLErrorNoErrorWrappingBody+Decodable.swift deleted file mode 100644 index e895ae65565..00000000000 --- a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLErrorNoErrorWrappingBody+Decodable.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct ComplexXMLErrorNoErrorWrappingBody: Equatable { - public let topLevel: String? - public let nested: ComplexXMLNestedErrorData? -} - -extension ComplexXMLErrorNoErrorWrappingBody: Decodable { - enum CodingKeys: String, CodingKey { - case nested = "Nested" - case topLevel = "TopLevel" - } - - public init (from decoder: Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let topLevelDecoded = try containerValues.decodeIfPresent(String.self, forKey: .topLevel) - topLevel = topLevelDecoded - let nestedDecoded = try containerValues.decodeIfPresent(ComplexXMLNestedErrorData.self, forKey: .nested) - nested = nestedDecoded - } -} diff --git a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLNestedErrorData+Codable.swift b/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLNestedErrorData+Codable.swift deleted file mode 100644 index 05046a73c6d..00000000000 --- a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLNestedErrorData+Codable.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -extension ComplexXMLNestedErrorData: Codable { - enum CodingKeys: String, CodingKey { - case foo = "Foo" - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: Key.self) - if let foo = foo { - try container.encode(foo, forKey: Key("foo")) - } - } - - public init (from decoder: Decoder) throws { - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let fooDecoded = try containerValues.decodeIfPresent(String.self, forKey: .foo) - foo = fooDecoded - } -} diff --git a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLNestedErrorData.swift b/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLNestedErrorData.swift deleted file mode 100644 index 53b390cd5af..00000000000 --- a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/ComplexXMLNestedErrorData.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -public struct ComplexXMLNestedErrorData: Equatable { - public let foo: String? - - public init ( - foo: String? = nil - ) - { - self.foo = foo - } -} diff --git a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/GreetingWithErrorsNoErrorWrappingOutputError+HttpResponseBinding.swift b/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/GreetingWithErrorsNoErrorWrappingOutputError+HttpResponseBinding.swift deleted file mode 100644 index 304e9ad28b3..00000000000 --- a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/GreetingWithErrorsNoErrorWrappingOutputError+HttpResponseBinding.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import AWSClientRuntime -import ClientRuntime - -public enum GreetingWithErrorsNoErrorWrappingOutputError: HttpResponseErrorBinding { - public static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder?) async throws -> Error { - let errorDetails = try await RestXMLError(httpResponse: httpResponse) - switch errorDetails.errorCode { - case "ComplexXMLErrorNoErrorWrapping": return try await ComplexXMLErrorNoErrorWrapping(httpResponse: httpResponse, decoder: decoder, message: errorDetails.message, requestID: errorDetails.requestId) - default: return UnknownAWSHTTPServiceError(httpResponse: httpResponse, message: errorDetails.message, requestID: errorDetails.requestId, typeName: errorDetails.errorCode) - } - } -} diff --git a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/GreetingWithErrorsOutputError+ResponseInit.swift b/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/GreetingWithErrorsOutputError+ResponseInit.swift deleted file mode 100644 index d0e65bf8e62..00000000000 --- a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/GreetingWithErrorsOutputError+ResponseInit.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -import AWSClientRuntime -import ClientRuntime - -public enum GreetingWithErrorsOutputError: HttpResponseErrorBinding { - public static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder?) async throws -> Error { - let errorDetails = try await RestXMLError(httpResponse: httpResponse) - switch errorDetails.errorCode { - case "ComplexXMLError": return try await ComplexXMLError(httpResponse: httpResponse, decoder: decoder, message: errorDetails.message, requestID: errorDetails.requestId) - case "InvalidGreeting": return try await InvalidGreeting(httpResponse: httpResponse, decoder: decoder, message: errorDetails.message, requestID: errorDetails.requestId) - default: return UnknownAWSHTTPServiceError(httpResponse: httpResponse, message: errorDetails.message, requestID: errorDetails.requestId, typeName: errorDetails.errorCode) - } - } -} - diff --git a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/InvalidGreeting+ResponseInit.swift b/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/InvalidGreeting+ResponseInit.swift deleted file mode 100644 index 83c90e5c6df..00000000000 --- a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/InvalidGreeting+ResponseInit.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import AWSClientRuntime -import ClientRuntime - -extension InvalidGreeting { - public init(httpResponse: HttpResponse, decoder: ResponseDecoder? = nil, message: String? = nil, requestID: String? = nil) async throws { - - if let data = try await httpResponse.body.readData(), let responseDecoder = decoder { - let output: InvalidGreetingBody = try responseDecoder.decode(responseBody: data) - self.message = output.message - } else { - self.message = nil - } - self.httpResponse = httpResponse - self.requestID = requestID - self.message = message - } -} diff --git a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/InvalidGreeting.swift b/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/InvalidGreeting.swift deleted file mode 100644 index 3860141dffd..00000000000 --- a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/InvalidGreeting.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import AWSClientRuntime -import ClientRuntime - -public struct InvalidGreeting: AWSServiceError, HTTPError, Error { - public var typeName: String? - public var httpResponse = HttpResponse() - public var requestID: String? - public var message: String? - - public init ( - message: String? = nil - ) - { - self.message = message - } -} diff --git a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/InvalidGreetingBody+Decodable.swift b/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/InvalidGreetingBody+Decodable.swift deleted file mode 100644 index d57ab9560ca..00000000000 --- a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/Models/Errors/InvalidGreetingBody+Decodable.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -// Code generated by smithy-swift-codegen. DO NOT EDIT! - -import ClientRuntime - -struct InvalidGreetingBody: Equatable { - public let message: String? -} - -extension InvalidGreetingBody: Decodable { - enum CodingKeys: String, CodingKey { - case message = "Message" - } - - public init (from decoder: Decoder) throws { - let containerValues = try decoder.container(keyedBy: Key.self) - let errorContainer = try containerValues.nestedContainer(keyedBy: CodingKeys.self, forKey: Key("Error")) - - let messageDecoded = try errorContainer.decodeIfPresent(String.self, forKey: .message) - message = messageDecoded - } -} diff --git a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/RestXMLErrorTests.swift b/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/RestXMLErrorTests.swift deleted file mode 100644 index 9fdcf2f85c9..00000000000 --- a/Tests/Core/AWSClientRuntimeTests/Protocols/RestXML/RestXMLErrorTests.swift +++ /dev/null @@ -1,202 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - - -import ClientRuntime -import SmithyTestUtil -import XCTest -@testable import AWSClientRuntime - -class RestXMLErrorTests: HttpResponseTestBase { - let host = "my-api.us-east-2.amazonaws.com" - - func testInvalidGreetingError() async { - do { - guard let httpResponse = buildHttpResponse( - code: 400, - headers: [ - "Content-Type": "application/xml" - ], - content: ByteStream.data(""" - - - Sender - InvalidGreeting - Hi - setting - - foo-id - - - """.data(using: .utf8)!) - ) else { - XCTFail("Something is wrong with the created http response") - return - } - - let decoder = XMLDecoder() - let greetingWithErrorsOutputError = try await GreetingWithErrorsOutputError.makeError(httpResponse: httpResponse, decoder: decoder) - - if let actual = greetingWithErrorsOutputError as? InvalidGreeting { - - let expected = InvalidGreeting( - message: "Hi" - ) - XCTAssertEqual(actual.httpResponse.statusCode, HttpStatusCode(rawValue: 400)) - XCTAssertEqual(expected.message, actual.message) - XCTAssertEqual("foo-id", actual.requestID) - } else { - XCTFail("The deserialized error type does not match expected type") - } - - } catch let err { - XCTFail(err.localizedDescription) - } - } - - func testComplexError() async { - do { - guard let httpResponse = buildHttpResponse( - code: 400, - headers: [ - "Content-Type": "application/xml", - "X-Header": "Header" - ], - content: ByteStream.data(""" - - - Sender - ComplexXMLError - Hi - Top level - - bar - - - foo-id - - - """.data(using: .utf8)!) - ) else { - XCTFail("Something is wrong with the created http response") - return - } - - let decoder = XMLDecoder() - let greetingWithErrorsOutputError = try await GreetingWithErrorsOutputError.makeError(httpResponse: httpResponse, decoder: decoder) - - if let actual = greetingWithErrorsOutputError as? ComplexXMLError { - - let expected = ComplexXMLError( - header: "Header", - nested: ComplexXMLNestedErrorData( - foo: "bar" - ), - topLevel: "Top level" - ) - XCTAssertEqual(actual.httpResponse.statusCode, HttpStatusCode(rawValue: 400)) - XCTAssertEqual(expected.header, actual.header) - XCTAssertEqual(expected.topLevel, actual.topLevel) - XCTAssertEqual(expected.nested, actual.nested) - XCTAssertEqual("Hi", actual.message) - XCTAssertEqual("foo-id", actual.requestID) - } else { - XCTFail("The deserialized error type does not match expected type") - } - - } catch let err { - XCTFail(err.localizedDescription) - } - } - - func testComplexErrorWithNoErrorWrapping() async { - do { - guard let httpResponse = buildHttpResponse( - code: 400, - headers: [ - "Content-Type": "application/xml", - "X-Header": "Header" - ], - content: ByteStream.data(""" - - Sender - ComplexXMLErrorNoErrorWrapping - Hi - Top level - - bar - - foo-id - - """.data(using: .utf8)!) - ) else { - XCTFail("Something is wrong with the created http response") - return - } - - let decoder = XMLDecoder() - let greetingWithErrorsOutputError = try await GreetingWithErrorsNoErrorWrappingOutputError.makeError(httpResponse: httpResponse, decoder: decoder) - - if let actual = greetingWithErrorsOutputError as? ComplexXMLErrorNoErrorWrapping { - - let expected = ComplexXMLErrorNoErrorWrapping( - header: "Header", - nested: ComplexXMLNestedErrorData( - foo: "bar" - ), - topLevel: "Top level" - ) - XCTAssertEqual(actual.httpResponse.statusCode, HttpStatusCode(rawValue: 400)) - XCTAssertEqual(expected.header, actual.header) - XCTAssertEqual(expected.topLevel, actual.topLevel) - XCTAssertEqual(expected.nested, actual.nested) - XCTAssertEqual("Hi", actual.message) - XCTAssertEqual("foo-id", actual.requestID) - } else { - XCTFail("The deserialized error type does not match expected type") - } - - } catch let err { - XCTFail(err.localizedDescription) - } - } - - func testUnhandledAccessDeniedErrors() async { - do { - guard let httpResponse = buildHttpResponse( - code: 403, - headers: [ - "Content-Type": "application/xml", - "X-Header": "Header" - ], - content: ByteStream.data(""" - - - AccessDenied - Access Denied - abcdefg123456 - 987654321abcdefg - - """.data(using: .utf8)!) - ) else { - XCTFail("Something is wrong with the created http response") - return - } - - let decoder = XMLDecoder() - let greetingWithErrorsOutputError = try await GreetingWithErrorsOutputError.makeError(httpResponse: httpResponse, decoder: decoder) - if let actual = greetingWithErrorsOutputError as? UnknownAWSHTTPServiceError { - XCTAssertEqual("Access Denied", actual.message) - XCTAssertEqual("abcdefg123456", actual.requestID) - } else { - XCTFail("The deserialized error type does not match expected type") - } - } catch let err { - XCTFail(err.localizedDescription) - } - } -} diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHttpBindingProtocolGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHttpBindingProtocolGenerator.kt index d57b3b6cdb2..5e130f9c323 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHttpBindingProtocolGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHttpBindingProtocolGenerator.kt @@ -21,7 +21,6 @@ import software.amazon.smithy.swift.codegen.SwiftWriter 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.HttpProtocolUnitTestGenerator import software.amazon.smithy.swift.codegen.integration.HttpProtocolUnitTestRequestGenerator import software.amazon.smithy.swift.codegen.integration.HttpProtocolUnitTestResponseGenerator import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator @@ -45,10 +44,6 @@ abstract class AWSHttpBindingProtocolGenerator : HttpBindingProtocolGenerator() override val httpProtocolClientGeneratorFactory = AWSHttpProtocolClientGeneratorFactory() - val serdeContextJSON = HttpProtocolUnitTestGenerator.SerdeContext("JSONEncoder()", "JSONDecoder()", ".secondsSince1970") - val serdeContextXML = HttpProtocolUnitTestGenerator.SerdeContext("XMLEncoder()", "XMLDecoder()") - abstract val serdeContext: HttpProtocolUnitTestGenerator.SerdeContext - val requestTestBuilder = HttpProtocolUnitTestRequestGenerator.Builder() val responseTestBuilder = HttpProtocolUnitTestResponseGenerator.Builder() val errorTestBuilder = HttpProtocolUnitTestErrorGenerator.Builder() @@ -68,7 +63,6 @@ abstract class AWSHttpBindingProtocolGenerator : HttpBindingProtocolGenerator() httpProtocolCustomizable, operationMiddleware, getProtocolHttpBindingResolver(ctx, defaultContentType), - serdeContext, imports, testsToIgnore, tagsToIgnore, @@ -108,6 +102,7 @@ abstract class AWSHttpBindingProtocolGenerator : HttpBindingProtocolGenerator() override fun renderStructDecode( ctx: ProtocolGenerator.GenerationContext, + shapeContainingMembers: Shape, shapeMetaData: Map, members: List, writer: SwiftWriter, diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHttpRequestXMLEncoder.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHttpRequestXMLEncoder.kt deleted file mode 100644 index a3ecbb642ce..00000000000 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHttpRequestXMLEncoder.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.aws.swift.codegen - -import software.amazon.smithy.swift.codegen.ClientRuntimeTypes -import software.amazon.smithy.swift.codegen.integration.HttpRequestEncoder - -class AWSHttpRequestXMLEncoder( - requestEncoderOptions: MutableMap = mutableMapOf() -) : HttpRequestEncoder(ClientRuntimeTypes.Serde.JSONEncoder, requestEncoderOptions) diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHttpResponseXMLDecoder.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHttpResponseXMLDecoder.kt deleted file mode 100644 index 6eacf6ae360..00000000000 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHttpResponseXMLDecoder.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.aws.swift.codegen - -import software.amazon.smithy.swift.codegen.ClientRuntimeTypes -import software.amazon.smithy.swift.codegen.integration.HttpResponseDecoder - -class AWSHttpResponseXMLDecoder( - responseDecoderOptions: MutableMap = mutableMapOf() -) : HttpResponseDecoder(ClientRuntimeTypes.Serde.XMLDecoder, responseDecoderOptions) diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/EndpointParamsGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/EndpointParamsGenerator.kt index 5b9fb59dc9d..093e98e970f 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/EndpointParamsGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/EndpointParamsGenerator.kt @@ -12,7 +12,6 @@ import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameter import software.amazon.smithy.rulesengine.language.syntax.parameters.ParameterType import software.amazon.smithy.swift.codegen.SwiftTypes import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.getOrNull import software.amazon.smithy.swift.codegen.model.boxed import software.amazon.smithy.swift.codegen.model.defaultValue import software.amazon.smithy.swift.codegen.utils.toLowerCamelCase @@ -54,7 +53,7 @@ class EndpointParamsGenerator(private val endpointRules: EndpointRuleSet?) { val memberName = param.name.toString().toLowerCamelCase() val memberSymbol = param.toSymbol() val optional = if (param.isRequired) "" else "?" - param.documentation.getOrNull()?.let { writer.write("/// $it") } + param.documentation.orElse(null)?.let { writer.write("/// $it") } writer.write("public let \$L: \$L$optional", memberName, memberSymbol) } } diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/MessageUnmarshallableGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/MessageUnmarshallableGenerator.kt index 9f40c0d0ae7..1f3ec4056a9 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/MessageUnmarshallableGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/MessageUnmarshallableGenerator.kt @@ -138,6 +138,7 @@ class MessageUnmarshallableGenerator(val ctx: ProtocolGenerator.GenerationContex "extension ${streamSymbol.fullName}: \$N {", "}", ClientRuntimeTypes.Serde.MessageUnmarshallable ) { + writer.write("") writer.openBlock( "public init(message: \$N, decoder: \$N) throws {", "}", ClientRuntimeTypes.EventStream.Message, diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/XMLMessageUnmarshallableGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/XMLMessageUnmarshallableGenerator.kt new file mode 100644 index 00000000000..21a45af59cd --- /dev/null +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/XMLMessageUnmarshallableGenerator.kt @@ -0,0 +1,204 @@ +package software.amazon.smithy.aws.swift.codegen + +import software.amazon.smithy.codegen.core.CodegenException +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.ShapeType +import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.model.traits.EventHeaderTrait +import software.amazon.smithy.model.traits.EventPayloadTrait +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.ProtocolGenerator +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.DocumentReadingClosureUtils +import software.amazon.smithy.swift.codegen.integration.serde.readwrite.ReadingClosureUtils +import software.amazon.smithy.swift.codegen.model.eventStreamErrors +import software.amazon.smithy.swift.codegen.model.eventStreamEvents +import software.amazon.smithy.swift.codegen.model.expectShape +import software.amazon.smithy.swift.codegen.model.hasTrait + +class XMLMessageUnmarshallableGenerator(val ctx: ProtocolGenerator.GenerationContext) { + fun render( + streamingMember: MemberShape + ) { + val symbol: Symbol = ctx.symbolProvider.toSymbol(ctx.model.expectShape(streamingMember.target)) + val rootNamespace = ctx.settings.moduleName + val streamMember = Symbol.builder() + .definitionFile("./$rootNamespace/models/${symbol.name}+MessageUnmarshallable.swift") + .name(symbol.name) + .build() + + val streamShape = ctx.model.expectShape(streamingMember.target) + val service = ctx.settings.getService(ctx.model) + val serviceSymbol = ctx.symbolProvider.toSymbol(service) + val streamSymbol = ctx.symbolProvider.toSymbol(streamShape) + + ctx.delegator.useShapeWriter(streamMember) { writer -> + + writer.addImport(SwiftDependency.CLIENT_RUNTIME.target) + writer.addImport(AWSSwiftDependency.AWS_CLIENT_RUNTIME.target) + writer.openBlock("extension \$L {", "}", streamSymbol.fullName) { + writer.openBlock( + "static var unmarshal: \$N<\$N> {", "}", + ClientRuntimeTypes.EventStream.UnmarshalClosure, + streamSymbol, + ) { + writer.openBlock("{ message in", "}") { + writer.write("switch try message.type() {") + writer.write("case .event(let params):") + writer.indent { + writer.write("switch params.eventType {") + streamShape.eventStreamEvents(ctx.model).forEach { member -> + writer.write("case \"${member.memberName}\":") + writer.indent { + renderDeserializeEventVariant(ctx, streamSymbol, member, writer) + } + } + writer.write("default:") + writer.indent { + writer.write("return .sdkUnknown(\"error processing event stream, unrecognized event: \\(params.eventType)\")") + } + writer.write("}") + } + writer.write("case .exception(let params):") + writer.indent { + writer.write( + "let makeError: (\$N, \$N) throws -> \$N = { message, params in", + ClientRuntimeTypes.EventStream.Message, + ClientRuntimeTypes.EventStream.ExceptionParams, + SwiftTypes.Error + ) + writer.indent { + writer.write("switch params.exceptionType {") + streamShape.eventStreamErrors(ctx.model).forEach { member -> + writer.write("case \"${member.memberName}\":") + writer.indent { + val targetShape = ctx.model.expectShape(member.target) + val symbol = ctx.symbolProvider.toSymbol(targetShape) + writer.write("return try decoder.decode(responseBody: message.payload) as \$N", symbol) + } + } + writer.write("default:") + writer.indent { + writer.write("let httpResponse = HttpResponse(body: .data(message.payload), statusCode: .ok)") + writer.write( + "return \$L(httpResponse: httpResponse, message: \"error processing event stream, unrecognized ':exceptionType': \\(params.exceptionType); contentType: \\(params.contentType ?? \"nil\")\", requestID: nil, typeName: nil)", + AWSClientRuntimeTypes.Core.UnknownAWSHTTPServiceError + ) + } + writer.write("}") + } + writer.write("}") + writer.write("let error = try makeError(message, params)") + writer.write("throw error") + } + writer.write("case .error(let params):") + writer.indent { + // this is a service exception still, just un-modeled + writer.write("let httpResponse = HttpResponse(body: .data(message.payload), statusCode: .ok)") + writer.write( + "throw \$L(httpResponse: httpResponse, message: \"error processing event stream, unrecognized ':errorType': \\(params.errorCode); message: \\(params.message ?? \"nil\")\", requestID: nil, typeName: nil)", + AWSClientRuntimeTypes.Core.UnknownAWSHTTPServiceError + ) + } + writer.write("case .unknown(messageType: let messageType):") + writer.indent { + // this is a client exception because we failed to parse it + writer.write( + "throw \$L(\"unrecognized event stream message ':message-type': \\(messageType)\")", + ClientRuntimeTypes.Core.UnknownClientError + ) + } + writer.write("}") + } + } + } + } + } + + private fun renderDeserializeEventVariant(ctx: ProtocolGenerator.GenerationContext, unionSymbol: Symbol, member: MemberShape, writer: SwiftWriter) { + val variant = ctx.model.expectShape(member.target) + + val eventHeaderBindings = variant.members().filter { it.hasTrait() } + val eventPayloadBinding = variant.members().firstOrNull { it.hasTrait() } + val unbound = variant.members().filterNot { it.hasTrait() || it.hasTrait() } + val memberName = ctx.symbolProvider.toMemberName(member) + + if (eventHeaderBindings.isEmpty() && eventPayloadBinding == null) { + val documentReadingClosure = DocumentReadingClosureUtils(ctx, writer).closure(member) + val readingClosure = ReadingClosureUtils(ctx, writer).readingClosure(member) + writer.write("return .\$L(try \$L(message.payload, \$L))", memberName, documentReadingClosure, readingClosure) + } else { + val variantSymbol = ctx.symbolProvider.toSymbol(variant) + writer.write("var event = \$N()", variantSymbol) + // render members bound to header + eventHeaderBindings.forEach { hdrBinding -> + val target = ctx.model.expectShape(hdrBinding.target) + + val conversionFn = when (target.type) { + ShapeType.BOOLEAN -> "bool" + ShapeType.BYTE -> "byte" + ShapeType.SHORT -> "int16" + ShapeType.INTEGER -> "int32" + ShapeType.LONG -> "int64" + ShapeType.BLOB -> "byteArray" + ShapeType.STRING -> "string" + ShapeType.TIMESTAMP -> "timestamp" + else -> throw CodegenException("unsupported eventHeader shape: member=$hdrBinding; targetShape=$target") + } + + writer.openBlock("if case .\$L(let value) = message.headers.value(name: \$S) {", "}", conversionFn, hdrBinding.memberName) { + val memberName = ctx.symbolProvider.toMemberName(hdrBinding) + when (target.type) { + ShapeType.INTEGER, ShapeType.LONG -> { + writer.write("event.\$L = Int(value)", memberName) + } + else -> { + writer.write("event.\$L = value", memberName) + } + } + } + } + + if (eventPayloadBinding != null) { + renderDeserializeExplicitEventPayloadMember(ctx, eventPayloadBinding, writer) + } else { + if (unbound.isNotEmpty()) { + // all remaining members are bound to payload (but not explicitly bound via @eventPayload) + // generate a payload deserializer specific to the unbound members (note this will be a deserializer + // for the overall event shape but only payload members will be considered for deserialization), + // and then assign each deserialized payload member to the current builder instance + unbound.forEach { + val memberName = ctx.symbolProvider.toMemberName(it) + val readingClosure = ReadingClosureUtils(ctx, writer).readingClosure(it) + val documentReadingClosure = DocumentReadingClosureUtils(ctx, writer).closure(it) + writer.write("event.\$L = try \$L(message.payload, \$L)", memberName, documentReadingClosure, readingClosure) + } + } + } + writer.write("return .\$L(event)", memberName) + } + } + + private fun renderDeserializeExplicitEventPayloadMember( + ctx: ProtocolGenerator.GenerationContext, + member: MemberShape, + writer: SwiftWriter, + ) { + val target = ctx.model.expectShape(member.target) + val memberName = ctx.symbolProvider.toMemberName(member) + when (target.type) { + ShapeType.BLOB -> writer.write("event.\$L = message.payload", memberName) + ShapeType.STRING -> writer.write("event.\$L = String(data: message.payload, encoding: .utf8)", memberName) + ShapeType.STRUCTURE, ShapeType.UNION -> { + val memberName = ctx.symbolProvider.toMemberName(member) + val readingClosure = ReadingClosureUtils(ctx, writer).readingClosure(member) + val documentReadingClosure = DocumentReadingClosureUtils(ctx, writer).closure(member) + writer.write("event.\$L = try \$L(message.payload, \$L)", memberName, documentReadingClosure, readingClosure) + } + else -> throw CodegenException("unsupported shape type `${target.type}` for target: $target; expected blob, string, structure, or union for eventPayload member: $member") + } + } +} diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsjson/AwsJson1_0_ProtocolGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsjson/AwsJson1_0_ProtocolGenerator.kt index c68d8e2aa3e..903a060845b 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsjson/AwsJson1_0_ProtocolGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsjson/AwsJson1_0_ProtocolGenerator.kt @@ -18,6 +18,7 @@ import software.amazon.smithy.swift.codegen.integration.HttpBindingResolver import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.codingKeys.DefaultCodingKeysCustomizable 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.HttpResponseGenerator import software.amazon.smithy.swift.codegen.integration.middlewares.ContentTypeMiddleware import software.amazon.smithy.swift.codegen.integration.middlewares.OperationInputBodyMiddleware @@ -32,9 +33,9 @@ open class AwsJson1_0_ProtocolGenerator : AWSHttpBindingProtocolGenerator() { override val httpResponseGenerator = HttpResponseGenerator( unknownServiceErrorSymbol, defaultTimestampFormat, - AWSJsonHttpResponseBindingErrorGenerator(), + HttpResponseBindingOutputGenerator(), + AWSJsonHttpResponseBindingErrorGenerator() ) - override val serdeContext = serdeContextJSON override val shouldRenderEncodableConformance: Boolean = true override val shouldRenderDecodableBodyStructForInputShapes: Boolean = true override val testsToIgnore = setOf( diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsjson/AwsJson1_1_ProtocolGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsjson/AwsJson1_1_ProtocolGenerator.kt index dee34af70e4..f499e553e5a 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsjson/AwsJson1_1_ProtocolGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsjson/AwsJson1_1_ProtocolGenerator.kt @@ -18,6 +18,7 @@ import software.amazon.smithy.swift.codegen.integration.HttpBindingResolver import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.codingKeys.DefaultCodingKeysCustomizable 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.HttpResponseGenerator import software.amazon.smithy.swift.codegen.integration.middlewares.ContentTypeMiddleware import software.amazon.smithy.swift.codegen.integration.middlewares.OperationInputBodyMiddleware @@ -32,9 +33,9 @@ class AwsJson1_1_ProtocolGenerator : AWSHttpBindingProtocolGenerator() { override val httpResponseGenerator = HttpResponseGenerator( unknownServiceErrorSymbol, defaultTimestampFormat, - AWSJsonHttpResponseBindingErrorGenerator(), + HttpResponseBindingOutputGenerator(), + AWSJsonHttpResponseBindingErrorGenerator() ) - override val serdeContext = serdeContextJSON override val shouldRenderEncodableConformance: Boolean = true override val shouldRenderDecodableBodyStructForInputShapes: Boolean = true override val testsToIgnore = setOf( diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/AWSHttpProtocolAwsQueryCustomizations.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/AWSHttpProtocolAwsQueryCustomizations.kt index 1e9e5a4704d..d55568400e6 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/AWSHttpProtocolAwsQueryCustomizations.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/AWSHttpProtocolAwsQueryCustomizations.kt @@ -7,7 +7,6 @@ package software.amazon.smithy.aws.swift.codegen.awsquery import software.amazon.smithy.aws.swift.codegen.AWSHttpProtocolCustomizations import software.amazon.smithy.aws.swift.codegen.AWSHttpRequestFormURLEncoder -import software.amazon.smithy.aws.swift.codegen.AWSHttpResponseXMLDecoder import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.protocoltests.traits.HttpRequestTestCase @@ -19,9 +18,7 @@ class AWSHttpProtocolAwsQueryCustomizations : AWSHttpProtocolCustomizations() { override fun getClientProperties(): List { val properties = mutableListOf() val requestEncoderOptions = mutableMapOf() - val responseDecoderOptions = mutableMapOf() properties.add(AWSHttpRequestFormURLEncoder(requestEncoderOptions)) - properties.add(AWSHttpResponseXMLDecoder(responseDecoderOptions)) return properties } diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/AwsQueryProtocolGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/AwsQueryProtocolGenerator.kt index 4b259ac85be..7b10f5a1cf9 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/AwsQueryProtocolGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/AwsQueryProtocolGenerator.kt @@ -8,7 +8,8 @@ package software.amazon.smithy.aws.swift.codegen.awsquery import software.amazon.smithy.aws.swift.codegen.AWSHttpBindingProtocolGenerator import software.amazon.smithy.aws.swift.codegen.AWSHttpProtocolClientCustomizableFactory import software.amazon.smithy.aws.swift.codegen.FormURLHttpBindingResolver -import software.amazon.smithy.aws.swift.codegen.restxml.AWSRestXMLHttpResponseBindingErrorGenerator +import software.amazon.smithy.aws.swift.codegen.XMLMessageUnmarshallableGenerator +import software.amazon.smithy.aws.swift.codegen.ec2query.httpResponse.AWSQueryHttpResponseBindingErrorGenerator import software.amazon.smithy.aws.swift.codegen.restxml.AWSXMLHttpResponseBindingErrorInitGeneratorFactory import software.amazon.smithy.aws.traits.protocols.AwsQueryTrait import software.amazon.smithy.model.shapes.MemberShape @@ -16,16 +17,18 @@ 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.SwiftTypes import software.amazon.smithy.swift.codegen.SwiftWriter import software.amazon.smithy.swift.codegen.integration.HttpBindingResolver -import software.amazon.smithy.swift.codegen.integration.HttpProtocolUnitTestGenerator import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.codingKeys.CodingKeysCustomizationXmlName import software.amazon.smithy.swift.codegen.integration.codingKeys.DefaultCodingKeysGenerator import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseGenerator +import software.amazon.smithy.swift.codegen.integration.httpResponse.XMLHttpResponseBindingOutputGenerator import software.amazon.smithy.swift.codegen.integration.middlewares.ContentTypeMiddleware import software.amazon.smithy.swift.codegen.integration.middlewares.OperationInputBodyMiddleware import software.amazon.smithy.swift.codegen.integration.serde.formurl.StructEncodeFormURLGenerator +import software.amazon.smithy.swift.codegen.integration.serde.xml.StructDecodeXMLGenerator import software.amazon.smithy.swift.codegen.middleware.MiddlewareStep import software.amazon.smithy.swift.codegen.model.ShapeMetadata @@ -38,22 +41,33 @@ open class AwsQueryProtocolGenerator : AWSHttpBindingProtocolGenerator() { override val httpResponseGenerator = HttpResponseGenerator( unknownServiceErrorSymbol, defaultTimestampFormat, - AWSRestXMLHttpResponseBindingErrorGenerator(), + XMLHttpResponseBindingOutputGenerator(), + AWSQueryHttpResponseBindingErrorGenerator(), AWSXMLHttpResponseBindingErrorInitGeneratorFactory(), ) - override val serdeContext = HttpProtocolUnitTestGenerator.SerdeContext("FormURLEncoder()", "XMLDecoder()") override fun getProtocolHttpBindingResolver(ctx: ProtocolGenerator.GenerationContext, defaultContentType: String): HttpBindingResolver = FormURLHttpBindingResolver(ctx, defaultContentType) override val shouldRenderDecodableBodyStructForInputShapes = false - override val shouldRenderCodingKeysForEncodable = false + override val shouldRenderCodingKeysForEncodable = true override val shouldRenderEncodableConformance = true override val testsToIgnore = setOf( "SDKAppliedContentEncoding_awsQuery", "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_awsQuery", ) override val tagsToIgnore = setOf("defaults") + override val codableProtocol = SwiftTypes.Protocols.Encodable + override val decodableProtocol = null + + override fun generateMessageMarshallable(ctx: ProtocolGenerator.GenerationContext) { + var streamingShapes = outputStreamingShapes(ctx) + val messageUnmarshallableGenerator = XMLMessageUnmarshallableGenerator(ctx) + streamingShapes.forEach { streamingMember -> + messageUnmarshallableGenerator.render(streamingMember) + } + } + override fun renderStructEncode( ctx: ProtocolGenerator.GenerationContext, shapeContainingMembers: Shape, @@ -63,21 +77,33 @@ open class AwsQueryProtocolGenerator : AWSHttpBindingProtocolGenerator() { defaultTimestampFormat: TimestampFormatTrait.Format, path: String?, ) { - val customizations = AwsQueryFormURLEncodeCustomizations() - val encoder = StructEncodeFormURLGenerator(ctx, customizations, shapeContainingMembers, shapeMetadata, members, writer, defaultTimestampFormat) - encoder.render() + StructEncodeFormURLGenerator( + ctx, + AwsQueryFormURLEncodeCustomizations(), + shapeContainingMembers, + shapeMetadata, + members, + writer, + defaultTimestampFormat + ).render() } override fun renderStructDecode( ctx: ProtocolGenerator.GenerationContext, + shapeContainingMembers: Shape, shapeMetadata: Map, members: List, writer: SwiftWriter, defaultTimestampFormat: TimestampFormatTrait.Format, path: String, ) { - val decoder = AwsQueryStructDecodeXMLGenerator(ctx, members, shapeMetadata, writer, defaultTimestampFormat) - decoder.render() + StructDecodeXMLGenerator( + ctx, + shapeContainingMembers, + members, + shapeMetadata, + writer, + ).render() } override fun addProtocolSpecificMiddleware(ctx: ProtocolGenerator.GenerationContext, operation: OperationShape) { @@ -91,4 +117,12 @@ open class AwsQueryProtocolGenerator : AWSHttpBindingProtocolGenerator() { operationMiddleware.removeMiddleware(operation, MiddlewareStep.SERIALIZESTEP, "ContentTypeMiddleware") operationMiddleware.appendMiddleware(operation, ContentTypeMiddleware(ctx.model, ctx.symbolProvider, resolver.determineRequestContentType(operation), true)) } + + override fun renderBodyStructAndDecodableExtension( + ctx: ProtocolGenerator.GenerationContext, + shape: Shape, + metadata: Map + ) { + // No operation + } } diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/AwsQueryStructDecodeXMLGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/AwsQueryStructDecodeXMLGenerator.kt deleted file mode 100644 index 73b128254ae..00000000000 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/AwsQueryStructDecodeXMLGenerator.kt +++ /dev/null @@ -1,44 +0,0 @@ -package software.amazon.smithy.aws.swift.codegen.awsquery - -import software.amazon.smithy.model.shapes.MemberShape -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.SwiftTypes -import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.serde.xml.StructDecodeXMLGenerator -import software.amazon.smithy.swift.codegen.model.ShapeMetadata - -class AwsQueryStructDecodeXMLGenerator( - ctx: ProtocolGenerator.GenerationContext, - private val members: List, - private val metadata: Map, - private val writer: SwiftWriter, - defaultTimestampFormat: TimestampFormatTrait.Format -) : StructDecodeXMLGenerator(ctx, members, metadata, writer, defaultTimestampFormat) { - override fun render() { - writer.openBlock("public init(from decoder: \$N) throws {", "}", SwiftTypes.Decoder) { - if (members.isNotEmpty()) { - renderDecodeBody() - } - } - } - - private fun renderDecodeBody() { - val containerName = "containerValues" - if (metadata.containsKey(ShapeMetadata.OPERATION_SHAPE)) { - val topLevelContainerName = "topLevelContainer" - writer.write("let $topLevelContainerName = try decoder.container(keyedBy: \$N.self)", ClientRuntimeTypes.Serde.Key) - - val operationShape = metadata[ShapeMetadata.OPERATION_SHAPE] as OperationShape - val wrappedKeyValue = operationShape.id.name + "Result" - writer.write("let $containerName = try $topLevelContainerName.nestedContainer(keyedBy: CodingKeys.self, forKey: \$N(\"$wrappedKeyValue\"))", ClientRuntimeTypes.Serde.Key) - } else { - writer.write("let $containerName = try decoder.container(keyedBy: CodingKeys.self)") - } - members.forEach { member -> - renderSingleMember(member, containerName) - } - } -} diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/httpResponse/AWSQueryHttpResponseBindingErrorGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/httpResponse/AWSQueryHttpResponseBindingErrorGenerator.kt new file mode 100644 index 00000000000..3e83cc8d4ed --- /dev/null +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/httpResponse/AWSQueryHttpResponseBindingErrorGenerator.kt @@ -0,0 +1,121 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.aws.swift.codegen.ec2query.httpResponse + +import software.amazon.smithy.aws.swift.codegen.AWSClientRuntimeTypes +import software.amazon.smithy.aws.swift.codegen.AWSSwiftDependency +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.SmithyXMLTypes +import software.amazon.smithy.swift.codegen.SwiftDependency +import software.amazon.smithy.swift.codegen.SwiftTypes +import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator +import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingErrorGeneratable +import software.amazon.smithy.swift.codegen.model.toUpperCamelCase +import software.amazon.smithy.swift.codegen.utils.errorShapeName + +class AWSQueryHttpResponseBindingErrorGenerator : 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(AWSSwiftDependency.AWS_CLIENT_RUNTIME.target) + addImport(SwiftDependency.CLIENT_RUNTIME.target) + openBlock("extension ${ctx.symbolProvider.toSymbol(ctx.service).name}Types {", "}") { + openBlock( + "static func makeServiceError(_ httpResponse: \$N, _ decoder: \$D, _ error: \$N) async throws -> \$N? {", + "}", + ClientRuntimeTypes.Http.HttpResponse, + ClientRuntimeTypes.Serde.ResponseDecoder, + AWSClientRuntimeTypes.EC2Query.Ec2QueryError, + SwiftTypes.Error + ) { + openBlock("switch error.errorCode {", "}") { + 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.message, requestID: error.requestId)", + errorShapeName, + errorShapeType + ) + } + write("default: return nil") + } + } + } + } + } + } + + override 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 -> + with(writer) { + addImport(AWSSwiftDependency.AWS_CLIENT_RUNTIME.target) + addImport(SwiftDependency.CLIENT_RUNTIME.target) + + openBlock("enum \$L {", "}", operationErrorName) { + writer.addImport(SwiftDependency.SMITHY_XML.target) + writer.write("") + openBlock( + "static var httpBinding: \$N<\$N> {", "}", + ClientRuntimeTypes.Http.HTTPResponseErrorBinding, + SmithyXMLTypes.Reader, + ) { + writer.openBlock("{ httpResponse, responseDocumentClosure in", "}") { + if (ctx.service.errors.isNotEmpty()) { + write("let serviceError = try await ${ctx.symbolProvider.toSymbol(ctx.service).name}Types.makeServiceError(httpResponse, decoder, ec2QueryError)") + write("if let error = serviceError { return error }") + } + writer.write("let responseReader = try await responseDocumentClosure(httpResponse)") + writer.write("let reader = responseReader[\"Error\"]") + writer.write("let requestID: String? = try responseReader[\"RequestId\"].readIfPresent()") + writer.write("let code: String? = try reader[\"Code\"].readIfPresent()") + writer.write("let message: String? = try reader[\"Message\"].readIfPresent()") + openBlock("switch code {", "}") { + 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.responseErrorBinding(httpResponse: httpResponse, reader: reader, message: message, requestID: requestID)", + errorShapeName, + errorShapeType + ) + } + write( + "default: return try await \$N.makeError(httpResponse: httpResponse, message: message, requestID: requestID, typeName: code)", + unknownServiceErrorSymbol + ) + } + } + } + } + } + } + } +} diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/flexiblechecksums/FlexibleChecksumsRequestIntegration.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/flexiblechecksums/FlexibleChecksumsRequestIntegration.kt index 8b22cb1e8c0..8fb48504025 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/flexiblechecksums/FlexibleChecksumsRequestIntegration.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/flexiblechecksums/FlexibleChecksumsRequestIntegration.kt @@ -7,7 +7,6 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.swift.codegen.ClientRuntimeTypes import software.amazon.smithy.swift.codegen.SwiftSettings import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.getOrNull import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.SwiftIntegration import software.amazon.smithy.swift.codegen.integration.middlewares.handlers.MiddlewareShapeUtils @@ -28,11 +27,11 @@ class FlexibleChecksumsRequestIntegration : SwiftIntegration { operationShape: OperationShape, operationMiddleware: OperationMiddleware, ) { - val httpChecksumTrait = operationShape.getTrait(HttpChecksumTrait::class.java).getOrNull() - val input = operationShape.input.getOrNull()?.let { ctx.model.expectShape(it) } + val httpChecksumTrait = operationShape.getTrait(HttpChecksumTrait::class.java).orElse(null) + val input = operationShape.input.orElse(null)?.let { ctx.model.expectShape(it) } val useFlexibleChecksum = (httpChecksumTrait != null) && - (httpChecksumTrait.requestAlgorithmMember?.getOrNull() != null) && + (httpChecksumTrait.requestAlgorithmMember?.orElse(null) != null) && (input?.memberNames?.any { it == httpChecksumTrait.requestAlgorithmMember.get() } == true) if (useFlexibleChecksum) { @@ -54,7 +53,7 @@ private object FlexibleChecksumRequestMiddleware : MiddlewareRenderable { override fun render(ctx: ProtocolGenerator.GenerationContext, writer: SwiftWriter, op: OperationShape, operationStackName: String) { val inputShapeName = MiddlewareShapeUtils.inputSymbol(ctx.symbolProvider, ctx.model, op).name val outputShapeName = MiddlewareShapeUtils.outputSymbol(ctx.symbolProvider, ctx.model, op).name - val httpChecksumTrait = op.getTrait(HttpChecksumTrait::class.java).getOrNull() + val httpChecksumTrait = op.getTrait(HttpChecksumTrait::class.java).orElse(null) val inputMemberName = httpChecksumTrait?.requestAlgorithmMember?.get()?.lowercaseFirstLetter() // Convert algorithmNames list to a Swift array representation diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/flexiblechecksums/FlexibleChecksumsResponseIntegration.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/flexiblechecksums/FlexibleChecksumsResponseIntegration.kt index 845bcd5653a..5b2fbab30c1 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/flexiblechecksums/FlexibleChecksumsResponseIntegration.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/flexiblechecksums/FlexibleChecksumsResponseIntegration.kt @@ -7,7 +7,6 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.swift.codegen.ClientRuntimeTypes import software.amazon.smithy.swift.codegen.SwiftSettings import software.amazon.smithy.swift.codegen.SwiftWriter -import software.amazon.smithy.swift.codegen.getOrNull import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.SwiftIntegration import software.amazon.smithy.swift.codegen.integration.middlewares.handlers.MiddlewareShapeUtils @@ -28,11 +27,11 @@ class FlexibleChecksumsResponseIntegration : SwiftIntegration { operationShape: OperationShape, operationMiddleware: OperationMiddleware, ) { - val httpChecksumTrait = operationShape.getTrait(HttpChecksumTrait::class.java).getOrNull() - val input = operationShape.input.getOrNull()?.let { ctx.model.expectShape(it) } + val httpChecksumTrait = operationShape.getTrait(HttpChecksumTrait::class.java).orElse(null) + val input = operationShape.input.orElse(null)?.let { ctx.model.expectShape(it) } val useFlexibleChecksum = (httpChecksumTrait != null) && - (httpChecksumTrait.requestValidationModeMember?.getOrNull() != null) && + (httpChecksumTrait.requestValidationModeMember?.orElse(null) != null) && (input?.memberNames?.any { it == httpChecksumTrait.requestValidationModeMember.get() } == true) if (useFlexibleChecksum) { @@ -50,10 +49,10 @@ private object FlexibleChecksumResponseMiddleware : MiddlewareRenderable { override fun render(ctx: ProtocolGenerator.GenerationContext, writer: SwiftWriter, op: OperationShape, operationStackName: String) { val outputShapeName = MiddlewareShapeUtils.outputSymbol(ctx.symbolProvider, ctx.model, op).name - val httpChecksumTrait = op.getTrait(HttpChecksumTrait::class.java).getOrNull() + val httpChecksumTrait = op.getTrait(HttpChecksumTrait::class.java).orElse(null) val inputMemberName = httpChecksumTrait?.requestValidationModeMember?.get() val validationModeMember = ctx.model.expectShape(op.inputShape).getMember(inputMemberName) - val requestValidationModeEnumShape = ctx.model.expectShape(validationModeMember.getOrNull()?.target) + val requestValidationModeEnumShape = ctx.model.expectShape(validationModeMember.orElse(null)?.target) // Will pass the validation mode to validation middleware val validationMode: Boolean = requestValidationModeEnumShape.members().map { it.memberName }.first().equals("ENABLED") diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/glacier/GlacierAcccountIdDefault.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/glacier/GlacierAcccountIdDefault.kt index 3474ab66007..60831502dcd 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/glacier/GlacierAcccountIdDefault.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/glacier/GlacierAcccountIdDefault.kt @@ -7,7 +7,6 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.swift.codegen.SwiftSettings -import software.amazon.smithy.swift.codegen.getOrNull import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.SwiftIntegration import software.amazon.smithy.swift.codegen.middleware.OperationMiddleware @@ -26,7 +25,7 @@ class GlacierAccountIdDefault : SwiftIntegration { operationShape: OperationShape, operationMiddleware: OperationMiddleware ) { - val input = operationShape.input.getOrNull()?.let { ctx.model.expectShape(it) } + val input = operationShape.input.orElse(null)?.let { ctx.model.expectShape(it) } val needsAccountIdMiddleware = input?.memberNames?.any { it.lowercase() == "accountid" } ?: false if (needsAccountIdMiddleware) { operationMiddleware.prependMiddleware( diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/route53/Route53InvalidBatchErrorIntegration.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/route53/Route53InvalidBatchErrorIntegration.kt index 57f3a778760..4766a775911 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/route53/Route53InvalidBatchErrorIntegration.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/route53/Route53InvalidBatchErrorIntegration.kt @@ -3,6 +3,7 @@ package software.amazon.smithy.aws.swift.codegen.customization.route53 import software.amazon.smithy.aws.swift.codegen.restxml.AWSRestXMLHttpResponseBindingErrorGenerator import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.swift.codegen.SmithyXMLTypes import software.amazon.smithy.swift.codegen.SwiftDelegator import software.amazon.smithy.swift.codegen.SwiftDependency import software.amazon.smithy.swift.codegen.SwiftSettings @@ -46,38 +47,48 @@ class Route53InvalidBatchErrorIntegration : SwiftIntegration { } private fun renderCustomInvalidBatchError(writer: SwiftWriter) { - writer.openBlock("struct CustomInvalidBatchError: Decodable {", "}") { + writer.openBlock("struct CustomInvalidBatchError {", "}") { writer.write("") - writer.openBlock("struct Message: Decodable {", "}") { - writer.write("let message: String") - writer.openBlock("enum CodingKeys: String, CodingKey {", "}") { - writer.write("case message = \"Message\"") + writer.openBlock("struct Message {", "}") { + writer.write("var message: String?") + writer.write("") + writer.write("init() {}") + writer.write("") + writer.openBlock( + "static func readingClosure(from reader: \$N) throws -> Message? {", + "}", + SmithyXMLTypes.Reader, + ) { + writer.write("guard reader.content != nil else { return nil }") + writer.write("var value = Message()") + writer.write("value.message = try reader[\"Message\"].readIfPresent()") + writer.write("return value") } } writer.write("") - writer.write("let requestID: String?") - writer.write("let message: String?") - writer.write("let messages: [String]?") + writer.write("var requestID: String?") + writer.write("var message: String?") + writer.write("var messages: [String]?") writer.write("") - writer.openBlock("enum CodingKeys: String, CodingKey {", "}") { - writer.write("case message = \"Message\"") - writer.write("case messages = \"Messages\"") - writer.write("case requestID = \"RequestId\"") - } + writer.write("init() {}") writer.write("") - writer.openBlock("init(from decoder: Decoder) throws {", "}") { - writer.write("let container = try decoder.container(keyedBy: CodingKeys.self)") - writer.write("self.requestID = try container.decode(String.self, forKey: .requestID)") - writer.write("self.message = try container.decodeIfPresent(String.self, forKey: .message)") - writer.write("let messages = try container.decodeIfPresent([Message].self, forKey: .messages)") - writer.write("self.messages = messages?.map(\\.message)") + writer.openBlock( + "static func readingClosure(from reader: \$N) throws -> CustomInvalidBatchError? {", + "}", + SmithyXMLTypes.Reader, + ) { + writer.write("guard reader.content != nil else { return nil }") + writer.write("var value = CustomInvalidBatchError()") + writer.write("value.requestID = try reader[\"RequestId\"].readIfPresent()") + writer.write("value.message = try reader[\"Message\"].readIfPresent()") + writer.write("value.messages = try reader[\"Messages\"].readListIfPresent(memberReadingClosure: Message.readingClosure(from:), memberNodeInfo: \"Message\", isFlattened: false)?.compactMap(\\.message)") + writer.write("return value") } writer.write("") writer.openBlock("static func makeFromHttpResponse(_ httpResponse: ClientRuntime.HttpResponse) async throws -> CustomInvalidBatchError? {", "}") { - writer.openBlock("guard let data = try await httpResponse.body.readData() else {", "}") { - writer.write("return nil") - } - writer.write("return try? XMLDecoder().decode(CustomInvalidBatchError.self, from: data)") + writer.write("guard let data = try await httpResponse.body.readData() else { return nil }") + writer.write("let reader = try \$N.from(data: data)", SmithyXMLTypes.Reader) + writer.write("return try Self.readingClosure(from: reader)") } } } diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/s3/S3ErrorIntegration.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/s3/S3ErrorIntegration.kt index e73d5b98637..819c071a418 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/s3/S3ErrorIntegration.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/s3/S3ErrorIntegration.kt @@ -2,20 +2,24 @@ package software.amazon.smithy.aws.swift.codegen.customization.s3 import software.amazon.smithy.aws.swift.codegen.AWSClientRuntimeTypes import software.amazon.smithy.aws.swift.codegen.restxml.AWSRestXMLHttpResponseBindingErrorGenerator +import software.amazon.smithy.aws.traits.protocols.RestXmlTrait import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.swift.codegen.ClientRuntimeTypes +import software.amazon.smithy.swift.codegen.SmithyXMLTypes import software.amazon.smithy.swift.codegen.StructureGenerator +import software.amazon.smithy.swift.codegen.SwiftDependency import software.amazon.smithy.swift.codegen.SwiftSettings import software.amazon.smithy.swift.codegen.SwiftTypes import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.SectionWriter import software.amazon.smithy.swift.codegen.integration.SectionWriterBinding import software.amazon.smithy.swift.codegen.integration.SwiftIntegration -import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingErrorInitGenerator +import software.amazon.smithy.swift.codegen.integration.httpResponse.XMLHttpResponseBindingErrorInitGenerator import software.amazon.smithy.swift.codegen.model.expectShape +import software.amazon.smithy.swift.codegen.model.getTrait import software.amazon.smithy.swift.codegen.utils.errorShapeName class S3ErrorIntegration : SwiftIntegration { @@ -27,26 +31,28 @@ class S3ErrorIntegration : SwiftIntegration { } override val sectionWriters: List get() = listOf( - SectionWriterBinding(HttpResponseBindingErrorInitGenerator.HttpResponseBindingErrorInit, s3MembersParams), - SectionWriterBinding(HttpResponseBindingErrorInitGenerator.HttpResponseBindingErrorInitMemberAssignment, s3MembersAssignment), + SectionWriterBinding(XMLHttpResponseBindingErrorInitGenerator.XMLHttpResponseBindingErrorInit, s3MembersParams), + SectionWriterBinding(XMLHttpResponseBindingErrorInitGenerator.XMLHttpResponseBindingErrorInitMemberAssignment, s3MembersAssignment), SectionWriterBinding(StructureGenerator.AdditionalErrorMembers, s3Members), SectionWriterBinding(AWSRestXMLHttpResponseBindingErrorGenerator.RestXMLResponseBindingSectionId, httpResponseBinding) ) private val s3MembersParams = SectionWriter { writer, _ -> + writer.addImport(SwiftDependency.SMITHY_XML.target) writer.write( - "public init(httpResponse: \$N, decoder: \$D, message: \$D, requestID: \$D, requestID2: \$D) async throws {", + "static func responseErrorBinding(httpResponse: \$N, reader: \$N, message: \$D, requestID: \$D, requestID2: \$D) async throws -> \$N {", ClientRuntimeTypes.Http.HttpResponse, - ClientRuntimeTypes.Serde.ResponseDecoder, + SmithyXMLTypes.Reader, SwiftTypes.String, SwiftTypes.String, - SwiftTypes.String + SwiftTypes.String, + SwiftTypes.Error, ) } private val s3MembersAssignment = SectionWriter { writer, _ -> - writer.write("self.requestID2 = requestID2") + writer.write("value.requestID2 = requestID2") } private val s3Members = SectionWriter { writer, _ -> @@ -56,14 +62,36 @@ class S3ErrorIntegration : SwiftIntegration { private val httpResponseBinding = SectionWriter { writer, _ -> val ctx = writer.getContext("ctx") as ProtocolGenerator.GenerationContext val errorShapes = writer.getContext("errorShapes") as List - writer.write("let restXMLError = try await \$N.makeError(from: httpResponse)", AWSClientRuntimeTypes.RestXML.RestXMLError) - writer.openBlock("switch restXMLError.errorCode {", "}") { + val noErrorWrapping = ctx.service.getTrait()?.let { it.isNoErrorWrapping } ?: false + writer.write("let responseReader = try await responseDocumentClosure(httpResponse)") + if (errorShapes.isNotEmpty() || ctx.service.errors.isNotEmpty()) { + writer.write( + "let errorBodyReader = \$N.errorBodyReader(responseReader: responseReader, noErrorWrapping: \$L)", + AWSClientRuntimeTypes.RestXML.RestXMLError, + noErrorWrapping + ) + } + if (ctx.service.errors.isNotEmpty()) { + writer.openBlock( + "if let serviceError = try await \$NTypes.responseErrorServiceBinding(httpResponse, errorBodyReader)", + "}", + ctx.symbolProvider.toSymbol(ctx.service) + ) { + writer.write("return serviceError") + } + } + writer.write("let restXMLError = try \$N(responseReader: responseReader, noErrorWrapping: \$L)", AWSClientRuntimeTypes.RestXML.RestXMLError, noErrorWrapping) + writer.openBlock("switch restXMLError.code {", "}") { for (errorShape in errorShapes) { var errorShapeName = errorShape.errorShapeName(ctx.symbolProvider) var errorShapeType = ctx.symbolProvider.toSymbol(errorShape).name - writer.write("case \$S: return try await \$L(httpResponse: httpResponse, decoder: decoder, message: restXMLError.message, requestID: restXMLError.requestId, requestID2: httpResponse.requestId2)", errorShapeName, errorShapeType) + writer.write( + "case \$S: return try await \$L.responseErrorBinding(httpResponse: httpResponse, reader: errorBodyReader, message: restXMLError.message, requestID: restXMLError.requestID, requestID2: httpResponse.requestId2)", + errorShapeName, + errorShapeType + ) } - writer.write("default: return try await \$unknownServiceErrorSymbol:N.makeError(httpResponse: httpResponse, message: restXMLError.message, requestID: restXMLError.requestId, requestID2: httpResponse.requestId2, typeName: restXMLError.errorCode)") + writer.write("default: return try await \$unknownServiceErrorSymbol:N.makeError(httpResponse: httpResponse, message: restXMLError.message, requestID: restXMLError.requestID, requestID2: httpResponse.requestId2, typeName: restXMLError.code)") } } diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/AWSHttpProtocolEc2QueryCustomizations.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/AWSHttpProtocolEc2QueryCustomizations.kt index 19ee3fed6d6..eaa013764c9 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/AWSHttpProtocolEc2QueryCustomizations.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/AWSHttpProtocolEc2QueryCustomizations.kt @@ -7,7 +7,6 @@ package software.amazon.smithy.aws.swift.codegen.ec2query import software.amazon.smithy.aws.swift.codegen.AWSHttpProtocolCustomizations import software.amazon.smithy.aws.swift.codegen.AWSHttpRequestFormURLEncoder -import software.amazon.smithy.aws.swift.codegen.AWSHttpResponseXMLDecoder import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.protocoltests.traits.HttpRequestTestCase @@ -19,9 +18,7 @@ class AWSHttpProtocolEc2QueryCustomizations : AWSHttpProtocolCustomizations() { override fun getClientProperties(): List { val properties = mutableListOf() val requestEncoderOptions = mutableMapOf() - val responseDecoderOptions = mutableMapOf() properties.add(AWSHttpRequestFormURLEncoder(requestEncoderOptions)) - properties.add(AWSHttpResponseXMLDecoder(responseDecoderOptions)) return properties } diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/Ec2QueryProtocolGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/Ec2QueryProtocolGenerator.kt index 737accb6d4f..dd82540f7b7 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/Ec2QueryProtocolGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/Ec2QueryProtocolGenerator.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.aws.swift.codegen.ec2query import software.amazon.smithy.aws.swift.codegen.AWSHttpBindingProtocolGenerator import software.amazon.smithy.aws.swift.codegen.AWSHttpProtocolClientCustomizableFactory import software.amazon.smithy.aws.swift.codegen.FormURLHttpBindingResolver +import software.amazon.smithy.aws.swift.codegen.XMLMessageUnmarshallableGenerator import software.amazon.smithy.aws.swift.codegen.ec2query.httpResponse.AWSEc2QueryHttpResponseBindingErrorGenerator import software.amazon.smithy.aws.swift.codegen.ec2query.httpResponse.AWSEc2QueryHttpResponseBindingErrorInitGeneratorFactory import software.amazon.smithy.aws.traits.protocols.Ec2QueryTrait @@ -16,13 +17,12 @@ 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.SwiftTypes import software.amazon.smithy.swift.codegen.SwiftWriter import software.amazon.smithy.swift.codegen.integration.HttpBindingResolver -import software.amazon.smithy.swift.codegen.integration.HttpProtocolUnitTestGenerator import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.codingKeys.CodingKeysCustomizationXmlName -import software.amazon.smithy.swift.codegen.integration.codingKeys.DefaultCodingKeysGenerator import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseGenerator +import software.amazon.smithy.swift.codegen.integration.httpResponse.XMLHttpResponseBindingOutputGenerator import software.amazon.smithy.swift.codegen.integration.middlewares.ContentTypeMiddleware import software.amazon.smithy.swift.codegen.integration.middlewares.OperationInputBodyMiddleware import software.amazon.smithy.swift.codegen.integration.serde.formurl.StructEncodeFormURLGenerator @@ -31,7 +31,7 @@ import software.amazon.smithy.swift.codegen.middleware.MiddlewareStep import software.amazon.smithy.swift.codegen.model.ShapeMetadata class Ec2QueryProtocolGenerator : AWSHttpBindingProtocolGenerator() { - override val codingKeysGenerator = DefaultCodingKeysGenerator(CodingKeysCustomizationXmlName()) + override val codingKeysGenerator = null override val defaultContentType = "application/x-www-form-urlencoded" override val defaultTimestampFormat = TimestampFormatTrait.Format.DATE_TIME override val protocol: ShapeId = Ec2QueryTrait.ID @@ -39,11 +39,11 @@ class Ec2QueryProtocolGenerator : AWSHttpBindingProtocolGenerator() { override val httpResponseGenerator = HttpResponseGenerator( unknownServiceErrorSymbol, defaultTimestampFormat, + XMLHttpResponseBindingOutputGenerator(), AWSEc2QueryHttpResponseBindingErrorGenerator(), AWSEc2QueryHttpResponseBindingErrorInitGeneratorFactory(), ) - override val serdeContext = HttpProtocolUnitTestGenerator.SerdeContext("FormURLEncoder()", "XMLDecoder()") override fun getProtocolHttpBindingResolver(ctx: ProtocolGenerator.GenerationContext, contentType: String): HttpBindingResolver = FormURLHttpBindingResolver(ctx, contentType) @@ -55,6 +55,17 @@ class Ec2QueryProtocolGenerator : AWSHttpBindingProtocolGenerator() { "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_ec2Query", ) override val tagsToIgnore = setOf("defaults") + override val codableProtocol = SwiftTypes.Protocols.Encodable + override val decodableProtocol = null + + override fun generateMessageMarshallable(ctx: ProtocolGenerator.GenerationContext) { + var streamingShapes = outputStreamingShapes(ctx) + val messageUnmarshallableGenerator = XMLMessageUnmarshallableGenerator(ctx) + streamingShapes.forEach { streamingMember -> + messageUnmarshallableGenerator.render(streamingMember) + } + } + override fun renderStructEncode( ctx: ProtocolGenerator.GenerationContext, shapeContainingMembers: Shape, @@ -71,13 +82,14 @@ class Ec2QueryProtocolGenerator : AWSHttpBindingProtocolGenerator() { override fun renderStructDecode( ctx: ProtocolGenerator.GenerationContext, + shapeContainingMembers: Shape, shapeMetadata: Map, members: List, writer: SwiftWriter, defaultTimestampFormat: TimestampFormatTrait.Format, path: String, ) { - val decoder = StructDecodeXMLGenerator(ctx, members, mapOf(), writer, defaultTimestampFormat) + val decoder = StructDecodeXMLGenerator(ctx, shapeContainingMembers, members, mapOf(), writer) decoder.render() } @@ -92,4 +104,12 @@ class Ec2QueryProtocolGenerator : AWSHttpBindingProtocolGenerator() { operationMiddleware.removeMiddleware(operation, MiddlewareStep.SERIALIZESTEP, "ContentTypeMiddleware") operationMiddleware.appendMiddleware(operation, ContentTypeMiddleware(ctx.model, ctx.symbolProvider, resolver.determineRequestContentType(operation), true)) } + + override fun renderBodyStructAndDecodableExtension( + ctx: ProtocolGenerator.GenerationContext, + shape: Shape, + metadata: Map + ) { + // No operation + } } diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/httpResponse/AWSEc2QueryHttpResponseBindingErrorGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/httpResponse/AWSEc2QueryHttpResponseBindingErrorGenerator.kt index cc5417d90d1..96aec868613 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/httpResponse/AWSEc2QueryHttpResponseBindingErrorGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/httpResponse/AWSEc2QueryHttpResponseBindingErrorGenerator.kt @@ -11,6 +11,7 @@ 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.SmithyXMLTypes import software.amazon.smithy.swift.codegen.SwiftDependency import software.amazon.smithy.swift.codegen.SwiftTypes import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator @@ -74,43 +75,43 @@ class AWSEc2QueryHttpResponseBindingErrorGenerator : HttpResponseBindingErrorGen addImport(AWSSwiftDependency.AWS_CLIENT_RUNTIME.target) addImport(SwiftDependency.CLIENT_RUNTIME.target) - openBlock( - "enum \$L: \$N {", - "}", - operationErrorName, - ClientRuntimeTypes.Http.HttpResponseErrorBinding - ) { + openBlock("enum \$L {", "}", operationErrorName) { + writer.addImport(SwiftDependency.SMITHY_XML.target) + writer.write("") openBlock( - "static func makeError(httpResponse: \$N, decoder: \$D) async throws -> \$N {", "}", - ClientRuntimeTypes.Http.HttpResponse, - ClientRuntimeTypes.Serde.ResponseDecoder, - SwiftTypes.Error + "static var httpBinding: \$N<\$N> {", "}", + ClientRuntimeTypes.Http.HTTPResponseErrorBinding, + SmithyXMLTypes.Reader, ) { - write("let ec2QueryError = try await Ec2QueryError(httpResponse: httpResponse)") - - if (ctx.service.errors.isNotEmpty()) { - write("let serviceError = try await ${ctx.symbolProvider.toSymbol(ctx.service).name}Types.makeServiceError(httpResponse, decoder, ec2QueryError)") - write("if let error = serviceError { return error }") - } - - openBlock("switch ec2QueryError.errorCode {", "}") { - 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 + writer.openBlock("{ httpResponse, responseDocumentClosure in", "}") { + if (ctx.service.errors.isNotEmpty()) { + write("let serviceError = try await ${ctx.symbolProvider.toSymbol(ctx.service).name}Types.makeServiceError(httpResponse, decoder, ec2QueryError)") + write("if let error = serviceError { return error }") + } + writer.write("let responseReader = try await responseDocumentClosure(httpResponse)") + writer.write("let reader = responseReader[\"Errors\"][\"Error\"]") + writer.write("let requestID: String? = try responseReader[\"RequestId\"].readIfPresent()") + writer.write("let errorCode: String? = try reader[\"Code\"].readIfPresent()") + writer.write("let message: String? = try reader[\"Message\"].readIfPresent()") + openBlock("switch errorCode {", "}") { + 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.responseErrorBinding(httpResponse: httpResponse, reader: reader, message: message, requestID: requestID)", + errorShapeName, + errorShapeType + ) + } write( - "case \$S: return try await \$L(httpResponse: httpResponse, decoder: decoder, message: ec2QueryError.message, requestID: ec2QueryError.requestId)", - errorShapeName, - errorShapeType + "default: return try await \$N.makeError(httpResponse: httpResponse, message: message, requestID: requestID, typeName: errorCode)", + unknownServiceErrorSymbol ) } - write( - "default: return try await \$N.makeError(httpResponse: httpResponse, message: ec2QueryError.message, requestID: ec2QueryError.requestId, typeName: ec2QueryError.errorCode)", - unknownServiceErrorSymbol - ) } } } diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/httpResponse/AWSEc2QueryHttpResponseBindingErrorInitGeneratorFactory.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/httpResponse/AWSEc2QueryHttpResponseBindingErrorInitGeneratorFactory.kt index ddcc266db77..aa756abd370 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/httpResponse/AWSEc2QueryHttpResponseBindingErrorInitGeneratorFactory.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/httpResponse/AWSEc2QueryHttpResponseBindingErrorInitGeneratorFactory.kt @@ -12,12 +12,12 @@ import software.amazon.smithy.swift.codegen.SwiftWriter 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.HttpResponseBindingErrorInitGenerator import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingErrorInitGeneratorFactory import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingRenderable -import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.HttpResponseTraitPayload +import software.amazon.smithy.swift.codegen.integration.httpResponse.XMLHttpResponseBindingErrorInitGenerator import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.HttpResponseTraitPayloadFactory import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.HttpResponseTraitWithoutHttpPayloadFactory +import software.amazon.smithy.swift.codegen.integration.httpResponse.bindingTraits.XMLHttpResponseTraitPayload class AWSEc2QueryHttpResponseBindingErrorInitGeneratorFactory : HttpResponseBindingErrorInitGeneratorFactory { override fun construct( @@ -26,7 +26,7 @@ class AWSEc2QueryHttpResponseBindingErrorInitGeneratorFactory : HttpResponseBind httpBindingResolver: HttpBindingResolver, defaultTimestampFormat: TimestampFormatTrait.Format, ): HttpResponseBindingRenderable { - return HttpResponseBindingErrorInitGenerator( + return XMLHttpResponseBindingErrorInitGenerator( ctx, structureShape, httpBindingResolver, @@ -43,7 +43,7 @@ class AWSEc2QueryHttpResponseTraitPayloadFactory : HttpResponseTraitPayloadFacto errorShape: Shape, writer: SwiftWriter ): HttpResponseBindingRenderable { - return HttpResponseTraitPayload( + return XMLHttpResponseTraitPayload( ctx, responseBindings, errorShape, diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/httpResponse/AWSEc2QueryHttpResponseTraitWithoutPayload.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/httpResponse/AWSEc2QueryHttpResponseTraitWithoutPayload.kt index 6fdcd785aed..c250e0848eb 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/httpResponse/AWSEc2QueryHttpResponseTraitWithoutPayload.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/httpResponse/AWSEc2QueryHttpResponseTraitWithoutPayload.kt @@ -5,24 +5,14 @@ package software.amazon.smithy.aws.swift.codegen.ec2query.httpResponse -import software.amazon.smithy.aws.swift.codegen.AWSClientRuntimeTypes 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.ShortShape -import software.amazon.smithy.model.traits.ErrorTrait 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 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.integration.serde.xml.MemberShapeDecodeXMLGenerator class AWSEc2QueryHttpResponseTraitWithoutPayload( val ctx: ProtocolGenerator.GenerationContext, @@ -31,46 +21,10 @@ class AWSEc2QueryHttpResponseTraitWithoutPayload( val writer: SwiftWriter ) : HttpResponseBindingRenderable { override fun render() { - val bodyMembers = responseBindings.filter { it.location == HttpBinding.Location.DOCUMENT } - - val bodyMembersWithoutQueryTrait = bodyMembers + val bodyMembersWithoutQueryTrait = responseBindings.filter { it.location == HttpBinding.Location.DOCUMENT } .filter { !it.member.hasTrait(HttpQueryTrait::class.java) } - .map { ctx.symbolProvider.toMemberName(it.member) } .toMutableSet() - - if (bodyMembersWithoutQueryTrait.isNotEmpty()) { - writer.write("if let data = try await httpResponse.body.readData(), let responseDecoder = decoder {") - writer.indent() - renderWithoutErrorResponseContainer(outputShape, bodyMembersWithoutQueryTrait) - - writer.dedent() - writer.write("} else {") - writer.indent() - val path = "properties.".takeIf { outputShape.hasTrait() } ?: "" - bodyMembers.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("}") - } - } - - fun renderWithoutErrorResponseContainer(outputShape: Shape, bodyMembersWithoutQueryTrait: Set) { - val outputShapeName = ctx.symbolProvider.toSymbol(outputShape).name - writer.addImport(AWSClientRuntimeTypes.EC2Query.Ec2NarrowedResponse) - writer.write("let output: \$N<${outputShapeName}Body> = try responseDecoder.decode(responseBody: data)", AWSClientRuntimeTypes.EC2Query.Ec2NarrowedResponse) - bodyMembersWithoutQueryTrait.sorted().forEach { - writer.write("self.properties.$it = output.errors.error.$it") - } + val generator = MemberShapeDecodeXMLGenerator(ctx, writer, outputShape) + bodyMembersWithoutQueryTrait.sortedBy { it.memberName }.forEach { generator.render(it.member) } } } diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/model/AWSClientContextParamsTransformer.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/model/AWSClientContextParamsTransformer.kt index e36f440bddf..0458c7e17ec 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/model/AWSClientContextParamsTransformer.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/model/AWSClientContextParamsTransformer.kt @@ -15,7 +15,6 @@ import software.amazon.smithy.rulesengine.traits.ClientContextParamDefinition import software.amazon.smithy.rulesengine.traits.ClientContextParamsTrait import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait import software.amazon.smithy.swift.codegen.SwiftSettings -import software.amazon.smithy.swift.codegen.getOrNull import software.amazon.smithy.swift.codegen.integration.SwiftIntegration import software.amazon.smithy.swift.codegen.model.getTrait @@ -36,12 +35,12 @@ class AWSClientContextParamsTransformer : SwiftIntegration { shape.getTrait()?.ruleSet?.let { ruleSet -> val endpointRuleSet = EndpointRuleSet.fromNode(ruleSet) endpointRuleSet.parameters.toList().filter { - it.builtIn?.getOrNull()?.let { builtIn -> + it.builtIn?.orElse(null)?.let { builtIn -> builtIn.split("::").size == 3 } ?: false }.map { val definition = ClientContextParamDefinition.builder().type(it.type.toShapeType()) - .documentation(it.documentation.getOrNull()) + .documentation(it.documentation.orElse(null)) it.name.toString() to definition.build() }.forEach { builder.putParameter(it.first, it.second) diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/model/AWSHttpTraitTransformer.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/model/AWSHttpTraitTransformer.kt index 291bfc9ee19..14ba5759343 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/model/AWSHttpTraitTransformer.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/model/AWSHttpTraitTransformer.kt @@ -13,7 +13,6 @@ import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rulesengine.traits.ContextParamTrait import software.amazon.smithy.swift.codegen.SwiftSettings -import software.amazon.smithy.swift.codegen.getOrNull import software.amazon.smithy.swift.codegen.integration.SwiftIntegration import software.amazon.smithy.swift.codegen.model.getTrait import software.amazon.smithy.swift.codegen.model.hasTrait @@ -31,7 +30,7 @@ class AWSHttpTraitTransformer : SwiftIntegration { when (shape) { is OperationShape -> { val shapeBuilder = shape.toBuilder() - shape.input.getOrNull()?.let { input -> + shape.input.orElse(null)?.let { input -> val inputShape = model.expectShape(input.toShapeId()) shape.getTrait()?.let { httpTrait -> val uriPattern = httpTrait.uri.toString() diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restjson/AWSRestJson1ProtocolGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restjson/AWSRestJson1ProtocolGenerator.kt index 5174e33eacb..1a7402c8c72 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restjson/AWSRestJson1ProtocolGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restjson/AWSRestJson1ProtocolGenerator.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.model.traits.TimestampFormatTrait 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.DefaultCodingKeysGenerator +import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingOutputGenerator import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseGenerator class AWSRestJson1ProtocolGenerator : AWSHttpBindingProtocolGenerator() { @@ -25,9 +26,9 @@ class AWSRestJson1ProtocolGenerator : AWSHttpBindingProtocolGenerator() { override val httpResponseGenerator = HttpResponseGenerator( unknownServiceErrorSymbol, defaultTimestampFormat, - AWSRestJson1HttpResponseBindingErrorGeneratable(), + HttpResponseBindingOutputGenerator(), + AWSRestJson1HttpResponseBindingErrorGeneratable() ) - override val serdeContext = serdeContextJSON override val testsToIgnore = setOf( "SDKAppliedContentEncoding_restJson1", "SDKAppendedGzipAfterProvidedEncoding_restJson1", diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/AWSHttpProtocolRestXMLCustomizations.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/AWSHttpProtocolRestXMLCustomizations.kt index ef0e3597362..bc75db1fb75 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/AWSHttpProtocolRestXMLCustomizations.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/AWSHttpProtocolRestXMLCustomizations.kt @@ -6,22 +6,11 @@ package software.amazon.smithy.aws.swift.codegen.restxml import software.amazon.smithy.aws.swift.codegen.AWSHttpProtocolCustomizations -import software.amazon.smithy.aws.swift.codegen.AWSHttpRequestXMLEncoder -import software.amazon.smithy.aws.swift.codegen.AWSHttpResponseXMLDecoder import software.amazon.smithy.swift.codegen.integration.ClientProperty class AWSHttpProtocolRestXMLCustomizations : AWSHttpProtocolCustomizations() { override fun getClientProperties(): List { - val properties = mutableListOf() - val requestEncoderOptions = mutableMapOf() - val responseDecoderOptions = mutableMapOf() - responseDecoderOptions["dateDecodingStrategy"] = ".secondsSince1970" - responseDecoderOptions["nonConformingFloatDecodingStrategy"] = ".convertFromString(positiveInfinity: \"Infinity\", negativeInfinity: \"-Infinity\", nan: \"NaN\")" - responseDecoderOptions["trimValueWhitespaces"] = "false" - responseDecoderOptions["removeWhitespaceElements"] = "true" - properties.add(AWSHttpRequestXMLEncoder(requestEncoderOptions)) - properties.add(AWSHttpResponseXMLDecoder(responseDecoderOptions)) - return properties + return listOf() } } diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/AWSRestXMLHttpResponseBindingErrorGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/AWSRestXMLHttpResponseBindingErrorGenerator.kt index b9d254e0fdb..a0370f0420f 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/AWSRestXMLHttpResponseBindingErrorGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/AWSRestXMLHttpResponseBindingErrorGenerator.kt @@ -7,16 +7,19 @@ package software.amazon.smithy.aws.swift.codegen.restxml import software.amazon.smithy.aws.swift.codegen.AWSClientRuntimeTypes import software.amazon.smithy.aws.swift.codegen.AWSSwiftDependency +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.SmithyXMLTypes import software.amazon.smithy.swift.codegen.SwiftDependency import software.amazon.smithy.swift.codegen.SwiftTypes import software.amazon.smithy.swift.codegen.declareSection import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.SectionId import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingErrorGeneratable +import software.amazon.smithy.swift.codegen.model.getTrait import software.amazon.smithy.swift.codegen.model.toUpperCamelCase import software.amazon.smithy.swift.codegen.utils.errorShapeName @@ -34,11 +37,9 @@ class AWSRestXMLHttpResponseBindingErrorGenerator : HttpResponseBindingErrorGene openBlock("extension ${ctx.symbolProvider.toSymbol(ctx.service).name}Types {", "}") { openBlock( - "static func makeServiceError(_ httpResponse: \$N, _ decoder: \$D, _ error: \$N) async throws -> \$N? {", - "}", + "static func responseServiceErrorBinding(httpResponse: \$N, reader: \$N) async throws -> \$N? {", "}", ClientRuntimeTypes.Http.HttpResponse, - ClientRuntimeTypes.Serde.ResponseDecoder, - AWSClientRuntimeTypes.RestXML.RestXMLError, + SmithyXMLTypes.Reader, SwiftTypes.Error ) { openBlock("switch error.errorCode {", "}") { @@ -51,7 +52,7 @@ class AWSRestXMLHttpResponseBindingErrorGenerator : HttpResponseBindingErrorGene 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.message, requestID: error.requestId)", + "case \$S: return try await \$L(httpResponse: httpResponse, reader: reader, message: error.message, requestID: error.requestId)", errorShapeName, errorShapeType ) @@ -78,18 +79,17 @@ class AWSRestXMLHttpResponseBindingErrorGenerator : HttpResponseBindingErrorGene with(writer) { addImport(AWSSwiftDependency.AWS_CLIENT_RUNTIME.target) addImport(SwiftDependency.CLIENT_RUNTIME.target) - + addImport(SwiftDependency.SMITHY_XML.target) openBlock( - "enum \$L: \$N {", + "enum \$L {", "}", - operationErrorName, - ClientRuntimeTypes.Http.HttpResponseErrorBinding + operationErrorName ) { + write("") openBlock( - "static func makeError(httpResponse: \$N, decoder: \$D) async throws -> \$N {", "}", - ClientRuntimeTypes.Http.HttpResponse, - ClientRuntimeTypes.Serde.ResponseDecoder, - SwiftTypes.Error + "static var httpBinding: \$N<\$N> {", "}", + ClientRuntimeTypes.Http.HTTPResponseErrorBinding, + SmithyXMLTypes.Reader, ) { val errorShapes = op.errors .map { ctx.model.expectShape(it) as StructureShape } @@ -101,28 +101,39 @@ class AWSRestXMLHttpResponseBindingErrorGenerator : HttpResponseBindingErrorGene "unknownServiceErrorSymbol" to unknownServiceErrorSymbol, "errorShapes" to errorShapes ) - - declareSection(RestXMLResponseBindingSectionId, context) { - write( - "let restXMLError = try await \$N(httpResponse: httpResponse)", - AWSClientRuntimeTypes.RestXML.RestXMLError - ) - - if (ctx.service.errors.isNotEmpty()) { - write("let serviceError = try await ${ctx.symbolProvider.toSymbol(ctx.service).name}Types.makeServiceError(httpResponse, decoder, restXMLError)") - write("if let error = serviceError { return error }") - } - openBlock("switch restXMLError.errorCode {", "}") { - 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: restXMLError.message, requestID: restXMLError.requestId)", - errorShapeName, - errorShapeType + writer.openBlock("{ httpResponse, responseDocumentClosure in", "}") { + declareSection(RestXMLResponseBindingSectionId, context) { + val noErrorWrapping = ctx.service.getTrait()?.let { it.isNoErrorWrapping } ?: false + writer.write("let responseReader = try await responseDocumentClosure(httpResponse)") + if (errorShapes.isNotEmpty() || ctx.service.errors.isNotEmpty()) { + writer.write( + "let errorBodyReader = \$N.errorBodyReader(responseReader: responseReader, noErrorWrapping: \$L)", + AWSClientRuntimeTypes.RestXML.RestXMLError, + noErrorWrapping ) } - write("default: return try await \$unknownServiceErrorSymbol:N.makeError(httpResponse: httpResponse, message: restXMLError.message, requestID: restXMLError.requestId, typeName: restXMLError.errorCode)") + if (ctx.service.errors.isNotEmpty()) { + openBlock( + "if let serviceError = try await \$NTypes.responseServiceErrorBinding(httpResponse, errorBodyReader) {", + "}", + ctx.symbolProvider.toSymbol(ctx.service), + ) { + write("return serviceError") + } + } + writer.write("let restXMLError = try \$N(responseReader: responseReader, noErrorWrapping: \$L)", AWSClientRuntimeTypes.RestXML.RestXMLError, noErrorWrapping) + openBlock("switch restXMLError.code {", "}") { + errorShapes.forEach { errorShape -> + val errorShapeName = errorShape.id.name + val errorShapeType = ctx.symbolProvider.toSymbol(errorShape).name + write( + "case \$S: return try await \$L.responseErrorBinding(httpResponse: httpResponse, reader: errorBodyReader, message: restXMLError.message, requestID: restXMLError.requestID)", + errorShapeName, + errorShapeType + ) + } + write("default: return try await \$unknownServiceErrorSymbol:N.makeError(httpResponse: httpResponse, message: restXMLError.message, requestID: restXMLError.requestID, typeName: restXMLError.code)") + } } } } diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/RestXmlProtocolGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/RestXmlProtocolGenerator.kt index 7e18cdf76e7..d00302b69b1 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/RestXmlProtocolGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/RestXmlProtocolGenerator.kt @@ -6,23 +6,22 @@ package software.amazon.smithy.aws.swift.codegen.restxml import software.amazon.smithy.aws.swift.codegen.AWSHttpBindingProtocolGenerator +import software.amazon.smithy.aws.swift.codegen.XMLMessageUnmarshallableGenerator import software.amazon.smithy.aws.traits.protocols.RestXmlTrait import software.amazon.smithy.model.shapes.MemberShape 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.SwiftTypes import software.amazon.smithy.swift.codegen.SwiftWriter import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.codingKeys.CodingKeysCustomizationXmlName -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.HttpResponseGenerator +import software.amazon.smithy.swift.codegen.integration.httpResponse.XMLHttpResponseBindingOutputGenerator 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.model.ShapeMetadata class RestXmlProtocolGenerator : AWSHttpBindingProtocolGenerator() { - override val codingKeysGenerator: CodingKeysGenerator = DefaultCodingKeysGenerator(CodingKeysCustomizationXmlName()) + override val codingKeysGenerator = null override val defaultContentType: String = "application/xml" override val defaultTimestampFormat: TimestampFormatTrait.Format = TimestampFormatTrait.Format.DATE_TIME override val protocol: ShapeId = RestXmlTrait.ID @@ -30,15 +29,15 @@ class RestXmlProtocolGenerator : AWSHttpBindingProtocolGenerator() { override val httpResponseGenerator = HttpResponseGenerator( unknownServiceErrorSymbol, defaultTimestampFormat, + XMLHttpResponseBindingOutputGenerator(), AWSRestXMLHttpResponseBindingErrorGenerator(), AWSXMLHttpResponseBindingErrorInitGeneratorFactory(), ) override val shouldRenderDecodableBodyStructForInputShapes = false - override val serdeContext = serdeContextXML - override val testsToIgnore = setOf( + override val shouldRenderCodingKeysForEncodable = false + override val testsToIgnore: Set = setOf( "S3DefaultAddressing", "S3VirtualHostAddressing", - "S3PathAddressing", "S3VirtualHostDualstackAddressing", "S3VirtualHostAccelerateAddressing", "S3VirtualHostDualstackAccelerateAddressing", @@ -51,9 +50,25 @@ class RestXmlProtocolGenerator : AWSHttpBindingProtocolGenerator() { ) override val tagsToIgnore = setOf("defaults") - override val codableProtocol = SwiftTypes.Protocols.Decodable + override val codableProtocol = null override val encodableProtocol = null - override val decodableProtocol = SwiftTypes.Protocols.Decodable + override val decodableProtocol = null + + override fun generateMessageMarshallable(ctx: ProtocolGenerator.GenerationContext) { + var streamingShapes = outputStreamingShapes(ctx) + val messageUnmarshallableGenerator = XMLMessageUnmarshallableGenerator(ctx) + streamingShapes.forEach { streamingMember -> + messageUnmarshallableGenerator.render(streamingMember) + } + } + + override fun generateDeserializers(ctx: ProtocolGenerator.GenerationContext) { + super.generateDeserializers(ctx) + val errorShapes = resolveErrorShapes(ctx) + for (shape in errorShapes) { + renderCodableExtension(ctx, shape, false, true) + } + } override fun renderStructEncode( ctx: ProtocolGenerator.GenerationContext, @@ -64,19 +79,37 @@ class RestXmlProtocolGenerator : AWSHttpBindingProtocolGenerator() { defaultTimestampFormat: TimestampFormatTrait.Format, path: String?, ) { - val encoder = StructEncodeXMLGenerator(ctx, shapeContainingMembers, members, writer) - encoder.render() + StructEncodeXMLGenerator( + ctx, + shapeContainingMembers, + members, + writer + ).render() } override fun renderStructDecode( ctx: ProtocolGenerator.GenerationContext, + shapeContainingMembers: Shape, shapeMetadata: Map, members: List, writer: SwiftWriter, defaultTimestampFormat: TimestampFormatTrait.Format, path: String, ) { - val decoder = RestXmlStructDecodeXMLGenerator(ctx, members, shapeMetadata, writer, defaultTimestampFormat) - decoder.render() + StructDecodeXMLGenerator( + ctx, + shapeContainingMembers, + members, + shapeMetadata, + writer, + ).render() + } + + override fun renderBodyStructAndDecodableExtension( + ctx: ProtocolGenerator.GenerationContext, + shape: Shape, + metadata: Map + ) { + // No operation } } diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/RestXmlStructDecodeXMLGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/RestXmlStructDecodeXMLGenerator.kt deleted file mode 100644 index b83c6130142..00000000000 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/RestXmlStructDecodeXMLGenerator.kt +++ /dev/null @@ -1,68 +0,0 @@ -package software.amazon.smithy.aws.swift.codegen.restxml - -import software.amazon.smithy.aws.traits.customizations.S3UnwrappedXmlOutputTrait -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.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.integration.serde.xml.StructDecodeXMLGenerator -import software.amazon.smithy.swift.codegen.model.ShapeMetadata -import software.amazon.smithy.swift.codegen.model.hasTrait - -class RestXmlStructDecodeXMLGenerator( - val ctx: ProtocolGenerator.GenerationContext, - private val members: List, - private val metadata: Map, - private val writer: SwiftWriter, - defaultTimestampFormat: TimestampFormatTrait.Format -) : StructDecodeXMLGenerator(ctx, members, metadata, writer, defaultTimestampFormat) { - override fun render() { - writer.openBlock("public init(from decoder: \$N) throws {", "}", SwiftTypes.Decoder) { - if (members.isNotEmpty()) { - renderDecodeBody() - } - } - } - - private fun renderDecodeBody() { - val containerName = "containerValues" - if (responseBodyIsNotWrapped()) { - writer.write("var $containerName = try decoder.unkeyedContainer()") - members.forEach { member -> - renderSingleMemberUnkeyed(member, containerName) - } - } else { - writer.write("let $containerName = try decoder.container(keyedBy: CodingKeys.self)") - members.forEach { member -> - renderSingleMember(member, containerName) - } - } - } - private fun responseBodyIsNotWrapped(): Boolean { - if (metadata.containsKey(ShapeMetadata.OPERATION_SHAPE)) { - val operationShape = metadata[ShapeMetadata.OPERATION_SHAPE] as OperationShape - if (operationShape.hasTrait()) { - return true - } - } - return false - } - - fun renderSingleMemberUnkeyed(member: MemberShape, containerName: String) { - val memberTarget = ctx.model.expectShape(member.target) - when (memberTarget) { - is CollectionShape, is MapShape, is TimestampShape, is BlobShape -> { - assert(false) { "Not implemented: renderSingleMemberUnkeyed - Collection, Map, Timestamp, Blob" } - } - else -> { - renderScalarMember(member, memberTarget, containerName, true) - } - } - } -} diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/httpResponse/AWSXMLHttpResponseBindingErrorInitGeneratorFactory.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/httpResponse/AWSXMLHttpResponseBindingErrorInitGeneratorFactory.kt index cb9063dd5fc..6698d93ce77 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/httpResponse/AWSXMLHttpResponseBindingErrorInitGeneratorFactory.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/httpResponse/AWSXMLHttpResponseBindingErrorInitGeneratorFactory.kt @@ -13,9 +13,9 @@ import software.amazon.smithy.swift.codegen.SwiftWriter 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.HttpResponseBindingErrorInitGenerator import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingErrorInitGeneratorFactory import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingRenderable +import software.amazon.smithy.swift.codegen.integration.httpResponse.XMLHttpResponseBindingErrorInitGenerator 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.HttpResponseTraitWithoutHttpPayloadFactory @@ -27,7 +27,7 @@ class AWSXMLHttpResponseBindingErrorInitGeneratorFactory : HttpResponseBindingEr httpBindingResolver: HttpBindingResolver, defaultTimestampFormat: TimestampFormatTrait.Format, ): HttpResponseBindingRenderable { - return HttpResponseBindingErrorInitGenerator( + return XMLHttpResponseBindingErrorInitGenerator( ctx, structureShape, httpBindingResolver, diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/httpResponse/AWSXMLHttpResponseTraitWithoutPayload.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/httpResponse/AWSXMLHttpResponseTraitWithoutPayload.kt index fd20be5508a..75cf7490f50 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/httpResponse/AWSXMLHttpResponseTraitWithoutPayload.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/httpResponse/AWSXMLHttpResponseTraitWithoutPayload.kt @@ -5,24 +5,14 @@ package software.amazon.smithy.aws.swift.codegen.restxml.httpResponse -import software.amazon.smithy.aws.swift.codegen.AWSClientRuntimeTypes.RestXML.ErrorResponseContainer -import software.amazon.smithy.aws.traits.protocols.RestXmlTrait 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.ShortShape 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 import software.amazon.smithy.swift.codegen.integration.httpResponse.HttpResponseBindingRenderable -import software.amazon.smithy.swift.codegen.model.getTrait -import software.amazon.smithy.swift.codegen.model.isBoxed +import software.amazon.smithy.swift.codegen.integration.serde.xml.MemberShapeDecodeXMLGenerator class AWSXMLHttpResponseTraitWithoutPayload( val ctx: ProtocolGenerator.GenerationContext, @@ -31,62 +21,11 @@ class AWSXMLHttpResponseTraitWithoutPayload( val writer: SwiftWriter ) : HttpResponseBindingRenderable { override fun render() { - val bodyMembers = responseBindings.filter { it.location == HttpBinding.Location.DOCUMENT } - - val bodyMembersWithoutQueryTrait = bodyMembers + val bodyMembersWithoutQueryTrait = responseBindings.filter { it.location == HttpBinding.Location.DOCUMENT } .filter { !it.member.hasTrait(HttpQueryTrait::class.java) } - .map { ctx.symbolProvider.toMemberName(it.member) } - .toMutableSet() - - if (bodyMembersWithoutQueryTrait.isNotEmpty()) { - writer.write("if let data = try await httpResponse.body.readData(), let responseDecoder = decoder {") - writer.indent() - val outputShapeName = ctx.symbolProvider.toSymbol(outputShape).name - if (serviceDisablesWrappingOfErrorProperties()) { - renderWithoutErrorResponseContainer(outputShapeName, bodyMembersWithoutQueryTrait) - } else { - renderWithErrorResponseContainer(outputShapeName, bodyMembersWithoutQueryTrait) - } - writer.dedent() - writer.write("} else {") - writer.indent() - bodyMembers.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.properties.$memberName = $value") - } - writer.dedent() - writer.write("}") - } - } - - fun serviceDisablesWrappingOfErrorProperties(): Boolean { - ctx.service.getTrait()?.let { - return it.isNoErrorWrapping - } - return false - } - - fun renderWithoutErrorResponseContainer(outputShapeName: String, bodyMembersWithoutQueryTrait: Set) { - writer.write("let output: ${outputShapeName}Body = try responseDecoder.decode(responseBody: data)") - bodyMembersWithoutQueryTrait.sorted().forEach { - writer.write("self.properties.$it = output.$it") - } - } - - fun renderWithErrorResponseContainer(outputShapeName: String, bodyMembersWithoutQueryTrait: Set) { - writer.addImport(ErrorResponseContainer) - writer.write("let output: \$N<${outputShapeName}Body> = try responseDecoder.decode(responseBody: data)", ErrorResponseContainer) - bodyMembersWithoutQueryTrait.sorted().forEach { - writer.write("self.properties.$it = output.error.$it") - } + .map { it.member } + .toSet() + val generator = MemberShapeDecodeXMLGenerator(ctx, writer, outputShape) + bodyMembersWithoutQueryTrait.sortedBy { it.memberName }.forEach { generator.render(it) } } } diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/PresignerGeneratorTests.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/PresignerGeneratorTests.kt index 11498f66f89..e4a29324d41 100644 --- a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/PresignerGeneratorTests.kt +++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/PresignerGeneratorTests.kt @@ -188,8 +188,6 @@ extension PutObjectInput { decoder.dateDecodingStrategy = .secondsSince1970 decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN") let context = ClientRuntime.HttpContextBuilder() - .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .put) .withServiceName(value: serviceName) .withOperation(value: "putObject") @@ -208,12 +206,12 @@ extension PutObjectInput { operation.buildStep.intercept(position: .before, middleware: EndpointResolverMiddleware(endpointResolver: config.serviceSpecific.endpointResolver, endpointParams: endpointParams)) operation.buildStep.intercept(position: .before, middleware: AWSClientRuntime.UserAgentMiddleware(metadata: AWSClientRuntime.AWSUserAgentMetadata.fromConfig(serviceID: serviceName, version: "1.0.0", config: config))) operation.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/json")) - operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(documentWritingClosure: SmithyXML.XMLReadWrite.documentWritingClosure(rootNodeInfo: .init("PutObjectInput")), inputWritingClosure: PutObjectInput.writingClosure(_:to:))) + operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(documentWritingClosure: SmithyXML.XMLReadWrite.documentWritingClosure(rootNodeInfo: "PutObjectInput"), inputWritingClosure: PutObjectInput.writingClosure(_:to:))) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) let sigv4Config = AWSClientRuntime.SigV4Config(useDoubleURIEncode: false, shouldNormalizeURIPath: false, expiration: expiration, signedBodyHeader: .contentSha256, unsignedBody: false, signingAlgorithm: .sigv4) operation.finalizeStep.intercept(position: .before, middleware: AWSClientRuntime.SigV4Middleware(config: sigv4Config)) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(PutObjectOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(PutObjectOutput.httpBinding, responseDocumentBinding), responseErrorClosure(PutObjectOutputError.httpBinding, responseDocumentBinding))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let presignedRequestBuilder = try await operation.presignedRequest(context: context, input: input, output: PutObjectOutput(), next: ClientRuntime.NoopHandler()) guard let builtRequest = presignedRequestBuilder?.build() else { diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/AWSQueryOperationStackTest.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/AWSQueryOperationStackTest.kt index b5d517e4cba..afcbba9c4fd 100644 --- a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/AWSQueryOperationStackTest.kt +++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/AWSQueryOperationStackTest.kt @@ -23,7 +23,6 @@ class AWSQueryOperationStackTest { public func noInputAndOutput(input: NoInputAndOutputInput) async throws -> NoInputAndOutputOutput { let context = ClientRuntime.HttpContextBuilder() .withEncoder(value: encoder) - .withDecoder(value: decoder) .withMethod(value: .post) .withServiceName(value: serviceName) .withOperation(value: "noInputAndOutput") @@ -43,7 +42,7 @@ class AWSQueryOperationStackTest { operation.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/x-www-form-urlencoded")) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) - operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(decoder: decoder), responseErrorClosure(NoInputAndOutputOutputError.self, decoder: decoder))) + operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(responseClosure(NoInputAndOutputOutput.httpBinding, responseDocumentBinding), responseErrorClosure(NoInputAndOutputOutputError.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 diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/BlobEncodeGeneratorTests.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/BlobEncodeGeneratorTests.kt index 504300f16ce..1631094cacf 100644 --- a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/BlobEncodeGeneratorTests.kt +++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/BlobEncodeGeneratorTests.kt @@ -19,67 +19,74 @@ class BlobEncodeGeneratorTests { val context = setupTests("awsquery/query-blobs.smithy", "aws.protocoltests.query#AwsQuery") val contents = getFileContents(context.manifest, "/Example/models/BlobInputParamsInput+Encodable.swift") contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension BlobInputParamsInput: Swift.Encodable { - public func encode(to encoder: Swift.Encoder) throws { - var container = encoder.container(keyedBy: ClientRuntime.Key.self) - if let blobList = blobList { - if !blobList.isEmpty { - var blobListContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("BlobList")) - for (index0, blob0) in blobList.enumerated() { - try blobListContainer.encode(blob0.base64EncodedString(), forKey: ClientRuntime.Key("member.\(index0.advanced(by: 1))")) - } - } - else { - var blobListContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("BlobList")) - try blobListContainer.encode("", forKey: ClientRuntime.Key("")) - } - } - if let blobListFlattened = blobListFlattened { - if !blobListFlattened.isEmpty { - for (index0, blob0) in blobListFlattened.enumerated() { - try container.encode(blob0.base64EncodedString(), forKey: ClientRuntime.Key("BlobListFlattened.\(index0.advanced(by: 1))")) - } - } - else { - var blobListFlattenedContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("BlobListFlattened")) - try blobListFlattenedContainer.encode("", forKey: ClientRuntime.Key("")) - } - } - if let blobMap = blobMap { - var blobMapContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("BlobMap")) - for (index0, element0) in blobMap.sorted(by: { ${'$'}0.key < ${'$'}1.key }).enumerated() { - let stringKey0 = element0.key - let blobValue0 = element0.value - var entryContainer0 = blobMapContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("entry.\(index0.advanced(by: 1))")) - var keyContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("key")) - try keyContainer0.encode(stringKey0, forKey: ClientRuntime.Key("")) - var valueContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("value")) - try valueContainer0.encode(blobValue0.base64EncodedString(), forKey: ClientRuntime.Key("")) - } - } - if let blobMapFlattened = blobMapFlattened { - if !blobMapFlattened.isEmpty { - for (index0, element0) in blobMapFlattened.sorted(by: { ${'$'}0.key < ${'$'}1.key }).enumerated() { - let stringKey0 = element0.key - let blobValue0 = element0.value - var nestedContainer0 = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("BlobMapFlattened.\(index0.advanced(by: 1))")) - var keyContainer0 = nestedContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("key")) - try keyContainer0.encode(stringKey0, forKey: ClientRuntime.Key("")) - var valueContainer0 = nestedContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("value")) - try valueContainer0.encode(blobValue0.base64EncodedString(), forKey: ClientRuntime.Key("")) - } - } - } - if let blobMember = blobMember { - try container.encode(blobMember.base64EncodedString(), forKey: ClientRuntime.Key("BlobMember")) - } - try container.encode("BlobInputParams", forKey:ClientRuntime.Key("Action")) - try container.encode("2020-01-08", forKey:ClientRuntime.Key("Version")) + val expectedContents = """ +extension BlobInputParamsInput: Swift.Encodable { + enum CodingKeys: Swift.String, Swift.CodingKey { + case blobList = "BlobList" + case blobListFlattened = "BlobListFlattened" + case blobMap = "BlobMap" + case blobMapFlattened = "BlobMapFlattened" + case blobMember = "BlobMember" + } + + public func encode(to encoder: Swift.Encoder) throws { + var container = encoder.container(keyedBy: ClientRuntime.Key.self) + if let blobList = blobList { + if !blobList.isEmpty { + var blobListContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("BlobList")) + for (index0, blob0) in blobList.enumerated() { + try blobListContainer.encode(blob0.base64EncodedString(), forKey: ClientRuntime.Key("member.\(index0.advanced(by: 1))")) + } + } + else { + var blobListContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("BlobList")) + try blobListContainer.encode("", forKey: ClientRuntime.Key("")) + } + } + if let blobListFlattened = blobListFlattened { + if !blobListFlattened.isEmpty { + for (index0, blob0) in blobListFlattened.enumerated() { + try container.encode(blob0.base64EncodedString(), forKey: ClientRuntime.Key("BlobListFlattened.\(index0.advanced(by: 1))")) } } - """.trimIndent() + else { + var blobListFlattenedContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("BlobListFlattened")) + try blobListFlattenedContainer.encode("", forKey: ClientRuntime.Key("")) + } + } + if let blobMap = blobMap { + var blobMapContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("BlobMap")) + for (index0, element0) in blobMap.sorted(by: { ${'$'}0.key < ${'$'}1.key }).enumerated() { + let stringKey0 = element0.key + let blobValue0 = element0.value + var entryContainer0 = blobMapContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("entry.\(index0.advanced(by: 1))")) + var keyContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("key")) + try keyContainer0.encode(stringKey0, forKey: ClientRuntime.Key("")) + var valueContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("value")) + try valueContainer0.encode(blobValue0.base64EncodedString(), forKey: ClientRuntime.Key("")) + } + } + if let blobMapFlattened = blobMapFlattened { + if !blobMapFlattened.isEmpty { + for (index0, element0) in blobMapFlattened.sorted(by: { ${'$'}0.key < ${'$'}1.key }).enumerated() { + let stringKey0 = element0.key + let blobValue0 = element0.value + var nestedContainer0 = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("BlobMapFlattened.\(index0.advanced(by: 1))")) + var keyContainer0 = nestedContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("key")) + try keyContainer0.encode(stringKey0, forKey: ClientRuntime.Key("")) + var valueContainer0 = nestedContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("value")) + try valueContainer0.encode(blobValue0.base64EncodedString(), forKey: ClientRuntime.Key("")) + } + } + } + if let blobMember = blobMember { + try container.encode(blobMember.base64EncodedString(), forKey: ClientRuntime.Key("BlobMember")) + } + try container.encode("BlobInputParams", forKey:ClientRuntime.Key("Action")) + try container.encode("2020-01-08", forKey:ClientRuntime.Key("Version")) + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/ListEncodeFormURLGeneratorTests.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/ListEncodeFormURLGeneratorTests.kt index 2039989e661..980ddb9d63b 100644 --- a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/ListEncodeFormURLGeneratorTests.kt +++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/ListEncodeFormURLGeneratorTests.kt @@ -19,100 +19,109 @@ class ListEncodeFormURLGeneratorTests { val context = setupTests("awsquery/query-lists.smithy", "aws.protocoltests.query#AwsQuery") val contents = getFileContents(context.manifest, "/Example/models/QueryListsInput+Encodable.swift") contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension QueryListsInput: 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 { - var complexListArgContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("ComplexListArg")) - for (index0, greetingstruct0) in complexListArg.enumerated() { - try complexListArgContainer.encode(greetingstruct0, forKey: ClientRuntime.Key("member.\(index0.advanced(by: 1))")) - } - } - else { - var complexListArgContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("ComplexListArg")) - try complexListArgContainer.encode("", forKey: ClientRuntime.Key("")) - } - } - if let flattenedListArg = flattenedListArg { - if !flattenedListArg.isEmpty { - for (index0, string0) in flattenedListArg.enumerated() { - var flattenedListArgContainer0 = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("FlattenedListArg.\(index0.advanced(by: 1))")) - try flattenedListArgContainer0.encode(string0, forKey: ClientRuntime.Key("")) - } - } - else { - var flattenedListArgContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("FlattenedListArg")) - try flattenedListArgContainer.encode("", forKey: ClientRuntime.Key("")) - } - } - if let flattenedListArgWithXmlName = flattenedListArgWithXmlName { - if !flattenedListArgWithXmlName.isEmpty { - for (index0, string0) in flattenedListArgWithXmlName.enumerated() { - var flattenedListArgWithXmlNameContainer0 = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("Hi.\(index0.advanced(by: 1))")) - try flattenedListArgWithXmlNameContainer0.encode(string0, forKey: ClientRuntime.Key("")) - } - } - else { - var flattenedListArgWithXmlNameContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("Hi")) - try flattenedListArgWithXmlNameContainer.encode("", forKey: ClientRuntime.Key("")) - } - } - if let listArg = listArg { - if !listArg.isEmpty { - var listArgContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("ListArg")) - for (index0, string0) in listArg.enumerated() { - try listArgContainer.encode(string0, forKey: ClientRuntime.Key("member.\(index0.advanced(by: 1))")) - } - } - else { - var listArgContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("ListArg")) - try listArgContainer.encode("", forKey: ClientRuntime.Key("")) - } - } - if let listArgWithXmlNameMember = listArgWithXmlNameMember { - if !listArgWithXmlNameMember.isEmpty { - var listArgWithXmlNameMemberContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("ListArgWithXmlNameMember")) - for (index0, string0) in listArgWithXmlNameMember.enumerated() { - try listArgWithXmlNameMemberContainer.encode(string0, forKey: ClientRuntime.Key("item.\(index0.advanced(by: 1))")) - } - } - else { - var listArgWithXmlNameMemberContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("ListArgWithXmlNameMember")) - try listArgWithXmlNameMemberContainer.encode("", forKey: ClientRuntime.Key("")) - } - } - if let flatTsList = flatTsList { - if !flatTsList.isEmpty { - for (index0, timestamp0) in flatTsList.enumerated() { - var flatTsListContainer0 = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("flatTsList.\(index0.advanced(by: 1))")) - try flatTsListContainer0.encodeTimestamp(timestamp0, format: .epochSeconds, forKey: ClientRuntime.Key("")) - } - } - else { - var flatTsListContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("flatTsList")) - try flatTsListContainer.encode("", forKey: ClientRuntime.Key("")) - } - } - if let tsList = tsList { - if !tsList.isEmpty { - var tsListContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("tsList")) - for (index0, timestamp0) in tsList.enumerated() { - try tsListContainer.encodeTimestamp(timestamp0, format: .epochSeconds, forKey: ClientRuntime.Key("member.\(index0.advanced(by: 1))")) - } - } - else { - var tsListContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("tsList")) - try tsListContainer.encode("", forKey: ClientRuntime.Key("")) - } - } - try container.encode("QueryLists", forKey:ClientRuntime.Key("Action")) - try container.encode("2020-01-08", forKey:ClientRuntime.Key("Version")) + val expectedContents = """ +extension QueryListsInput: Swift.Encodable { + enum CodingKeys: Swift.String, Swift.CodingKey { + case complexListArg = "ComplexListArg" + case flattenedListArg = "FlattenedListArg" + case flattenedListArgWithXmlName = "Hi" + case listArg = "ListArg" + case listArgWithXmlNameMember = "ListArgWithXmlNameMember" + case flatTsList + case tsList + } + + public func encode(to encoder: Swift.Encoder) throws { + var container = encoder.container(keyedBy: ClientRuntime.Key.self) + if let complexListArg = complexListArg { + if !complexListArg.isEmpty { + var complexListArgContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("ComplexListArg")) + for (index0, greetingstruct0) in complexListArg.enumerated() { + try complexListArgContainer.encode(greetingstruct0, forKey: ClientRuntime.Key("member.\(index0.advanced(by: 1))")) + } + } + else { + var complexListArgContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("ComplexListArg")) + try complexListArgContainer.encode("", forKey: ClientRuntime.Key("")) + } + } + if let flattenedListArg = flattenedListArg { + if !flattenedListArg.isEmpty { + for (index0, string0) in flattenedListArg.enumerated() { + var flattenedListArgContainer0 = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("FlattenedListArg.\(index0.advanced(by: 1))")) + try flattenedListArgContainer0.encode(string0, forKey: ClientRuntime.Key("")) + } + } + else { + var flattenedListArgContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("FlattenedListArg")) + try flattenedListArgContainer.encode("", forKey: ClientRuntime.Key("")) + } + } + if let flattenedListArgWithXmlName = flattenedListArgWithXmlName { + if !flattenedListArgWithXmlName.isEmpty { + for (index0, string0) in flattenedListArgWithXmlName.enumerated() { + var flattenedListArgWithXmlNameContainer0 = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("Hi.\(index0.advanced(by: 1))")) + try flattenedListArgWithXmlNameContainer0.encode(string0, forKey: ClientRuntime.Key("")) + } + } + else { + var flattenedListArgWithXmlNameContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("Hi")) + try flattenedListArgWithXmlNameContainer.encode("", forKey: ClientRuntime.Key("")) + } + } + if let listArg = listArg { + if !listArg.isEmpty { + var listArgContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("ListArg")) + for (index0, string0) in listArg.enumerated() { + try listArgContainer.encode(string0, forKey: ClientRuntime.Key("member.\(index0.advanced(by: 1))")) } } - """.trimIndent() + else { + var listArgContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("ListArg")) + try listArgContainer.encode("", forKey: ClientRuntime.Key("")) + } + } + if let listArgWithXmlNameMember = listArgWithXmlNameMember { + if !listArgWithXmlNameMember.isEmpty { + var listArgWithXmlNameMemberContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("ListArgWithXmlNameMember")) + for (index0, string0) in listArgWithXmlNameMember.enumerated() { + try listArgWithXmlNameMemberContainer.encode(string0, forKey: ClientRuntime.Key("item.\(index0.advanced(by: 1))")) + } + } + else { + var listArgWithXmlNameMemberContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("ListArgWithXmlNameMember")) + try listArgWithXmlNameMemberContainer.encode("", forKey: ClientRuntime.Key("")) + } + } + if let flatTsList = flatTsList { + if !flatTsList.isEmpty { + for (index0, timestamp0) in flatTsList.enumerated() { + var flatTsListContainer0 = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("flatTsList.\(index0.advanced(by: 1))")) + try flatTsListContainer0.encodeTimestamp(timestamp0, format: .epochSeconds, forKey: ClientRuntime.Key("")) + } + } + else { + var flatTsListContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("flatTsList")) + try flatTsListContainer.encode("", forKey: ClientRuntime.Key("")) + } + } + if let tsList = tsList { + if !tsList.isEmpty { + var tsListContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("tsList")) + for (index0, timestamp0) in tsList.enumerated() { + try tsListContainer.encodeTimestamp(timestamp0, format: .epochSeconds, forKey: ClientRuntime.Key("member.\(index0.advanced(by: 1))")) + } + } + else { + var tsListContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("tsList")) + try tsListContainer.encode("", forKey: ClientRuntime.Key("")) + } + } + try container.encode("QueryLists", forKey:ClientRuntime.Key("Action")) + try container.encode("2020-01-08", forKey:ClientRuntime.Key("Version")) + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/MapEncodeFormURLGeneratorTests.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/MapEncodeFormURLGeneratorTests.kt index 5b165d9d099..c3303fd42a1 100644 --- a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/MapEncodeFormURLGeneratorTests.kt +++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/MapEncodeFormURLGeneratorTests.kt @@ -20,104 +20,113 @@ class MapEncodeFormURLGeneratorTests { val context = setupTests("awsquery/query-maps.smithy", "aws.protocoltests.query#AwsQuery") val contents = getFileContents(context.manifest, "/Example/models/QueryMapsInput+Encodable.swift") contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension QueryMapsInput: Swift.Encodable { - public func encode(to encoder: Swift.Encoder) throws { - var container = encoder.container(keyedBy: ClientRuntime.Key.self) - if let complexMapArg = complexMapArg { - var complexMapArgContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("ComplexMapArg")) - for (index0, element0) in complexMapArg.sorted(by: { ${'$'}0.key < ${'$'}1.key }).enumerated() { - let stringKey0 = element0.key - let greetingstructValue0 = element0.value - var entryContainer0 = complexMapArgContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("entry.\(index0.advanced(by: 1))")) - var keyContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("key")) - try keyContainer0.encode(stringKey0, forKey: ClientRuntime.Key("")) - var valueContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("value")) - try valueContainer0.encode(greetingstructValue0, forKey: ClientRuntime.Key("")) - } - } - if let flattenedMap = flattenedMap { - if !flattenedMap.isEmpty { - for (index0, element0) in flattenedMap.sorted(by: { ${'$'}0.key < ${'$'}1.key }).enumerated() { - let stringKey0 = element0.key - let stringValue0 = element0.value - var nestedContainer0 = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("FlattenedMap.\(index0.advanced(by: 1))")) - var keyContainer0 = nestedContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("key")) - try keyContainer0.encode(stringKey0, forKey: ClientRuntime.Key("")) - var valueContainer0 = nestedContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("value")) - try valueContainer0.encode(stringValue0, forKey: ClientRuntime.Key("")) - } - } - } - if let flattenedMapWithXmlName = flattenedMapWithXmlName { - if !flattenedMapWithXmlName.isEmpty { - for (index0, element0) in flattenedMapWithXmlName.sorted(by: { ${'$'}0.key < ${'$'}1.key }).enumerated() { - let stringKey0 = element0.key - let stringValue0 = element0.value - var nestedContainer0 = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("Hi.\(index0.advanced(by: 1))")) - var keyContainer0 = nestedContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("K")) - try keyContainer0.encode(stringKey0, forKey: ClientRuntime.Key("")) - var valueContainer0 = nestedContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("V")) - try valueContainer0.encode(stringValue0, forKey: ClientRuntime.Key("")) - } - } - } - if let mapArg = mapArg { - var mapArgContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("MapArg")) - for (index0, element0) in mapArg.sorted(by: { ${'$'}0.key < ${'$'}1.key }).enumerated() { - let stringKey0 = element0.key - let stringValue0 = element0.value - var entryContainer0 = mapArgContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("entry.\(index0.advanced(by: 1))")) - var keyContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("key")) - try keyContainer0.encode(stringKey0, forKey: ClientRuntime.Key("")) - var valueContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("value")) - try valueContainer0.encode(stringValue0, forKey: ClientRuntime.Key("")) - } - } - if let mapOfLists = mapOfLists { - var mapOfListsContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("MapOfLists")) - for (index0, element0) in mapOfLists.sorted(by: { ${'$'}0.key < ${'$'}1.key }).enumerated() { - let stringKey0 = element0.key - let stringlistValue0 = element0.value - var entryContainer0 = mapOfListsContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("entry.\(index0.advanced(by: 1))")) - var keyContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("key")) - try keyContainer0.encode(stringKey0, forKey: ClientRuntime.Key("")) - var valueContainer1 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("value")) - for (index1, string1) in stringlistValue0.enumerated() { - try valueContainer1.encode(string1, forKey: ClientRuntime.Key("member.\(index1.advanced(by: 1))")) - } - } - } - if let mapWithXmlMemberName = mapWithXmlMemberName { - var mapWithXmlMemberNameContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("MapWithXmlMemberName")) - for (index0, element0) in mapWithXmlMemberName.sorted(by: { ${'$'}0.key < ${'$'}1.key }).enumerated() { - let stringKey0 = element0.key - let stringValue0 = element0.value - var entryContainer0 = mapWithXmlMemberNameContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("entry.\(index0.advanced(by: 1))")) - var keyContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("K")) - try keyContainer0.encode(stringKey0, forKey: ClientRuntime.Key("")) - var valueContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("V")) - try valueContainer0.encode(stringValue0, forKey: ClientRuntime.Key("")) - } - } - if let renamedMapArg = renamedMapArg { - var renamedMapArgContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("Foo")) - for (index0, element0) in renamedMapArg.sorted(by: { ${'$'}0.key < ${'$'}1.key }).enumerated() { - let stringKey0 = element0.key - let stringValue0 = element0.value - var entryContainer0 = renamedMapArgContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("entry.\(index0.advanced(by: 1))")) - var keyContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("key")) - try keyContainer0.encode(stringKey0, forKey: ClientRuntime.Key("")) - var valueContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("value")) - try valueContainer0.encode(stringValue0, forKey: ClientRuntime.Key("")) - } - } - try container.encode("QueryMaps", forKey:ClientRuntime.Key("Action")) - try container.encode("2020-01-08", forKey:ClientRuntime.Key("Version")) + val expectedContents = """ +extension QueryMapsInput: Swift.Encodable { + enum CodingKeys: Swift.String, Swift.CodingKey { + case complexMapArg = "ComplexMapArg" + case flattenedMap = "FlattenedMap" + case flattenedMapWithXmlName = "Hi" + case mapArg = "MapArg" + case mapOfLists = "MapOfLists" + case mapWithXmlMemberName = "MapWithXmlMemberName" + case renamedMapArg = "Foo" + } + + public func encode(to encoder: Swift.Encoder) throws { + var container = encoder.container(keyedBy: ClientRuntime.Key.self) + if let complexMapArg = complexMapArg { + var complexMapArgContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("ComplexMapArg")) + for (index0, element0) in complexMapArg.sorted(by: { ${'$'}0.key < ${'$'}1.key }).enumerated() { + let stringKey0 = element0.key + let greetingstructValue0 = element0.value + var entryContainer0 = complexMapArgContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("entry.\(index0.advanced(by: 1))")) + var keyContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("key")) + try keyContainer0.encode(stringKey0, forKey: ClientRuntime.Key("")) + var valueContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("value")) + try valueContainer0.encode(greetingstructValue0, forKey: ClientRuntime.Key("")) + } + } + if let flattenedMap = flattenedMap { + if !flattenedMap.isEmpty { + for (index0, element0) in flattenedMap.sorted(by: { ${'$'}0.key < ${'$'}1.key }).enumerated() { + let stringKey0 = element0.key + let stringValue0 = element0.value + var nestedContainer0 = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("FlattenedMap.\(index0.advanced(by: 1))")) + var keyContainer0 = nestedContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("key")) + try keyContainer0.encode(stringKey0, forKey: ClientRuntime.Key("")) + var valueContainer0 = nestedContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("value")) + try valueContainer0.encode(stringValue0, forKey: ClientRuntime.Key("")) + } + } + } + if let flattenedMapWithXmlName = flattenedMapWithXmlName { + if !flattenedMapWithXmlName.isEmpty { + for (index0, element0) in flattenedMapWithXmlName.sorted(by: { ${'$'}0.key < ${'$'}1.key }).enumerated() { + let stringKey0 = element0.key + let stringValue0 = element0.value + var nestedContainer0 = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("Hi.\(index0.advanced(by: 1))")) + var keyContainer0 = nestedContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("K")) + try keyContainer0.encode(stringKey0, forKey: ClientRuntime.Key("")) + var valueContainer0 = nestedContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("V")) + try valueContainer0.encode(stringValue0, forKey: ClientRuntime.Key("")) + } + } + } + if let mapArg = mapArg { + var mapArgContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("MapArg")) + for (index0, element0) in mapArg.sorted(by: { ${'$'}0.key < ${'$'}1.key }).enumerated() { + let stringKey0 = element0.key + let stringValue0 = element0.value + var entryContainer0 = mapArgContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("entry.\(index0.advanced(by: 1))")) + var keyContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("key")) + try keyContainer0.encode(stringKey0, forKey: ClientRuntime.Key("")) + var valueContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("value")) + try valueContainer0.encode(stringValue0, forKey: ClientRuntime.Key("")) + } + } + if let mapOfLists = mapOfLists { + var mapOfListsContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("MapOfLists")) + for (index0, element0) in mapOfLists.sorted(by: { ${'$'}0.key < ${'$'}1.key }).enumerated() { + let stringKey0 = element0.key + let stringlistValue0 = element0.value + var entryContainer0 = mapOfListsContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("entry.\(index0.advanced(by: 1))")) + var keyContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("key")) + try keyContainer0.encode(stringKey0, forKey: ClientRuntime.Key("")) + var valueContainer1 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("value")) + for (index1, string1) in stringlistValue0.enumerated() { + try valueContainer1.encode(string1, forKey: ClientRuntime.Key("member.\(index1.advanced(by: 1))")) } } - """.trimIndent() + } + if let mapWithXmlMemberName = mapWithXmlMemberName { + var mapWithXmlMemberNameContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("MapWithXmlMemberName")) + for (index0, element0) in mapWithXmlMemberName.sorted(by: { ${'$'}0.key < ${'$'}1.key }).enumerated() { + let stringKey0 = element0.key + let stringValue0 = element0.value + var entryContainer0 = mapWithXmlMemberNameContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("entry.\(index0.advanced(by: 1))")) + var keyContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("K")) + try keyContainer0.encode(stringKey0, forKey: ClientRuntime.Key("")) + var valueContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("V")) + try valueContainer0.encode(stringValue0, forKey: ClientRuntime.Key("")) + } + } + if let renamedMapArg = renamedMapArg { + var renamedMapArgContainer = container.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("Foo")) + for (index0, element0) in renamedMapArg.sorted(by: { ${'$'}0.key < ${'$'}1.key }).enumerated() { + let stringKey0 = element0.key + let stringValue0 = element0.value + var entryContainer0 = renamedMapArgContainer.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("entry.\(index0.advanced(by: 1))")) + var keyContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("key")) + try keyContainer0.encode(stringKey0, forKey: ClientRuntime.Key("")) + var valueContainer0 = entryContainer0.nestedContainer(keyedBy: ClientRuntime.Key.self, forKey: ClientRuntime.Key("value")) + try valueContainer0.encode(stringValue0, forKey: ClientRuntime.Key("")) + } + } + try container.encode("QueryMaps", forKey:ClientRuntime.Key("Action")) + try container.encode("2020-01-08", forKey:ClientRuntime.Key("Version")) + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/QueryIdempotencyTokenAutoFillGeneratorTests.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/QueryIdempotencyTokenAutoFillGeneratorTests.kt index 3b56924c537..7454da63aec 100644 --- a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/QueryIdempotencyTokenAutoFillGeneratorTests.kt +++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/QueryIdempotencyTokenAutoFillGeneratorTests.kt @@ -20,19 +20,22 @@ class QueryIdempotencyTokenAutoFillGeneratorTests { val context = setupTests("awsquery/query-idempotency-token.smithy", "aws.protocoltests.query#AwsQuery") val contents = getFileContents(context.manifest, "/Example/models/QueryIdempotencyTokenAutoFillInput+Encodable.swift") contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension QueryIdempotencyTokenAutoFillInput: Swift.Encodable { - public func encode(to encoder: Swift.Encoder) throws { - var container = encoder.container(keyedBy: ClientRuntime.Key.self) - if let token = token { - try container.encode(token, forKey: ClientRuntime.Key("token")) - } - try container.encode("QueryIdempotencyTokenAutoFill", forKey:ClientRuntime.Key("Action")) - try container.encode("2020-01-08", forKey:ClientRuntime.Key("Version")) - } - } - """.trimIndent() + val expectedContents = """ +extension QueryIdempotencyTokenAutoFillInput: Swift.Encodable { + enum CodingKeys: Swift.String, Swift.CodingKey { + case token + } + + public func encode(to encoder: Swift.Encoder) throws { + var container = encoder.container(keyedBy: ClientRuntime.Key.self) + if let token = token { + try container.encode(token, forKey: ClientRuntime.Key("token")) + } + try container.encode("QueryIdempotencyTokenAutoFill", forKey:ClientRuntime.Key("Action")) + try container.encode("2020-01-08", forKey:ClientRuntime.Key("Version")) + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/StructDecodeWrappedXMLGeneratorTests.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/StructDecodeWrappedXMLGeneratorTests.kt index 67061e5a8dc..349cc0f2da3 100644 --- a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/StructDecodeWrappedXMLGeneratorTests.kt +++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/StructDecodeWrappedXMLGeneratorTests.kt @@ -17,43 +17,21 @@ class StructDecodeWrappedXMLGeneratorTests { @Test fun `wrapped map decodable`() { val context = setupTests("awsquery/flattened-map.smithy", "aws.protocoltests.query#AwsQuery") - val contents = getFileContents(context.manifest, "/Example/models/FlattenedXmlMapOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/Example/models/FlattenedXmlMapOutput+HttpResponseBinding.swift") val expectedContents = """ - struct FlattenedXmlMapOutputBody: Swift.Equatable { - let myMap: [Swift.String:Swift.String]? - } - - extension FlattenedXmlMapOutputBody: Swift.Decodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case myMap - } - - public init(from decoder: Swift.Decoder) throws { - let topLevelContainer = try decoder.container(keyedBy: ClientRuntime.Key.self) - let containerValues = try topLevelContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: ClientRuntime.Key("FlattenedXmlMapResult")) - if containerValues.contains(.myMap) { - struct KeyVal0{struct key{}; struct value{}} - let myMapWrappedContainer = containerValues.nestedContainerNonThrowable(keyedBy: ClientRuntime.MapEntry.CodingKeys.self, forKey: .myMap) - if myMapWrappedContainer != nil { - let myMapContainer = try containerValues.decodeIfPresent([ClientRuntime.MapKeyValue].self, forKey: .myMap) - var myMapBuffer: [Swift.String:Swift.String]? = nil - if let myMapContainer = myMapContainer { - myMapBuffer = [Swift.String:Swift.String]() - for stringContainer0 in myMapContainer { - myMapBuffer?[stringContainer0.key] = stringContainer0.value - } - } - myMap = myMapBuffer - } else { - myMap = [:] - } - } else { - myMap = nil - } - } - } - """.trimIndent() +extension FlattenedXmlMapOutput { + static var httpBinding: ClientRuntime.HTTPResponseOutputBinding { + { httpResponse, responseDocumentClosure in + let responseReader = try await responseDocumentClosure(httpResponse) + let reader = responseReader["FlattenedXmlMapResult"] + var value = FlattenedXmlMapOutput() + value.myMap = try reader["myMap"].readMapIfPresent(valueReadingClosure: Swift.String.readingClosure, keyNodeInfo: "key", valueNodeInfo: "value", isFlattened: true) + return value + } + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/TimestampGeneratorTests.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/TimestampGeneratorTests.kt index 12be916500e..3813de23668 100644 --- a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/TimestampGeneratorTests.kt +++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/TimestampGeneratorTests.kt @@ -20,25 +20,30 @@ class TimestampGeneratorTests { val context = setupTests("awsquery/query-timestamp.smithy", "aws.protocoltests.query#AwsQuery") val contents = getFileContents(context.manifest, "/Example/models/QueryTimestampsInput+Encodable.swift") contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension QueryTimestampsInput: Swift.Encodable { - public func encode(to encoder: Swift.Encoder) throws { - var container = encoder.container(keyedBy: ClientRuntime.Key.self) - if let epochMember = epochMember { - try container.encodeTimestamp(epochMember, format: .epochSeconds, forKey: ClientRuntime.Key("epochMember")) - } - if let epochTarget = epochTarget { - try container.encodeTimestamp(epochTarget, format: .epochSeconds, forKey: ClientRuntime.Key("epochTarget")) - } - if let normalFormat = normalFormat { - try container.encodeTimestamp(normalFormat, format: .dateTime, forKey: ClientRuntime.Key("normalFormat")) - } - try container.encode("QueryTimestamps", forKey:ClientRuntime.Key("Action")) - try container.encode("2020-01-08", forKey:ClientRuntime.Key("Version")) - } - } - """.trimIndent() + val expectedContents = """ +extension QueryTimestampsInput: Swift.Encodable { + enum CodingKeys: Swift.String, Swift.CodingKey { + case epochMember + case epochTarget + case normalFormat + } + + public func encode(to encoder: Swift.Encoder) throws { + var container = encoder.container(keyedBy: ClientRuntime.Key.self) + if let epochMember = epochMember { + try container.encodeTimestamp(epochMember, format: .epochSeconds, forKey: ClientRuntime.Key("epochMember")) + } + if let epochTarget = epochTarget { + try container.encodeTimestamp(epochTarget, format: .epochSeconds, forKey: ClientRuntime.Key("epochTarget")) + } + if let normalFormat = normalFormat { + try container.encodeTimestamp(normalFormat, format: .dateTime, forKey: ClientRuntime.Key("normalFormat")) + } + try container.encode("QueryTimestamps", forKey:ClientRuntime.Key("Action")) + try container.encode("2020-01-08", forKey:ClientRuntime.Key("Version")) + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/customizations/Route53InvalidBatchErrorIntegrationTests.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/customizations/Route53InvalidBatchErrorIntegrationTests.kt index cf213ef3430..7c884ed0f2f 100644 --- a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/customizations/Route53InvalidBatchErrorIntegrationTests.kt +++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/customizations/Route53InvalidBatchErrorIntegrationTests.kt @@ -14,52 +14,53 @@ class Route53InvalidBatchErrorIntegrationTests { val context = setupTests("route53-invalidbatch.smithy", "com.amazonaws.route53#Route53") val contents = TestContextGenerator.getFileContents(context.manifest, "/Example/models/ChangeResourceRecordSetsOutputError+Customization.swift") contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - struct CustomInvalidBatchError: Decodable { - - struct Message: Decodable { - let message: String - enum CodingKeys: String, CodingKey { - case message = "Message" - } - } - - let requestID: String? - let message: String? - let messages: [String]? - - enum CodingKeys: String, CodingKey { - case message = "Message" - case messages = "Messages" - case requestID = "RequestId" - } - - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.requestID = try container.decode(String.self, forKey: .requestID) - self.message = try container.decodeIfPresent(String.self, forKey: .message) - let messages = try container.decodeIfPresent([Message].self, forKey: .messages) - self.messages = messages?.map(\.message) - } - - static func makeFromHttpResponse(_ httpResponse: ClientRuntime.HttpResponse) async throws -> CustomInvalidBatchError? { - guard let data = try await httpResponse.body.readData() else { - return nil - } - return try? XMLDecoder().decode(CustomInvalidBatchError.self, from: data) - } - } - - extension InvalidChangeBatch { - init(customError: CustomInvalidBatchError, httpResponse: ClientRuntime.HttpResponse) { - self.init(messages: customError.messages) - self.message = customError.message - self.requestID = customError.requestID - self.httpResponse = httpResponse - } - } - """.trimIndent() + val expectedContents = """ +struct CustomInvalidBatchError { + + struct Message { + var message: String? + + init() {} + + static func readingClosure(from reader: SmithyXML.Reader) throws -> Message? { + guard reader.content != nil else { return nil } + var value = Message() + value.message = try reader["Message"].readIfPresent() + return value + } + } + + var requestID: String? + var message: String? + var messages: [String]? + + init() {} + + static func readingClosure(from reader: SmithyXML.Reader) throws -> CustomInvalidBatchError? { + guard reader.content != nil else { return nil } + var value = CustomInvalidBatchError() + value.requestID = try reader["RequestId"].readIfPresent() + value.message = try reader["Message"].readIfPresent() + value.messages = try reader["Messages"].readListIfPresent(memberReadingClosure: Message.readingClosure(from:), memberNodeInfo: "Message", isFlattened: false)?.compactMap(\.message) + return value + } + + static func makeFromHttpResponse(_ httpResponse: ClientRuntime.HttpResponse) async throws -> CustomInvalidBatchError? { + guard let data = try await httpResponse.body.readData() else { return nil } + let reader = try SmithyXML.Reader.from(data: data) + return try Self.readingClosure(from: reader) + } +} + +extension InvalidChangeBatch { + init(customError: CustomInvalidBatchError, httpResponse: ClientRuntime.HttpResponse) { + self.init(messages: customError.messages) + self.message = customError.message + self.requestID = customError.requestID + self.httpResponse = httpResponse + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } @@ -68,24 +69,28 @@ class Route53InvalidBatchErrorIntegrationTests { val context = setupTests("route53-invalidbatch.smithy", "com.amazonaws.route53#Route53") val contents = TestContextGenerator.getFileContents(context.manifest, "/Example/models/ChangeResourceRecordSetsOutputError+HttpResponseErrorBinding.swift") contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - enum ChangeResourceRecordSetsOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - if let customBatchError = try await CustomInvalidBatchError.makeFromHttpResponse(httpResponse) { - return InvalidChangeBatch( - customError: customBatchError, - httpResponse: httpResponse - ) - } - let restXMLError = try await AWSClientRuntime.RestXMLError(httpResponse: httpResponse) - switch restXMLError.errorCode { - case "InvalidChangeBatch": return try await InvalidChangeBatch(httpResponse: httpResponse, decoder: decoder, message: restXMLError.message, requestID: restXMLError.requestId) - default: return try await AWSClientRuntime.UnknownAWSHTTPServiceError.makeError(httpResponse: httpResponse, message: restXMLError.message, requestID: restXMLError.requestId, typeName: restXMLError.errorCode) - } - } + val expectedContents = """ +enum ChangeResourceRecordSetsOutputError { + + static var httpBinding: ClientRuntime.HTTPResponseErrorBinding { + { httpResponse, responseDocumentClosure in + if let customBatchError = try await CustomInvalidBatchError.makeFromHttpResponse(httpResponse) { + return InvalidChangeBatch( + customError: customBatchError, + httpResponse: httpResponse + ) + } + let responseReader = try await responseDocumentClosure(httpResponse) + let errorBodyReader = AWSClientRuntime.RestXMLError.errorBodyReader(responseReader: responseReader, noErrorWrapping: false) + let restXMLError = try AWSClientRuntime.RestXMLError(responseReader: responseReader, noErrorWrapping: false) + switch restXMLError.code { + case "InvalidChangeBatch": return try await InvalidChangeBatch.responseErrorBinding(httpResponse: httpResponse, reader: errorBodyReader, message: restXMLError.message, requestID: restXMLError.requestID) + default: return try await AWSClientRuntime.UnknownAWSHTTPServiceError.makeError(httpResponse: httpResponse, message: restXMLError.message, requestID: restXMLError.requestID, typeName: restXMLError.code) } - """.trimIndent() + } + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/Ec2QueryHttpResponseBindingErrorGeneratorTests.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/Ec2QueryHttpResponseBindingErrorGeneratorTests.kt index 52d14b16252..78c783709fa 100644 --- a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/Ec2QueryHttpResponseBindingErrorGeneratorTests.kt +++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/ec2query/Ec2QueryHttpResponseBindingErrorGeneratorTests.kt @@ -19,21 +19,27 @@ class Ec2QueryHttpResponseBindingErrorGeneratorTests { val context = setupTests("ec2query/query-error.smithy", "aws.protocoltests.ec2#AwsEc2") val contents = TestContextGenerator.getFileContents(context.manifest, "/Example/models/GreetingWithErrorsOutputError+HttpResponseErrorBinding.swift") contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - enum GreetingWithErrorsOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let ec2QueryError = try await Ec2QueryError(httpResponse: httpResponse) - let serviceError = try await EC2ProtocolClientTypes.makeServiceError(httpResponse, decoder, ec2QueryError) - if let error = serviceError { return error } - switch ec2QueryError.errorCode { - case "ComplexError": return try await ComplexError(httpResponse: httpResponse, decoder: decoder, message: ec2QueryError.message, requestID: ec2QueryError.requestId) - case "InvalidGreeting": return try await InvalidGreeting(httpResponse: httpResponse, decoder: decoder, message: ec2QueryError.message, requestID: ec2QueryError.requestId) - default: return try await AWSClientRuntime.UnknownAWSHTTPServiceError.makeError(httpResponse: httpResponse, message: ec2QueryError.message, requestID: ec2QueryError.requestId, typeName: ec2QueryError.errorCode) - } - } + val expectedContents = """ +enum GreetingWithErrorsOutputError { + + static var httpBinding: ClientRuntime.HTTPResponseErrorBinding { + { httpResponse, responseDocumentClosure in + let serviceError = try await EC2ProtocolClientTypes.makeServiceError(httpResponse, decoder, ec2QueryError) + if let error = serviceError { return error } + let responseReader = try await responseDocumentClosure(httpResponse) + let reader = responseReader["Errors"]["Error"] + let requestID: String? = try responseReader["RequestId"].readIfPresent() + let errorCode: String? = try reader["Code"].readIfPresent() + let message: String? = try reader["Message"].readIfPresent() + switch errorCode { + case "ComplexError": return try await ComplexError.responseErrorBinding(httpResponse: httpResponse, reader: reader, message: message, requestID: requestID) + case "InvalidGreeting": return try await InvalidGreeting.responseErrorBinding(httpResponse: httpResponse, reader: reader, message: message, requestID: requestID) + default: return try await AWSClientRuntime.UnknownAWSHTTPServiceError.makeError(httpResponse: httpResponse, message: message, requestID: requestID, typeName: errorCode) } - """.trimIndent() + } + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } @@ -42,24 +48,20 @@ class Ec2QueryHttpResponseBindingErrorGeneratorTests { val context = setupTests("ec2query/query-error.smithy", "aws.protocoltests.ec2#AwsEc2") val contents = TestContextGenerator.getFileContents(context.manifest, "/Example/models/ComplexError+Init.swift") contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension ComplexError { - 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: AWSClientRuntime.Ec2NarrowedResponse = try responseDecoder.decode(responseBody: data) - self.properties.nested = output.errors.error.nested - self.properties.topLevel = output.errors.error.topLevel - } else { - self.properties.nested = nil - self.properties.topLevel = nil - } - self.httpResponse = httpResponse - self.requestID = requestID - self.message = message - } - } - """.trimIndent() + val expectedContents = """ +extension ComplexError { + + static func responseErrorBinding(httpResponse: ClientRuntime.HttpResponse, reader: SmithyXML.Reader, message: Swift.String? = nil, requestID: Swift.String? = nil) async throws -> Swift.Error { + var value = ComplexError() + value.properties.nested = try reader["Nested"].readIfPresent(readingClosure: EC2ProtocolClientTypes.ComplexNestedErrorData.readingClosure) + value.properties.topLevel = try reader["TopLevel"].readIfPresent() + value.httpResponse = httpResponse + value.requestID = requestID + value.message = message + return value + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/AWSRestXMLHttpResponseBindingErrorGeneratorTests.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/AWSRestXMLHttpResponseBindingErrorGeneratorTests.kt index da6d9bf363a..4770f178888 100644 --- a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/AWSRestXMLHttpResponseBindingErrorGeneratorTests.kt +++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/AWSRestXMLHttpResponseBindingErrorGeneratorTests.kt @@ -20,21 +20,26 @@ class AWSRestXMLHttpResponseBindingErrorGeneratorTests { val context = setupTests("restxml/xml-errors.smithy", "aws.protocoltests.restxml#RestXml") val contents = getFileContents(context.manifest, "/Example/models/GreetingWithErrorsOutputError+HttpResponseErrorBinding.swift") contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - enum GreetingWithErrorsOutputError: ClientRuntime.HttpResponseErrorBinding { - static func makeError(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil) async throws -> Swift.Error { - let restXMLError = try await AWSClientRuntime.RestXMLError(httpResponse: httpResponse) - let serviceError = try await RestXmlerrorsClientTypes.makeServiceError(httpResponse, decoder, restXMLError) - if let error = serviceError { return error } - switch restXMLError.errorCode { - case "ComplexXMLError": return try await ComplexXMLError(httpResponse: httpResponse, decoder: decoder, message: restXMLError.message, requestID: restXMLError.requestId) - case "InvalidGreeting": return try await InvalidGreeting(httpResponse: httpResponse, decoder: decoder, message: restXMLError.message, requestID: restXMLError.requestId) - default: return try await AWSClientRuntime.UnknownAWSHTTPServiceError.makeError(httpResponse: httpResponse, message: restXMLError.message, requestID: restXMLError.requestId, typeName: restXMLError.errorCode) - } - } + val expectedContents = """ +enum GreetingWithErrorsOutputError { + + static var httpBinding: ClientRuntime.HTTPResponseErrorBinding { + { httpResponse, responseDocumentClosure in + let responseReader = try await responseDocumentClosure(httpResponse) + let errorBodyReader = AWSClientRuntime.RestXMLError.errorBodyReader(responseReader: responseReader, noErrorWrapping: false) + if let serviceError = try await ClientRuntime.RestXmlerrorsClientTypes.responseServiceErrorBinding(httpResponse, errorBodyReader) { + return serviceError } - """.trimIndent() + let restXMLError = try AWSClientRuntime.RestXMLError(responseReader: responseReader, noErrorWrapping: false) + switch restXMLError.code { + case "ComplexXMLError": return try await ComplexXMLError.responseErrorBinding(httpResponse: httpResponse, reader: errorBodyReader, message: restXMLError.message, requestID: restXMLError.requestID) + case "InvalidGreeting": return try await InvalidGreeting.responseErrorBinding(httpResponse: httpResponse, reader: errorBodyReader, message: restXMLError.message, requestID: restXMLError.requestID) + default: return try await AWSClientRuntime.UnknownAWSHTTPServiceError.makeError(httpResponse: httpResponse, message: restXMLError.message, requestID: restXMLError.requestID, typeName: restXMLError.code) + } + } + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } @Test @@ -42,29 +47,23 @@ class AWSRestXMLHttpResponseBindingErrorGeneratorTests { val context = setupTests("restxml/xml-errors.smithy", "aws.protocoltests.restxml#RestXml") val contents = getFileContents(context.manifest, "/Example/models/ComplexXMLError+Init.swift") contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension ComplexXMLError { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil, message: Swift.String? = nil, requestID: Swift.String? = nil) async throws { - if let headerHeaderValue = httpResponse.headers.value(for: "X-Header") { - self.properties.header = headerHeaderValue - } else { - self.properties.header = nil - } - if let data = try await httpResponse.body.readData(), let responseDecoder = decoder { - let output: AWSClientRuntime.ErrorResponseContainer = try responseDecoder.decode(responseBody: data) - self.properties.nested = output.error.nested - self.properties.topLevel = output.error.topLevel - } else { - self.properties.nested = nil - self.properties.topLevel = nil - } - self.httpResponse = httpResponse - self.requestID = requestID - self.message = message - } - } - """.trimIndent() + val expectedContents = """ +extension ComplexXMLError { + + static func responseErrorBinding(httpResponse: ClientRuntime.HttpResponse, reader: SmithyXML.Reader, message: Swift.String? = nil, requestID: Swift.String? = nil) async throws -> Swift.Error { + var value = ComplexXMLError() + if let headerHeaderValue = httpResponse.headers.value(for: "X-Header") { + value.properties.header = headerHeaderValue + } + value.properties.nested = try reader["Nested"].readIfPresent(readingClosure: RestXmlerrorsClientTypes.ComplexXMLNestedErrorData.readingClosure) + value.properties.topLevel = try reader["TopLevel"].readIfPresent() + value.httpResponse = httpResponse + value.requestID = requestID + value.message = message + return value + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } @Test @@ -110,29 +109,23 @@ class AWSRestXMLHttpResponseBindingErrorGeneratorTests { val context = setupTests("restxml/xml-errors-noerrorwrapping.smithy", "aws.protocoltests.restxml#RestXml") val contents = getFileContents(context.manifest, "/Example/models/ComplexXMLErrorNoErrorWrapping+Init.swift") contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension ComplexXMLErrorNoErrorWrapping { - public init(httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil, message: Swift.String? = nil, requestID: Swift.String? = nil) async throws { - if let headerHeaderValue = httpResponse.headers.value(for: "X-Header") { - self.properties.header = headerHeaderValue - } else { - self.properties.header = nil - } - if let data = try await httpResponse.body.readData(), let responseDecoder = decoder { - let output: ComplexXMLErrorNoErrorWrappingBody = try responseDecoder.decode(responseBody: data) - self.properties.nested = output.nested - self.properties.topLevel = output.topLevel - } else { - self.properties.nested = nil - self.properties.topLevel = nil - } - self.httpResponse = httpResponse - self.requestID = requestID - self.message = message - } - } - """.trimIndent() + val expectedContents = """ +extension ComplexXMLErrorNoErrorWrapping { + + static func responseErrorBinding(httpResponse: ClientRuntime.HttpResponse, reader: SmithyXML.Reader, message: Swift.String? = nil, requestID: Swift.String? = nil) async throws -> Swift.Error { + var value = ComplexXMLErrorNoErrorWrapping() + if let headerHeaderValue = httpResponse.headers.value(for: "X-Header") { + value.properties.header = headerHeaderValue + } + value.properties.nested = try reader["Nested"].readIfPresent(readingClosure: RestXmlerrorsClientTypes.ComplexXMLNestedErrorData.readingClosure) + value.properties.topLevel = try reader["TopLevel"].readIfPresent() + value.httpResponse = httpResponse + value.requestID = requestID + value.message = message + return value + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } @@ -141,17 +134,16 @@ class AWSRestXMLHttpResponseBindingErrorGeneratorTests { val context = setupTests("restxml/xml-errors.smithy", "aws.protocoltests.restxml#RestXml") val contents = getFileContents(context.manifest, "/Example/models/RestXml+ServiceErrorHelperMethod.swift") contents.shouldSyntacticSanityCheck() - val expectedContents = - """ - extension RestXmlerrorsClientTypes { - static func makeServiceError(_ httpResponse: ClientRuntime.HttpResponse, _ decoder: ClientRuntime.ResponseDecoder? = nil, _ error: AWSClientRuntime.RestXMLError) async throws -> Swift.Error? { - switch error.errorCode { - case "ExampleServiceError": return try await ExampleServiceError(httpResponse: httpResponse, decoder: decoder, message: error.message, requestID: error.requestId) - default: return nil - } - } - } - """.trimIndent() + val expectedContents = """ +extension RestXmlerrorsClientTypes { + static func responseServiceErrorBinding(httpResponse: ClientRuntime.HttpResponse, reader: SmithyXML.Reader) async throws -> Swift.Error? { + switch error.errorCode { + case "ExampleServiceError": return try await ExampleServiceError(httpResponse: httpResponse, reader: reader, message: error.message, requestID: error.requestId) + default: return nil + } + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/serde/S3UnwrappedXMLOutputTraitTests.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/serde/S3UnwrappedXMLOutputTraitTests.kt index 623d4def08a..44ed7bf21e4 100644 --- a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/serde/S3UnwrappedXMLOutputTraitTests.kt +++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/restxml/serde/S3UnwrappedXMLOutputTraitTests.kt @@ -12,23 +12,22 @@ class S3UnwrappedXMLOutputTraitTests { @Test fun `001 S3UnwrappedXmlOutputTrait`() { val context = setupTests("restxml/serde/s3unwrappedxmloutput.smithy", "aws.protocoltests.restxml#RestXml") - val contents = getFileContents(context.manifest, "/Example/models/GetBucketLocationOutputBody+Decodable.swift") + val contents = getFileContents(context.manifest, "/Example/models/GetBucketLocationOutput+HttpResponseBinding.swift") - val expectedContents = - """ - extension GetBucketLocationOutputBody: Swift.Decodable { - enum CodingKeys: Swift.String, Swift.CodingKey { - case locationConstraint = "LocationConstraint" - } - - public init(from decoder: Swift.Decoder) throws { - var containerValues = try decoder.unkeyedContainer() - let locationConstraintDecoded = try containerValues.decodeIfPresent(S3ClientTypes.BucketLocationConstraint.self) - locationConstraint = locationConstraintDecoded - } - } - """.trimIndent() + val expectedContents = """ +extension GetBucketLocationOutput { + static var httpBinding: ClientRuntime.HTTPResponseOutputBinding { + { httpResponse, responseDocumentClosure in + let responseReader = try await responseDocumentClosure(httpResponse) + let reader = responseReader.unwrap() + var value = GetBucketLocationOutput() + value.locationConstraint = try reader["LocationConstraint"].readIfPresent() + return value + } + } +} +""" contents.shouldContainOnlyOnce(expectedContents) } diff --git a/sdk.properties b/sdk.properties index be5af62ff19..af54bddc8bb 100644 --- a/sdk.properties +++ b/sdk.properties @@ -1 +1,10 @@ # No excluded models + +# One service from each AWS protocol: +# - S3 is RestXML +# - STS is AwsQuery +# - EC2 is Ec2Query +# - Bedrock-runtime is RestJSON1 +# - DynamoDB is awsJSON 1.0 +# - Cognito-identity is awsJSON 1.1 +# onlyIncludeModels=s3.2006-03-01,sts.2011-06-15,ec2.2016-11-15,bedrock-runtime.2023-09-30,dynamodb.2012-08-10,cognito-identity.2014-06-30