From 2b1bf54e9bbae601bf76a75dd74f8fc52ad839b7 Mon Sep 17 00:00:00 2001 From: Sebastien Stormacq Date: Fri, 24 Oct 2025 13:06:10 +0200 Subject: [PATCH 1/8] Add a multi Source API example --- Examples/MultiSourceAPI/.aws-sam/build.toml | 5 + .../.aws-sam/build/template.yaml | 147 ++++++++++++++++++ Examples/MultiSourceAPI/.gitignore | 9 ++ Examples/MultiSourceAPI/Package.swift | 53 +++++++ Examples/MultiSourceAPI/README.md | 65 ++++++++ Examples/MultiSourceAPI/Sources/main.swift | 66 ++++++++ Examples/MultiSourceAPI/samconfig.toml | 12 ++ Examples/MultiSourceAPI/template.yaml | 138 ++++++++++++++++ 8 files changed, 495 insertions(+) create mode 100644 Examples/MultiSourceAPI/.aws-sam/build.toml create mode 100644 Examples/MultiSourceAPI/.aws-sam/build/template.yaml create mode 100644 Examples/MultiSourceAPI/.gitignore create mode 100644 Examples/MultiSourceAPI/Package.swift create mode 100644 Examples/MultiSourceAPI/README.md create mode 100644 Examples/MultiSourceAPI/Sources/main.swift create mode 100644 Examples/MultiSourceAPI/samconfig.toml create mode 100644 Examples/MultiSourceAPI/template.yaml diff --git a/Examples/MultiSourceAPI/.aws-sam/build.toml b/Examples/MultiSourceAPI/.aws-sam/build.toml new file mode 100644 index 00000000..678bce43 --- /dev/null +++ b/Examples/MultiSourceAPI/.aws-sam/build.toml @@ -0,0 +1,5 @@ +# This file is auto generated by SAM CLI build command + +[function_build_definitions] + +[layer_build_definitions] diff --git a/Examples/MultiSourceAPI/.aws-sam/build/template.yaml b/Examples/MultiSourceAPI/.aws-sam/build/template.yaml new file mode 100644 index 00000000..abfbf7e3 --- /dev/null +++ b/Examples/MultiSourceAPI/.aws-sam/build/template.yaml @@ -0,0 +1,147 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: Multi-source API Lambda function with ALB and API Gateway V2 +Resources: + MultiSourceAPIFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: ../../.build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/MultiSourceAPI/MultiSourceAPI.zip + Handler: provided + Runtime: provided.al2 + Architectures: + - arm64 + MemorySize: 256 + Timeout: 30 + Environment: + Variables: + LOG_LEVEL: trace + Events: + ApiGatewayEvent: + Type: HttpApi + Properties: + Path: /{proxy+} + Method: ANY + VPC: + Type: AWS::EC2::VPC + Properties: + CidrBlock: 10.0.0.0/16 + EnableDnsHostnames: true + EnableDnsSupport: true + PublicSubnet1: + Type: AWS::EC2::Subnet + Properties: + VpcId: + Ref: VPC + CidrBlock: 10.0.1.0/24 + AvailabilityZone: + Fn::Select: + - 0 + - Fn::GetAZs: '' + MapPublicIpOnLaunch: true + PublicSubnet2: + Type: AWS::EC2::Subnet + Properties: + VpcId: + Ref: VPC + CidrBlock: 10.0.2.0/24 + AvailabilityZone: + Fn::Select: + - 1 + - Fn::GetAZs: '' + MapPublicIpOnLaunch: true + InternetGateway: + Type: AWS::EC2::InternetGateway + AttachGateway: + Type: AWS::EC2::VPCGatewayAttachment + Properties: + VpcId: + Ref: VPC + InternetGatewayId: + Ref: InternetGateway + RouteTable: + Type: AWS::EC2::RouteTable + Properties: + VpcId: + Ref: VPC + Route: + Type: AWS::EC2::Route + DependsOn: AttachGateway + Properties: + RouteTableId: + Ref: RouteTable + DestinationCidrBlock: '0.0.0.0/0' + GatewayId: + Ref: InternetGateway + SubnetRouteTableAssociation1: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: + Ref: PublicSubnet1 + RouteTableId: + Ref: RouteTable + SubnetRouteTableAssociation2: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: + Ref: PublicSubnet2 + RouteTableId: + Ref: RouteTable + ALBSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Security group for ALB + VpcId: + Ref: VPC + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 80 + ToPort: 80 + CidrIp: '0.0.0.0/0' + ApplicationLoadBalancer: + Type: AWS::ElasticLoadBalancingV2::LoadBalancer + Properties: + Scheme: internet-facing + Subnets: + - Ref: PublicSubnet1 + - Ref: PublicSubnet2 + SecurityGroups: + - Ref: ALBSecurityGroup + ALBTargetGroup: + Type: AWS::ElasticLoadBalancingV2::TargetGroup + DependsOn: ALBLambdaInvokePermission + Properties: + TargetType: lambda + Targets: + - Id: + Fn::GetAtt: + - MultiSourceAPIFunction + - Arn + ALBListener: + Type: AWS::ElasticLoadBalancingV2::Listener + Properties: + LoadBalancerArn: + Ref: ApplicationLoadBalancer + Port: 80 + Protocol: HTTP + DefaultActions: + - Type: forward + TargetGroupArn: + Ref: ALBTargetGroup + ALBLambdaInvokePermission: + Type: AWS::Lambda::Permission + Properties: + FunctionName: + Fn::GetAtt: + - MultiSourceAPIFunction + - Arn + Action: lambda:InvokeFunction + Principal: elasticloadbalancing.amazonaws.com +Outputs: + ApiGatewayUrl: + Description: API Gateway endpoint URL + Value: + Fn::Sub: https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com + ALBUrl: + Description: Application Load Balancer URL + Value: + Fn::Sub: http://${ApplicationLoadBalancer.DNSName} diff --git a/Examples/MultiSourceAPI/.gitignore b/Examples/MultiSourceAPI/.gitignore new file mode 100644 index 00000000..47bd4c18 --- /dev/null +++ b/Examples/MultiSourceAPI/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc +Package.resolved diff --git a/Examples/MultiSourceAPI/Package.swift b/Examples/MultiSourceAPI/Package.swift new file mode 100644 index 00000000..414f4e1a --- /dev/null +++ b/Examples/MultiSourceAPI/Package.swift @@ -0,0 +1,53 @@ +// swift-tools-version:6.2 + +import PackageDescription + +// needed for CI to test the local version of the library +import struct Foundation.URL + +let package = Package( + name: "MultiSourceAPI", + platforms: [.macOS(.v15)], + products: [ + .executable(name: "MultiSourceAPI", targets: ["MultiSourceAPI"]) + ], + dependencies: [ + .package(url: "https://github.com/awslabs/swift-aws-lambda-runtime.git", from: "2.0.0"), + .package(url: "https://github.com/awslabs/swift-aws-lambda-events.git", from: "1.0.0"), + ], + targets: [ + .executableTarget( + name: "MultiSourceAPI", + dependencies: [ + .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"), + .product(name: "AWSLambdaEvents", package: "swift-aws-lambda-events"), + ], + path: "Sources" + ) + ] +) + +if let localDepsPath = Context.environment["LAMBDA_USE_LOCAL_DEPS"], + localDepsPath != "", + let v = try? URL(fileURLWithPath: localDepsPath).resourceValues(forKeys: [.isDirectoryKey]), + v.isDirectory == true +{ + let indexToRemove = package.dependencies.firstIndex { dependency in + if case .sourceControl( + name: _, + location: "https://github.com/awslabs/swift-aws-lambda-runtime.git", + requirement: _ + ) = dependency.kind { + return true + } + return false + } + if let indexToRemove { + package.dependencies.remove(at: indexToRemove) + } + + print("[INFO] Compiling against swift-aws-lambda-runtime located at \(localDepsPath)") + package.dependencies += [ + .package(name: "swift-aws-lambda-runtime", path: localDepsPath) + ] +} diff --git a/Examples/MultiSourceAPI/README.md b/Examples/MultiSourceAPI/README.md new file mode 100644 index 00000000..097e4f2f --- /dev/null +++ b/Examples/MultiSourceAPI/README.md @@ -0,0 +1,65 @@ +# Multi-Source API Example + +This example demonstrates a Lambda function that handles requests from both Application Load Balancer (ALB) and API Gateway V2 by accepting a raw `ByteBuffer` and decoding the appropriate event type. + +## Overview + +The Lambda handler receives events as `ByteBuffer` and attempts to decode them as either: +- `ALBTargetGroupRequest` - for requests from Application Load Balancer +- `APIGatewayV2Request` - for requests from API Gateway V2 + +Based on the successfully decoded type, it returns an appropriate response. + +## Building + +```bash +swift package archive --allow-network-connections docker +``` + +## Deploying + +Deploy using SAM: + +```bash +sam deploy \ + --resolve-s3 \ + --template-file template.yaml \ + --stack-name MultiSourceAPI \ + --capabilities CAPABILITY_IAM +``` + +## Testing + +After deployment, SAM will output two URLs: + +### Test API Gateway V2: +```bash +curl https://.execute-api..amazonaws.com/apigw/test +``` + +Expected response: +```json +{"source":"APIGatewayV2","path":"/apigw/test"} +``` + +### Test ALB: +```bash +curl http:///alb/test +``` + +Expected response: +```json +{"source":"ALB","path":"/alb/test"} +``` + +## How It Works + +The handler uses Swift's type-safe decoding to determine the event source: + +1. Receives raw `ByteBuffer` event +2. Attempts to decode as `ALBTargetGroupRequest` +3. If that fails, attempts to decode as `APIGatewayV2Request` +4. Returns appropriate response based on the decoded type +5. Throws error if neither decoding succeeds + +This pattern is useful when a single Lambda function needs to handle requests from multiple sources. diff --git a/Examples/MultiSourceAPI/Sources/main.swift b/Examples/MultiSourceAPI/Sources/main.swift new file mode 100644 index 00000000..508cb226 --- /dev/null +++ b/Examples/MultiSourceAPI/Sources/main.swift @@ -0,0 +1,66 @@ +import AWSLambdaEvents +import AWSLambdaRuntime +import HTTPTypes +import NIOCore + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +struct MultiSourceHandler: StreamingLambdaHandler { + func handle( + _ event: ByteBuffer, + responseWriter: some LambdaResponseStreamWriter, + context: LambdaContext + ) async throws { + let decoder = JSONDecoder() + let data = Data(event.readableBytesView) + + // Try to decode as ALBTargetGroupRequest first + if let albRequest = try? decoder.decode(ALBTargetGroupRequest.self, from: data) { + context.logger.info("Received ALB request to path: \(albRequest.path)") + + let response = ALBTargetGroupResponse( + statusCode: .ok, + headers: ["Content-Type": "application/json"], + body: "{\"source\":\"ALB\",\"path\":\"\(albRequest.path)\"}" + ) + + let encoder = JSONEncoder() + let responseData = try encoder.encode(response) + try await responseWriter.write(ByteBuffer(bytes: responseData)) + try await responseWriter.finish() + return + } + + // Try to decode as APIGatewayV2Request + if let apiGwRequest = try? decoder.decode(APIGatewayV2Request.self, from: data) { + context.logger.info("Received API Gateway V2 request to path: \(apiGwRequest.rawPath)") + + let response = APIGatewayV2Response( + statusCode: .ok, + headers: ["Content-Type": "application/json"], + body: "{\"source\":\"APIGatewayV2\",\"path\":\"\(apiGwRequest.rawPath)\"}" + ) + + let encoder = JSONEncoder() + let responseData = try encoder.encode(response) + try await responseWriter.write(ByteBuffer(bytes: responseData)) + try await responseWriter.finish() + return + } + + // Unknown event type + context.logger.error("Unable to decode event as ALB or API Gateway V2 request") + throw LambdaError.invalidEvent + } +} + +enum LambdaError: Error { + case invalidEvent +} + +let runtime = LambdaRuntime(handler: MultiSourceHandler()) +try await runtime.run() diff --git a/Examples/MultiSourceAPI/samconfig.toml b/Examples/MultiSourceAPI/samconfig.toml new file mode 100644 index 00000000..132480d4 --- /dev/null +++ b/Examples/MultiSourceAPI/samconfig.toml @@ -0,0 +1,12 @@ +version = 0.1 + +[default.deploy.parameters] +stack_name = "sam-app" +resolve_s3 = true +s3_prefix = "sam-app" +region = "eu-west-3" +capabilities = "CAPABILITY_IAM" +image_repositories = [] + +[default.global.parameters] +region = "eu-west-3" diff --git a/Examples/MultiSourceAPI/template.yaml b/Examples/MultiSourceAPI/template.yaml new file mode 100644 index 00000000..1a6e4b0b --- /dev/null +++ b/Examples/MultiSourceAPI/template.yaml @@ -0,0 +1,138 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: Multi-source API Lambda function with ALB and API Gateway V2 + +Resources: + MultiSourceAPIFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: .build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/MultiSourceAPI/MultiSourceAPI.zip + Handler: provided + Runtime: provided.al2 + Architectures: + - arm64 + MemorySize: 256 + Timeout: 30 + Environment: + Variables: + LOG_LEVEL: trace + Events: + ApiGatewayEvent: + Type: HttpApi + Properties: + Path: /{proxy+} + Method: ANY + + # VPC for ALB + VPC: + Type: AWS::EC2::VPC + Properties: + CidrBlock: 10.0.0.0/16 + EnableDnsHostnames: true + EnableDnsSupport: true + + PublicSubnet1: + Type: AWS::EC2::Subnet + Properties: + VpcId: !Ref VPC + CidrBlock: 10.0.1.0/24 + AvailabilityZone: !Select [0, !GetAZs ''] + MapPublicIpOnLaunch: true + + PublicSubnet2: + Type: AWS::EC2::Subnet + Properties: + VpcId: !Ref VPC + CidrBlock: 10.0.2.0/24 + AvailabilityZone: !Select [1, !GetAZs ''] + MapPublicIpOnLaunch: true + + InternetGateway: + Type: AWS::EC2::InternetGateway + + AttachGateway: + Type: AWS::EC2::VPCGatewayAttachment + Properties: + VpcId: !Ref VPC + InternetGatewayId: !Ref InternetGateway + + RouteTable: + Type: AWS::EC2::RouteTable + Properties: + VpcId: !Ref VPC + + Route: + Type: AWS::EC2::Route + DependsOn: AttachGateway + Properties: + RouteTableId: !Ref RouteTable + DestinationCidrBlock: 0.0.0.0/0 + GatewayId: !Ref InternetGateway + + SubnetRouteTableAssociation1: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref PublicSubnet1 + RouteTableId: !Ref RouteTable + + SubnetRouteTableAssociation2: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref PublicSubnet2 + RouteTableId: !Ref RouteTable + + # Application Load Balancer + ALBSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Security group for ALB + VpcId: !Ref VPC + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 80 + ToPort: 80 + CidrIp: 0.0.0.0/0 + + ApplicationLoadBalancer: + Type: AWS::ElasticLoadBalancingV2::LoadBalancer + Properties: + Scheme: internet-facing + Subnets: + - !Ref PublicSubnet1 + - !Ref PublicSubnet2 + SecurityGroups: + - !Ref ALBSecurityGroup + + ALBTargetGroup: + Type: AWS::ElasticLoadBalancingV2::TargetGroup + DependsOn: ALBLambdaInvokePermission + Properties: + TargetType: lambda + Targets: + - Id: !GetAtt MultiSourceAPIFunction.Arn + + ALBListener: + Type: AWS::ElasticLoadBalancingV2::Listener + Properties: + LoadBalancerArn: !Ref ApplicationLoadBalancer + Port: 80 + Protocol: HTTP + DefaultActions: + - Type: forward + TargetGroupArn: !Ref ALBTargetGroup + + ALBLambdaInvokePermission: + Type: AWS::Lambda::Permission + Properties: + FunctionName: !GetAtt MultiSourceAPIFunction.Arn + Action: lambda:InvokeFunction + Principal: elasticloadbalancing.amazonaws.com + +Outputs: + ApiGatewayUrl: + Description: API Gateway endpoint URL + Value: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com" + + ALBUrl: + Description: Application Load Balancer URL + Value: !Sub "http://${ApplicationLoadBalancer.DNSName}" From cb508ddc5f6eb586b6a41aad1c1dece8cdf89390 Mon Sep 17 00:00:00 2001 From: Sebastien Stormacq Date: Thu, 30 Oct 2025 11:27:21 +0100 Subject: [PATCH 2/8] add MultiSourceAPI to the CI --- .github/workflows/pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 0d0b8886..6245e4af 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -39,7 +39,7 @@ jobs: # We pass the list of examples here, but we can't pass an array as argument # Instead, we pass a String with a valid JSON array. # The workaround is mentioned here https://github.com/orgs/community/discussions/11692 - examples: "[ 'APIGatewayV1', 'APIGatewayV2', 'APIGatewayV2+LambdaAuthorizer', 'BackgroundTasks', 'HelloJSON', 'HelloWorld', 'HelloWorldNoTraits', 'HummingbirdLambda', 'ResourcesPackaging', 'S3EventNotifier', 'S3_AWSSDK', 'S3_Soto', 'Streaming', 'Streaming+Codable', 'ServiceLifecycle+Postgres', 'Testing', 'Tutorial' ]" + examples: "[ 'APIGatewayV1', 'APIGatewayV2', 'APIGatewayV2+LambdaAuthorizer', 'BackgroundTasks', 'HelloJSON', 'HelloWorld', 'HelloWorldNoTraits', 'HummingbirdLambda', 'MultiSourceAPI', 'ResourcesPackaging', 'S3EventNotifier', 'S3_AWSSDK', 'S3_Soto', 'Streaming', 'Streaming+Codable', 'ServiceLifecycle+Postgres', 'Testing', 'Tutorial' ]" archive_plugin_examples: "[ 'HelloWorld', 'ResourcesPackaging' ]" archive_plugin_enabled: true From 7e4beb5877fc4ba31edb9e4948251531f933e9a0 Mon Sep 17 00:00:00 2001 From: Sebastien Stormacq Date: Thu, 30 Oct 2025 11:34:49 +0100 Subject: [PATCH 3/8] remove .aws-sam and samconfig.toml --- Examples/MultiSourceAPI/.aws-sam/build.toml | 5 - .../.aws-sam/build/template.yaml | 147 ------------------ Examples/MultiSourceAPI/.gitignore | 2 + 3 files changed, 2 insertions(+), 152 deletions(-) delete mode 100644 Examples/MultiSourceAPI/.aws-sam/build.toml delete mode 100644 Examples/MultiSourceAPI/.aws-sam/build/template.yaml diff --git a/Examples/MultiSourceAPI/.aws-sam/build.toml b/Examples/MultiSourceAPI/.aws-sam/build.toml deleted file mode 100644 index 678bce43..00000000 --- a/Examples/MultiSourceAPI/.aws-sam/build.toml +++ /dev/null @@ -1,5 +0,0 @@ -# This file is auto generated by SAM CLI build command - -[function_build_definitions] - -[layer_build_definitions] diff --git a/Examples/MultiSourceAPI/.aws-sam/build/template.yaml b/Examples/MultiSourceAPI/.aws-sam/build/template.yaml deleted file mode 100644 index abfbf7e3..00000000 --- a/Examples/MultiSourceAPI/.aws-sam/build/template.yaml +++ /dev/null @@ -1,147 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: Multi-source API Lambda function with ALB and API Gateway V2 -Resources: - MultiSourceAPIFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: ../../.build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/MultiSourceAPI/MultiSourceAPI.zip - Handler: provided - Runtime: provided.al2 - Architectures: - - arm64 - MemorySize: 256 - Timeout: 30 - Environment: - Variables: - LOG_LEVEL: trace - Events: - ApiGatewayEvent: - Type: HttpApi - Properties: - Path: /{proxy+} - Method: ANY - VPC: - Type: AWS::EC2::VPC - Properties: - CidrBlock: 10.0.0.0/16 - EnableDnsHostnames: true - EnableDnsSupport: true - PublicSubnet1: - Type: AWS::EC2::Subnet - Properties: - VpcId: - Ref: VPC - CidrBlock: 10.0.1.0/24 - AvailabilityZone: - Fn::Select: - - 0 - - Fn::GetAZs: '' - MapPublicIpOnLaunch: true - PublicSubnet2: - Type: AWS::EC2::Subnet - Properties: - VpcId: - Ref: VPC - CidrBlock: 10.0.2.0/24 - AvailabilityZone: - Fn::Select: - - 1 - - Fn::GetAZs: '' - MapPublicIpOnLaunch: true - InternetGateway: - Type: AWS::EC2::InternetGateway - AttachGateway: - Type: AWS::EC2::VPCGatewayAttachment - Properties: - VpcId: - Ref: VPC - InternetGatewayId: - Ref: InternetGateway - RouteTable: - Type: AWS::EC2::RouteTable - Properties: - VpcId: - Ref: VPC - Route: - Type: AWS::EC2::Route - DependsOn: AttachGateway - Properties: - RouteTableId: - Ref: RouteTable - DestinationCidrBlock: '0.0.0.0/0' - GatewayId: - Ref: InternetGateway - SubnetRouteTableAssociation1: - Type: AWS::EC2::SubnetRouteTableAssociation - Properties: - SubnetId: - Ref: PublicSubnet1 - RouteTableId: - Ref: RouteTable - SubnetRouteTableAssociation2: - Type: AWS::EC2::SubnetRouteTableAssociation - Properties: - SubnetId: - Ref: PublicSubnet2 - RouteTableId: - Ref: RouteTable - ALBSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - GroupDescription: Security group for ALB - VpcId: - Ref: VPC - SecurityGroupIngress: - - IpProtocol: tcp - FromPort: 80 - ToPort: 80 - CidrIp: '0.0.0.0/0' - ApplicationLoadBalancer: - Type: AWS::ElasticLoadBalancingV2::LoadBalancer - Properties: - Scheme: internet-facing - Subnets: - - Ref: PublicSubnet1 - - Ref: PublicSubnet2 - SecurityGroups: - - Ref: ALBSecurityGroup - ALBTargetGroup: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - DependsOn: ALBLambdaInvokePermission - Properties: - TargetType: lambda - Targets: - - Id: - Fn::GetAtt: - - MultiSourceAPIFunction - - Arn - ALBListener: - Type: AWS::ElasticLoadBalancingV2::Listener - Properties: - LoadBalancerArn: - Ref: ApplicationLoadBalancer - Port: 80 - Protocol: HTTP - DefaultActions: - - Type: forward - TargetGroupArn: - Ref: ALBTargetGroup - ALBLambdaInvokePermission: - Type: AWS::Lambda::Permission - Properties: - FunctionName: - Fn::GetAtt: - - MultiSourceAPIFunction - - Arn - Action: lambda:InvokeFunction - Principal: elasticloadbalancing.amazonaws.com -Outputs: - ApiGatewayUrl: - Description: API Gateway endpoint URL - Value: - Fn::Sub: https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com - ALBUrl: - Description: Application Load Balancer URL - Value: - Fn::Sub: http://${ApplicationLoadBalancer.DNSName} diff --git a/Examples/MultiSourceAPI/.gitignore b/Examples/MultiSourceAPI/.gitignore index 47bd4c18..b3b842e5 100644 --- a/Examples/MultiSourceAPI/.gitignore +++ b/Examples/MultiSourceAPI/.gitignore @@ -7,3 +7,5 @@ DerivedData/ .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc Package.resolved +aws-sam +samconfig.toml From 421eb88317a078af313c8a2cacd8c6175958985c Mon Sep 17 00:00:00 2001 From: Sebastien Stormacq Date: Thu, 30 Oct 2025 11:37:42 +0100 Subject: [PATCH 4/8] add license header --- Examples/MultiSourceAPI/Sources/main.swift | 16 +++++++++++++++- Examples/MultiSourceAPI/samconfig.toml | 12 ------------ 2 files changed, 15 insertions(+), 13 deletions(-) delete mode 100644 Examples/MultiSourceAPI/samconfig.toml diff --git a/Examples/MultiSourceAPI/Sources/main.swift b/Examples/MultiSourceAPI/Sources/main.swift index 508cb226..7e780a33 100644 --- a/Examples/MultiSourceAPI/Sources/main.swift +++ b/Examples/MultiSourceAPI/Sources/main.swift @@ -1,6 +1,20 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftAWSLambdaRuntime open source project +// +// Copyright SwiftAWSLambdaRuntime project authors +// Copyright (c) Amazon.com, Inc. or its affiliates. +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + import AWSLambdaEvents import AWSLambdaRuntime -import HTTPTypes import NIOCore #if canImport(FoundationEssentials) diff --git a/Examples/MultiSourceAPI/samconfig.toml b/Examples/MultiSourceAPI/samconfig.toml deleted file mode 100644 index 132480d4..00000000 --- a/Examples/MultiSourceAPI/samconfig.toml +++ /dev/null @@ -1,12 +0,0 @@ -version = 0.1 - -[default.deploy.parameters] -stack_name = "sam-app" -resolve_s3 = true -s3_prefix = "sam-app" -region = "eu-west-3" -capabilities = "CAPABILITY_IAM" -image_repositories = [] - -[default.global.parameters] -region = "eu-west-3" From 3985358ea447ce2c3ccf01de558a8406247ec1e8 Mon Sep 17 00:00:00 2001 From: Sebastien Stormacq Date: Thu, 30 Oct 2025 12:11:05 +0100 Subject: [PATCH 5/8] fix yaml lint --- Examples/MultiSourceAPI/template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples/MultiSourceAPI/template.yaml b/Examples/MultiSourceAPI/template.yaml index 1a6e4b0b..2ad85a3d 100644 --- a/Examples/MultiSourceAPI/template.yaml +++ b/Examples/MultiSourceAPI/template.yaml @@ -132,7 +132,7 @@ Outputs: ApiGatewayUrl: Description: API Gateway endpoint URL Value: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com" - + ALBUrl: Description: Application Load Balancer URL Value: !Sub "http://${ApplicationLoadBalancer.DNSName}" From 3401b76a3fb068395af40b25aeb93af0d3aaa517 Mon Sep 17 00:00:00 2001 From: Sebastien Stormacq Date: Thu, 30 Oct 2025 12:11:31 +0100 Subject: [PATCH 6/8] fix swift-format --- Examples/MultiSourceAPI/Sources/main.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Examples/MultiSourceAPI/Sources/main.swift b/Examples/MultiSourceAPI/Sources/main.swift index 7e780a33..461de51d 100644 --- a/Examples/MultiSourceAPI/Sources/main.swift +++ b/Examples/MultiSourceAPI/Sources/main.swift @@ -31,41 +31,41 @@ struct MultiSourceHandler: StreamingLambdaHandler { ) async throws { let decoder = JSONDecoder() let data = Data(event.readableBytesView) - + // Try to decode as ALBTargetGroupRequest first if let albRequest = try? decoder.decode(ALBTargetGroupRequest.self, from: data) { context.logger.info("Received ALB request to path: \(albRequest.path)") - + let response = ALBTargetGroupResponse( statusCode: .ok, headers: ["Content-Type": "application/json"], body: "{\"source\":\"ALB\",\"path\":\"\(albRequest.path)\"}" ) - + let encoder = JSONEncoder() let responseData = try encoder.encode(response) try await responseWriter.write(ByteBuffer(bytes: responseData)) try await responseWriter.finish() return } - + // Try to decode as APIGatewayV2Request if let apiGwRequest = try? decoder.decode(APIGatewayV2Request.self, from: data) { context.logger.info("Received API Gateway V2 request to path: \(apiGwRequest.rawPath)") - + let response = APIGatewayV2Response( statusCode: .ok, headers: ["Content-Type": "application/json"], body: "{\"source\":\"APIGatewayV2\",\"path\":\"\(apiGwRequest.rawPath)\"}" ) - + let encoder = JSONEncoder() let responseData = try encoder.encode(response) try await responseWriter.write(ByteBuffer(bytes: responseData)) try await responseWriter.finish() return } - + // Unknown event type context.logger.error("Unable to decode event as ALB or API Gateway V2 request") throw LambdaError.invalidEvent From f8317c3c65c7dc5fcf29979cda62ad057f2f6c28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Sat, 1 Nov 2025 09:35:04 +0100 Subject: [PATCH 7/8] Update Examples/MultiSourceAPI/Package.swift Co-authored-by: Josh Elkins --- Examples/MultiSourceAPI/Package.swift | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Examples/MultiSourceAPI/Package.swift b/Examples/MultiSourceAPI/Package.swift index 414f4e1a..5d2a0985 100644 --- a/Examples/MultiSourceAPI/Package.swift +++ b/Examples/MultiSourceAPI/Package.swift @@ -33,14 +33,12 @@ if let localDepsPath = Context.environment["LAMBDA_USE_LOCAL_DEPS"], v.isDirectory == true { let indexToRemove = package.dependencies.firstIndex { dependency in - if case .sourceControl( - name: _, - location: "https://github.com/awslabs/swift-aws-lambda-runtime.git", - requirement: _ - ) = dependency.kind { + switch dependency.kind { + case .sourceControl(name: _, location: "https://github.com/awslabs/swift-aws-lambda-runtime.git", requirement: _): return true + default: + return false } - return false } if let indexToRemove { package.dependencies.remove(at: indexToRemove) From b37ecca8e42ddc6e401ee1c545fcb1fd24859200 Mon Sep 17 00:00:00 2001 From: Sebastien Stormacq Date: Sun, 2 Nov 2025 21:51:00 +0100 Subject: [PATCH 8/8] swift-format --- Examples/MultiSourceAPI/Package.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Examples/MultiSourceAPI/Package.swift b/Examples/MultiSourceAPI/Package.swift index 5d2a0985..83ec9e02 100644 --- a/Examples/MultiSourceAPI/Package.swift +++ b/Examples/MultiSourceAPI/Package.swift @@ -34,7 +34,11 @@ if let localDepsPath = Context.environment["LAMBDA_USE_LOCAL_DEPS"], { let indexToRemove = package.dependencies.firstIndex { dependency in switch dependency.kind { - case .sourceControl(name: _, location: "https://github.com/awslabs/swift-aws-lambda-runtime.git", requirement: _): + case .sourceControl( + name: _, + location: "https://github.com/awslabs/swift-aws-lambda-runtime.git", + requirement: _ + ): return true default: return false