Skip to content

Commit 6a54484

Browse files
authored
Add quote api example (#20)
In preparation for a v2 release that will support the Lambda Runtime v2, I'm adding an end-to-end example project
1 parent 78dcf0c commit 6a54484

File tree

13 files changed

+578
-0
lines changed

13 files changed

+578
-0
lines changed

Examples/quoteapi/.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/.aws-sam
2+
/.build
3+
/.swiftpm
4+
/.vscode
5+
Package.resolved
6+
samconfig.toml
7+
*.d
8+
*o
9+
*swiftdeps

Examples/quoteapi/Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# image used to compile your Swift code
2+
FROM public.ecr.aws/docker/library/swift:5.9.1-amazonlinux2
3+
RUN yum -y install git jq tar zip openssl-devel

Examples/quoteapi/Makefile

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
### Add functions here and link them to builder-bot format MUST BE "build-FunctionResourceName in template.yaml"
2+
3+
build-QuoteService: builder-bot
4+
5+
# Helper commands
6+
build:
7+
sam build
8+
9+
deploy:
10+
sam deploy
11+
12+
logs:
13+
sam logs --stack-name QuoteService --name QuoteService
14+
15+
tail:
16+
sam logs --stack-name QuoteService --name QuoteService --tail
17+
18+
local:
19+
LOCAL_LAMBDA_SERVER_ENABLED=true swift run QuoteService
20+
21+
invoke:
22+
curl -v -H 'Authorization: 123' https://k3lbszo7x6.execute-api.us-east-1.amazonaws.com/stocks/AAPL
23+
24+
###################### No Change required below this line ##########################
25+
26+
builder-bot:
27+
$(eval $@PRODUCT = $(subst build-,,$(MAKECMDGOALS)))
28+
$(eval $@BUILD_DIR = $(PWD)/.aws-sam/build-swift)
29+
$(eval $@STAGE = $($@BUILD_DIR)/lambda)
30+
$(eval $@ARTIFACTS_DIR = $(PWD)/.aws-sam/build/$($@PRODUCT))
31+
32+
## Building from swift-openapi-lambda in a local directory (not from Github)
33+
## 1. git clone https://github.com/swift-server/swift-openapi-lambda ..
34+
## 2. Change `Package.swift` dependency to ../swift-openapi-lambda
35+
36+
## 3. add /.. to BUILD_SRC
37+
## $(eval $@BUILD_SRC = $(PWD)/..)
38+
$(eval $@BUILD_SRC = $(PWD))
39+
40+
## 4. add `cd quoteapi &&` to the docker BUILD_CMD
41+
## $(eval $@BUILD_CMD = "ls && cd quoteapi && swift build --static-swift-stdlib --product $($@PRODUCT) -c release --build-path /build-target")
42+
43+
$(eval $@BUILD_CMD = "swift build --static-swift-stdlib --product $($@PRODUCT) -c release --build-path /build-target")
44+
45+
# build docker image to compile Swift for Linux
46+
docker build -f Dockerfile . -t swift-builder
47+
48+
# prep directories
49+
mkdir -p $($@BUILD_DIR)/lambda $($@ARTIFACTS_DIR)
50+
51+
# compile application inside Docker image using source code from local project folder
52+
53+
docker run --rm -v $($@BUILD_DIR):/build-target -v $($@BUILD_SRC):/build-src -w /build-src swift-builder bash -cl $($@BUILD_CMD)
54+
55+
# create lambda bootstrap file
56+
docker run --rm -v $($@BUILD_DIR):/build-target -v `pwd`:/build-src -w /build-src swift-builder bash -cl "cd /build-target/lambda && ln -s $($@PRODUCT) /bootstrap"
57+
58+
# copy binary to stage
59+
cp $($@BUILD_DIR)/release/$($@PRODUCT) $($@STAGE)/bootstrap
60+
61+
# copy app from stage to artifacts dir
62+
cp $($@STAGE)/* $($@ARTIFACTS_DIR)

Examples/quoteapi/Package.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// swift-tools-version: 5.9
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "QuoteService",
8+
platforms: [
9+
.macOS(.v13), .iOS(.v15), .tvOS(.v15), .watchOS(.v6),
10+
],
11+
products: [
12+
.executable(name: "QuoteService", targets: ["QuoteService"])
13+
],
14+
dependencies: [
15+
.package(url: "https://github.com/apple/swift-openapi-generator.git", from: "1.4.0"),
16+
.package(url: "https://github.com/apple/swift-openapi-runtime.git", from: "1.5.0"),
17+
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", from: "1.0.0-alpha.3"),
18+
.package(url: "https://github.com/swift-server/swift-aws-lambda-events.git", from: "0.4.0"),
19+
.package(url: "https://github.com/swift-server/swift-openapi-lambda.git", from: "0.2.0"),
20+
// .package(name: "swift-openapi-lambda", path: "../swift-openapi-lambda")
21+
],
22+
targets: [
23+
.executableTarget(
24+
name: "QuoteService",
25+
dependencies: [
26+
.product(name: "OpenAPIRuntime", package: "swift-openapi-runtime"),
27+
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
28+
.product(name: "AWSLambdaEvents", package: "swift-aws-lambda-events"),
29+
.product(name: "OpenAPILambda", package: "swift-openapi-lambda"),
30+
],
31+
path: "Sources",
32+
resources: [
33+
.copy("openapi.yaml"),
34+
.copy("openapi-generator-config.yaml"),
35+
],
36+
plugins: [
37+
.plugin(
38+
name: "OpenAPIGenerator",
39+
package: "swift-openapi-generator"
40+
)
41+
]
42+
)
43+
]
44+
)

Examples/quoteapi/README.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# QuoteAPI
2+
3+
This application illustrates how to deploy a Server-Side Swift workload on AWS using the [AWS Serverless Application Model (SAM)](https://aws.amazon.com/serverless/sam/) toolkit. The workload is a simple REST API that returns a string from an Amazon API Gateway. Requests to the API Gateway endpoint are handled by an AWS Lambda Function written in Swift.
4+
5+
6+
## Prerequisites
7+
8+
To build this sample application, you need:
9+
10+
- [AWS Account](https://console.aws.amazon.com/)
11+
- [AWS Command Line Interface (AWS CLI)](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html) - install the CLI and [configure](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html) it with credentials to your AWS account
12+
- [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html) - a command-line tool used to create serverless workloads on AWS
13+
- [Docker Desktop](https://www.docker.com/products/docker-desktop/) - to compile your Swift code for Linux deployment to AWS Lambda
14+
15+
## Build the application
16+
17+
The **sam build** command uses Docker to compile your Swift Lambda function and package it for deployment to AWS.
18+
19+
```bash
20+
sam build
21+
```
22+
23+
## Deploy the application
24+
25+
The **sam deploy** command creates the Lambda function and API Gateway in your AWS account.
26+
27+
```bash
28+
sam deploy --guided
29+
```
30+
31+
Accept the default response to every prompt, except the following warning:
32+
33+
```bash
34+
QuoteService may not have authorization defined, Is this okay? [y/N]: y
35+
```
36+
37+
The project creates a publicly accessible API endpoint. This is a warning to inform you the API does not have authorization. If you are interested in adding authorization to the API, please refer to the [SAM Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-httpapi.html).
38+
39+
## Use the API
40+
41+
At the end of the deployment, SAM displays the endpoint of your API Gateway:
42+
43+
```bash
44+
Outputs
45+
----------------------------------------------------------------------------------------
46+
Key SwiftAPIEndpoint
47+
Description API Gateway endpoint URL for your application
48+
Value https://[your-api-id].execute-api.[your-aws-region].amazonaws.com
49+
----------------------------------------------------------------------------------------
50+
```
51+
52+
Use cURL or a tool such as [Postman](https://www.postman.com/) to interact with your API. Replace **[your-api-endpoint]** with the SwiftAPIEndpoint value from the deployment output.
53+
54+
**Invoke the API Endpoint**
55+
56+
```bash
57+
curl https://[your-api-endpoint]/stocks/AMZN
58+
```
59+
60+
## Test the API Locally
61+
SAM also allows you to execute your Lambda functions locally on your development computer. Follow these instructions to execute the Lambda function locally. Further capabilities can be explored in the [SAM Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-using-invoke.html).
62+
63+
**Event Files**
64+
65+
When a Lambda function is invoked, API Gateway sends an event to the function with all the data packaged with the API call. When running the functions locally, you pass in a json file to the function that simulates the event data. The **events** folder contains a json file for the function.
66+
67+
**Invoke the Lambda Function Locally**
68+
69+
```bash
70+
sam local invoke QuoteService --event events/GetQuote.json
71+
```
72+
73+
## Cleanup
74+
75+
When finished with your application, use SAM to delete it from your AWS account. Answer **Yes (y)** to all prompts. This will delete all of the application resources created in your AWS account.
76+
77+
```bash
78+
sam delete
79+
```
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift OpenAPI Lambda open source project
4+
//
5+
// Copyright (c) 2023 Amazon.com, Inc. or its affiliates
6+
// and the Swift OpenAPI Lambda project authors
7+
// Licensed under Apache License v2.0
8+
//
9+
// See LICENSE.txt for license information
10+
// See CONTRIBUTORS.txt for the list of Swift OpenAPI Lambda project authors
11+
//
12+
// SPDX-License-Identifier: Apache-2.0
13+
//
14+
//===----------------------------------------------------------------------===//
15+
16+
import Foundation
17+
import OpenAPIRuntime
18+
import OpenAPILambda
19+
20+
@main
21+
struct QuoteServiceImpl: APIProtocol, OpenAPILambdaHttpApi {
22+
23+
init(transport: OpenAPILambdaTransport) throws {
24+
try self.registerHandlers(on: transport)
25+
}
26+
27+
func getQuote(_ input: Operations.getQuote.Input) async throws -> Operations.getQuote.Output {
28+
29+
let symbol = input.path.symbol
30+
31+
var date: Date = Date()
32+
if let dateString = input.query.date {
33+
let dateFormatter = DateFormatter()
34+
dateFormatter.dateFormat = "yyyyMMdd"
35+
date = dateFormatter.date(from: dateString) ?? Date()
36+
}
37+
38+
let price = Components.Schemas.quote(
39+
symbol: symbol,
40+
price: Double.random(in: 100..<150).rounded(),
41+
change: Double.random(in: -5..<5).rounded(),
42+
changePercent: Double.random(in: -0.05..<0.05),
43+
volume: Double.random(in: 10000..<100000).rounded(),
44+
timestamp: date
45+
)
46+
47+
return .ok(.init(body: .json(price)))
48+
}
49+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
generate:
2+
- types
3+
- server
4+
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
openapi: 3.1.0
2+
info:
3+
title: StockQuoteService
4+
version: 1.0.0
5+
6+
components:
7+
schemas:
8+
quote:
9+
type: object
10+
properties:
11+
symbol:
12+
type: string
13+
price:
14+
type: number
15+
change:
16+
type: number
17+
changePercent:
18+
type: number
19+
volume:
20+
type: number
21+
timestamp:
22+
type: string
23+
format: date-time
24+
25+
paths:
26+
/stocks/{symbol}:
27+
get:
28+
summary: Get the latest quote for a stock
29+
operationId: getQuote
30+
parameters:
31+
- name: symbol
32+
in: path
33+
required: true
34+
schema:
35+
type: string
36+
- name: date
37+
in: query
38+
required: false
39+
schema:
40+
type: string
41+
format: date
42+
tags:
43+
- stocks
44+
responses:
45+
200:
46+
description: OK
47+
content:
48+
application/json:
49+
schema:
50+
$ref: '#/components/schemas/quote'
51+
400:
52+
description: Bad Request
53+
404:
54+
description: Not Found
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"rawQueryString": "",
3+
"headers": {
4+
"host": "b2k1t8fon7.execute-api.us-east-1.amazonaws.com",
5+
"x-forwarded-port": "443",
6+
"content-length": "0",
7+
"x-amzn-trace-id": "Root=1-6571d134-63dbe8ee21efa87555d59265",
8+
"x-forwarded-for": "191.95.148.219",
9+
"x-forwarded-proto": "https",
10+
"accept": "*/*",
11+
"user-agent": "curl/8.1.2"
12+
},
13+
"requestContext": {
14+
"apiId": "b2k1t8fon7",
15+
"http": {
16+
"sourceIp": "191.95.148.219",
17+
"userAgent": "curl/8.1.2",
18+
"method": "GET",
19+
"path": "/stocks/AAPL",
20+
"protocol": "HTTP/1.1"
21+
},
22+
"timeEpoch": 1701957940365,
23+
"domainPrefix": "b2k1t8fon7",
24+
"accountId": "486652066693",
25+
"time": "07/Dec/2023:14:05:40 +0000",
26+
"stage": "$default",
27+
"domainName": "b2k1t8fon7.execute-api.us-east-1.amazonaws.com",
28+
"requestId": "Pk2gOia2IAMEPOw="
29+
},
30+
"isBase64Encoded": false,
31+
"version": "2.0",
32+
"routeKey": "$default",
33+
"rawPath": "/stocks/AAPL"
34+
}

0 commit comments

Comments
 (0)