Skip to content

Commit 45d8ac7

Browse files
Merge pull request #1 from swift-sprinter/feature/support-webhook
Add environment parameter to httpApiLambda
2 parents afb5b2f + bddcce7 commit 45d8ac7

6 files changed

+408
-65
lines changed

Sources/SLSAdapter/Function+Extensions.swift

+21
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,25 @@ public extension Function {
9191
events: [.init(httpAPI: event)]
9292
)
9393
}
94+
95+
static func httpApiLambda(
96+
handler: String,
97+
description: String?,
98+
memorySize: Int?,
99+
environment: YAMLContent?,
100+
runtime: Runtime?,
101+
package: Package?,
102+
event: EventHTTPAPI
103+
) throws -> Function {
104+
Function(
105+
handler: handler,
106+
runtime: runtime,
107+
memorySize: memorySize,
108+
environment: environment,
109+
description: description ?? "[${sls:stage}] \(event.method) \(event.path)",
110+
package: package,
111+
layers: nil,
112+
events: [.init(httpAPI: event)]
113+
)
114+
}
94115
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
service: swift-webhook
2+
frameworkVersion: '3'
3+
configValidationMode: warn
4+
useDotenv: false
5+
provider:
6+
name: aws
7+
region: us-east-1
8+
disableRollback: false
9+
runtime: provided.al2
10+
httpApi:
11+
payload: '2.0'
12+
cors: false
13+
architecture: arm64
14+
versionFunctions: true
15+
iam:
16+
role:
17+
statements:
18+
- Effect: Allow
19+
Action:
20+
- logs:CreateLogGroup
21+
- logs:CreateLogStream
22+
- logs:PutLogEvents
23+
Resource: '*'
24+
25+
package:
26+
individually: true
27+
functions:
28+
postWebHook:
29+
handler: post-webhook
30+
memorySize: 256
31+
description: '[${sls:stage}] post /webhook'
32+
package:
33+
artifact: build/WebHook/WebHook.zip
34+
events:
35+
- httpApi:
36+
path: /webhook
37+
method: post
38+
getWebHook:
39+
handler: get-webhook
40+
memorySize: 256
41+
description: '[${sls:stage}] get /webhook'
42+
package:
43+
artifact: build/WebHook/WebHook.zip
44+
events:
45+
- httpApi:
46+
path: /webhook
47+
method: get
48+
githubWebHook:
49+
handler: github-webhook
50+
memorySize: 256
51+
description: '[${sls:stage}] post /github-webhook'
52+
package:
53+
artifact: build/GitHubWebHook/GitHubWebHook.zip
54+
environment:
55+
WEBHOOK_SECRET: '${ssm:/dev/swift-webhook/webhook_secret}'
56+
events:
57+
- httpApi:
58+
path: /github-webhook
59+
method: post
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
Copyright 2023 (c) Andrea Scuderi - https://github.com/swift-sprinter
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import Foundation
18+
import SLSAdapter
19+
import Yams
20+
21+
public struct HttpAPILambdaParams {
22+
public let name: String
23+
public let handler: String
24+
public let event: EventHTTPAPI
25+
public let environment: YAMLContent?
26+
public let artifact: String
27+
28+
public init(name: String, handler: String, event: EventHTTPAPI, environment: YAMLContent?, artifact: String) {
29+
self.name = name
30+
self.handler = handler
31+
self.event = event
32+
self.environment = environment
33+
self.artifact = artifact
34+
}
35+
}
36+
37+
public extension Function {
38+
static func httpAPILambda(params: HttpAPILambdaParams, memorySize: Int?) throws -> Function {
39+
try .httpApiLambda(
40+
handler: params.handler,
41+
description: nil,
42+
memorySize: memorySize,
43+
environment: params.environment,
44+
runtime: nil,
45+
package: .init(patterns: nil,
46+
individually: nil,
47+
artifact: params.artifact),
48+
event: params.event
49+
)
50+
}
51+
}
52+
53+
extension Array where Element == HttpAPILambdaParams {
54+
func buildFunctions(memorySize: Int) throws -> [String: Function] {
55+
var functions: [String: Function] = [:]
56+
for lambdasParam in self {
57+
functions[lambdasParam.name] = try Function.httpAPILambda(params: lambdasParam, memorySize: memorySize)
58+
}
59+
return functions
60+
}
61+
}

Tests/SLSAdapterTests/SLSAdapterTests.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ final class SwiftSlsAdapterTests: XCTestCase {
3131
return try Data(contentsOf: fixtureUrl)
3232
}
3333

