diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 2a064dd..8e9ee20 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM swift:6.2.0 +FROM swift:6.2.3 RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ && apt-get -y install --no-install-recommends make git \ No newline at end of file diff --git a/.github/workflows/swift-test.yml b/.github/workflows/swift-test.yml index 0a968b2..fbdcb27 100644 --- a/.github/workflows/swift-test.yml +++ b/.github/workflows/swift-test.yml @@ -18,8 +18,7 @@ jobs: strategy: matrix: image: - - swift:6.1.2 - - swift:6.2.0 + - swift:6.2.3 services: localstack: image: localstack/localstack diff --git a/Package.swift b/Package.swift index d5ebb6e..7dec751 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 6.1 +// swift-tools-version: 6.2 import PackageDescription @@ -31,14 +31,16 @@ let package = Package( .package(url: "https://github.com/swift-server/swift-service-lifecycle.git", from: "2.0.0"), .package(url: "https://github.com/soto-project/soto.git", from: "7.0.0"), .package(url: "https://github.com/apple/swift-log.git", from: "1.6.2"), - .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"), + .package(url: "https://github.com/apple/swift-configuration", from: "1.0.0") ], targets: [ .executableTarget( name: "BreezeLambdaItemAPI", dependencies: [ - "BreezeLambdaAPI" - ] + "BreezeLambdaAPI", + .product(name: "Configuration", package: "swift-configuration") + ], + resources: [.copy("Resources")] ), .target( name: "BreezeDynamoDBService", @@ -54,6 +56,7 @@ let package = Package( .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"), .product(name: "AWSLambdaEvents", package: "swift-aws-lambda-events"), .product(name: "ServiceLifecycle", package: "swift-service-lifecycle"), + .product(name: "Configuration", package: "swift-configuration"), "BreezeDynamoDBService" ] ), diff --git a/README.md b/README.md index a3e1e75..4017c4a 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Add the dependency `BreezeLambdaDynamoDBAPI` to a package: ```swift -// swift-tools-version:6.1 +// swift-tools-version:6.2 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription diff --git a/Sources/BreezeLambdaAPI/BreezeAPIConfiguration.swift b/Sources/BreezeLambdaAPI/BreezeAPIConfiguration.swift index 21b1a79..e38f629 100644 --- a/Sources/BreezeLambdaAPI/BreezeAPIConfiguration.swift +++ b/Sources/BreezeLambdaAPI/BreezeAPIConfiguration.swift @@ -7,10 +7,10 @@ import SotoDynamoDB import BreezeDynamoDBService -import AWSLambdaRuntime +import Configuration /// Defines the configuration for the Breeze Lambda API. -public protocol APIConfiguring { +public protocol APIConfiguring: Sendable { var dbTimeout: Int64 { get } func operation() throws -> BreezeOperation func getConfig() throws -> BreezeDynamoDBConfig @@ -26,11 +26,30 @@ public protocol APIConfiguring { /// - `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 { + private enum Keys { + static let handler: ConfigKey = "_HANDLER" + static let awsRegion: ConfigKey = "AWS_REGION" + static let tableName: ConfigKey = "DYNAMO_DB_TABLE_NAME" + static let keyName: ConfigKey = "DYNAMO_DB_KEY" + static let localstackEndpoint: ConfigKey = "LOCALSTACK_ENDPOINT" + static let dbTimeout: ConfigKey = "BREEZE_DB_TIMEOUT" + } - public init() {} + private let reader: ConfigReader - /// Timeout for database operations in seconds. - public let dbTimeout: Int64 = 30 + /// Creates the configuration using the supplied providers. Defaults to environment variables. + public init( + reader: ConfigReader = ConfigReader( + providers: [EnvironmentVariablesProvider()] + ) + ) { + self.reader = reader + } + + /// Timeout for database operations in seconds, configurable via `BREEZE_DB_TIMEOUT`. + public var dbTimeout: Int64 { + Int64(reader.int(forKey: Keys.dbTimeout, default: 30)) + } /// The operation handler for Breeze operations. /// @@ -43,7 +62,7 @@ public struct BreezeAPIConfiguration: APIConfiguring { /// /// See BreezeOperation for more details. public func operation() throws -> BreezeOperation { - guard let handler = Lambda.env("_HANDLER"), + guard let handler = reader.string(forKey: Keys.handler), let operation = BreezeOperation(handler: handler) else { throw BreezeLambdaAPIError.invalidHandler @@ -78,12 +97,10 @@ public struct BreezeAPIConfiguration: APIConfiguring { /// /// This method is used to determine the AWS region where the DynamoDB table is located. func currentRegion() -> Region { - if let awsRegion = Lambda.env("AWS_REGION") { - let value = Region(rawValue: awsRegion) - return value - } else { - return .useast1 + if let awsRegion = reader.string(forKey: Keys.awsRegion) { + return Region(rawValue: awsRegion) } + return .useast1 } /// Returns the DynamoDB table name from the `DYNAMO_DB_TABLE_NAME` environment variable. @@ -92,7 +109,7 @@ public struct BreezeAPIConfiguration: APIConfiguring { /// This method is used to retrieve the name of the DynamoDB table that will be used by the Breeze Lambda API. /// - Important: The table name is essential for performing operations on the DynamoDB table. func tableName() throws -> String { - guard let tableName = Lambda.env("DYNAMO_DB_TABLE_NAME") else { + guard let tableName = reader.string(forKey: Keys.tableName) else { throw BreezeLambdaAPIError.tableNameNotFound } return tableName @@ -104,7 +121,7 @@ public struct BreezeAPIConfiguration: APIConfiguring { /// This method is used to retrieve the name of the primary key in the DynamoDB table that will be used by the Breeze Lambda API. /// - Important: The key name is essential for identifying items in the DynamoDB table. func keyName() throws -> String { - guard let keyName = Lambda.env("DYNAMO_DB_KEY") else { + guard let keyName = reader.string(forKey: Keys.keyName) else { throw BreezeLambdaAPIError.keyNameNotFound } return keyName @@ -120,7 +137,7 @@ public struct BreezeAPIConfiguration: APIConfiguring { /// - To set it you need to set the `LOCALSTACK_ENDPOINT` environment variable to the URL of your LocalStack instance. /// - The Default LocalStack endpoint is `http://localhost:4566` func endpoint() -> String? { - if let localstack = Lambda.env("LOCALSTACK_ENDPOINT"), + if let localstack = reader.string(forKey: Keys.localstackEndpoint), !localstack.isEmpty { return localstack } diff --git a/Sources/BreezeLambdaAPI/BreezeLambdaAPI.swift b/Sources/BreezeLambdaAPI/BreezeLambdaAPI.swift index 854b3a7..d7fcd09 100644 --- a/Sources/BreezeLambdaAPI/BreezeLambdaAPI.swift +++ b/Sources/BreezeLambdaAPI/BreezeLambdaAPI.swift @@ -71,7 +71,7 @@ public actor BreezeLambdaAPI: Service { let breezeApi = BreezeLambdaHandler(dbManager: dbManager, operation: operation) let runtime = LambdaRuntime(body: breezeApi.handle) self.serviceGroup = ServiceGroup( - services: [runtime, dynamoDBService], + services: [dynamoDBService, runtime], gracefulShutdownSignals: [.sigint], cancellationSignals: [.sigterm], logger: logger diff --git a/Sources/BreezeLambdaAPI/Docs.docc/Docs.md b/Sources/BreezeLambdaAPI/Docs.docc/Docs.md index 73be241..9cf9a7f 100644 --- a/Sources/BreezeLambdaAPI/Docs.docc/Docs.md +++ b/Sources/BreezeLambdaAPI/Docs.docc/Docs.md @@ -45,7 +45,7 @@ public protocol BreezeCodable: Codable, Sendable { ### Add the dependency ```swift -// swift-tools-version:6.1 +// swift-tools-version:6.2 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription diff --git a/Sources/BreezeLambdaItemAPI/BreezeLambdaItemAPI.swift b/Sources/BreezeLambdaItemAPI/BreezeLambdaItemAPI.swift index a4a352f..b077e47 100644 --- a/Sources/BreezeLambdaItemAPI/BreezeLambdaItemAPI.swift +++ b/Sources/BreezeLambdaItemAPI/BreezeLambdaItemAPI.swift @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +import Configuration import BreezeLambdaAPI import BreezeDynamoDBService @@ -39,31 +40,20 @@ struct Item: Codable { /// BreezeCodable is a protocol that allows the Item struct to be used with Breeze Lambda API. extension Item: BreezeCodable { } -/// APIConfiguration is a struct that conforms to APIConfiguring. -/// It provides the configuration for the Breeze Lambda API, including the DynamoDB table name, key name, and endpoint. -/// It also specifies the operation to be performed, which in this case is listing items. -struct APIConfiguration: APIConfiguring { - let dbTimeout: Int64 = 30 - func operation() throws -> BreezeOperation { - .list - } - - /// Get the configuration for the DynamoDB service. - /// It specifies the region, table name, key name, and endpoint. - /// In this example, it uses a local Localstack endpoint for testing purposes. - /// You can change the region, table name, key name, and endpoint as needed for your application. - /// Remove the endpoint for production use. - func getConfig() throws -> BreezeDynamoDBConfig { - BreezeDynamoDBConfig(region: .useast1, tableName: "Breeze", keyName: "itemKey", endpoint: "http://localstack:4566") - } -} @main struct BreezeLambdaItemAPI { static func main() async throws { #if DEBUG do { - let lambdaAPIService = try await BreezeLambdaAPI(apiConfig: APIConfiguration()) + let config = ConfigReader( + providers: [ + EnvironmentVariablesProvider(), + try await FileProvider(filePath: "Sources/BreezeLambdaItemAPI/Resources/breeze-item-config.json") + ] + ) + let configuration = BreezeAPIConfiguration(reader: config) + let lambdaAPIService = try await BreezeLambdaAPI(apiConfig: configuration) try await lambdaAPIService.run() } catch { print(error.localizedDescription) diff --git a/Sources/BreezeLambdaItemAPI/Resources/breeze-item-config.json b/Sources/BreezeLambdaItemAPI/Resources/breeze-item-config.json new file mode 100644 index 0000000..f7e3de8 --- /dev/null +++ b/Sources/BreezeLambdaItemAPI/Resources/breeze-item-config.json @@ -0,0 +1,8 @@ +{ + "_HANDLER": "BreezeLambdaItemAPI.create", + "AWS_REGION": "us-east-1", + "DYNAMO_DB_TABLE_NAME": "Breeze", + "DYNAMO_DB_KEY": "itemKey", + "LOCALSTACK_ENDPOINT": "http://localstack:4566", + "BREEZE_DB_TIMEOUT": 30 +}