Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Examples/quoteapi/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/.aws-sam
/.build
/.swiftpm
/.vscode
Package.resolved
samconfig.toml
*.d
*o
*swiftdeps
3 changes: 3 additions & 0 deletions Examples/quoteapi/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# image used to compile your Swift code
FROM public.ecr.aws/docker/library/swift:5.9.1-amazonlinux2
RUN yum -y install git jq tar zip openssl-devel
62 changes: 62 additions & 0 deletions Examples/quoteapi/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
### Add functions here and link them to builder-bot format MUST BE "build-FunctionResourceName in template.yaml"

build-QuoteService: builder-bot

# Helper commands
build:
sam build

deploy:
sam deploy

logs:
sam logs --stack-name QuoteService --name QuoteService

tail:
sam logs --stack-name QuoteService --name QuoteService --tail

local:
LOCAL_LAMBDA_SERVER_ENABLED=true swift run QuoteService

invoke:
curl -v -H 'Authorization: 123' https://k3lbszo7x6.execute-api.us-east-1.amazonaws.com/stocks/AAPL

###################### No Change required below this line ##########################

builder-bot:
$(eval $@PRODUCT = $(subst build-,,$(MAKECMDGOALS)))
$(eval $@BUILD_DIR = $(PWD)/.aws-sam/build-swift)
$(eval $@STAGE = $($@BUILD_DIR)/lambda)
$(eval $@ARTIFACTS_DIR = $(PWD)/.aws-sam/build/$($@PRODUCT))

## Building from swift-openapi-lambda in a local directory (not from Github)
## 1. git clone https://github.com/swift-server/swift-openapi-lambda ..
## 2. Change `Package.swift` dependency to ../swift-openapi-lambda

## 3. add /.. to BUILD_SRC
## $(eval $@BUILD_SRC = $(PWD)/..)
$(eval $@BUILD_SRC = $(PWD))

## 4. add `cd quoteapi &&` to the docker BUILD_CMD
## $(eval $@BUILD_CMD = "ls && cd quoteapi && swift build --static-swift-stdlib --product $($@PRODUCT) -c release --build-path /build-target")

$(eval $@BUILD_CMD = "swift build --static-swift-stdlib --product $($@PRODUCT) -c release --build-path /build-target")

# build docker image to compile Swift for Linux
docker build -f Dockerfile . -t swift-builder

# prep directories
mkdir -p $($@BUILD_DIR)/lambda $($@ARTIFACTS_DIR)

# compile application inside Docker image using source code from local project folder

docker run --rm -v $($@BUILD_DIR):/build-target -v $($@BUILD_SRC):/build-src -w /build-src swift-builder bash -cl $($@BUILD_CMD)

# create lambda bootstrap file
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"

# copy binary to stage
cp $($@BUILD_DIR)/release/$($@PRODUCT) $($@STAGE)/bootstrap

