diff --git a/.doc_gen/metadata/lambda_metadata.yaml b/.doc_gen/metadata/lambda_metadata.yaml index 9cc00078ee0..3c97190cf4e 100644 --- a/.doc_gen/metadata/lambda_metadata.yaml +++ b/.doc_gen/metadata/lambda_metadata.yaml @@ -284,6 +284,15 @@ lambda_DeleteFunction: excerpts: - snippet_tags: - lambda.rust.scenario.delete_function + Swift: + versions: + - sdk_version: 1 + github: swift/example_code/lambda/basics + excerpts: + - description: + snippet_tags: + - swift.lambda-basics.imports + - swift.lambda-basics.DeleteFunction services: lambda: {DeleteFunction} lambda_Invoke: @@ -699,6 +708,15 @@ lambda_UpdateFunctionConfiguration: excerpts: - snippet_tags: - lambda.rust.scenario.update_function_configuration + Swift: + versions: + - sdk_version: 1 + github: swift/example_code/lambda/basics + excerpts: + - description: + snippet_tags: + - swift.lambda-basics.imports + - swift.lambda-basics.UpdateFunctionConfiguration services: lambda: {UpdateFunctionConfiguration} lambda_ListFunctions: diff --git a/swift/example_code/lambda/README.md b/swift/example_code/lambda/README.md index 6190f766b5d..f05e14df9d8 100644 --- a/swift/example_code/lambda/README.md +++ b/swift/example_code/lambda/README.md @@ -40,11 +40,13 @@ Code examples that show you how to perform the essential operations within a ser Code excerpts that show you how to call individual service functions. -- [CreateFunction](basics/lambda-basics/Sources/entry.swift#L177) -- [GetFunction](basics/lambda-basics/Sources/entry.swift#L142) -- [Invoke](basics/lambda-basics/Sources/entry.swift#L313) -- [ListFunctions](basics/lambda-basics/Sources/entry.swift#L283) -- [UpdateFunctionCode](basics/lambda-basics/Sources/entry.swift#L236) +- [CreateFunction](basics/lambda-basics/Sources/entry.swift#L176) +- [DeleteFunction](basics/lambda-basics/Sources/entry.swift#L549) +- [GetFunction](basics/lambda-basics/Sources/entry.swift#L141) +- [Invoke](basics/lambda-basics/Sources/entry.swift#L366) +- [ListFunctions](basics/lambda-basics/Sources/entry.swift#L336) +- [UpdateFunctionCode](basics/lambda-basics/Sources/entry.swift#L237) +- [UpdateFunctionConfiguration](basics/lambda-basics/Sources/entry.swift#L284) diff --git a/swift/example_code/lambda/basics/calculator/Package.swift b/swift/example_code/lambda/basics/calculator/Package.swift index bcdfe598955..f5abca7f1a4 100644 --- a/swift/example_code/lambda/basics/calculator/Package.swift +++ b/swift/example_code/lambda/basics/calculator/Package.swift @@ -17,7 +17,7 @@ let package = Package( // Dependencies declare other packages that this package depends on. .package( url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", - from: "1.0.0-alpha"), + branch: "main"), ], targets: [ // Targets are the basic building blocks of a package, defining a module or a test suite. diff --git a/swift/example_code/lambda/basics/calculator/Sources/calculator.swift b/swift/example_code/lambda/basics/calculator/Sources/calculator.swift index 8adc51f5ea3..26760dbdfc7 100644 --- a/swift/example_code/lambda/basics/calculator/Sources/calculator.swift +++ b/swift/example_code/lambda/basics/calculator/Sources/calculator.swift @@ -51,63 +51,46 @@ struct Response: Encodable, Sendable { // snippet-end:[lambda.swift.calculator.types] -// snippet-start:[lambda.swift.calculator.handler] -/// A Swift AWS Lambda Runtime `LambdaHandler` lets you both perform needed -/// initialization and handle AWS Lambda requests. There are other handler -/// protocols available for other use cases. -@main -struct CalculatorLambda: LambdaHandler { +// snippet-start:[lambda.swift.calculator.runtime] +/// The Lambda function's entry point. Called by the Lambda runtime. +/// +/// - Parameters: +/// - event: The `Request` describing the request made by the +/// client. +/// - context: A `LambdaContext` describing the context in +/// which the lambda function is running. +/// +/// - Returns: A `Response` object that will be encoded to JSON and sent +/// to the client by the Lambda runtime. +let calculatorLambdaRuntime = LambdaRuntime { + (_ event: Request, context: LambdaContext) -> Response in + let action = event.action + var answer: Int? + var actionFunc: ((Int, Int) -> Int)? - // snippet-start:[lambda.swift.calculator.handler.init] - /// Initialize the AWS Lambda runtime. - /// - /// ^ The logger is a standard Swift logger. You can control the verbosity - /// by setting the `LOG_LEVEL` environment variable. - init(context: LambdaInitializationContext) async throws { - // Display the `LOG_LEVEL` configuration for this process. - context.logger.info( - "Log Level env var : \(ProcessInfo.processInfo.environment["LOG_LEVEL"] ?? "info" )" - ) - } - // snippet-end:[lambda.swift.calculator.handler.init] - - // snippet-start:[lambda.swift.calculator.handler.handle] - /// The Lambda function's entry point. Called by the Lambda runtime. - /// - /// - Parameters: - /// - event: The `Request` describing the request made by the - /// client. - /// - context: A `LambdaContext` describing the context in - /// which the lambda function is running. - /// - /// - Returns: A `Response` object that will be encoded to JSON and sent - /// to the client by the Lambda runtime. - func handle(_ event: Request, context: LambdaContext) async throws -> Response { - let action = event.action - var answer: Int? - var actionFunc: ((Int, Int) -> Int)? - - // Get the closure to run to perform the calculation. - - actionFunc = actions[action] + // Get the closure to run to perform the calculation. - guard let actionFunc else { - context.logger.error("Unrecognized operation '\(action)\'") - return Response(answer: nil) - } + actionFunc = await actions[action] - // Perform the calculation and return the answer. + guard let actionFunc else { + context.logger.error("Unrecognized operation '\(action)\'") + return Response(answer: nil) + } - answer = actionFunc(event.x, event.y) + // Perform the calculation and return the answer. - guard let answer else { - context.logger.error("Error computing \(event.x) \(action) \(event.y)") - } - context.logger.info("\(event.x) \(action) \(event.y) = \(answer)") + answer = actionFunc(event.x, event.y) - return Response(answer: answer) + guard let answer else { + context.logger.error("Error computing \(event.x) \(action) \(event.y)") } - // snippet-end:[lambda.swift.calculator.handler.handle] + context.logger.info("\(event.x) \(action) \(event.y) = \(answer)") + + return Response(answer: answer) } -// snippet-end:[lambda.swift.calculator.handler] +// snippet-end:[lambda.swift.calculator.runtime] + +// snippet-start:[lambda.swift.calculator.run] +try await calculatorLambdaRuntime.run() +// snippet-end:[lambda.swift.calculator.run] // snippet-end:[lambda.swift.calculator.complete] diff --git a/swift/example_code/lambda/basics/increment/Package.swift b/swift/example_code/lambda/basics/increment/Package.swift index 92b05d5b435..2bae1a13d6e 100644 --- a/swift/example_code/lambda/basics/increment/Package.swift +++ b/swift/example_code/lambda/basics/increment/Package.swift @@ -17,7 +17,7 @@ let package = Package( // Dependencies declare other packages that this package depends on. .package( url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", - from: "1.0.0-alpha"), + branch: "main"), ], targets: [ // Targets are the basic building blocks of a package, defining a module or a test suite. diff --git a/swift/example_code/lambda/basics/increment/Sources/increment.swift b/swift/example_code/lambda/basics/increment/Sources/increment.swift index a35121982aa..0f6822474d2 100644 --- a/swift/example_code/lambda/basics/increment/Sources/increment.swift +++ b/swift/example_code/lambda/basics/increment/Sources/increment.swift @@ -30,52 +30,37 @@ struct Response: Encodable, Sendable { // snippet-end:[lambda.swift.increment.types] -// snippet-start:[lambda.swift.increment.handler] -/// A Swift AWS Lambda Runtime `LambdaHandler` lets you both perform needed -/// initialization and handle AWS Lambda requests. There are other handler -/// protocols available for other use cases. -@main -struct IncrementLambda: LambdaHandler { +// snippet-start:[lambda.swift.increment.runtime] +/// The Lambda function body. +/// +/// - Parameters: +/// - event: The `Request` describing the request made by the +/// client. +/// - context: A `LambdaContext` describing the context in +/// which the lambda function is running. +/// +/// - Returns: A `Response` object that will be encoded to JSON and sent +/// to the client by the Lambda runtime. +let incrementLambdaRuntime = LambdaRuntime { + (event: Request, context: LambdaContext) -> Response in + let action = event.action + var answer: Int? - // snippet-start:[lambda.swift.increment.handler.init] - /// Initialize the AWS Lambda runtime. - /// - /// ^ The logger is a standard Swift logger. You can control the verbosity - /// by setting the `LOG_LEVEL` environment variable. - init(context: LambdaInitializationContext) async throws { - // Display the `LOG_LEVEL` configuration for this process. - context.logger.info( - "Log Level env var : \(ProcessInfo.processInfo.environment["LOG_LEVEL"] ?? "info" )" - ) + if action != "increment" { + context.logger.error("Unrecognized operation: \"\(action)\". The only supported action is \"increment\".") + } else { + answer = event.number + 1 + context.logger.info("The calculated answer is \(answer!).") } - // snippet-end:[lambda.swift.increment.handler.init] - // snippet-start:[lambda.swift.increment.handler.handle] - /// The Lambda function's entry point. Called by the Lambda runtime. - /// - /// - Parameters: - /// - event: The `Request` describing the request made by the - /// client. - /// - context: A `LambdaContext` describing the context in - /// which the lambda function is running. - /// - /// - Returns: A `Response` object that will be encoded to JSON and sent - /// to the client by the Lambda runtime. - func handle(_ event: Request, context: LambdaContext) async throws -> Response { - let action = event.action - var answer: Int? + let response = Response(answer: answer) + return response +} +// snippet-end:[lambda.swift.increment.runtime] - if action != "increment" { - context.logger.error("Unrecognized operation: \"\(action)\". The only supported action is \"increment\".") - } else { - answer = event.number + 1 - context.logger.info("The calculated answer is \(answer!).") - } +// Run the Lambda runtime code. - let response = Response(answer: answer) - return response - } - // snippet-end:[lambda.swift.increment.handler.handle] -} -// snippet-end:[lambda.swift.increment.handler] -// snippet-end:[lambda.swift.increment.complete] +// snippet-start:[lambda.swift.increment.run] +try await incrementLambdaRuntime.run() +// snippet-end:[lambda.swift.increment.run] +// snippet-end:[lambda.swift.increment.complete] \ No newline at end of file diff --git a/swift/example_code/lambda/basics/lambda-basics/Sources/ExampleError.swift b/swift/example_code/lambda/basics/lambda-basics/Sources/ExampleError.swift index f41027ee9bb..9adfb05ee76 100644 --- a/swift/example_code/lambda/basics/lambda-basics/Sources/ExampleError.swift +++ b/swift/example_code/lambda/basics/lambda-basics/Sources/ExampleError.swift @@ -26,6 +26,16 @@ enum ExampleError: Error { case listFunctionsError /// Unable to update the AWS Lambda function. case updateFunctionError + /// Unable to update the function configuration. + case updateFunctionConfigurationError + /// The environment response is missing after an + /// UpdateEnvironmentConfiguration attempt. + case environmentResponseMissingError + /// The environment variables are missing from the EnvironmentResponse and + /// no errors occurred. + case environmentVariablesMissingError + /// The log level is incorrect after attempting to set it. + case logLevelIncorrectError /// Unable to load the AWS Lambda function's Zip file. case zipFileReadError @@ -53,6 +63,14 @@ enum ExampleError: Error { return "Unable to list the AWS Lambda functions." case .updateFunctionError: return "Unable to update the AWS lambda function." + case .updateFunctionConfigurationError: + return "Unable to update the AWS lambda function configuration." + case .environmentResponseMissingError: + return "The environment is missing from the response after updating the function configuration." + case .environmentVariablesMissingError: + return "While no error occurred, no environment variables were returned following function configuration." + case .logLevelIncorrectError: + return "The log level is incorrect after attempting to set it to DEBUG." case .zipFileReadError: return "Unable to read the AWS Lambda function." } diff --git a/swift/example_code/lambda/basics/lambda-basics/Sources/entry.swift b/swift/example_code/lambda/basics/lambda-basics/Sources/entry.swift index ec0564c35c4..cf7e972d117 100644 --- a/swift/example_code/lambda/basics/lambda-basics/Sources/entry.swift +++ b/swift/example_code/lambda/basics/lambda-basics/Sources/entry.swift @@ -1,8 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // -/// An example that demonstrates how to watch an transcribe event stream to -/// transcribe audio from a file to the console. +/// An example demonstrating a variety of important AWS Lambda functions. // snippet-start:[swift.lambda-basics.imports-all] import ArgumentParser @@ -121,6 +120,10 @@ struct ExampleCommand: ParsableCommand { ) ) + guard let role = output.role else { + throw ExampleError.roleCreateError + } + // Wait for the role to be ready for use. _ = try await iamClient.waitUntilRoleExists( @@ -132,10 +135,6 @@ struct ExampleCommand: ParsableCommand { input: GetRoleInput(roleName: roleName) ) - guard let role = output.role else { - throw ExampleError.roleCreateError - } - return role } @@ -166,13 +165,13 @@ struct ExampleCommand: ParsableCommand { /// /// - Parameters: /// - lambdaClient: The `LambdaClient` to use. - /// - name: The name of the AWS Lambda function to create. + /// - functionName: The name of the AWS Lambda function to create. /// - roleArn: The ARN of the role to apply to the function. /// - path: The path of the Zip archive containing the function. /// /// - Returns: `true` if the AWS Lambda was successfully created; `false` /// if it wasn't. - func createFunction(lambdaClient: LambdaClient, name: String, + func createFunction(lambdaClient: LambdaClient, functionName: String, roleArn: String?, path: String) async throws -> Bool { // snippet-start:[swift.lambda-basics.CreateFunction] do { @@ -188,13 +187,15 @@ struct ExampleCommand: ParsableCommand { _ = try await lambdaClient.createFunction( input: CreateFunctionInput( code: LambdaClientTypes.FunctionCode(zipFile: zipData), - functionName: name, + functionName: functionName, handler: "handle", role: roleArn, runtime: .providedal2 ) ) } catch { + print("*** Error creating Lambda function:") + dump(error) return false } // snippet-end:[swift.lambda-basics.CreateFunction] @@ -207,7 +208,7 @@ struct ExampleCommand: ParsableCommand { minDelay: 0.5, maxDelay: 2 ), - input: GetFunctionInput(functionName: name) + input: GetFunctionInput(functionName: functionName) ) switch output.result { @@ -225,13 +226,13 @@ struct ExampleCommand: ParsableCommand { /// /// - Parameters: /// - lambdaClient: The `LambdaClient` to use. - /// - name: The name of the AWS Lambda function to update. + /// - functionName: The name of the AWS Lambda function to update. /// - path: The pathname of the Zip file containing the packaged Lambda /// function. /// - Throws: `ExampleError.zipFileReadError` /// - Returns: `true` if the function's code is updated successfully. /// Otherwise, returns `false`. - func updateFunctionCode(lambdaClient: LambdaClient, name: String, + func updateFunctionCode(lambdaClient: LambdaClient, functionName: String, path: String) async throws -> Bool { // snippet-start:[swift.lambda-basics.UpdateFunctionCode] let zipUrl = URL(fileURLWithPath: path) @@ -251,7 +252,7 @@ struct ExampleCommand: ParsableCommand { do { _ = try await lambdaClient.updateFunctionCode( input: UpdateFunctionCodeInput( - functionName: name, + functionName: functionName, zipFile: zipData ) ) @@ -267,7 +268,7 @@ struct ExampleCommand: ParsableCommand { maxDelay: 2 ), input: GetFunctionInput( - functionName: name + functionName: functionName ) ) @@ -280,6 +281,58 @@ struct ExampleCommand: ParsableCommand { } // snippet-end:[swift.lambda-basics.UpdateFunctionCode.wait] + // snippet-start:[swift.lambda-basics.UpdateFunctionConfiguration] + /// Tell the server-side component to log debug output by setting its + /// environment's `LOG_LEVEL` to `DEBUG`. + /// + /// - Parameters: + /// - lambdaClient: The `LambdaClient` to use. + /// - functionName: The name of the AWS Lambda function to enable debug + /// logging for. + /// + /// - Throws: `ExampleError.environmentResponseMissingError`, + /// `ExampleError.updateFunctionConfigurationError`, + /// `ExampleError.environmentVariablesMissingError`, + /// `ExampleError.logLevelIncorrectError`, + /// `ExampleError.updateFunctionConfigurationError` + func enableDebugLogging(lambdaClient: LambdaClient, functionName: String) async throws { + let envVariables = [ + "LOG_LEVEL": "DEBUG" + ] + let environment = LambdaClientTypes.Environment(variables: envVariables) + + do { + let output = try await lambdaClient.updateFunctionConfiguration( + input: UpdateFunctionConfigurationInput( + environment: environment, + functionName: functionName + ) + ) + + guard let response = output.environment else { + throw ExampleError.environmentResponseMissingError + } + + if response.error != nil { + throw ExampleError.updateFunctionConfigurationError + } + + guard let retVariables = response.variables else { + throw ExampleError.environmentVariablesMissingError + } + + for envVar in retVariables { + if envVar.key == "LOG_LEVEL" && envVar.value != "DEBUG" { + print("*** Log level is not set to DEBUG!") + throw ExampleError.logLevelIncorrectError + } + } + } catch { + throw ExampleError.updateFunctionConfigurationError + } + } + // snippet-end:[swift.lambda-basics.UpdateFunctionConfiguration] + // snippet-start:[swift.lambda-basics.ListFunctionsPaginated] /// Returns an array containing the names of all AWS Lambda functions /// available to the user. @@ -436,8 +489,9 @@ struct ExampleCommand: ParsableCommand { // function. print("Creating the increment Lambda function...") - if try await createFunction(lambdaClient: lambdaClient, name: basicsFunctionName, + if try await createFunction(lambdaClient: lambdaClient, functionName: basicsFunctionName, roleArn: iamRole.arn, path: incpath) { + print("Running increment function calls...") for number in 0...4 { do { let answer = try await invokeIncrement(lambdaClient: lambdaClient, number: number) @@ -446,15 +500,23 @@ struct ExampleCommand: ParsableCommand { print("Error incrementing \(number): ", error.localizedDescription) } } + } else { + print("*** Failed to create the increment function.") } + // Enable debug logging. + + print("\nEnabling debug logging...") + try await enableDebugLogging(lambdaClient: lambdaClient, functionName: basicsFunctionName) + // Change it to a basic arithmetic calculator. Then invoke it a few // times. print("\nReplacing the Lambda function with a calculator...") - if try await updateFunctionCode(lambdaClient: lambdaClient, name: "lambda-basics-function", + if try await updateFunctionCode(lambdaClient: lambdaClient, functionName: basicsFunctionName, path: calcpath) { + print("Running calculator function calls...") for x in [6, 10] { for y in [2, 4] { for action in ["plus", "minus", "times", "divided-by"] {