From 6bbd2874e97c39588eab02173fef58d3b35e2d72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Fri, 20 Dec 2024 10:25:14 +0100 Subject: [PATCH 1/6] add an encodable version of the APIGatewayResponse --- .../APIGateway+V2+Encodable.swift | 50 +++++++++++++++++ .../APIGateway+V2+EncodableTests.swift | 53 +++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 Sources/AWSLambdaEvents/APIGateway+V2+Encodable.swift create mode 100644 Tests/AWSLambdaEventsTests/APIGateway+V2+EncodableTests.swift diff --git a/Sources/AWSLambdaEvents/APIGateway+V2+Encodable.swift b/Sources/AWSLambdaEvents/APIGateway+V2+Encodable.swift new file mode 100644 index 0000000..c7bd0e0 --- /dev/null +++ b/Sources/AWSLambdaEvents/APIGateway+V2+Encodable.swift @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftAWSLambdaRuntime open source project +// +// Copyright (c) 2017-2022 Apple Inc. and the SwiftAWSLambdaRuntime project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +#if canImport(FoundationEssentials) +import class FoundationEssentials.JSONEncoder +import class FoundationEssentials.Data +#else +import class Foundation.JSONEncoder +import struct Foundation.Data +#endif + +import HTTPTypes + +public enum APIGatewayResponseError: Error { + case failedToEncodeBody(Error) +} + +extension APIGatewayV2Response { + + public init ( + statusCode: HTTPResponse.Status, + headers: HTTPHeaders? = nil, + body: Input, + isBase64Encoded: Bool? = nil, + cookies: [String]? = nil + ) throws { + let encodedBody: Data + do { + encodedBody = try JSONEncoder().encode(body) + } catch { + throw APIGatewayResponseError.failedToEncodeBody(error) + } + self.statusCode = statusCode + self.headers = headers + self.body = String(data: encodedBody, encoding: .utf8) ?? "" + self.isBase64Encoded = isBase64Encoded + self.cookies = cookies + } +} diff --git a/Tests/AWSLambdaEventsTests/APIGateway+V2+EncodableTests.swift b/Tests/AWSLambdaEventsTests/APIGateway+V2+EncodableTests.swift new file mode 100644 index 0000000..1ebf00e --- /dev/null +++ b/Tests/AWSLambdaEventsTests/APIGateway+V2+EncodableTests.swift @@ -0,0 +1,53 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftAWSLambdaRuntime open source project +// +// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Testing + +import Foundation + +@testable import AWSLambdaEvents + +struct APIGatewayV2EncodableResponseTests { + + + // MARK: Encoding + struct BusinessResponse: Codable, Equatable { + let message: String + let code: Int + } + + @Test + func testResponseEncoding() throws { + + // given + let businessResponse = BusinessResponse(message: "Hello World", code: 200) + + var response: APIGatewayV2Response? = nil + #expect(throws: Never.self) { + try response = APIGatewayV2Response(statusCode: .ok, body: businessResponse) + } + try #require(response?.body != nil) + + // when + let body = response?.body?.data(using: .utf8) + try #require(body != nil) + + #expect(throws: Never.self) { + let encodedBody = try JSONDecoder().decode(BusinessResponse.self, from: body!) + + // then + #expect(encodedBody == businessResponse) + } + } +} From 5781034fcbc5764e54605e4be6434112867342d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Fri, 20 Dec 2024 10:57:14 +0100 Subject: [PATCH 2/6] apply swift format --- .../APIGateway+V2+Encodable.swift | 6 ++--- .../APIGateway+V2+EncodableTests.swift | 22 +++++++++---------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/Sources/AWSLambdaEvents/APIGateway+V2+Encodable.swift b/Sources/AWSLambdaEvents/APIGateway+V2+Encodable.swift index c7bd0e0..0402b7e 100644 --- a/Sources/AWSLambdaEvents/APIGateway+V2+Encodable.swift +++ b/Sources/AWSLambdaEvents/APIGateway+V2+Encodable.swift @@ -12,6 +12,8 @@ // //===----------------------------------------------------------------------===// +import HTTPTypes + #if canImport(FoundationEssentials) import class FoundationEssentials.JSONEncoder import class FoundationEssentials.Data @@ -20,15 +22,13 @@ import class Foundation.JSONEncoder import struct Foundation.Data #endif -import HTTPTypes - public enum APIGatewayResponseError: Error { case failedToEncodeBody(Error) } extension APIGatewayV2Response { - public init ( + public init( statusCode: HTTPResponse.Status, headers: HTTPHeaders? = nil, body: Input, diff --git a/Tests/AWSLambdaEventsTests/APIGateway+V2+EncodableTests.swift b/Tests/AWSLambdaEventsTests/APIGateway+V2+EncodableTests.swift index 1ebf00e..2dcf864 100644 --- a/Tests/AWSLambdaEventsTests/APIGateway+V2+EncodableTests.swift +++ b/Tests/AWSLambdaEventsTests/APIGateway+V2+EncodableTests.swift @@ -12,40 +12,38 @@ // //===----------------------------------------------------------------------===// +import Foundation import Testing -import Foundation - @testable import AWSLambdaEvents struct APIGatewayV2EncodableResponseTests { - // MARK: Encoding struct BusinessResponse: Codable, Equatable { let message: String let code: Int } - @Test + @Test func testResponseEncoding() throws { - // given + // given let businessResponse = BusinessResponse(message: "Hello World", code: 200) - + var response: APIGatewayV2Response? = nil - #expect(throws: Never.self) { + #expect(throws: Never.self) { try response = APIGatewayV2Response(statusCode: .ok, body: businessResponse) - } + } try #require(response?.body != nil) - // when - let body = response?.body?.data(using: .utf8) + // when + let body = response?.body?.data(using: .utf8) try #require(body != nil) - #expect(throws: Never.self) { + #expect(throws: Never.self) { let encodedBody = try JSONDecoder().decode(BusinessResponse.self, from: body!) - + // then #expect(encodedBody == businessResponse) } From 25ea4465f7ff516363590cd2c37066da1687db37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Fri, 20 Dec 2024 11:24:06 +0100 Subject: [PATCH 3/6] fix build on Linux --- Sources/AWSLambdaEvents/APIGateway+V2+Encodable.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/AWSLambdaEvents/APIGateway+V2+Encodable.swift b/Sources/AWSLambdaEvents/APIGateway+V2+Encodable.swift index 0402b7e..5b8a856 100644 --- a/Sources/AWSLambdaEvents/APIGateway+V2+Encodable.swift +++ b/Sources/AWSLambdaEvents/APIGateway+V2+Encodable.swift @@ -16,7 +16,7 @@ import HTTPTypes #if canImport(FoundationEssentials) import class FoundationEssentials.JSONEncoder -import class FoundationEssentials.Data +import struct FoundationEssentials.Data #else import class Foundation.JSONEncoder import struct Foundation.Data From 6a3aa0f43692eab097f597bda5c884129221510f Mon Sep 17 00:00:00 2001 From: Sebastien Stormacq Date: Fri, 20 Dec 2024 16:04:28 +0100 Subject: [PATCH 4/6] simplify code and add the same initializer on APIGatewayResponse --- ...dable.swift => APIGateway+Encodable.swift} | 44 +++++++++++++------ ....swift => APIGateway+EncodableTests.swift} | 28 +++++++++++- .../APIGatewayTests.swift | 4 +- 3 files changed, 58 insertions(+), 18 deletions(-) rename Sources/AWSLambdaEvents/{APIGateway+V2+Encodable.swift => APIGateway+Encodable.swift} (53%) rename Tests/AWSLambdaEventsTests/{APIGateway+V2+EncodableTests.swift => APIGateway+EncodableTests.swift} (65%) diff --git a/Sources/AWSLambdaEvents/APIGateway+V2+Encodable.swift b/Sources/AWSLambdaEvents/APIGateway+Encodable.swift similarity index 53% rename from Sources/AWSLambdaEvents/APIGateway+V2+Encodable.swift rename to Sources/AWSLambdaEvents/APIGateway+Encodable.swift index 5b8a856..8f06d07 100644 --- a/Sources/AWSLambdaEvents/APIGateway+V2+Encodable.swift +++ b/Sources/AWSLambdaEvents/APIGateway+Encodable.swift @@ -22,8 +22,29 @@ import class Foundation.JSONEncoder import struct Foundation.Data #endif -public enum APIGatewayResponseError: Error { - case failedToEncodeBody(Error) +extension Encodable { + fileprivate func string() throws -> String { + let encoded = try JSONEncoder().encode(self) + return String(decoding: encoded, as: UTF8.self) + } +} + +extension APIGatewayResponse { + + public init( + statusCode: HTTPResponse.Status, + headers: HTTPHeaders? = nil, + multiValueHeaders: HTTPMultiValueHeaders? = nil, + body: Input + ) throws { + self.init( + statusCode: statusCode, + headers: headers, + multiValueHeaders: multiValueHeaders, + body: try body.string(), + isBase64Encoded: nil + ) + } } extension APIGatewayV2Response { @@ -32,19 +53,14 @@ extension APIGatewayV2Response { statusCode: HTTPResponse.Status, headers: HTTPHeaders? = nil, body: Input, - isBase64Encoded: Bool? = nil, cookies: [String]? = nil ) throws { - let encodedBody: Data - do { - encodedBody = try JSONEncoder().encode(body) - } catch { - throw APIGatewayResponseError.failedToEncodeBody(error) - } - self.statusCode = statusCode - self.headers = headers - self.body = String(data: encodedBody, encoding: .utf8) ?? "" - self.isBase64Encoded = isBase64Encoded - self.cookies = cookies + self.init( + statusCode: statusCode, + headers: headers, + body: try body.string(), + isBase64Encoded: nil, + cookies: cookies + ) } } diff --git a/Tests/AWSLambdaEventsTests/APIGateway+V2+EncodableTests.swift b/Tests/AWSLambdaEventsTests/APIGateway+EncodableTests.swift similarity index 65% rename from Tests/AWSLambdaEventsTests/APIGateway+V2+EncodableTests.swift rename to Tests/AWSLambdaEventsTests/APIGateway+EncodableTests.swift index 2dcf864..d10f22b 100644 --- a/Tests/AWSLambdaEventsTests/APIGateway+V2+EncodableTests.swift +++ b/Tests/AWSLambdaEventsTests/APIGateway+EncodableTests.swift @@ -17,7 +17,7 @@ import Testing @testable import AWSLambdaEvents -struct APIGatewayV2EncodableResponseTests { +struct APIGatewayEncodableResponseTests { // MARK: Encoding struct BusinessResponse: Codable, Equatable { @@ -26,7 +26,7 @@ struct APIGatewayV2EncodableResponseTests { } @Test - func testResponseEncoding() throws { + func testResponseEncodingV2() throws { // given let businessResponse = BusinessResponse(message: "Hello World", code: 200) @@ -48,4 +48,28 @@ struct APIGatewayV2EncodableResponseTests { #expect(encodedBody == businessResponse) } } + + @Test + func testResponseEncoding() throws { + + // given + let businessResponse = BusinessResponse(message: "Hello World", code: 200) + + var response: APIGatewayResponse? = nil + #expect(throws: Never.self) { + try response = APIGatewayResponse(statusCode: .ok, body: businessResponse) + } + try #require(response?.body != nil) + + // when + let body = response?.body?.data(using: .utf8) + try #require(body != nil) + + #expect(throws: Never.self) { + let encodedBody = try JSONDecoder().decode(BusinessResponse.self, from: body!) + + // then + #expect(encodedBody == businessResponse) + } + } } diff --git a/Tests/AWSLambdaEventsTests/APIGatewayTests.swift b/Tests/AWSLambdaEventsTests/APIGatewayTests.swift index 2f55dfe..a7fd405 100644 --- a/Tests/AWSLambdaEventsTests/APIGatewayTests.swift +++ b/Tests/AWSLambdaEventsTests/APIGatewayTests.swift @@ -95,8 +95,8 @@ class APIGatewayTests: XCTestCase { let isBase64Encoded: Bool? } - func testResponseEncoding() { - let resp = APIGatewayResponse( + func testResponseEncoding() throws { + let resp = try APIGatewayResponse( statusCode: .ok, headers: ["Server": "Test"], body: "abc123" From 7424005bcee927975fb65046a24772fe00f3e2a3 Mon Sep 17 00:00:00 2001 From: Sebastien Stormacq Date: Fri, 20 Dec 2024 16:25:57 +0100 Subject: [PATCH 5/6] change the order of the args to avoid breaking the existing API --- Sources/AWSLambdaEvents/APIGateway+Encodable.swift | 6 +++--- Tests/AWSLambdaEventsTests/APIGateway+EncodableTests.swift | 4 ++-- Tests/AWSLambdaEventsTests/APIGatewayTests.swift | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Sources/AWSLambdaEvents/APIGateway+Encodable.swift b/Sources/AWSLambdaEvents/APIGateway+Encodable.swift index 8f06d07..4d084b3 100644 --- a/Sources/AWSLambdaEvents/APIGateway+Encodable.swift +++ b/Sources/AWSLambdaEvents/APIGateway+Encodable.swift @@ -32,10 +32,10 @@ extension Encodable { extension APIGatewayResponse { public init( + body: Input, statusCode: HTTPResponse.Status, headers: HTTPHeaders? = nil, - multiValueHeaders: HTTPMultiValueHeaders? = nil, - body: Input + multiValueHeaders: HTTPMultiValueHeaders? = nil ) throws { self.init( statusCode: statusCode, @@ -50,9 +50,9 @@ extension APIGatewayResponse { extension APIGatewayV2Response { public init( + body: Input, statusCode: HTTPResponse.Status, headers: HTTPHeaders? = nil, - body: Input, cookies: [String]? = nil ) throws { self.init( diff --git a/Tests/AWSLambdaEventsTests/APIGateway+EncodableTests.swift b/Tests/AWSLambdaEventsTests/APIGateway+EncodableTests.swift index d10f22b..7f5d3e9 100644 --- a/Tests/AWSLambdaEventsTests/APIGateway+EncodableTests.swift +++ b/Tests/AWSLambdaEventsTests/APIGateway+EncodableTests.swift @@ -33,7 +33,7 @@ struct APIGatewayEncodableResponseTests { var response: APIGatewayV2Response? = nil #expect(throws: Never.self) { - try response = APIGatewayV2Response(statusCode: .ok, body: businessResponse) + try response = APIGatewayV2Response(body: businessResponse, statusCode: .ok) } try #require(response?.body != nil) @@ -57,7 +57,7 @@ struct APIGatewayEncodableResponseTests { var response: APIGatewayResponse? = nil #expect(throws: Never.self) { - try response = APIGatewayResponse(statusCode: .ok, body: businessResponse) + try response = APIGatewayResponse(body: businessResponse, statusCode: .ok) } try #require(response?.body != nil) diff --git a/Tests/AWSLambdaEventsTests/APIGatewayTests.swift b/Tests/AWSLambdaEventsTests/APIGatewayTests.swift index a7fd405..2f55dfe 100644 --- a/Tests/AWSLambdaEventsTests/APIGatewayTests.swift +++ b/Tests/AWSLambdaEventsTests/APIGatewayTests.swift @@ -95,8 +95,8 @@ class APIGatewayTests: XCTestCase { let isBase64Encoded: Bool? } - func testResponseEncoding() throws { - let resp = try APIGatewayResponse( + func testResponseEncoding() { + let resp = APIGatewayResponse( statusCode: .ok, headers: ["Server": "Test"], body: "abc123" From feb352be596ee8da32447636a230065107c0335e Mon Sep 17 00:00:00 2001 From: Sebastien Stormacq Date: Sat, 21 Dec 2024 10:18:38 +0100 Subject: [PATCH 6/6] change name of arguments to avoid overriding other init() methods --- Sources/AWSLambdaEvents/APIGateway+Encodable.swift | 10 +++++----- .../APIGateway+EncodableTests.swift | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Sources/AWSLambdaEvents/APIGateway+Encodable.swift b/Sources/AWSLambdaEvents/APIGateway+Encodable.swift index 4d084b3..b9d7893 100644 --- a/Sources/AWSLambdaEvents/APIGateway+Encodable.swift +++ b/Sources/AWSLambdaEvents/APIGateway+Encodable.swift @@ -32,16 +32,16 @@ extension Encodable { extension APIGatewayResponse { public init( - body: Input, statusCode: HTTPResponse.Status, headers: HTTPHeaders? = nil, - multiValueHeaders: HTTPMultiValueHeaders? = nil + multiValueHeaders: HTTPMultiValueHeaders? = nil, + encodableBody: Input ) throws { self.init( statusCode: statusCode, headers: headers, multiValueHeaders: multiValueHeaders, - body: try body.string(), + body: try encodableBody.string(), isBase64Encoded: nil ) } @@ -50,15 +50,15 @@ extension APIGatewayResponse { extension APIGatewayV2Response { public init( - body: Input, statusCode: HTTPResponse.Status, headers: HTTPHeaders? = nil, + encodableBody: Input, cookies: [String]? = nil ) throws { self.init( statusCode: statusCode, headers: headers, - body: try body.string(), + body: try encodableBody.string(), isBase64Encoded: nil, cookies: cookies ) diff --git a/Tests/AWSLambdaEventsTests/APIGateway+EncodableTests.swift b/Tests/AWSLambdaEventsTests/APIGateway+EncodableTests.swift index 7f5d3e9..02025b4 100644 --- a/Tests/AWSLambdaEventsTests/APIGateway+EncodableTests.swift +++ b/Tests/AWSLambdaEventsTests/APIGateway+EncodableTests.swift @@ -33,7 +33,7 @@ struct APIGatewayEncodableResponseTests { var response: APIGatewayV2Response? = nil #expect(throws: Never.self) { - try response = APIGatewayV2Response(body: businessResponse, statusCode: .ok) + try response = APIGatewayV2Response(statusCode: .ok, encodableBody: businessResponse) } try #require(response?.body != nil) @@ -57,7 +57,7 @@ struct APIGatewayEncodableResponseTests { var response: APIGatewayResponse? = nil #expect(throws: Never.self) { - try response = APIGatewayResponse(body: businessResponse, statusCode: .ok) + try response = APIGatewayResponse(statusCode: .ok, encodableBody: businessResponse) } try #require(response?.body != nil)