# copy app from stage to artifacts dir
cp $($@STAGE)/* $($@ARTIFACTS_DIR)
44 changes: 44 additions & 0 deletions Examples/quoteapi/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "QuoteService",
platforms: [
.macOS(.v13), .iOS(.v15), .tvOS(.v15), .watchOS(.v6),
],
products: [
.executable(name: "QuoteService", targets: ["QuoteService"])
],
dependencies: [
.package(url: "https://github.com/apple/swift-openapi-generator.git", from: "1.4.0"),
.package(url: "https://github.com/apple/swift-openapi-runtime.git", from: "1.5.0"),
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", from: "1.0.0-alpha.3"),
.package(url: "https://github.com/swift-server/swift-aws-lambda-events.git", from: "0.4.0"),
.package(url: "https://github.com/swift-server/swift-openapi-lambda.git", from: "0.2.0"),
// .package(name: "swift-openapi-lambda", path: "../swift-openapi-lambda")
],
targets: [
.executableTarget(
name: "QuoteService",
dependencies: [
.product(name: "OpenAPIRuntime", package: "swift-openapi-runtime"),
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
.product(name: "AWSLambdaEvents", package: "swift-aws-lambda-events"),
.product(name: "OpenAPILambda", package: "swift-openapi-lambda"),
],
path: "Sources",
resources: [
.copy("openapi.yaml"),
.copy("openapi-generator-config.yaml"),
],
plugins: [
.plugin(
name: "OpenAPIGenerator",
package: "swift-openapi-generator"
)
]
)
]
)
79 changes: 79 additions & 0 deletions Examples/quoteapi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# QuoteAPI

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.


## Prerequisites

To build this sample application, you need:

- [AWS Account](https://console.aws.amazon.com/)
- [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
- [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
- [Docker Desktop](https://www.docker.com/products/docker-desktop/) - to compile your Swift code for Linux deployment to AWS Lambda

## Build the application

The **sam build** command uses Docker to compile your Swift Lambda function and package it for deployment to AWS.

```bash
sam build
```

## Deploy the application

The **sam deploy** command creates the Lambda function and API Gateway in your AWS account.

```bash
sam deploy --guided
```

Accept the default response to every prompt, except the following warning:

```bash
QuoteService may not have authorization defined, Is this okay? [y/N]: y
```

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).

## Use the API

At the end of the deployment, SAM displays the endpoint of your API Gateway:

```bash
Outputs
----------------------------------------------------------------------------------------
Key SwiftAPIEndpoint
Description API Gateway endpoint URL for your application
Value https://[your-api-id].execute-api.[your-aws-region].amazonaws.com
----------------------------------------------------------------------------------------
```

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.

**Invoke the API Endpoint**

```bash
curl https://[your-api-endpoint]/stocks/AMZN
```

## Test the API Locally
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).

**Event Files**

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.

**Invoke the Lambda Function Locally**

```bash
sam local invoke QuoteService --event events/GetQuote.json
```

## Cleanup

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.

```bash
sam delete
```
49 changes: 49 additions & 0 deletions Examples/quoteapi/Sources/QuoteService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift OpenAPI Lambda open source project
//
// Copyright (c) 2023 Amazon.com, Inc. or its affiliates
// and the Swift OpenAPI Lambda project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift OpenAPI Lambda project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import Foundation
import OpenAPIRuntime
import OpenAPILambda

@main
struct QuoteServiceImpl: APIProtocol, OpenAPILambdaHttpApi {

init(transport: OpenAPILambdaTransport) throws {
try self.registerHandlers(on: transport)
}

func getQuote(_ input: Operations.getQuote.Input) async throws -> Operations.getQuote.Output {

let symbol = input.path.symbol

var date: Date = Date()
if let dateString = input.query.date {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyMMdd"
date = dateFormatter.date(from: dateString) ?? Date()
}

let price = Components.Schemas.quote(
symbol: symbol,
price: Double.random(in: 100..<150).rounded(),
change: Double.random(in: -5..<5).rounded(),
changePercent: Double.random(in: -0.05..<0.05),
volume: Double.random(in: 10000..<100000).rounded(),
timestamp: date
)

return .ok(.init(body: .json(price)))
}
}
4 changes: 4 additions & 0 deletions Examples/quoteapi/Sources/openapi-generator-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
generate:
- types
- server

54 changes: 54 additions & 0 deletions Examples/quoteapi/Sources/openapi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
openapi: 3.1.0
info:
title: StockQuoteService
version: 1.0.0

components:
schemas:
quote:
type: object
properties:
symbol:
type: string
price:
type: number
change:
type: number
changePercent:
type: number
volume:
type: number
timestamp:
type: string
format: date-time

paths:
/stocks/{symbol}:
get:
summary: Get the latest quote for a stock
operationId: getQuote
parameters:
- name: symbol
in: path
required: true
schema:
type: string
- name: date
in: query
required: false
schema:
type: string
format: date
tags:
- stocks
responses:
200:
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/quote'
400:
description: Bad Request
404:
description: Not Found
34 changes: 34 additions & 0 deletions Examples/quoteapi/events/GetQuote.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"rawQueryString": "",
"headers": {
"host": "b2k1t8fon7.execute-api.us-east-1.amazonaws.com",
"x-forwarded-port": "443",
"content-length": "0",
"x-amzn-trace-id": "Root=1-6571d134-63dbe8ee21efa87555d59265",
"x-forwarded-for": "191.95.148.219",
"x-forwarded-proto": "https",
"accept": "*/*",
"user-agent": "curl/8.1.2"
},
"requestContext": {
"apiId": "b2k1t8fon7",
"http": {
"sourceIp": "191.95.148.219",
"userAgent": "curl/8.1.2",
"method": "GET",
"path": "/stocks/AAPL",
"protocol": "HTTP/1.1"
},
"timeEpoch": 1701957940365,
"domainPrefix": "b2k1t8fon7",
"accountId": "486652066693",
"time": "07/Dec/2023:14:05:40 +0000",
"stage": "$default",
"domainName": "b2k1t8fon7.execute-api.us-east-1.amazonaws.com",
"requestId": "Pk2gOia2IAMEPOw="
},
"isBase64Encoded": false,
"version": "2.0",
"routeKey": "$default",
"rawPath": "/stocks/AAPL"
}
Loading