34-
func testReadServerlessYml() throws {
34+
func test_ReadServerlessYml() throws {
3535
let serverlessYml = try fixture(name: "serverless", type: "yml")
3636

3737
let decoder = YAMLDecoder()
@@ -162,7 +162,7 @@ final class SwiftSlsAdapterTests: XCTestCase {
162162
XCTAssertEqual(productTableProperties.dictionary?["BillingMode"]?.string, "PAY_PER_REQUEST")
163163
}
164164

165-
func testReadWriteServerlessYml() throws {
165+
func test_ReadWriteServerlessYml() throws {
166166
let serverlessYml = try fixture(name: "serverless", type: "yml")
167167
let decoder = YAMLDecoder()
168168
let serverlessConfig = try decoder.decode(ServerlessConfig.self, from: serverlessYml)
@@ -179,7 +179,7 @@ final class SwiftSlsAdapterTests: XCTestCase {
179179
let path: String
180180
}
181181

182-
func testInitServerlessYml() throws {
182+
func test_InitServerlessYml() throws {
183183
let decoder = YAMLDecoder()
184184
let serverlessYml = try fixture(name: "serverless", type: "yml")
185185
let serverlessConfig2 = try decoder.decode(ServerlessConfig.self, from: serverlessYml)
@@ -215,7 +215,7 @@ final class SwiftSlsAdapterTests: XCTestCase {
215215
XCTAssertEqual(serverlessConfig.resources, serverlessConfig2.resources)
216216
}
217217

218-
func testInitServerlessNolLayerYml() throws {
218+
func test_InitServerlessNolLayerYml() throws {
219219
let decoder = YAMLDecoder()
220220
let serverlessYml = try fixture(name: "serverless_no_layer", type: "yml")
221221
let serverlessConfig2 = try decoder.decode(ServerlessConfig.self, from: serverlessYml)

Tests/SLSAdapterTests/ServerlessConfig+Exensions.swift

+96-61
Original file line numberDiff line numberDiff line change
@@ -129,75 +129,110 @@ extension ServerlessConfig {
129129
executable: String,
130130
artifact: String
131131
) throws -> ServerlessConfig {
132-
let keyedPath = "\(httpAPIPath)/{\(dynamoDBKey)}"
133-
let dynamoResourceName = "\(executable)Table"
134-
135-
let environmentTableName = "DYNAMO_DB_TABLE_NAME"
136-
let environmentKeyName = "DYNAMO_DB_KEY"
137-
138-
let iam = Iam(
139-
role: Role(
140-
statements: [.allowLogAccess(resource: try YAMLContent(with: "*")),
141-
.allowDynamoDBReadWrite(resource: try YAMLContent(with: [["Fn::GetAtt": [dynamoResourceName, "Arn"]]]))]
142-
)
132+
let keyedPath = "\(httpAPIPath)/{\(dynamoDBKey)}"
133+
let dynamoResourceName = "\(executable)Table"
134+
135+
let environmentTableName = "DYNAMO_DB_TABLE_NAME"
136+
let environmentKeyName = "DYNAMO_DB_KEY"
137+
let dynamoResource = try YAMLContent(with: [["Fn::GetAtt": [dynamoResourceName, "Arn"]]])
138+
let iam = Iam(
139+
role: Role(
140+
statements: [.allowLogAccess(resource: try YAMLContent(with: "*")),
141+
.allowDynamoDBReadWrite(resource: dynamoResource)]
143142
)
144-
let environment = try YAMLContent(with: [environmentTableName: "${self:custom.tableName}",
145-
environmentKeyName: "${self:custom.keyName}"])
146-
let provider = Provider(
147-
name: .aws,
148-
region: region,
149-
runtime: runtime,
150-
environment: environment,
151-
architecture: architecture,
152-
httpAPI: .init(
153-
payload: "2.0",
154-
cors: true,
155-
authorizers:
143+
)
144+
let environment = try YAMLContent(with: [environmentTableName: "${self:custom.tableName}",
145+
environmentKeyName: "${self:custom.keyName}"])
146+
let provider = Provider(
147+
name: .aws,
148+
region: region,
149+
runtime: runtime,
150+
environment: environment,
151+
architecture: architecture,
152+
httpAPI: .init(
153+
payload: "2.0",
154+
cors: true,
155+
authorizers:
156156
.dictionary([
157157
"JWTAuthorizer": .buildJWTAuthorizer(issuerUrl: "https://appleid.apple.com",
158-
audience: ["com.mydomain.myhost"]),
158+
audience: ["com.mydomain.myhost"]),
159159
"customAuthorizer": .buildCustomAuthorizer(name: "LambdaAuthorizer",
160160
functionName: "lambdaAuthorizer",
161161
identitySource: ["$request.header.SEC-X-API-KEY",
162162
"$request.header.User-Agent"])
163163
])
164-
),
165-
iam: iam
166-
)
167-
let custom = try YAMLContent(with: ["tableName": "\(dynamoDBTableNamePrefix)-table-${sls:stage}",
168-
"keyName": dynamoDBKey])
169-
170-
let endpoints = [
171-
Endpoint(handler: "create", method: .post, path: httpAPIPath),
172-
Endpoint(handler: "read", method: .get, path: keyedPath),
173-
Endpoint(handler: "update", method: .put, path: httpAPIPath),
174-
Endpoint(handler: "delete", method: .delete, path: keyedPath),
175-
Endpoint(handler: "list", method: .get, path: httpAPIPath)
176-
]
177-
var functions: [String: Function] = [:]
178-
for endpoint in endpoints {
179-
let function = try Function.httpApiLambda(
180-
handler: "\(endpoint.handler)",
181-
description: nil,
182-
memorySize: memorySize,
183-
runtime: nil,
184-
package: nil,
185-
event: .init(path: endpoint.path, method: endpoint.method)
186-
)
187-
functions["\(endpoint.handler)\(executable)"] = function
188-
}
189-
190-
let resource = Resource.dynamoDBResource(tableName: "${self:custom.tableName}", key: "${self:custom.keyName}")
191-
let resources = Resources.resources(with: [dynamoResourceName: resource])
192-
193-
return ServerlessConfig(
194-
service: service,
195-
provider: provider,
196-
package: .init(patterns: nil, individually: nil, artifact: artifact),
197-
custom: custom,
198-
layers: nil,
199-
functions: functions,
200-
resources: try YAMLContent(with: resources)
164+
),
165+
iam: iam
166+
)
167+
let custom = try YAMLContent(with: ["tableName": "\(dynamoDBTableNamePrefix)-table-${sls:stage}",
168+
"keyName": dynamoDBKey])
169+
170+
let endpoints = [
171+
Endpoint(handler: "create", method: .post, path: httpAPIPath),
172+
Endpoint(handler: "read", method: .get, path: keyedPath),
173+
Endpoint(handler: "update", method: .put, path: httpAPIPath),
174+
Endpoint(handler: "delete", method: .delete, path: keyedPath),
175+
Endpoint(handler: "list", method: .get, path: httpAPIPath)
176+
]
177+
var functions: [String: Function] = [:]
178+
for endpoint in endpoints {
179+
let function = try Function.httpApiLambda(
180+
handler: "\(endpoint.handler)",
181+
description: nil,
182+
memorySize: memorySize,
183+
runtime: nil,
184+
package: nil,
185+
event: .init(path: endpoint.path, method: endpoint.method)
201186
)
187+
functions["\(endpoint.handler)\(executable)"] = function
202188
}
189+
190+
let resource = Resource.dynamoDBResource(tableName: "${self:custom.tableName}", key: "${self:custom.keyName}")
191+
let resources = Resources.resources(with: [dynamoResourceName: resource])
192+
193+
return ServerlessConfig(
194+
service: service,
195+
provider: provider,
196+
package: .init(patterns: nil, individually: nil, artifact: artifact),
197+
custom: custom,
198+
layers: nil,
199+
functions: functions,
200+
resources: try YAMLContent(with: resources)
201+
)
202+
}
203+
204+
static func webhookLambdaAPI(
205+
service: String,
206+
region: Region,
207+
runtime: Runtime,
208+
architecture: Architecture,
209+
memorySize: Int,
210+
lambdasParams: [HttpAPILambdaParams]
211+
) throws -> ServerlessConfig {
212+
let iam = Iam(
213+
role: Role(
214+
statements: [.allowLogAccess(resource: try YAMLContent(with: "*"))]
215+
)
216+
)
217+
let provider = Provider(
218+
name: .aws,
219+
region: region,
220+
runtime: runtime,
221+
environment: nil,
222+
architecture: architecture,
223+
httpAPI: .init(payload: "2.0", cors: false),
224+
iam: iam
225+
)
226+
let package = Package(patterns: nil, individually: true)
227+
let functions = try lambdasParams.buildFunctions(memorySize: memorySize)
228+
return ServerlessConfig(
229+
service: service,
230+
provider: provider,
231+
package: package,
232+
custom: nil,
233+
layers: nil,
234+
functions: functions,
235+
resources: nil
236+
)
237+
}
203238
}

0 commit comments

Comments
 (0)