diff --git a/Makefile b/Makefile index 30ed515..09bf950 100644 --- a/Makefile +++ b/Makefile @@ -49,6 +49,12 @@ generate_docc: --hosting-base-path "https://swift-serverless.github.io/BreezeLambdaDynamoDBAPI/BreezeDynamoDBService/" \ --output-path docs/BreezeDynamoDBService +preview_docc_lambda_api: + swift package --disable-sandbox preview-documentation --target BreezeLambdaAPI + +preview_docc_dynamo_db_service: + swift package --disable-sandbox preview-documentation --target BreezeDynamoDBService + coverage: llvm-cov export $(TEST_PACKAGE) \ --instr-profile=$(SWIFT_BIN_PATH)/codecov/default.profdata \ diff --git a/README.md b/README.md index d03e893..a3e1e75 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Add the dependency `BreezeLambdaDynamoDBAPI` to a package: ```swift -// swift-tools-version:5.7 +// swift-tools-version:6.1 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription diff --git a/Sources/BreezeDynamoDBService/BreezeCodable.swift b/Sources/BreezeDynamoDBService/BreezeCodable.swift index ccb0006..5a5f360 100644 --- a/Sources/BreezeDynamoDBService/BreezeCodable.swift +++ b/Sources/BreezeDynamoDBService/BreezeCodable.swift @@ -18,12 +18,13 @@ import FoundationEssentials import Foundation #endif -/// CodableSendable is a protocol that combines Sendable and Codable. +/// Protocol that combines Sendable and Codable. public protocol CodableSendable: Sendable, Codable { } -/// BreezeCodable is a protocol that extends CodableSendable to include properties +/// Protocol that extends CodableSendable to include properties /// for a key, creation date, and update date. -/// It is designed to be used with Breeze services that require these common fields +/// +/// BreezeCodable is designed to be used with Breeze services that require these common fields /// for items stored in a database, such as DynamoDB. /// - Parameters: /// - key: A unique identifier for the item. diff --git a/Sources/BreezeDynamoDBService/BreezeDynamoDBConfig.swift b/Sources/BreezeDynamoDBService/BreezeDynamoDBConfig.swift index 40b69c9..47d3d1f 100644 --- a/Sources/BreezeDynamoDBService/BreezeDynamoDBConfig.swift +++ b/Sources/BreezeDynamoDBService/BreezeDynamoDBConfig.swift @@ -14,8 +14,9 @@ import SotoCore -/// BreezeDynamoDBConfig is a configuration structure for Breeze DynamoDB service. -/// It contains the necessary parameters to connect to a DynamoDB instance, including the region, table name, key name, and an optional endpoint. +/// Configuration structure for Breeze DynamoDB service. +/// +/// BreezeDynamoDBConfig contains the necessary parameters to connect to a DynamoDB instance, including the region, table name, key name, and an optional endpoint. public struct BreezeDynamoDBConfig: Sendable { /// Initializes a new instance of BreezeDynamoDBConfig. diff --git a/Sources/BreezeDynamoDBService/BreezeDynamoDBManager.swift b/Sources/BreezeDynamoDBService/BreezeDynamoDBManager.swift index 8e19312..57a68b5 100644 --- a/Sources/BreezeDynamoDBService/BreezeDynamoDBManager.swift +++ b/Sources/BreezeDynamoDBService/BreezeDynamoDBManager.swift @@ -16,10 +16,12 @@ import struct Foundation.Date import NIO import SotoDynamoDB -/// BreezeDynamoDBManager is a manager for handling DynamoDB operations in Breeze. -/// It provides methods to create, read, update, delete, and list items in a DynamoDB table. +/// Manager for handling DynamoDB operations in Breeze. +/// +/// BreezeDynamoDBManager provides methods to create, read, update, delete, and list items in a DynamoDB table. +/// /// It conforms to the BreezeDynamoDBManaging protocol, which defines the necessary operations for Breeze's DynamoDB integration. -/// - Note: This manager requires a DynamoDB instance, a table name, and a key name to operate. +/// - Note: BreezeDynamoDBManager requires a DynamoDB instance, a table name, and a key name to operate. /// It uses the SotoDynamoDB library to interact with AWS DynamoDB services. public struct BreezeDynamoDBManager: BreezeDynamoDBManaging { diff --git a/Sources/BreezeDynamoDBService/BreezeDynamoDBManaging.swift b/Sources/BreezeDynamoDBService/BreezeDynamoDBManaging.swift index 3733b3b..ca4f00d 100644 --- a/Sources/BreezeDynamoDBService/BreezeDynamoDBManaging.swift +++ b/Sources/BreezeDynamoDBService/BreezeDynamoDBManaging.swift @@ -14,7 +14,7 @@ import SotoDynamoDB -/// BreezeDynamoDBManaging is a protocol that defines the methods for managing DynamoDB items. +/// Defines the methods for managing DynamoDB items. public protocol BreezeDynamoDBManaging: Sendable { /// The keyName is the name of the primary key in the DynamoDB table. var keyName: String { get } @@ -53,7 +53,6 @@ public protocol BreezeDynamoDBManaging: Sendable { /// Deletes an item from the DynamoDB table. /// - Parameter item: The item to delete, conforming to BreezeCodable. /// - Throws: An error if the item could not be deleted. - /// - Returns: Void if the item was successfully deleted. /// - Note: /// - The item must conform to BreezeCodable. /// - The item must have the same primary key as an existing item in the table. diff --git a/Sources/BreezeDynamoDBService/BreezeDynamoDBService.swift b/Sources/BreezeDynamoDBService/BreezeDynamoDBService.swift index 89e6d79..819da6d 100644 --- a/Sources/BreezeDynamoDBService/BreezeDynamoDBService.swift +++ b/Sources/BreezeDynamoDBService/BreezeDynamoDBService.swift @@ -17,14 +17,15 @@ import AsyncHTTPClient import ServiceLifecycle import Logging -/// BreezeDynamoDBServing -/// A protocol that defines the interface for a Breeze DynamoDB service. -/// It provides methods to access the database manager and to gracefully shutdown the service. +/// Defines the interface for a Breeze DynamoDB service. +/// +/// Provides methods to access the database manager and to gracefully shutdown the service. public protocol BreezeDynamoDBServing: Actor { func dbManager() async -> BreezeDynamoDBManaging func gracefulShutdown() throws } +/// Provides methods to access the DynamoDB database manager and to gracefully shutdown the service. public actor BreezeDynamoDBService: BreezeDynamoDBServing { private let dbManager: BreezeDynamoDBManaging @@ -82,6 +83,7 @@ public actor BreezeDynamoDBService: BreezeDynamoDBServing { } /// Gracefully shutdown the service and its components. + /// /// - Throws: An error if the shutdown process fails. /// This method ensures that the AWS client and HTTP client are properly shutdown before marking the service as shutdown. /// It also logs the shutdown process. @@ -89,13 +91,19 @@ public actor BreezeDynamoDBService: BreezeDynamoDBServing { /// - Important: This method must be called at leat once to ensure that resources are released properly. If the method is not called, it will lead to a crash. public func gracefulShutdown() throws { guard !isShutdown else { return } + isShutdown = true logger.info("Stopping DynamoDBService...") try awsClient.syncShutdown() logger.info("DynamoDBService is stopped.") logger.info("Stopping HTTPClient...") try httpClient.syncShutdown() logger.info("HTTPClient is stopped.") - isShutdown = true + } + + deinit { + guard !isShutdown else { return } + try? awsClient.syncShutdown() + try? httpClient.syncShutdown() } } diff --git a/Sources/BreezeDynamoDBService/BreezeHTTPClientConfig.swift b/Sources/BreezeDynamoDBService/BreezeHTTPClientConfig.swift index 271d718..cfd39d8 100644 --- a/Sources/BreezeDynamoDBService/BreezeHTTPClientConfig.swift +++ b/Sources/BreezeDynamoDBService/BreezeHTTPClientConfig.swift @@ -15,12 +15,12 @@ import Logging import NIOCore -/// BreezeClientServiceError defines the errors that can occur in the Breeze Client Service. +/// Defines the errors that can occur in the Breeze Client Service. public enum BreezeClientServiceError: Error { case invalidHttpClient } -/// BreezeHTTPClientConfig is a configuration structure for the Breeze HTTP client. +/// Configuration structure for the Breeze HTTP client. public struct BreezeHTTPClientConfig: Sendable { /// Initializes a new instance of BreezeHTTPClientConfig. diff --git a/Sources/BreezeDynamoDBService/Docs.docc/theme-settings.json b/Sources/BreezeDynamoDBService/Docs.docc/theme-settings.json new file mode 100644 index 0000000..e4bfe52 --- /dev/null +++ b/Sources/BreezeDynamoDBService/Docs.docc/theme-settings.json @@ -0,0 +1,12 @@ +{ + "theme": { + "color": { + "header": "#DE5E44", + "documentation-intro-title": "#FFFFFF", + "documentation-intro-fill": "linear-gradient(30deg, #DE5E44, #A2331D)", + "documentation-intro-accent": "#FFFFFF", + "documentation-intro-eyebrow": "#FFFFFF", + "link": "var(--color-header)" + } + } +} diff --git a/Sources/BreezeDynamoDBService/Foundation+Extension.swift b/Sources/BreezeDynamoDBService/Foundation+Extension.swift index 89dff5f..23f21cf 100644 --- a/Sources/BreezeDynamoDBService/Foundation+Extension.swift +++ b/Sources/BreezeDynamoDBService/Foundation+Extension.swift @@ -16,7 +16,8 @@ import class Foundation.DateFormatter import struct Foundation.Date import struct Foundation.TimeZone -/// This file contains extensions for DateFormatter, Date, and String to handle ISO 8601 date formatting and parsing. +/// Entension to DateFormatter to handle ISO 8601. +/// /// These extensions provide a convenient way to convert between `Date` objects and their ISO 8601 string representations. extension DateFormatter { static var iso8061: DateFormatter { @@ -27,6 +28,7 @@ extension DateFormatter { } } +/// Entension to Date to handle ISO 8601. extension Date { /// Returns a string representation of the date in ISO 8601 format. var iso8601: String { @@ -35,6 +37,7 @@ extension Date { } } +/// Entension to String to handle ISO 8601. extension String { /// Attempts to parse the string as an ISO 8601 date. var iso8601: Date? { diff --git a/Sources/BreezeDynamoDBService/ListResponse.swift b/Sources/BreezeDynamoDBService/ListResponse.swift index fa94eec..7f73126 100644 --- a/Sources/BreezeDynamoDBService/ListResponse.swift +++ b/Sources/BreezeDynamoDBService/ListResponse.swift @@ -19,6 +19,7 @@ import Foundation #endif /// Model representing a paginated list response from a DynamoDB operation. +/// /// This struct contains an array of items and an optional last evaluated key for pagination. /// This struct conforms to `CodableSendable`, allowing it to be encoded and decoded for network transmission or storage. public struct ListResponse: CodableSendable { diff --git a/Sources/BreezeLambdaAPI/APIGatewayV2Request+Extensions.swift b/Sources/BreezeLambdaAPI/APIGatewayV2Request+Extensions.swift index 478972e..db4883e 100644 --- a/Sources/BreezeLambdaAPI/APIGatewayV2Request+Extensions.swift +++ b/Sources/BreezeLambdaAPI/APIGatewayV2Request+Extensions.swift @@ -16,11 +16,11 @@ import struct AWSLambdaEvents.APIGatewayV2Request import class Foundation.JSONDecoder extension APIGatewayV2Request { - /// queryStringParameter - /// - Parameter param: Query string parameter + /// Attempt to convert the query string parameter to an Integer value. + /// - Parameter key: The `key` of the query string parameter. /// - Returns: Query string Int value for parameter param if exists - public func queryStringParameter(_ param: String) -> Int? { - guard let value = queryStringParameters?[param] else { + public func queryStringParameterToInt(_ key: String) -> Int? { + guard let value = queryStringParameters?[key] else { return nil } return Int(value) diff --git a/Sources/BreezeLambdaAPI/BreezeAPIConfiguration.swift b/Sources/BreezeLambdaAPI/BreezeAPIConfiguration.swift index 7f622bf..f4aa1a0 100644 --- a/Sources/BreezeLambdaAPI/BreezeAPIConfiguration.swift +++ b/Sources/BreezeLambdaAPI/BreezeAPIConfiguration.swift @@ -9,30 +9,38 @@ import SotoDynamoDB import BreezeDynamoDBService import AWSLambdaRuntime -/// APIConfiguring is a protocol that defines the configuration for the Breeze Lambda API. +/// Defines the configuration for the Breeze Lambda API. public protocol APIConfiguring { var dbTimeout: Int64 { get } func operation() throws -> BreezeOperation func getConfig() throws -> BreezeDynamoDBConfig } -/// BreezeAPIConfiguration is a struct that conforms to APIConfiguring. -/// It provides the necessary configuration for the Breeze Lambda API, including the DynamoDB table name, key name, and AWS region. -/// It also defines the operation handler for Breeze operations. +/// A struct that conforms to APIConfiguring protocol, providing essential configuration for Lambda functions that interact with DynamoDB. +/// +/// It fetches the necessary configuration from environment variables, such as the Handler, AWS region, DynamoDB table name, and key name. +/// +/// To configure the Lambda function, you need to set up the following environment variables: +/// - `_HANDLER`: The handler for the Lambda function, in the format `module.operation`. +/// - `AWS_REGION`: The AWS region where the DynamoDB table is located. +/// - `DYNAMO_DB_TABLE_NAME`: The name of the DynamoDB table. +/// - `DYNAMO_DB_KEY`: The name of the primary key in the DynamoDB table. public struct BreezeAPIConfiguration: APIConfiguring { public init() {} - /// The timeout for database operations in seconds. + /// Timeout for database operations in seconds. public let dbTimeout: Int64 = 30 /// The operation handler for Breeze operations. + /// + /// Resturns the operation that will be executed by the Breeze Lambda API. /// This method retrieves the handler from the environment variable `_HANDLER`. /// - Throws: `BreezeLambdaAPIError.invalidHandler` if the handler is not found or cannot be parsed. /// - Returns: A `BreezeOperation` instance initialized with the handler. /// - /// This method is used to determine the operation that will be executed by the Breeze Lambda API. - /// It expects the `_HANDLER` environment variable to be set, which should contain the handler in the format `module.function`. + /// - Note: It expects the `_HANDLER` environment variable to be set in the format `module.operation`. + /// /// See BreezeOperation for more details. public func operation() throws -> BreezeOperation { guard let handler = Lambda.env("_HANDLER"), @@ -43,12 +51,18 @@ public struct BreezeAPIConfiguration: APIConfiguring { return operation } - /// Returns the configuration for the Breeze DynamoDB service. - /// - Throws: - /// - `BreezeLambdaAPIError.tableNameNotFound` if the DynamoDB table name is not found in the environment variables. - /// - `BreezeLambdaAPIError.keyNameNotFound` if the DynamoDB key name is not found in the environment variables. + /// Gets the configuration from the process environment. /// - /// This method retrieves the AWS region, DynamoDB table name, key name, and optional endpoint from the environment variables. + /// - Throws: + /// - `BreezeLambdaAPIError.tableNameNotFound` if the DynamoDB table name is not found in the environment variables. + /// - `BreezeLambdaAPIError.keyNameNotFound` if the DynamoDB key name is not found in the environment variables. + /// - Returns: A `BreezeDynamoDBConfig` instance containing the configuration for the Breeze DynamoDB service. + /// This method is used to retrieve the necessary configuration for the Breeze Lambda API to interact with DynamoDB. + /// It includes the AWS region, DynamoDB table name, key name, and an optional endpoint for LocalStack. + /// - Important: The configuration is essential for the Breeze Lambda API to function correctly with DynamoDB. This method retrieves the configuration from environment variables: + /// - `AWS_REGION`: The AWS region where the DynamoDB table is located. + /// - `DYNAMO_DB_TABLE_NAME`: The name of the DynamoDB table. + /// - `DYNAMO_DB_KEY`: The name of the primary key in the DynamoDB table. public func getConfig() throws -> BreezeDynamoDBConfig { BreezeDynamoDBConfig( region: currentRegion(), diff --git a/Sources/BreezeLambdaAPI/BreezeEmptyResponse.swift b/Sources/BreezeLambdaAPI/BreezeEmptyResponse.swift index cfb1a85..82d6e49 100644 --- a/Sources/BreezeLambdaAPI/BreezeEmptyResponse.swift +++ b/Sources/BreezeLambdaAPI/BreezeEmptyResponse.swift @@ -15,5 +15,5 @@ import class Foundation.JSONDecoder import class Foundation.JSONEncoder -/// A simple struct representing an empty response for Breeze Lambda API. +/// Empty Codable response. struct BreezeEmptyResponse: Codable {} diff --git a/Sources/BreezeLambdaAPI/BreezeLambdaAPI.swift b/Sources/BreezeLambdaAPI/BreezeLambdaAPI.swift index 67fab77..a13c55f 100644 --- a/Sources/BreezeLambdaAPI/BreezeLambdaAPI.swift +++ b/Sources/BreezeLambdaAPI/BreezeLambdaAPI.swift @@ -17,9 +17,26 @@ import ServiceLifecycle import BreezeDynamoDBService import AWSLambdaRuntime -/// BreezeLambdaAPI is a service that integrates with AWS Lambda to provide a Breeze API for DynamoDB operations. -/// It supports operations such as create, read, update, delete, and list items in a DynamoDB table using a BreezeCodable. -/// This Service is designed to work with ServiceLifecycle, allowing it to be run and stopped gracefully. +/// Actor implementing a service which transforms API Gateway events containing BreezeCodable items into DynamoDB operations. +/// +/// `BreezeLambdaAPI` acts as a bridge between AWS API Gateway and DynamoDB, handling the conversion +/// of incoming requests to the appropriate database operations. The generic parameter `T` represents +/// the data model type that conforms to `BreezeCodable`, ensuring type-safe operations. +/// +/// It supports standard CRUD operations: +/// - Create: Insert new items into the DynamoDB table +/// - Read: Retrieve items by their identifier +/// - Update: Modify existing items in the table +/// - Delete: Remove items from the table +/// - List: Query and retrieve multiple items matching specific criteria +/// +/// This service leverages the `ServiceLifecycle` package to manage its lifecycle, providing +/// graceful shutdown mechanism. It internally manages a `ServiceGroup` containing +/// a `BreezeLambdaService` and a `BreezeDynamoDBService`, which handle the actual processing +/// of requests and database operations. +/// +/// The service is designed to be efficient and scalable for AWS Lambda environments, with configurable +/// timeout settings and comprehensive logging for monitoring and debugging. public actor BreezeLambdaAPI: Service { let logger = Logger(label: "service-group-breeze-lambda-api") @@ -67,8 +84,7 @@ public actor BreezeLambdaAPI: Service { } } - /// Runs the BreezeLambdaAPI service. - /// This method starts the internal ServiceGroup and begins processing requests. + /// Starts the internal ServiceGroup and begins processing requests. /// - Throws: An error if the service fails to start or if an issue occurs during execution. /// /// The internal ServiceGroup will handle the lifecycle of the BreezeLambdaAPI, including starting and stopping the service gracefully. diff --git a/Sources/BreezeLambdaAPI/BreezeLambdaAPIError.swift b/Sources/BreezeLambdaAPI/BreezeLambdaAPIError.swift index 2d10e67..056846e 100644 --- a/Sources/BreezeLambdaAPI/BreezeLambdaAPIError.swift +++ b/Sources/BreezeLambdaAPI/BreezeLambdaAPIError.swift @@ -18,7 +18,7 @@ import FoundationEssentials import Foundation #endif -/// BreezeLambdaAPIError is an enumeration that defines various errors that can occur in the Breeze Lambda API. +/// Enumeration that defines various errors that can occur in the Breeze Lambda API. enum BreezeLambdaAPIError: Error { /// Indicates that an item is invalid. case invalidItem diff --git a/Sources/BreezeLambdaAPI/BreezeLambdaHandler.swift b/Sources/BreezeLambdaAPI/BreezeLambdaHandler.swift index 1bdc95c..3e65001 100644 --- a/Sources/BreezeLambdaAPI/BreezeLambdaHandler.swift +++ b/Sources/BreezeLambdaAPI/BreezeLambdaHandler.swift @@ -17,10 +17,12 @@ import AWSLambdaRuntime import BreezeDynamoDBService import Logging -/// BreezeLambdaHandler implements a Lambda handler for Breeze operations. -/// It conforms to the `LambdaHandler` protocol and is generic over a type `T` that conforms to `BreezeCodable`. +/// Lambda handler implementing the followig operations: create, read, update, delete, and list. /// -/// This handler supports the following operations: +/// Conforms to the `LambdaHandler` protocol and is generic over a type `T` that conforms to `BreezeCodable`. +/// Implements the logic for handling Breeze operations on a DynamoDB table by utilizing a `BreezeDynamoDBManaging` instance. +/// +/// The handler supports the following operations: /// /// - Create: Creates a new item in the DynamoDB table. /// - Read: Reads an item from the DynamoDB table based on the provided key. @@ -128,7 +130,7 @@ struct BreezeLambdaHandler: LambdaHandler, Sendable { func listLambdaHandler(context: LambdaContext, event: APIGatewayV2Request) async -> APIGatewayV2Response { do { let key = event.queryStringParameters?["exclusiveStartKey"] - let limit: Int? = event.queryStringParameter("limit") + let limit: Int? = event.queryStringParameterToInt("limit") let result: ListResponse = try await dbManager.listItems(key: key, limit: limit) return APIGatewayV2Response(with: result, statusCode: .ok) } catch { diff --git a/Sources/BreezeLambdaAPI/BreezeLambdaService.swift b/Sources/BreezeLambdaAPI/BreezeLambdaService.swift index e0615c8..94b5c5d 100644 --- a/Sources/BreezeLambdaAPI/BreezeLambdaService.swift +++ b/Sources/BreezeLambdaAPI/BreezeLambdaService.swift @@ -25,32 +25,52 @@ import FoundationEssentials import Foundation #endif -/// BreezeLambdaService is an actor that provides a service for handling AWS Lambda events using BreezeCodable models. -/// It conforms to the `Service` protocol and implements the `handler` method to process incoming events. -/// It manages the lifecycle of a BreezeLambdaHandler, which is responsible for handling the actual business logic. -/// It also provides a method to run the service and handle graceful shutdowns. -/// it operates on a BreezeCodable model type `T` that conforms to the BreezeCodable protocol. +/// Service for processing AWS API Gateway events with BreezeCodable models. +/// +/// `BreezeLambdaService` is a key component in the serverless architecture that: +/// - Acts as a bridge between AWS Lambda runtime and DynamoDB operations +/// - Processes incoming API Gateway events through a type-safe interface +/// - Manages the lifecycle of AWS Lambda handlers for BreezeCodable models +/// - Coordinates graceful shutdown procedures to ensure clean resource release +/// +/// The service leverages Swift concurrency features through the actor model to ensure +/// thread-safe access to shared resources while processing multiple Lambda invocations. +/// It delegates the actual processing of events to a specialized `BreezeLambdaHandler` +/// which performs the database operations via the injected `BreezeDynamoDBService`. +/// +/// This service is designed to be initialized and run as part of a `ServiceGroup` +/// within the AWS Lambda execution environment. actor BreezeLambdaService: Service { - /// DynamoDBService is an instance of BreezeDynamoDBServing that provides access to the DynamoDB database manager. + /// Database service that provides access to the underlying DynamoDB operations. + /// + /// This service is responsible for all database interactions and connection management. let dynamoDBService: BreezeDynamoDBServing - /// Operation is an instance of BreezeOperation that defines the operation to be performed by the BreezeLambdaHandler. + + /// Operation type that determines the behavior of this service instance. + /// + /// Defines whether this Lambda will perform create, read, update, delete, or list operation let operation: BreezeOperation - /// Logger is an instance of Logger for logging messages during the service's operation. + + /// Logger instance for tracking service lifecycle events and errors. + /// + /// Used throughout the service to provide consistent logging patterns. let logger: Logger /// Initializes a new instance of `BreezeLambdaService`. /// - Parameters: - /// - dynamoDBService: An instance of `BreezeDynamoDBServing` that provides access to the DynamoDB database manager. - /// - operation: The `BreezeOperation` that defines the operation to be performed by the BreezeLambdaHandler. - /// - logger: A `Logger` instance for logging messages during the service's operation. + /// - dynamoDBService: Service providing DynamoDB operations and connection management + /// - operation: The specific CRUD operation this Lambda instance will perform + /// - logger: Logger instance for service monitoring and debugging init(dynamoDBService: BreezeDynamoDBServing, operation: BreezeOperation, logger: Logger) { self.dynamoDBService = dynamoDBService self.operation = operation self.logger = logger } - /// BreezeLambdaHandler is an optional instance of BreezeLambdaHandler that will handle the actual business logic. + /// Handler instance that processes business logic for the configured operation. + /// + /// Lazily initialized during the `run()` method to ensure proper service startup sequence. var breezeApi: BreezeLambdaHandler? /// Handler method that processes incoming AWS Lambda events. @@ -59,7 +79,8 @@ actor BreezeLambdaService: Service { return try await breezeApi.handle(event, context: context) } - /// Runs the BreezeLambdaService, initializing the BreezeLambdaHandler and starting the Lambda runtime. + /// Runs the service allowing graceful shutdown. + /// /// - Throws: An error if the service fails to initialize or run. func run() async throws { let dbManager = await dynamoDBService.dbManager() @@ -82,6 +103,7 @@ actor BreezeLambdaService: Service { } /// Runs a task with cancellation on graceful shutdown. + /// /// - Note: It's required to allow a full process shutdown without leaving tasks hanging. private func runTaskWithCancellationOnGracefulShutdown( operation: @escaping @Sendable () async throws -> Void, diff --git a/Sources/BreezeLambdaAPI/BreezeOperation.swift b/Sources/BreezeLambdaAPI/BreezeOperation.swift index 22aa6eb..70d14e2 100644 --- a/Sources/BreezeLambdaAPI/BreezeOperation.swift +++ b/Sources/BreezeLambdaAPI/BreezeOperation.swift @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -/// BreezeOperation is an enumeration that defines the operations supported by Breeze Lambda API. -/// It includes operations such as create, read, update, delete, and list. +/// Defines the operations supported by Breeze Lambda API. +/// +/// Supported operations are create, read, update, delete, and list. public enum BreezeOperation: String, Sendable { case create case read @@ -21,7 +22,7 @@ public enum BreezeOperation: String, Sendable { case delete case list - /// Initializes a BreezeOperation from a handler string. + /// Initializes from a handler string. /// /// - Parameter handler: A string representing the handler, typically in the format "module.operation". /// - Returns: An optional BreezeOperation if the handler string can be parsed successfully. diff --git a/Sources/BreezeLambdaAPI/Docs.docc/Docs.md b/Sources/BreezeLambdaAPI/Docs.docc/Docs.md new file mode 100644 index 0000000..73be241 --- /dev/null +++ b/Sources/BreezeLambdaAPI/Docs.docc/Docs.md @@ -0,0 +1,145 @@ +# ``BreezeLambdaAPI`` + +@Metadata { + @PageImage(purpose: icon, source: "wind") +} + +## Overview + +The BreezeLambdaAPI implements a Lambda which processes events from AWS API Gateway and performs CRUD operations on AWS DynamoDB, allowing you to build serverless applications with ease. + +![BreezeLambdaAPI Diagram](api) + +### Key Features + +- Serverless Architecture: Runs on AWS Lambda with API Gateway integration +- DynamoDB Integration: Seamless CRUD operations with DynamoDB +- Optimistic Concurrency Control: Ensures data integrity during updates and deletes +- Type Safety: Leverages Swift's type system with Codable support +- Swift Concurrency: Fully compatible with Swift's async/await model +- Service Lifecycle: Handles graceful shutdown and initialization of services +- Minimal Boilerplate: Focus on your business logic, not infrastructure code + +### API Operations + +- **Create**: Add new items to DynamoDB with automatic timestamp handling +- **Read**: Retrieve items using a unique key +- **Update**: Modify existing items with optimistic concurrency control +- **Delete**: Remove items with concurrency checks +- **List**: Retrieve all items with optional pagination + +### The BreezeCodable Protocol + +Your data models must conform to the `BreezeCodable` protocol, which extends `Codable` and provides additional properties for managing timestamps and keys. + +```swift +public protocol BreezeCodable: Codable, Sendable { + var key: String { get set } + var createdAt: String? { get set } + var updatedAt: String? { get set } +} +``` + +## Getting Started + +### Add the dependency + +```swift +// swift-tools-version:6.1 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "BreezeItemAPI", + platforms: [ + .macOS(.v13), + ], + products: [ + .executable(name: "ItemAPI", targets: ["ItemAPI"]), + ], + dependencies: [ + .package(url: "https://github.com/swift-sprinter/BreezeLambdaDynamoDBAPI.git", from: "0.4.0") + ], + targets: [ + .executableTarget( + name: "ItemAPI", + dependencies: [ + .product(name: "BreezeLambdaAPI", package: "Breeze"), + .product(name: "BreezeDynamoDBService", package: "Breeze"), + ] + ) + ] +) +``` + +### Define Your Data Model + +Create a `Codable` struct that conforms to the `BreezeCodable` protocol. This struct will represent the items you want to store in DynamoDB. + +```swift +import Foundation +import BreezeLambdaAPI +import BreezeDynamoDBService + +struct Item: Codable { + public var key: String + public let name: String + public let description: String + public var createdAt: String? + public var updatedAt: String? + + enum CodingKeys: String, CodingKey { + case key = "itemKey" + case name + case description + case createdAt + case updatedAt + } +} + +extension Item: BreezeCodable { } +``` + +### Implement the Lambda Handler + +Create a file named `main.swift` and implement the Lambda handler using the `BreezeLambdaAPI` class. + +This simple runner will handle the CRUD operations for your `Item` model. + +Once compiled, this will be your Lambda function and must be deployed to AWS Lambda. + +```swift +@main +struct BreezeLambdaItemAPI { + static func main() async throws { + do { + try await BreezeLambdaAPI().run() + } catch { + print(error.localizedDescription) + } + } +} +``` + +### Configure the Lambda + +To configure the Lambda function, you need to set up the following environment variables: +- `_HANDLER`: The handler for the Lambda function, in the format `module.operation`. +- `AWS_REGION`: The AWS region where the DynamoDB table is located. +- `DYNAMO_DB_TABLE_NAME`: The name of the DynamoDB table. +- `DYNAMO_DB_KEY`: The name of the primary key in the DynamoDB table. + +## Deployment + +Deploy your Lambda function using AWS CDK, SAM, Serverless or Terraform. The Lambda requires: + +1. API Gateway integration for HTTP requests +2. DynamoDB table with appropriate permissions +3. Environment variables for configuration + +For step-by-step deployment instructions and templates, see the [Breeze project repository](https://github.com/swift-serverless/Breeze) for more info on how to deploy it on AWS. + + + + diff --git a/Sources/BreezeLambdaAPI/Docs.docc/Resources/api.svg b/Sources/BreezeLambdaAPI/Docs.docc/Resources/api.svg new file mode 100644 index 0000000..44cd589 --- /dev/null +++ b/Sources/BreezeLambdaAPI/Docs.docc/Resources/api.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Sources/BreezeLambdaAPI/Docs.docc/Resources/wind.svg b/Sources/BreezeLambdaAPI/Docs.docc/Resources/wind.svg new file mode 100644 index 0000000..ce319fa --- /dev/null +++ b/Sources/BreezeLambdaAPI/Docs.docc/Resources/wind.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/Sources/BreezeLambdaAPI/Docs.docc/theme-settings.json b/Sources/BreezeLambdaAPI/Docs.docc/theme-settings.json new file mode 100644 index 0000000..e4bfe52 --- /dev/null +++ b/Sources/BreezeLambdaAPI/Docs.docc/theme-settings.json @@ -0,0 +1,12 @@ +{ + "theme": { + "color": { + "header": "#DE5E44", + "documentation-intro-title": "#FFFFFF", + "documentation-intro-fill": "linear-gradient(30deg, #DE5E44, #A2331D)", + "documentation-intro-accent": "#FFFFFF", + "documentation-intro-eyebrow": "#FFFFFF", + "link": "var(--color-header)" + } + } +} diff --git a/Sources/BreezeLambdaDynamoDBAPI.docc/BreezeLambdaDynamoDBAPI.md b/Sources/BreezeLambdaDynamoDBAPI.docc/BreezeLambdaDynamoDBAPI.md deleted file mode 100644 index 17a884b..0000000 --- a/Sources/BreezeLambdaDynamoDBAPI.docc/BreezeLambdaDynamoDBAPI.md +++ /dev/null @@ -1,16 +0,0 @@ -# ``BreezeLambdaDynamoDBAPI`` - -Implement a CRUD API based on AWS Lambda, APIGateway, DynamoDB. - -@Metadata { - @PageImage(purpose: icon, source: "Icon") -} - -## Essentials - -Learn how you can make the most of BreezeLambdaDynamoDBAPI with these guides: - -@Links(visualStyle: detailedGrid) { - - - - -}