Skip to content

Fix Unstable Tests & Improve Documentation #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 4 additions & 3 deletions Sources/BreezeDynamoDBService/BreezeCodable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 3 additions & 2 deletions Sources/BreezeDynamoDBService/BreezeDynamoDBConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 5 additions & 3 deletions Sources/BreezeDynamoDBService/BreezeDynamoDBManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down
3 changes: 1 addition & 2 deletions Sources/BreezeDynamoDBService/BreezeDynamoDBManaging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -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.
Expand Down
16 changes: 12 additions & 4 deletions Sources/BreezeDynamoDBService/BreezeDynamoDBService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -82,20 +83,27 @@ 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.
/// This method is idempotent;
/// - 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()
}
}

4 changes: 2 additions & 2 deletions Sources/BreezeDynamoDBService/BreezeHTTPClientConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
12 changes: 12 additions & 0 deletions Sources/BreezeDynamoDBService/Docs.docc/theme-settings.json
Original file line number Diff line number Diff line change
@@ -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)"
}
}
}
5 changes: 4 additions & 1 deletion Sources/BreezeDynamoDBService/Foundation+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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? {
Expand Down
1 change: 1 addition & 0 deletions Sources/BreezeDynamoDBService/ListResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<Item: CodableSendable>: CodableSendable {
Expand Down
8 changes: 4 additions & 4 deletions Sources/BreezeLambdaAPI/APIGatewayV2Request+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
38 changes: 26 additions & 12 deletions Sources/BreezeLambdaAPI/BreezeAPIConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand All @@ -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(),
Expand Down
2 changes: 1 addition & 1 deletion Sources/BreezeLambdaAPI/BreezeEmptyResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
26 changes: 21 additions & 5 deletions Sources/BreezeLambdaAPI/BreezeLambdaAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>` 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<T: BreezeCodable>: Service {

let logger = Logger(label: "service-group-breeze-lambda-api")
Expand Down Expand Up @@ -67,8 +84,7 @@ public actor BreezeLambdaAPI<T: BreezeCodable>: 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.
Expand Down
2 changes: 1 addition & 1 deletion Sources/BreezeLambdaAPI/BreezeLambdaAPIError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 6 additions & 4 deletions Sources/BreezeLambdaAPI/BreezeLambdaHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -128,7 +130,7 @@ struct BreezeLambdaHandler<T: BreezeCodable>: 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<T> = try await dbManager.listItems(key: key, limit: limit)
return APIGatewayV2Response(with: result, statusCode: .ok)
} catch {
Expand Down
Loading