Skip to content

Commit 749bb17

Browse files
committed
Make ApiGw response composing more convenient
1 parent a7eb085 commit 749bb17

File tree

3 files changed

+146
-7
lines changed

3 files changed

+146
-7
lines changed

Sources/TencentSCFEvents/APIGateway.swift

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
//===------------------------------------------------------------------------------------===//
1414

1515
import struct Foundation.Data
16+
import class Foundation.JSONEncoder
1617

1718
// https://cloud.tencent.com/document/product/583/12513
1819

@@ -67,24 +68,58 @@ public enum APIGateway {
6768
public let body: String
6869
public let isBase64Encoded: Bool
6970

71+
public init<T: Encodable>(
72+
statusCode: HTTPResponseStatus,
73+
headers: HTTPHeaders = [:],
74+
codableBody: T?
75+
) {
76+
var headers = headers
77+
headers["Content-Type"] = MIME.json.rawValue
78+
self.headers = headers
79+
do {
80+
self.body = String(
81+
data: try JSONEncoder().encode(codableBody),
82+
encoding: .utf8
83+
) ?? ""
84+
self.statusCode = statusCode
85+
} catch let err {
86+
self.body = #"{"errorType":"FunctionError","errorMsg":"\#(err.localizedDescription)"}"#
87+
self.statusCode = .internalServerError
88+
}
89+
self.isBase64Encoded = false
90+
}
91+
7092
public init(
7193
statusCode: HTTPResponseStatus,
7294
headers: HTTPHeaders = [:],
73-
body: String? = nil,
74-
isBase64Encoded: Bool = false
95+
type: MIME? = nil,
96+
body: String? = nil
7597
) {
7698
self.statusCode = statusCode
99+
var headers = headers
100+
if let type = type?.rawValue {
101+
headers["Content-Type"] = type
102+
} else {
103+
headers["Content-Type"] = MIME.text.rawValue
104+
}
77105
self.headers = headers
78106
self.body = body ?? ""
79-
self.isBase64Encoded = isBase64Encoded
107+
self.isBase64Encoded = false
80108
}
81109

82110
public init(
83111
statusCode: HTTPResponseStatus,
84112
headers: HTTPHeaders = [:],
113+
type: MIME? = nil,
85114
body: Data
86115
) {
87116
self.statusCode = statusCode
117+
var headers = headers
118+
if let type = type?.rawValue {
119+
headers["Content-Type"] = type
120+
} else {
121+
headers["Content-Type"] = MIME.octet.rawValue
122+
}
88123
self.headers = headers
89124
self.body = body.base64EncodedString()
90125
self.isBase64Encoded = true
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//===------------------------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftTencentSCFRuntime open source project
4+
//
5+
// Copyright (c) 2020 stevapple and the SwiftTencentSCFRuntime project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftTencentSCFRuntime project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===------------------------------------------------------------------------------------===//
14+
15+
public struct MIME: RawRepresentable, CustomStringConvertible, Equatable, Hashable {
16+
public typealias RawValue = String
17+
18+
public let rawValue: String
19+
20+
public init(rawValue: String) {
21+
self.rawValue = rawValue
22+
}
23+
24+
public var description: String {
25+
self.rawValue
26+
}
27+
28+
public static var json: Self { MIME(rawValue: "application/json") }
29+
public static var xml: Self { MIME(rawValue: "text/xml") }
30+
public static var html: Self { MIME(rawValue: "text/html") }
31+
public static var text: Self { MIME(rawValue: "text/plain") }
32+
public static var octet: Self { MIME(rawValue: "application/octet-stream") }
33+
}
34+
35+
extension MIME: Codable {
36+
public init(from decoder: Decoder) throws {
37+
let container = try decoder.singleValueContainer()
38+
let region = try container.decode(String.self)
39+
self.init(rawValue: region)
40+
}
41+
42+
public func encode(to encoder: Encoder) throws {
43+
var container = encoder.singleValueContainer()
44+
try container.encode(self.rawValue)
45+
}
46+
}

Tests/TencentSCFEventsTests/APIGatewayTests.swift

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class APIGatewayTests: XCTestCase {
8181
"accept": "text/html,application/xml,application/json",
8282
"host": "service-3ei3tii4-251000691.ap-guangzhou.apigateway.myqloud.com",
8383
"user-agent": "User Agent String",
84-
"x-anonymous-consumer": "true",
84+
"x-anonymous-consumer": "true",
8585
"x-api-requestid": "24281851d905b02add27dad71656f29b",
8686
"x-b3-traceid": "24281851d905b02add27dad71656f29b",
8787
"x-qualifier": "$DEFAULT"])
@@ -104,7 +104,7 @@ class APIGatewayTests: XCTestCase {
104104
func testResponseEncodingWithText() {
105105
let resp = APIGateway.Response(
106106
statusCode: .ok,
107-
headers: ["Content-Type": "text/plain"],
107+
type: .text,
108108
body: "abc123"
109109
)
110110

@@ -123,7 +123,6 @@ class APIGatewayTests: XCTestCase {
123123
let body = #"{"hello":"swift"}"#
124124
let resp = APIGateway.Response(
125125
statusCode: .ok,
126-
headers: ["Content-Type": "application/json"],
127126
body: body.data(using: .utf8)!
128127
)
129128

@@ -139,7 +138,66 @@ class APIGatewayTests: XCTestCase {
139138

140139
XCTAssertEqual(newResp.statusCode, resp.statusCode)
141140
XCTAssertEqual(newResp.isBase64Encoded, true)
142-
XCTAssertEqual(newResp.headers["Content-Type"], "application/json")
141+
XCTAssertEqual(newResp.headers["Content-Type"], "application/octet-stream")
143142
XCTAssertEqual(Data(base64Encoded: newResp.body), body.data(using: .utf8))
144143
}
144+
145+
func testResponseEncodingWithCodable() {
146+
struct Point: Codable, Equatable {
147+
let x, y: Double
148+
}
149+
let point = Point(x: 1.0, y: -0.01)
150+
let resp = APIGateway.Response(
151+
statusCode: .ok,
152+
codableBody: point
153+
)
154+
155+
var data: Data?
156+
XCTAssertNoThrow(data = try JSONEncoder().encode(resp))
157+
var json: APIGateway.Response?
158+
XCTAssertNoThrow(json = try JSONDecoder().decode(APIGateway.Response.self, from: XCTUnwrap(data)))
159+
160+
guard let newResp = json else {
161+
XCTFail("Expected to have value")
162+
return
163+
}
164+
165+
XCTAssertEqual(newResp.statusCode, resp.statusCode)
166+
XCTAssertEqual(newResp.isBase64Encoded, false)
167+
XCTAssertEqual(newResp.headers["Content-Type"], "application/json")
168+
XCTAssertEqual(try JSONDecoder().decode(Point.self, from: (newResp.body.data(using: .utf8))!), point)
169+
}
170+
171+
func testResponseEncodingWithNil() {
172+
let resp = APIGateway.Response(statusCode: .ok)
173+
174+
var data: Data?
175+
XCTAssertNoThrow(data = try JSONEncoder().encode(resp))
176+
var json: APIGateway.Response?
177+
XCTAssertNoThrow(json = try JSONDecoder().decode(APIGateway.Response.self, from: XCTUnwrap(data)))
178+
179+
XCTAssertEqual(json?.statusCode, resp.statusCode)
180+
XCTAssertEqual(json?.headers["Content-Type"], "text/plain")
181+
XCTAssertEqual(json?.isBase64Encoded, false)
182+
XCTAssertEqual(json?.body, "")
183+
}
184+
185+
func testResponseEncodingWithCustomMIME() {
186+
let mime = "application/x-javascript"
187+
let resp = APIGateway.Response(
188+
statusCode: .ok,
189+
type: .init(rawValue: mime),
190+
body: "console.log(\"Hello world!\");"
191+
)
192+
193+
var data: Data?
194+
XCTAssertNoThrow(data = try JSONEncoder().encode(resp))
195+
var json: APIGateway.Response?
196+
XCTAssertNoThrow(json = try JSONDecoder().decode(APIGateway.Response.self, from: XCTUnwrap(data)))
197+
198+
XCTAssertEqual(json?.statusCode, resp.statusCode)
199+
XCTAssertEqual(json?.body, resp.body)
200+
XCTAssertEqual(json?.isBase64Encoded, false)
201+
XCTAssertEqual(json?.headers["Content-Type"], mime)
202+
}
145203
}

0 commit comments

Comments
 (0)