Skip to content

Commit cad583a

Browse files
Add support for swift 6
Add support for Swift 6
2 parents 7ba793f + 637c7cb commit cad583a

23 files changed

+804
-151
lines changed

.github/workflows/swift-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
strategy:
1919
matrix:
2020
image:
21-
- swift:5.10.1
21+
- swift:6.1.2
2222
container:
2323
image: ${{ matrix.image }}
2424
# Use the Bash shell regardless whether the GitHub Actions runner is ubuntu-latest, macos-latest, or windows-latest

.spi.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
version: 1
2+
builder:
3+
configs:
4+
- documentation_targets: [BreezeLambdaWebHook]

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,8 @@ test:
2626
coverage:
2727
llvm-cov export $(TEST_PACKAGE) \
2828
--instr-profile=$(SWIFT_BIN_PATH)/codecov/default.profdata \
29-
--format=lcov > $(GITHUB_WORKSPACE)/lcov.info
29+
--format=lcov > $(GITHUB_WORKSPACE)/lcov.info
30+
31+
preview_docc_lambda_api:
32+
swift package --disable-sandbox preview-documentation --target BreezeLambdaWebHook
33+

Package.swift

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,29 @@
1-
// swift-tools-version: 5.7
1+
// swift-tools-version: 6.1
22
// The swift-tools-version declares the minimum version of Swift required to build this package.
33

44
import PackageDescription
55

