Skip to content

Commit

Permalink
Merge pull request #1 from swift-sprinter/feature/support-webhook
Browse files Browse the repository at this point in the history
Add environment parameter to httpApiLambda
  • Loading branch information
Andrea-Scuderi authored Dec 26, 2023
2 parents afb5b2f + bddcce7 commit 45d8ac7
Show file tree
Hide file tree
Showing 6 changed files with 408 additions and 65 deletions.
21 changes: 21 additions & 0 deletions Sources/SLSAdapter/Function+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,25 @@ public extension Function {
events: [.init(httpAPI: event)]
)
}

static func httpApiLambda(
handler: String,
description: String?,
memorySize: Int?,
environment: YAMLContent?,
runtime: Runtime?,
package: Package?,
event: EventHTTPAPI
) throws -> Function {
Function(
handler: handler,
runtime: runtime,
memorySize: memorySize,
environment: environment,
description: description ?? "[${sls:stage}] \(event.method) \(event.path)",
package: package,
layers: nil,
events: [.init(httpAPI: event)]
)
}
}
59 changes: 59 additions & 0 deletions Tests/SLSAdapterTests/Fixtures/serverless_webhook.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
service: swift-webhook
frameworkVersion: '3'
configValidationMode: warn
useDotenv: false
provider:
name: aws
region: us-east-1
disableRollback: false
runtime: provided.al2
httpApi:
payload: '2.0'
cors: false
architecture: arm64
versionFunctions: true
iam:
role:
statements:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: '*'

package:
individually: true
functions:
postWebHook:
handler: post-webhook
memorySize: 256
description: '[${sls:stage}] post /webhook'
package:
artifact: build/WebHook/WebHook.zip
events:
- httpApi:
path: /webhook
method: post
getWebHook:
handler: get-webhook
memorySize: 256
description: '[${sls:stage}] get /webhook'
package:
artifact: build/WebHook/WebHook.zip
events:
- httpApi:
path: /webhook
method: get
githubWebHook:
handler: github-webhook
memorySize: 256
description: '[${sls:stage}] post /github-webhook'
package:
artifact: build/GitHubWebHook/GitHubWebHook.zip
environment:
WEBHOOK_SECRET: '${ssm:/dev/swift-webhook/webhook_secret}'
events:
- httpApi:
path: /github-webhook
method: post
61 changes: 61 additions & 0 deletions Tests/SLSAdapterTests/HttpAPILambdaParams.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
Copyright 2023 (c) Andrea Scuderi - https://github.com/swift-sprinter

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import Foundation
import SLSAdapter
import Yams

public struct HttpAPILambdaParams {
public let name: String
public let handler: String
public let event: EventHTTPAPI
public let environment: YAMLContent?
public let artifact: String

public init(name: String, handler: String, event: EventHTTPAPI, environment: YAMLContent?, artifact: String) {
self.name = name
self.handler = handler
self.event = event
self.environment = environment
self.artifact = artifact
}
}

public extension Function {
static func httpAPILambda(params: HttpAPILambdaParams, memorySize: Int?) throws -> Function {
try .httpApiLambda(
handler: params.handler,
description: nil,
memorySize: memorySize,
environment: params.environment,
runtime: nil,
package: .init(patterns: nil,
individually: nil,
artifact: params.artifact),
event: params.event
)
}
}

extension Array where Element == HttpAPILambdaParams {
func buildFunctions(memorySize: Int) throws -> [String: Function] {
var functions: [String: Function] = [:]
for lambdasParam in self {
functions[lambdasParam.name] = try Function.httpAPILambda(params: lambdasParam, memorySize: memorySize)
}
return functions
}
}
8 changes: 4 additions & 4 deletions Tests/SLSAdapterTests/SLSAdapterTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ final class SwiftSlsAdapterTests: XCTestCase {
return try Data(contentsOf: fixtureUrl)
}

