├── .gitattributes ├── .gitignore ├── Samples ├── Avatar │ ├── Dockerfile │ └── Package.swift ├── JWT │ ├── Dockerfile │ ├── serverless.yml │ ├── Package.swift │ └── main.swift ├── Moderation │ └── main.swift └── ChatBot │ └── main.swift ├── Dockerfile ├── Scripts ├── deploy.sh ├── package.sh └── build-and-package.sh ├── handler.sh ├── Lambda.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcuserdata │ │ └── cardoso.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── GeneratedModuleMap │ └── CBacktrace │ │ └── module.modulemap ├── NIO_Info.plist ├── Backtrace_Info.plist ├── CBacktrace_Info.plist ├── CNIOAtomics_Info.plist ├── CNIODarwin_Info.plist ├── CNIOLinux_Info.plist ├── CNIOSHA1_Info.plist ├── Logging_Info.plist ├── NIOHTTP1_Info.plist ├── SwiftGD_Info.plist ├── AWSLambdaEvents_Info.plist ├── AWSLambdaRuntime_Info.plist ├── CNIOHTTPParser_Info.plist ├── AWSLambdaRuntimeCore_Info.plist ├── NIOConcurrencyHelpers_Info.plist ├── NIOFoundationCompat_Info.plist └── xcshareddata │ └── xcschemes │ ├── Lambda.xcscheme │ └── Lambda-Package.xcscheme ├── .github └── workflows │ └── swift.yml ├── serverless.yml ├── Sources └── Lambda │ └── main.swift ├── bootstrap ├── Package.swift ├── LICENSE ├── Package.resolved └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * linguist-vendored 2 | *.swift linguist-vendored=false 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .build 2 | .swiftpm 3 | .serverless 4 | .DS_Store 5 | node_modules 6 | -------------------------------------------------------------------------------- /Samples/Avatar/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM swift:5.3-amazonlinux2 2 | 3 | RUN yum -y install zip gd-devel 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM swift:5.3.1-amazonlinux2 2 | 3 | RUN yum -y install zip 4 | RUN yum -y install openssl-devel 5 | -------------------------------------------------------------------------------- /Scripts/deploy.sh: -------------------------------------------------------------------------------- 1 | sh ./Scripts/build-and-package.sh 2 | serverless deploy --config "./serverless.yml" --stage dev -v 3 | -------------------------------------------------------------------------------- /Samples/JWT/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM swift:5.3.1-amazonlinux2 2 | 3 | RUN yum -y install zip 4 | RUN yum -y install openssl-devel 5 | -------------------------------------------------------------------------------- /handler.sh: -------------------------------------------------------------------------------- 1 | function hello () { 2 | EVENT_DATA=$1 3 | echo "$EVENT_DATA" 1>&2; 4 | RESPONSE="{\"body\": {\"input\": $EVENT_DATA, \"msg\": \"Wecome to serverless!\"}}" 5 | 6 | echo $RESPONSE 7 | } 8 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/GeneratedModuleMap/CBacktrace/module.modulemap: -------------------------------------------------------------------------------- 1 | module CBacktrace { 2 | umbrella "/Users/cardoso/dev/getstream/swift-lambda/.build/checkouts/swift-backtrace/Sources/CBacktrace/include" 3 | export * 4 | } 5 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/project.xcworkspace/xcuserdata/cardoso.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/swift-lambda/HEAD/Lambda.xcodeproj/project.xcworkspace/xcuserdata/cardoso.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /.github/workflows/swift.yml: -------------------------------------------------------------------------------- 1 | name: Swift 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: macos-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Build 17 | run: swift build 18 | -------------------------------------------------------------------------------- /serverless.yml: -------------------------------------------------------------------------------- 1 | service: swift-lambda 2 | 3 | provider: 4 | name: aws 5 | runtime: provided 6 | 7 | package: 8 | artifact: .build/lambda/Lambda/lambda.zip 9 | 10 | functions: 11 | lambda: 12 | handler: handler.lambda 13 | events: 14 | - http: 15 | path: / 16 | method: post 17 | -------------------------------------------------------------------------------- /Scripts/package.sh: -------------------------------------------------------------------------------- 1 | set -eu 2 | 3 | executable="Lambda" 4 | 5 | target=.build/lambda/$executable 6 | rm -rf "$target" 7 | mkdir -p "$target" 8 | cp ".build/release/$executable" "$target/" 9 | cp -Pv /usr/lib/swift/linux/lib*so* "$target" 10 | cd "$target" 11 | ln -s "$executable" "bootstrap" 12 | zip --symlinks lambda.zip * -------------------------------------------------------------------------------- /Samples/JWT/serverless.yml: -------------------------------------------------------------------------------- 1 | service: swift-lambda 2 | 3 | provider: 4 | name: aws 5 | runtime: provided 6 | 7 | package: 8 | artifact: .build/lambda/Lambda/lambda.zip 9 | 10 | functions: 11 | lambda: 12 | handler: handler.lambda 13 | events: 14 | - http: 15 | path: / 16 | method: post 17 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | -------------------------------------------------------------------------------- /Scripts/build-and-package.sh: -------------------------------------------------------------------------------- 1 | set -eu 2 | 3 | executable="Lambda" 4 | 5 | echo "preparing docker build image" 6 | docker build . -t builder 7 | echo "done" 8 | 9 | echo "building executable" 10 | docker run --rm -v "$(pwd)":/workspace -w /workspace builder bash -cl "swift build --product $executable -c release -Xswiftc -g" 11 | echo "done" 12 | 13 | echo "packaging lambda" 14 | docker run --rm -v "$(pwd)":/workspace -w /workspace builder bash -cl "./scripts/package.sh $executable" 15 | -------------------------------------------------------------------------------- /Sources/Lambda/main.swift: -------------------------------------------------------------------------------- 1 | import AWSLambdaEvents 2 | import AWSLambdaRuntime 3 | import NIO 4 | 5 | // MARK: - Run Lambda 6 | Lambda.run(APIGatewayProxyLambda()) 7 | 8 | // MARK: - Handler, Request and Response 9 | // FIXME: Use proper Event abstractions once added to AWSLambdaRuntime 10 | struct APIGatewayProxyLambda: EventLoopLambdaHandler { 11 | public typealias In = APIGateway.Request 12 | public typealias Out = APIGateway.Response 13 | 14 | public func handle(context: Lambda.Context, event: APIGateway.Request) -> EventLoopFuture { 15 | context.logger.debug("hello, api gateway!") 16 | return context.eventLoop.makeSucceededFuture(APIGateway.Response(statusCode: .ok, body: "Hello, world!")) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -euo pipefail 4 | 5 | # Initialization - load function handler 6 | source $LAMBDA_TASK_ROOT/"$(echo $_HANDLER | cut -d. -f1).sh" 7 | 8 | # Processing 9 | while true 10 | do 11 | HEADERS="$(mktemp)" 12 | # Get an event 13 | EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next") 14 | REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2) 15 | 16 | # Execute the handler function from the script 17 | RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA") 18 | 19 | # Send the response 20 | curl -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "$RESPONSE" 21 | done 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.2 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "Lambda", 6 | platforms: [ 7 | .macOS(.v10_13), 8 | ], 9 | products: [ 10 | .executable(name: "Lambda", targets: ["Lambda"]), 11 | ], 12 | dependencies: [ 13 | .package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", from: "0.2.0"), 14 | .package(name: "SwiftJWT", url: "https://github.com/Kitura/Swift-JWT.git", from: "3.6.2") 15 | ], 16 | targets: [ 17 | .target(name: "Lambda", dependencies: [ 18 | .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"), 19 | .product(name: "AWSLambdaEvents", package: "swift-aws-lambda-runtime"), 20 | "SwiftJWT" 21 | ]) 22 | ] 23 | ) 24 | -------------------------------------------------------------------------------- /Samples/JWT/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.2 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "Lambda", 6 | platforms: [ 7 | .macOS(.v10_13), 8 | ], 9 | products: [ 10 | .executable(name: "Lambda", targets: ["Lambda"]), 11 | ], 12 | dependencies: [ 13 | .package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", from: "0.2.0"), 14 | .package(name: "SwiftJWT", url: "https://github.com/Kitura/Swift-JWT.git", from: "3.6.2") 15 | ], 16 | targets: [ 17 | .target(name: "Lambda", dependencies: [ 18 | .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"), 19 | .product(name: "AWSLambdaEvents", package: "swift-aws-lambda-runtime"), 20 | "SwiftJWT" 21 | ]) 22 | ] 23 | ) 24 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/NIO_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/Backtrace_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/CBacktrace_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/CNIOAtomics_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/CNIODarwin_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/CNIOLinux_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/CNIOSHA1_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/Logging_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/NIOHTTP1_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/SwiftGD_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/AWSLambdaEvents_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/AWSLambdaRuntime_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/CNIOHTTPParser_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Samples/Avatar/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.2 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "Lambda", 6 | platforms: [ 7 | .macOS(.v10_13), 8 | ], 9 | products: [ 10 | .executable(name: "Lambda", targets: ["Lambda"]), 11 | ], 12 | dependencies: [ 13 | .package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", from: "0.2.0"), 14 | .package(url: "https://github.com/twostraws/SwiftGD.git", from: "2.5.0") 15 | ], 16 | targets: [ 17 | .target(name: "Lambda", dependencies: [ 18 | .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"), 19 | .product(name: "AWSLambdaEvents", package: "swift-aws-lambda-runtime"), 20 | .product(name: "SwiftGD", package: "SwiftGD") 21 | ]) 22 | ] 23 | ) 24 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/AWSLambdaRuntimeCore_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/NIOConcurrencyHelpers_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/NIOFoundationCompat_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 cardoso 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Samples/JWT/main.swift: -------------------------------------------------------------------------------- 1 | import AWSLambdaEvents 2 | import AWSLambdaRuntime 3 | import SwiftJWT 4 | import Foundation 5 | 6 | Lambda.run { (context: Lambda.Context, event: APIGateway.Request, callback: @escaping (Result) -> Void) in 7 | guard 8 | let bodyData = event.body?.data(using: .utf8), 9 | let json = try? JSONSerialization.jsonObject(with: bodyData, options: []) as? [String: Any], 10 | let userId = json["user_id"] as? String 11 | else { 12 | callback(.success(APIGateway.Response(statusCode: .badRequest))) 13 | return 14 | } 15 | 16 | if let jwt = try? generateJWT(for: userId) { 17 | callback(.success(APIGateway.Response(statusCode: .ok, body: jwt))) 18 | } else { 19 | callback(.success(.init(statusCode: .internalServerError))) 20 | } 21 | } 22 | 23 | func generateJWT(for userId: String) throws -> String { 24 | struct ClientClaims: Claims { 25 | let user_id: String 26 | } 27 | 28 | let claims = ClientClaims(user_id: userId) 29 | 30 | var jwt = JWT(claims: claims) 31 | 32 | let key = "[replace_your_api_key_here]" 33 | let keyData = key.data(using: .utf8)! 34 | 35 | let signer = JWTSigner.hs256(key: keyData) 36 | 37 | return try jwt.sign(using: signer) 38 | } 39 | -------------------------------------------------------------------------------- /Samples/Moderation/main.swift: -------------------------------------------------------------------------------- 1 | import AWSLambdaEvents 2 | import AWSLambdaRuntime 3 | import NIO 4 | 5 | import Foundation 6 | #if canImport(FoundationNetworking) 7 | import FoundationNetworking 8 | #endif 9 | 10 | Lambda.run { (context: Lambda.Context, event: APIGateway.Request, callback: @escaping (Result) -> Void) in 11 | guard 12 | let data = event.body?.data(using: .utf8), 13 | let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any], 14 | var message = json["message"] as? [String: Any] 15 | else { 16 | callback(.success(APIGateway.Response(statusCode: .ok))) 17 | return 18 | } 19 | 20 | // rewrite message's text with redacted version 21 | message["text"] = redactCreditCardNumbers(from: text) 22 | 23 | let body = ["message": message] 24 | let bodyData = try! JSONSerialization.data(withJSONObject: body) 25 | let string = String(data: bodyData, encoding: .utf8) 26 | 27 | callback(.success(APIGateway.Response(statusCode: .ok, body: string))) 28 | } 29 | 30 | func redactCreditCardNumbers(from text: String) -> String { 31 | var text = text 32 | 33 | let americanExpress = "(?:3[47][0-9]{13})"; 34 | let dinersClub = "(?:3(?:0[0-5]|[68][0-9])[0-9]{11})"; 35 | let discover = "(?:6(?:011|5[0-9]{2})(?:[0-9]{12}))"; 36 | let jcb = "(?:(?:2131|1800|35\\d{3})\\d{11})"; 37 | let maestro = "(?:(?:5[0678]\\d\\d|6304|6390|67\\d\\d)\\d{8,15})"; 38 | let mastercard = "(?:(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12})"; 39 | let visa = "(?:4[0-9]{12})(?:[0-9]{3})?"; 40 | 41 | let all = [americanExpress, dinersClub, discover, jcb, maestro, mastercard, visa].joined(separator: "|") 42 | 43 | let regex = try! NSRegularExpression(pattern: all) 44 | 45 | let res = regex.matches(in: text, options: [], range: NSRange(location: 0, length: text.count)) 46 | 47 | res.forEach { 48 | text = text.replacingCharacters( 49 | in: Range($0.range, in: text)!, 50 | with: String(repeating: "*", count: $0.range.length) 51 | ) 52 | } 53 | 54 | return text 55 | } 56 | -------------------------------------------------------------------------------- /Samples/ChatBot/main.swift: -------------------------------------------------------------------------------- 1 | import AWSLambdaEvents 2 | import AWSLambdaRuntime 3 | import NIO 4 | 5 | import Foundation 6 | #if canImport(FoundationNetworking) 7 | import FoundationNetworking 8 | #endif 9 | 10 | Lambda.run { (context: Lambda.Context, event: APIGateway.Request, callback: @escaping (Result) -> Void) in 11 | context.logger.debug("hello, api gateway!") 12 | 13 | let botId = "Agent" 14 | 15 | guard 16 | let bodyData = event.body?.data(using: .utf8), 17 | let json = try? JSONSerialization.jsonObject(with: bodyData, options: []) as? [String: Any], 18 | json["type"] as? String == "message.new", 19 | let channelType = json["channel_type"] as? String, 20 | let channelId = json["channel_id"] as? String, 21 | let message = json["message"] as? [String: Any], 22 | let user = message["user"] as? [String: Any], 23 | let userId = user["id"] as? String, 24 | userId != botId 25 | else { 26 | callback(.success(APIGateway.Response(statusCode: .ok, body: "nope!"))) 27 | return 28 | } 29 | 30 | botReply(channelType: channelType, channelId: channelId, botId: botId) { 31 | callback(.success(APIGateway.Response(statusCode: .ok))) 32 | } 33 | } 34 | 35 | func botReply(channelType: String, channelId: String, botId: String, completion: @escaping () -> Void) { 36 | let apiKey = "[insert_your_api_key_here]" 37 | let jwt = "[insert_your_jwt_here]" 38 | 39 | let response = [ 40 | "I am a bot", 41 | "Beep boop", 42 | "Having trouble?", 43 | "I can help" 44 | ].randomElement() ?? "" 45 | 46 | let url = URL(string: "https://chat-us-east-1.stream-io-api.com/channels/\(channelType)/\(channelId)/message?api_key=\(apiKey)")! 47 | var request = URLRequest(url: url) 48 | request.allHTTPHeaderFields = ["Authorization": jwt, "stream-auth-type": "jwt"] 49 | 50 | request.httpMethod = "POST" 51 | request.httpBody = 52 | """ 53 | { 54 | "message" : { 55 | "text" : "\(response)", 56 | "silent" : false, 57 | "user_id": "\(botId)" 58 | } 59 | } 60 | """.data(using: .utf8) 61 | 62 | let task = URLSession.shared.dataTask(with: request) { _,_,_ in 63 | completion() 64 | } 65 | 66 | task.resume() 67 | } 68 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/xcshareddata/xcschemes/Lambda.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 61 | 63 | 64 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /Lambda.xcodeproj/xcshareddata/xcschemes/Lambda-Package.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 61 | 63 | 64 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "Cryptor", 6 | "repositoryURL": "https://github.com/Kitura/BlueCryptor.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "ee5880e031da4c609f372cf7472476ab51d5dd19", 10 | "version": "1.0.200" 11 | } 12 | }, 13 | { 14 | "package": "CryptorECC", 15 | "repositoryURL": "https://github.com/Kitura/BlueECC.git", 16 | "state": { 17 | "branch": null, 18 | "revision": "baf6ed3fc1a622675f0041b4aff7c02dd1a93818", 19 | "version": "1.2.200" 20 | } 21 | }, 22 | { 23 | "package": "CryptorRSA", 24 | "repositoryURL": "https://github.com/Kitura/BlueRSA.git", 25 | "state": { 26 | "branch": null, 27 | "revision": "440f78db26d8bb073f29590f1c7bd31004da09ae", 28 | "version": "1.0.201" 29 | } 30 | }, 31 | { 32 | "package": "KituraContracts", 33 | "repositoryURL": "https://github.com/Kitura/KituraContracts.git", 34 | "state": { 35 | "branch": null, 36 | "revision": "8418006e39e2efae9b31ae92721cb597ac29c617", 37 | "version": "1.2.200" 38 | } 39 | }, 40 | { 41 | "package": "LoggerAPI", 42 | "repositoryURL": "https://github.com/Kitura/LoggerAPI.git", 43 | "state": { 44 | "branch": null, 45 | "revision": "e82d34eab3f0b05391082b11ea07d3b70d2f65bb", 46 | "version": "1.9.200" 47 | } 48 | }, 49 | { 50 | "package": "OpenSSL", 51 | "repositoryURL": "https://github.com/Kitura/OpenSSL.git", 52 | "state": { 53 | "branch": null, 54 | "revision": "80b04f33b086fc90e28d9ae159d43705fb348e16", 55 | "version": "2.2.200" 56 | } 57 | }, 58 | { 59 | "package": "swift-aws-lambda-runtime", 60 | "repositoryURL": "https://github.com/swift-server/swift-aws-lambda-runtime.git", 61 | "state": { 62 | "branch": null, 63 | "revision": "2bac89639fffd7b1197ab597473a4d10c459a230", 64 | "version": "0.2.0" 65 | } 66 | }, 67 | { 68 | "package": "swift-backtrace", 69 | "repositoryURL": "https://github.com/swift-server/swift-backtrace.git", 70 | "state": { 71 | "branch": null, 72 | "revision": "f2fd8c4845a123419c348e0bc4b3839c414077d5", 73 | "version": "1.2.0" 74 | } 75 | }, 76 | { 77 | "package": "SwiftJWT", 78 | "repositoryURL": "https://github.com/Kitura/Swift-JWT.git", 79 | "state": { 80 | "branch": null, 81 | "revision": "2f2fc12ae88660e0760b04d9e6c341517b31ad7b", 82 | "version": "3.6.200" 83 | } 84 | }, 85 | { 86 | "package": "swift-log", 87 | "repositoryURL": "https://github.com/apple/swift-log.git", 88 | "state": { 89 | "branch": null, 90 | "revision": "173f567a2dfec11d74588eea82cecea555bdc0bc", 91 | "version": "1.4.0" 92 | } 93 | }, 94 | { 95 | "package": "swift-nio", 96 | "repositoryURL": "https://github.com/apple/swift-nio.git", 97 | "state": { 98 | "branch": null, 99 | "revision": "8a865bd15e69526cbdfcfd7c47698eb20b2ba951", 100 | "version": "2.19.0" 101 | } 102 | } 103 | ] 104 | }, 105 | "version": 1 106 | } 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swift Lambda λ 2 | [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/getstream/swift-lambda/Swift)](https://github.com/GetStream/swift-lambda/actions?query=workflow%3ASwift) 3 | [![License](https://img.shields.io/github/license/getstream/swift-lambda)](/LICENSE) 4 | [![Twitter](https://img.shields.io/twitter/url?url=https%3A%2F%2Fgithub.com%2FGetStream%2Fswift-lambda)](https://twitter.com/intent/tweet?text=Wow:&url=https%3A%2F%2Fgithub.com%2FGetStream%2Fswift-lambda) 5 | [![Twitter Follow](https://img.shields.io/twitter/follow/getstream_io?style=social)](https://twitter.com/intent/follow?screen_name=getstream_io) 6 | 7 | This project is a starting point for writing an HTTP service in Swift. It contains configuration files and scripts to fully automate deploying to AWS in a matter of seconds using the Serverless Framework. It is based on samples and documentation from [swift-server/swift-aws-lambda-runtime](https://github.com/swift-server/swift-aws-lambda-runtime) 8 | 9 | Built with 💘 by the folks @ [Stream](https://getstream.io). 10 | 11 | 12 | 13 | 14 | ## 🤖 Chatbot Sample 15 | 16 | In the [Samples](Samples) folder you can find a Chatbot sample built from this tutorial: [Write a Chatbot in Swift and Deploy to AWS Lambda](https://getstream.io/blog/swift-lambda-chat-bot/) 17 | 18 | ## ⚙️ Getting started 19 | 20 | ### ✅ Prerequisites 21 | - [Docker](https://docs.docker.com/docker-for-mac/install/) 22 | - [Serverless Framework](https://www.serverless.com/framework/docs/getting-started/) 23 | - [AWS Credentials](https://www.serverless.com/framework/docs/providers/aws/guide/credentials/) 24 | 25 | ### ✍️ Write your function 26 | 27 | Write your code in [`Sources/Lambda/main.swift`](Sources/Lambda/main.swift). 28 | 29 | ### 🚀 Deploy 30 | 31 | Run `./Scripts/deploy.sh`. 32 | 33 | ### 🔎 Verify 34 | 35 | Open the output URL in your browser. 36 | 37 | And done! 38 | 39 |
40 | 41 | ## 👩‍💻 Developing 42 | 43 | There is some code already present in the `Sources/Lambda/main.swift` file. It simply outputs "Hello, world!" in plain text. 44 | 45 | ```swift 46 | import AWSLambdaEvents 47 | import AWSLambdaRuntime 48 | import NIO 49 | 50 | // MARK: - Run Lambda 51 | Lambda.run(APIGatewayProxyLambda()) 52 | 53 | // MARK: - Handler, Request and Response 54 | // FIXME: Use proper Event abstractions once added to AWSLambdaRuntime 55 | struct APIGatewayProxyLambda: EventLoopLambdaHandler { 56 | public typealias In = APIGateway.Request 57 | public typealias Out = APIGateway.Response 58 | 59 | public func handle(context: Lambda.Context, event: APIGateway.Request) -> EventLoopFuture { 60 | context.logger.debug("hello, api gateway!") 61 | return context.eventLoop.makeSucceededFuture(APIGateway.Response(statusCode: .ok, body: "Hello, world!")) 62 | } 63 | } 64 | ``` 65 | 66 | If you want to output some HTML, just set the "Content-Type" header to "text/html; charset=UTF-8" 67 | 68 | ```swift 69 | ... 70 | return context.eventLoop.makeSucceededFuture(APIGateway.Response( 71 | statusCode: .ok, 72 | headers: ["Content-Type": "text/html; charset=UTF-8"], 73 | body: """ 74 |

75 |

Hello, world! From Swift 5.2 💘

76 |

77 | 78 |

\(event.requestContext.identity.userAgent ?? "")

79 | """) 80 | ) 81 | ... 82 | ``` 83 | 84 | ![](https://i.imgur.com/CBWG1vG.png) 85 | 86 | For more information on the available settings and methods, refer to the [Swift AWS Lambda Runtime README](https://github.com/swift-server/swift-aws-lambda-runtime) 87 | 88 | ## 📡 Endpoint Settings 89 | 90 | To change some characteristics of your HTTP endpoint, such as the method expected, you should modify the [`serverless.yml`](https://github.com/GetStream/swift-lambda/blob/main/serverless.yml#L70-L72) file. For more information on the available parameters, refer to the [Serverless.yml Reference](https://www.serverless.com/framework/docs/providers/aws/guide/serverless.yml/). 91 | 92 | ## Contributing 93 | 94 | If you have a suggestion or bug report, please [file an issue in the Swift Lambda repository](https://github.com/GetStream/swift-lambda/issues/new). If you want to take a stab at contributing code, don't hesitate in submitting a PR. Don't forget to leave a star if you liked it :) 95 | --------------------------------------------------------------------------------