66
let package = Package(
77
name: "BreezeLambdaWebHook",
88
platforms: [
9-
.macOS(.v13),
10-
.iOS(.v15)
9+
.macOS(.v15)
1110
],
1211
products: [
1312
.library(
1413
name: "BreezeLambdaWebHook",
1514
targets: ["BreezeLambdaWebHook"]
15+
),
16+
.executable(
17+
name: "BreezeDemoHTTPApplication",
18+
targets: ["BreezeDemoHTTPApplication"]
1619
)
1720
],
1821
dependencies: [
19-
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", from: "1.0.0-alpha.2"),
20-
.package(url: "https://github.com/swift-server/swift-aws-lambda-events.git", from: "0.1.0"),
21-
.package(url: "https://github.com/swift-server/async-http-client.git", from: "1.11.2"),
22+
.package(url: "https://github.com/andrea-scuderi/swift-aws-lambda-runtime.git", branch: "main"),
23+
.package(url: "https://github.com/swift-server/swift-aws-lambda-events.git", from: "0.5.0"),
24+
.package(url: "https://github.com/swift-server/async-http-client.git", from: "1.22.0"),
25+
.package(url: "https://github.com/swift-server/swift-service-lifecycle.git", from: "2.6.3"),
26+
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
2227
],
2328
targets: [
2429
.target(
@@ -29,10 +34,17 @@ let package = Package(
2934
.product(name: "AsyncHTTPClient", package: "async-http-client"),
3035
]
3136
),
37+
.executableTarget(
38+
name: "BreezeDemoHTTPApplication",
39+
dependencies: [
40+
"BreezeLambdaWebHook"
41+
]
42+
),
3243
.testTarget(
3344
name: "BreezeLambdaWebHookTests",
3445
dependencies: [
35-
.product(name: "AWSLambdaTesting", package: "swift-aws-lambda-runtime"),
46+
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
47+
.product(name: "ServiceLifecycleTestKit", package: "swift-service-lifecycle"),
3648
"BreezeLambdaWebHook"
3749
],
3850
resources: [.copy("Fixtures")]
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2024 (c) Andrea Scuderi - https://github.com/swift-serverless
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import BreezeLambdaWebHook
16+
import AWSLambdaEvents
17+
import AWSLambdaRuntime
18+
import AsyncHTTPClient
19+
import Logging
20+
import NIOCore
21+
22+
/// This is a simple example of a Breeze Lambda WebHook handler.
23+
/// It uses the BreezeHTTPClientService to make an HTTP request to example.com
24+
/// and returns the response body as a string.
25+
struct DemoLambdaHandler: BreezeLambdaWebHookHandler, Sendable {
26+
var handlerContext: HandlerContext
27+
28+
init(handlerContext: HandlerContext) {
29+
self.handlerContext = handlerContext
30+
}
31+
32+
func handle(_ event: APIGatewayV2Request, context: LambdaContext) async throws -> APIGatewayV2Response {
33+
context.logger.info("Received event: \(event)")
34+
let request = HTTPClientRequest(url: "https://example.com")
35+
let response = try await handlerContext.httpClient.execute(request, timeout: .seconds(5))
36+
let bytes = try await response.body.collect(upTo: 1024 * 1024) // 1 MB Buffer
37+
let body = String(buffer: bytes)
38+
context.logger.info("Response body: \(body)")
39+
return APIGatewayV2Response(with: body, statusCode: .ok)
40+
}
41+
}
42+
43+
/// This is the main entry point for the Breeze Lambda WebHook application.
44+
/// It creates an instance of the BreezeHTTPApplication and runs it.
45+
/// The application name is used for logging and metrics.
46+
/// The timeout is used to set the maximum time allowed for the Lambda function to run.
47+
/// The default timeout is 30 seconds, but it can be changed to any value.
48+
///
49+
/// Local Testing:
50+
///
51+
/// The application will listen for incoming HTTP requests on port 7000 when run locally.
52+
///
53+
/// Use CURL to invoke the Lambda function, passing a JSON file containg API Gateway V2 request:
54+
///
55+
/// `curl -X POST 127.0.0.1:7000/invoke -H "Content-Type: application/json" -d @Tests/BreezeLambdaWebHookTests/Fixtures/get_webhook_api_gtw.json`
56+
@main
57+
struct BreezeDemoHTTPApplication {
58+
59+
static func main() async throws {
60+
let lambda = BreezeLambdaWebHook<DemoLambdaHandler>(name: "DemoLambdaHandler")
61+
try await lambda.run()
62+
}
63+
}

Sources/BreezeLambdaWebHook/APIGatewayV2Response+Extensions.swift

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,16 @@ import struct AWSLambdaEvents.APIGatewayV2Response
1616
import HTTPTypes
1717
import class Foundation.JSONEncoder
1818

19+
/// Extensions for `APIGatewayV2Response` to simplify response creation
1920
public extension APIGatewayV2Response {
2021
private static let encoder = JSONEncoder()
21-
22-
/// defaultHeaders
23-
/// Override the headers in APIGatewayV2Response
24-
static var defaultHeaders = [ "Content-Type": "application/json" ]
2522

23+
/// Body of an error response
2624
struct BodyError: Codable {
2725
public let error: String
2826
}
2927

30-
/// init
28+
/// Initializer with body error and status code
3129
/// - Parameters:
3230
/// - error: Error
3331
/// - statusCode: HTTP Status Code
@@ -36,18 +34,28 @@ public extension APIGatewayV2Response {
3634
self.init(with: bodyError, statusCode: statusCode)
3735
}
3836

39-
/// init
37+
/// Initializer with decodable object, status code, and headers
4038
/// - Parameters:
4139
/// - object: Encodable Object
4240
/// - statusCode: HTTP Status Code
43-
init<Output: Encodable>(with object: Output, statusCode: HTTPResponse.Status) {
41+
/// - headers: HTTP Headers
42+
/// - Returns: APIGatewayV2Response
43+
///
44+
/// This initializer encodes the object to JSON and sets it as the body of the response.
45+
/// If encoding fails, it defaults to an empty JSON object.
46+
/// - Note: The `Content-Type` header is set to `application/json` by default.
47+
init<Output: Encodable>(
48+
with object: Output,
49+
statusCode: HTTPResponse.Status,
50+
headers: [String: String] = [ "Content-Type": "application/json" ]
51+
) {
4452
var body = "{}"
4553
if let data = try? Self.encoder.encode(object) {
4654
body = String(data: data, encoding: .utf8) ?? body
4755
}
4856
self.init(
4957
statusCode: statusCode,
50-
headers: APIGatewayV2Response.defaultHeaders,
58+
headers: headers,
5159
body: body,
5260
isBase64Encoded: false
5361
)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2024 (c) Andrea Scuderi - https://github.com/swift-serverless
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import Logging
16+
import NIOCore
17+
18+
/// Error types for BreezeClientService
19+
public enum BreezeClientServiceError: Error {
20+
/// The handler is invalid or not set
21+
case invalidHandler
22+
}
23+
24+
/// Configuration for the Breeze HTTP Client
25+
public struct BreezeHTTPClientConfig: Sendable {
26+
public init(
27+
timeout: TimeAmount = .seconds(30),
28+
logger: Logger = Logger(label: "BreezeLambdaWebHookLogger")
29+
) {
30+
self.timeout = timeout
31+
self.logger = logger
32+
}
33+
34+
public let timeout: TimeAmount
35+
public let logger: Logger
36+
}
Lines changed: 50 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2023 (c) Andrea Scuderi - https://github.com/swift-serverless
1+
// Copyright 2024 (c) Andrea Scuderi - https://github.com/swift-serverless
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -12,65 +12,60 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
import AsyncHTTPClient
1615
import AWSLambdaEvents
1716
import AWSLambdaRuntime
18-
import AWSLambdaRuntimeCore
19-
import Foundation
17+
import ServiceLifecycle
18+
import Logging
19+
import NIOCore
2020

21-
public extension LambdaInitializationContext {
22-
enum WebHook {
23-
public static var timeout: Int64 = 30
24-
}
25-
}
26-
27-
public struct HandlerContext {
28-
public let handler: String?
29-
public let httpClient: HTTPClient
30-
}
31-
32-
public class BreezeLambdaWebHook<Handler: BreezeLambdaWebHookHandler>: LambdaHandler {
33-
public typealias Event = APIGatewayV2Request
34-
public typealias Output = APIGatewayV2Response
21+
/// The Service that handles Breeze Lambda WebHook functionality.
22+
public struct BreezeLambdaWebHook<LambdaHandler: BreezeLambdaWebHookHandler>: Service {
3523

36-
let handlerContext: HandlerContext
37-
38-
public required init(context: LambdaInitializationContext) async throws {
39-
let handler = Lambda.env("_HANDLER")
40-
context.logger.info("handler: \(handler ?? "")")
41-
42-
let timeout = HTTPClient.Configuration.Timeout(
43-
connect: .seconds(LambdaInitializationContext.WebHook.timeout),
44-
read: .seconds(LambdaInitializationContext.WebHook.timeout)
45-
)
46-
47-
let configuration = HTTPClient.Configuration(timeout: timeout)
48-
let httpClient = HTTPClient(
49-
eventLoopGroupProvider: .shared(context.eventLoop),
50-
configuration: configuration
24+
/// The name of the service, used for logging and identification.
25+
public let name: String
26+
/// Configuration for the Breeze HTTP Client.
27+
public let config: BreezeHTTPClientConfig
28+
29+
/// Initializes a new instance of with the given name and configuration.
30+
/// - Parameters:
31+
/// - name: The name of the service.
32+
/// - config: Configuration for the Breeze HTTP Client.
33+
///
34+
/// This initializer sets up the Breeze Lambda WebHook service with a specified name and configuration.
35+
///
36+
/// - Note: If no configuration is provided, a default configuration with a 30-second timeout and a logger will be used.
37+
public init(
38+
name: String,
39+
config: BreezeHTTPClientConfig? = nil
40+
) {
41+
self.name = name
42+
let defaultConfig = BreezeHTTPClientConfig(
43+
timeout: .seconds(30),
44+
logger: Logger(label: "\(name)")
5145
)
52-
53-
handlerContext = HandlerContext(handler: handler, httpClient: httpClient)
54-
55-
context.terminator.register(name: "shutdown") { eventLoop in
56-
context.logger.info("shutdown: started")
57-
let promise = eventLoop.makePromise(of: Void.self)
58-
Task {
59-
do {
60-
try await self.handlerContext.httpClient.shutdown()
61-
promise.succeed()
62-
context.logger.info("shutdown: succeed")
63-
} catch {
64-
promise.fail(error)
65-
context.logger.info("shutdown: fail")
66-
}
67-
}
68-
return promise.futureResult
69-
}
46+
self.config = config ?? defaultConfig
7047
}
71-
72-
public func handle(_ event: AWSLambdaEvents.APIGatewayV2Request, context: AWSLambdaRuntimeCore.LambdaContext) async throws -> AWSLambdaEvents.APIGatewayV2Response {
73-
return await Handler(handlerContext: handlerContext).handle(context: context, event: event)
48+
49+
/// Runs the Breeze Lambda WebHook service.
50+
/// - Throws: An error if the service fails to start or run.
51+
///
52+
/// This method initializes the Breeze Lambda WebHook service and starts it,
53+
/// handling any errors that may occur during the process.
54+
/// It gracefully shuts down the service on termination signals.
55+
public func run() async throws {
56+
do {
57+
let lambdaService = BreezeLambdaWebHookService<LambdaHandler>(
58+
config: config
59+
)
60+
let serviceGroup = ServiceGroup(
61+
services: [lambdaService],
62+
gracefulShutdownSignals: [.sigterm, .sigint],
63+
logger: config.logger
64+
)
65+
config.logger.error("Starting \(name) ...")
66+
try await serviceGroup.run()
67+
} catch {
68+
config.logger.error("Error running \(name): \(error.localizedDescription)")
69+
}
7470
}
7571
}
76-

Sources/BreezeLambdaWebHook/BreezeLambdaWebHookError.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,14 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
#if canImport(FoundationEssentials)
16+
import FoundationEssentials
17+
#else
1518
import Foundation
19+
#endif
1620

21+
/// Error types for BreezeLambdaWebHook
1722
public enum BreezeLambdaWebHookError: Error {
23+
/// The request is invalid or malformed
1824
case invalidRequest
1925
}

0 commit comments

Comments
 (0)