Files
swift-openapi-lambda/Sources/OpenAPILambdaHandler.swift
T
Sébastien Stormacq c72834d93c Update to support AWS Lambda Runtime for Swift v2 (#22)
- Update this library to support the upcoming release of AWS Lambda
Runtime for Swift v2.
This change affects the public API (some `Sendable` added) and requires
a major version bump. I propose to release v2 to align this library's
major with the Swift AWS Lambda Runtime's major.

- Add a QuoteAPI example in the `Examples` directory
2025-09-06 11:34:56 +02:00

118 lines
5.0 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift OpenAPI Lambda open source project
//
// Copyright (c) 2023 Amazon.com, Inc. or its affiliates
// and the Swift OpenAPI Lambda project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift OpenAPI Lambda project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import AWSLambdaRuntime
import AWSLambdaEvents
import OpenAPIRuntime
import HTTPTypes
/// Specialization of LambdaHandler which runs an OpenAPILambda
public struct OpenAPILambdaHandler<OALS: OpenAPILambdaService>: LambdaHandler, Sendable {
private let transport: OpenAPILambdaTransport
private let openAPIService: OALS
/// the input type for this Lambda handler (received from the `OpenAPILambda`)
public typealias Event = OALS.Event
/// the output type for this Lambda handler (received from the `OpenAPILambda`)
public typealias Output = OALS.Output
/// Initialize `OpenAPILambdaHandler`.
///
/// This initializer decouples the OpenAPILambdaService creation from the registration of the transport
/// this allows users to control the lifecycle of their service and to inject dependencies.
///
/// - Parameters
/// - withService: The OpenAPI Lambda service to bind to this Lambda handler function
public init(withService openAPILambdaService: OALS) throws {
self.openAPIService = openAPILambdaService
self.transport = OpenAPILambdaTransport(router: TrieRouter())
try self.openAPIService.register(transport: self.transport)
}
/// The Lambda handling method.
/// Concrete Lambda handlers implement this method to provide the Lambda functionality.
///
/// - Parameters:
/// - event: Event of type `Event` representing the event or request.
/// - context: Runtime ``LambdaContext``.
///
/// - Returns: A Lambda result ot type `Output`.
public func handle(_ event: OALS.Event, context: AWSLambdaRuntime.LambdaContext) async throws -> OALS.Output {
// by default returns HTTP 500
var lambdaResponse: OpenAPILambdaResponse = (HTTPResponse(status: .internalServerError), "unknown error")
do {
// convert Lambda event source to OpenAPILambdaRequest
let request = try openAPIService.request(context: context, from: event)
// route the request to find the handlers and extract the paramaters
let (handler, parameters) = try self.transport.router.route(method: request.0.method, path: request.0.path!)
// call the request handler (and extract the HTTPRequest and HTTPBody)
let httpRequest = request.0
let httpBody = HTTPBody(stringLiteral: request.1 ?? "")
let response = try await handler(httpRequest, httpBody, ServerRequestMetadata(pathParameters: parameters))
// transform the response to an OpenAPILambdaResponse
let maxPayloadSize = 10 * 1024 * 1024 // APIGateway payload is 10M max
let body: String? = try? await String(collecting: response.1 ?? "", upTo: maxPayloadSize)
lambdaResponse = (response.0, body)
}
catch OpenAPILambdaRouterError.noHandlerForPath(let path) {
// There is no hadler registered for this path. This is a programming error.
lambdaResponse = (
HTTPResponse(status: .internalServerError),
"There is no OpenAPI handler registered for the path \(path)"
)
}
catch OpenAPILambdaRouterError.noRouteForMethod(let method) {
// There is no hadler registered for this path. This is a programming error.
lambdaResponse = (HTTPResponse(status: .notFound), "There is no route registered for the method \(method)")
}
catch OpenAPILambdaRouterError.noRouteForPath(let method, let path) {
// There is no hadler registered for this path. This is a programming error.
lambdaResponse = (
HTTPResponse(status: .notFound), "There is no route registered for the path \(method) \(path)"
)
}
catch OpenAPILambdaHttpError.invalidMethod(let method) {
// the APIGateway HTTP verb is rejected by HTTTypes HTTPRequest.Method => HTTP 500
// this should never happen
lambdaResponse = (
HTTPResponse(status: .internalServerError),
"Type mismatch between APIGatewayV2 and HTTPRequest.Method. \(method) verb is rejected by HTTPRequest.Method 🤷‍♂️"
)
}
catch {
// some other error happened
lambdaResponse = (HTTPResponse(status: .internalServerError), "Unknown error: \(String(reflecting: error))")
}
// transform the OpenAPILambdaResponse to the Lambda Output
return openAPIService.output(from: lambdaResponse)
}
}