func testReadServerlessYml() throws {
func test_ReadServerlessYml() throws {
let serverlessYml = try fixture(name: "serverless", type: "yml")

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

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

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

func testInitServerlessNolLayerYml() throws {
func test_InitServerlessNolLayerYml() throws {
let decoder = YAMLDecoder()
let serverlessYml = try fixture(name: "serverless_no_layer", type: "yml")
let serverlessConfig2 = try decoder.decode(ServerlessConfig.self, from: serverlessYml)
Expand Down
157 changes: 96 additions & 61 deletions Tests/SLSAdapterTests/ServerlessConfig+Exensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -129,75 +129,110 @@ extension ServerlessConfig {
executable: String,
artifact: String
) throws -> ServerlessConfig {
let keyedPath = "\(httpAPIPath)/{\(dynamoDBKey)}"
let dynamoResourceName = "\(executable)Table"

let environmentTableName = "DYNAMO_DB_TABLE_NAME"
let environmentKeyName = "DYNAMO_DB_KEY"

let iam = Iam(
role: Role(
statements: [.allowLogAccess(resource: try YAMLContent(with: "*")),
.allowDynamoDBReadWrite(resource: try YAMLContent(with: [["Fn::GetAtt": [dynamoResourceName, "Arn"]]]))]
)
let keyedPath = "\(httpAPIPath)/{\(dynamoDBKey)}"
let dynamoResourceName = "\(executable)Table"

let environmentTableName = "DYNAMO_DB_TABLE_NAME"
let environmentKeyName = "DYNAMO_DB_KEY"
let dynamoResource = try YAMLContent(with: [["Fn::GetAtt": [dynamoResourceName, "Arn"]]])
let iam = Iam(
role: Role(
statements: [.allowLogAccess(resource: try YAMLContent(with: "*")),
.allowDynamoDBReadWrite(resource: dynamoResource)]
)
let environment = try YAMLContent(with: [environmentTableName: "${self:custom.tableName}",
environmentKeyName: "${self:custom.keyName}"])
let provider = Provider(
name: .aws,
region: region,
runtime: runtime,
environment: environment,
architecture: architecture,
httpAPI: .init(
payload: "2.0",
cors: true,
authorizers:
)
let environment = try YAMLContent(with: [environmentTableName: "${self:custom.tableName}",
environmentKeyName: "${self:custom.keyName}"])
let provider = Provider(
name: .aws,
region: region,
runtime: runtime,
environment: environment,
architecture: architecture,
httpAPI: .init(
payload: "2.0",
cors: true,
authorizers:
.dictionary([
"JWTAuthorizer": .buildJWTAuthorizer(issuerUrl: "https://appleid.apple.com",
audience: ["com.mydomain.myhost"]),
audience: ["com.mydomain.myhost"]),
"customAuthorizer": .buildCustomAuthorizer(name: "LambdaAuthorizer",
functionName: "lambdaAuthorizer",
identitySource: ["$request.header.SEC-X-API-KEY",
"$request.header.User-Agent"])
])
),
iam: iam
)
let custom = try YAMLContent(with: ["tableName": "\(dynamoDBTableNamePrefix)-table-${sls:stage}",
"keyName": dynamoDBKey])

let endpoints = [
Endpoint(handler: "create", method: .post, path: httpAPIPath),
Endpoint(handler: "read", method: .get, path: keyedPath),
Endpoint(handler: "update", method: .put, path: httpAPIPath),
Endpoint(handler: "delete", method: .delete, path: keyedPath),
Endpoint(handler: "list", method: .get, path: httpAPIPath)
]
var functions: [String: Function] = [:]
for endpoint in endpoints {
let function = try Function.httpApiLambda(
handler: "\(endpoint.handler)",
description: nil,
memorySize: memorySize,
runtime: nil,
package: nil,
event: .init(path: endpoint.path, method: endpoint.method)
)
functions["\(endpoint.handler)\(executable)"] = function
}

let resource = Resource.dynamoDBResource(tableName: "${self:custom.tableName}", key: "${self:custom.keyName}")
let resources = Resources.resources(with: [dynamoResourceName: resource])

return ServerlessConfig(
service: service,
provider: provider,
package: .init(patterns: nil, individually: nil, artifact: artifact),
custom: custom,
layers: nil,
functions: functions,
resources: try YAMLContent(with: resources)
),
iam: iam
)
let custom = try YAMLContent(with: ["tableName": "\(dynamoDBTableNamePrefix)-table-${sls:stage}",
"keyName": dynamoDBKey])

let endpoints = [
Endpoint(handler: "create", method: .post, path: httpAPIPath),
Endpoint(handler: "read", method: .get, path: keyedPath),
Endpoint(handler: "update", method: .put, path: httpAPIPath),
Endpoint(handler: "delete", method: .delete, path: keyedPath),
Endpoint(handler: "list", method: .get, path: httpAPIPath)
]
var functions: [String: Function] = [:]
for endpoint in endpoints {
let function = try Function.httpApiLambda(
handler: "\(endpoint.handler)",
description: nil,
memorySize: memorySize,
runtime: nil,
package: nil,
event: .init(path: endpoint.path, method: endpoint.method)
)
functions["\(endpoint.handler)\(executable)"] = function
}

let resource = Resource.dynamoDBResource(tableName: "${self:custom.tableName}", key: "${self:custom.keyName}")
let resources = Resources.resources(with: [dynamoResourceName: resource])

return ServerlessConfig(
service: service,
provider: provider,
package: .init(patterns: nil, individually: nil, artifact: artifact),
custom: custom,
layers: nil,
functions: functions,
resources: try YAMLContent(with: resources)
)
}

static func webhookLambdaAPI(
service: String,
region: Region,
runtime: Runtime,
architecture: Architecture,
memorySize: Int,
lambdasParams: [HttpAPILambdaParams]
) throws -> ServerlessConfig {
let iam = Iam(
role: Role(
statements: [.allowLogAccess(resource: try YAMLContent(with: "*"))]
)
)
let provider = Provider(
name: .aws,
region: region,
runtime: runtime,
environment: nil,
architecture: architecture,
httpAPI: .init(payload: "2.0", cors: false),
iam: iam
)
let package = Package(patterns: nil, individually: true)
let functions = try lambdasParams.buildFunctions(memorySize: memorySize)
return ServerlessConfig(
service: service,
provider: provider,
package: package,
custom: nil,
layers: nil,
functions: functions,
resources: nil
)
}
}
Loading

0 comments on commit 45d8ac7

Please sign in to comment.