Skip to content

Commit

Permalink
Add an encodable version of the APIGatewayResponse (#86)
Browse files Browse the repository at this point in the history
Add a convenience initializer to the APIGatewayResponse v2 that accepts
an Encodable object

### Motivation:

Most Lambda developers will return a JSON object when exposing their
function through the API Gateway. The current initializer only accepts a
`String` body as per the AWS message definition.

This obliges all developers to write two lines of code + error handling
to encode their object and transform the `Data` to a string.

### Modifications:

Add a new initializer that accepts a `body` as `Encodable`

```swift
public init<Input: Encodable> (
        statusCode: HTTPResponse.Status,
        headers: HTTPHeaders? = nil,
        body: Input,
        isBase64Encoded: Bool? = nil,
        cookies: [String]? = nil
) throws { ... }
```

### Result:

Developers can now pass a Swift struct to the APIGatewayResponse.

```swift
let businessResponse = BusinessResponse(message: "Hello World", code: 200)
return APIGatewayV2Response(statusCode: .ok, body: businessResponse)
```
  • Loading branch information
sebsto authored Dec 21, 2024
1 parent 20fa5fe commit 0025b1a
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 0 deletions.
66 changes: 66 additions & 0 deletions Sources/AWSLambdaEvents/APIGateway+Encodable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

import HTTPTypes

#if canImport(FoundationEssentials)
import class FoundationEssentials.JSONEncoder
import struct FoundationEssentials.Data
#else
import class Foundation.JSONEncoder
import struct Foundation.Data
#endif

extension Encodable {
fileprivate func string() throws -> String {
let encoded = try JSONEncoder().encode(self)
return String(decoding: encoded, as: UTF8.self)
}
}

extension APIGatewayResponse {

public init<Input: Encodable>(
statusCode: HTTPResponse.Status,
headers: HTTPHeaders? = nil,
multiValueHeaders: HTTPMultiValueHeaders? = nil,
encodableBody: Input
) throws {
self.init(
statusCode: statusCode,
headers: headers,
multiValueHeaders: multiValueHeaders,
body: try encodableBody.string(),
isBase64Encoded: nil
)
}
}

extension APIGatewayV2Response {

public init<Input: Encodable>(
statusCode: HTTPResponse.Status,
headers: HTTPHeaders? = nil,
encodableBody: Input,
cookies: [String]? = nil
) throws {
self.init(
statusCode: statusCode,
headers: headers,
body: try encodableBody.string(),
isBase64Encoded: nil,
cookies: cookies
)
}
}
75 changes: 75 additions & 0 deletions Tests/AWSLambdaEventsTests/APIGateway+EncodableTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//===----------------------------------------------------------------------===//
//
// 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 Foundation
import Testing

@testable import AWSLambdaEvents

struct APIGatewayEncodableResponseTests {

// MARK: Encoding
struct BusinessResponse: Codable, Equatable {
let message: String
let code: Int
}

@Test
func testResponseEncodingV2() throws {

// given
let businessResponse = BusinessResponse(message: "Hello World", code: 200)

var response: APIGatewayV2Response? = nil
#expect(throws: Never.self) {
try response = APIGatewayV2Response(statusCode: .ok, encodableBody: 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)
}
}

@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, encodableBody: 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)
}
}
}

0 comments on commit 0025b1a

Please sign in to comment.