Skip to content

Commit 1a5ba0c

Browse files
tomerdGitHub Enterprise
authored and
GitHub Enterprise
committed
add docs (#8)
motivation: help users better undersatnd how to use the library changes: * add API docs to public APIs * add narattive docs in README
1 parent dd92021 commit 1a5ba0c

File tree

9 files changed

+95
-26
lines changed

9 files changed

+95
-26
lines changed

Sources/SwiftAwsLambda/HttpClient.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import Foundation
1616
import NIO
1717
import NIOHTTP1
1818

19+
/// A barebone HTTP client to interact with AWS Runtime Engine which is an HTTP server.
1920
internal class HTTPClient {
2021
let eventLoop: EventLoop
2122

Sources/SwiftAwsLambda/Lambda+Codable.swift

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,19 @@
1414

1515
import Foundation
1616

17+
/// Extension to the `Lambda` companion to enable execution of Lambdas that take and return `Codable` payloads.
18+
/// This is the most common way to use this library in AWS Lambda, since its JSON based.
1719
extension Lambda {
20+
/// Run a Lambda defined by implementing the `LambdaCodableClosure` closure, having `In` and `Out` extending `Decodable` and `Encodable` respectively.
21+
///
22+
/// - note: This is a blocking operation that will run forever, as it's lifecycle is managed by the AWS Lambda Runtime Engine.
1823
public static func run<In: Decodable, Out: Encodable>(_ closure: @escaping LambdaCodableClosure<In, Out>) {
1924
self.run(LambdaClosureWrapper(closure))
2025
}
2126

27+
/// Run a Lambda defined by implementing the `LambdaCodableHandler` protocol, having `In` and `Out` are `Decodable` and `Encodable` respectively.
28+
///
29+
/// - note: This is a blocking operation that will run forever, as it's lifecycle is managed by the AWS Lambda Runtime Engine.
2230
public static func run<T>(_ handler: T) where T: LambdaCodableHandler {
2331
self.run(handler as LambdaHandler)
2432
}
@@ -34,12 +42,18 @@ extension Lambda {
3442
}
3543
}
3644

45+
/// A result type for a Lambda that returns a generic `Out`, having `Out` extend `Encodable`.
3746
public typealias LambdaCodableResult<Out> = Result<Out, String>
3847

48+
/// A callback for a Lambda that returns a `LambdaCodableResult<Out>` result type, having `Out` extend `Encodable`.
3949
public typealias LambdaCodableCallback<Out> = (LambdaCodableResult<Out>) -> Void
4050

51+
/// A processing closure for a Lambda that takes an `In` and returns an `Out` via `LambdaCodableCallback<Out>` asynchronously,
52+
/// having `In` and `Out` extending `Decodable` and `Encodable` respectively.
4153
public typealias LambdaCodableClosure<In, Out> = (LambdaContext, In, LambdaCodableCallback<Out>) -> Void
4254

55+
/// A processing protocol for a Lambda that takes an `In` and returns an `Out` via `LambdaCodableCallback<Out>` asynchronously,
56+
/// having `In` and `Out` extending `Decodable` and `Encodable` respectively.
4357
public protocol LambdaCodableHandler: LambdaHandler {
4458
associatedtype In: Decodable
4559
associatedtype Out: Encodable
@@ -48,19 +62,22 @@ public protocol LambdaCodableHandler: LambdaHandler {
4862
var codec: LambdaCodableCodec<In, Out> { get }
4963
}
5064

51-
// default uses json codec. advanced users that want to inject their own codec can do it here
65+
/// Default implementation for `LambdaCodableHandler` codec which uses JSON via `LambdaCodableJsonCodec`.
66+
/// Advanced users that want to inject their own codec can do it by overriding this.
5267
public extension LambdaCodableHandler {
5368
var codec: LambdaCodableCodec<In, Out> {
5469
return LambdaCodableJsonCodec<In, Out>()
5570
}
5671
}
5772

73+
/// LambdaCodableCodec is an abstract/empty implementation for codec which does `Encodable` -> `[UInt8]` encoding and `[UInt8]` -> `Decodable' decoding.
5874
// TODO: would be nicer to use a protocol instead of this "abstract class", but generics get in the way
5975
public class LambdaCodableCodec<In: Decodable, Out: Encodable> {
6076
func encode(_: Out) -> [UInt8]? { return nil }
6177
func decode(_: [UInt8]) -> In? { return nil }
6278
}
6379

80+
/// Default implementation of `Encodable` -> `[UInt8]` encoding and `[UInt8]` -> `Decodable' decoding
6481
public extension LambdaCodableHandler {
6582
func handle(context: LambdaContext, payload: [UInt8], callback: @escaping (LambdaResult) -> Void) {
6683
guard let payloadAsCodable = codec.decode(payload) else {
@@ -80,6 +97,8 @@ public extension LambdaCodableHandler {
8097
}
8198
}
8299

100+
/// LambdaCodableJsonCodec is an implementation of `LambdaCodableCodec` which does `Encodable` -> `[UInt8]` encoding and `[UInt8]` -> `Decodable' decoding
101+
/// using JSONEncoder and JSONDecoder respectively.
83102
// This is a class as encoder amd decoder are a class, which means its cheaper to hold a reference to both in a class then a struct.
84103
private class LambdaCodableJsonCodec<In: Decodable, Out: Encodable>: LambdaCodableCodec<In, Out> {
85104
private let encoder = JSONEncoder()

Sources/SwiftAwsLambda/Lambda+String.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,18 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15+
/// Extension to the `Lambda` companion to enable execution of Lambdas that take and return `String` payloads.
1516
extension Lambda {
17+
/// Run a Lambda defined by implementing the `LambdaStringClosure` protocol.
18+
///
19+
/// - note: This is a blocking operation that will run forever, as it's lifecycle is managed by the AWS Lambda Runtime Engine.
1620
public static func run(_ closure: @escaping LambdaStringClosure) {
1721
self.run(LambdaClosureWrapper(closure))
1822
}
1923

24+
/// Run a Lambda defined by implementing the `LambdaStringHandler` protocol.
25+
///
26+
/// - note: This is a blocking operation that will run forever, as it's lifecycle is managed by the AWS Lambda Runtime Engine.
2027
public static func run(_ handler: LambdaStringHandler) {
2128
self.run(handler as LambdaHandler)
2229
}
@@ -32,16 +39,21 @@ extension Lambda {
3239
}
3340
}
3441

42+
/// A result type for a Lambda that returns a `String`.
3543
public typealias LambdaStringResult = Result<String, String>
3644

45+
/// A callback for a Lambda that returns a `LambdaStringResult` result type.
3746
public typealias LambdaStringCallback = (LambdaStringResult) -> Void
3847

48+
/// A processing closure for a Lambda that takes a `String` and returns a `LambdaStringResult` via `LambdaStringCallback` asynchronously.
3949
public typealias LambdaStringClosure = (LambdaContext, String, LambdaStringCallback) -> Void
4050

51+
/// A processing protocol for a Lambda that takes a `String` and returns a `LambdaStringResult` via `LambdaStringCallback` asynchronously.
4152
public protocol LambdaStringHandler: LambdaHandler {
4253
func handle(context: LambdaContext, payload: String, callback: @escaping LambdaStringCallback)
4354
}
4455

56+
/// Default implementation of `String` -> `[UInt8]` encoding and `[UInt8]` -> `String' decoding
4557
public extension LambdaStringHandler {
4658
func handle(context: LambdaContext, payload: [UInt8], callback: @escaping LambdaCallback) {
4759
guard let payloadAsString = String(bytes: payload, encoding: .utf8) else {

Sources/SwiftAwsLambda/Lambda.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,16 @@ import Foundation
1616
import NIO
1717

1818
public enum Lambda {
19+
/// Run a Lambda defined by implementing the `LambdaClosure` closure.
20+
///
21+
/// - note: This is a blocking operation that will run forever, as it's lifecycle is managed by the AWS Lambda Runtime Engine.
1922
public static func run(_ closure: @escaping LambdaClosure) {
2023
let _: LambdaLifecycleResult = run(closure)
2124
}
2225

26+
/// Run a Lambda defined by implementing the `LambdaHandler` protocol.
27+
///
28+
/// - note: This is a blocking operation that will run forever, as it's lifecycle is managed by the AWS Lambda Runtime Engine.
2329
public static func run(_ handler: LambdaHandler) {
2430
let _: LambdaLifecycleResult = run(handler: handler)
2531
}
@@ -132,12 +138,15 @@ public enum Lambda {
132138
}
133139
}
134140

141+
/// A result type for a Lambda that returns a `[UInt8]`.
135142
public typealias LambdaResult = Result<[UInt8], String>
136143

137144
public typealias LambdaCallback = (LambdaResult) -> Void
138145

146+
/// A processing closure for a Lambda that takes a `[UInt8]` and returns a `LambdaResult` result type asynchronously.
139147
public typealias LambdaClosure = (LambdaContext, [UInt8], LambdaCallback) -> Void
140148

149+
/// A processing protocol for a Lambda that takes a `[UInt8]` and returns a `LambdaResult` result type asynchronously.
141150
public protocol LambdaHandler {
142151
func handle(context: LambdaContext, payload: [UInt8], callback: @escaping LambdaCallback)
143152
}

Sources/SwiftAwsLambda/LambdaRunner.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import Foundation
1616
import NIO
1717

18+
/// LambdaRunner manages the Lambda runtime workflow, or business logic.
1819
internal final class LambdaRunner {
1920
private let lambdaHandler: LambdaHandler
2021
private let runtimeClient: LambdaRuntimeClient

Sources/SwiftAwsLambda/LambdaRuntimeClient.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ import Foundation
1616
import NIO
1717
import NIOHTTP1
1818

19+
/// An HTTP based client for AWS Runtime Engine. This encapsulates the RESTful methods exposed by the Runtime Engine:
20+
/// * /runtime/invocation/next
21+
/// * /runtime/invocation/response
22+
/// * /runtime/invocation/error
1923
internal class LambdaRuntimeClient {
2024
private let baseUrl: String
2125
private let httpClient: HTTPClient

Sources/SwiftAwsLambda/Utils.swift

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,17 @@
1515
import Dispatch
1616
import NIO
1717

18+
/// Genric result type, that is used throught the library.
1819
public enum Result<Value, Error> {
1920
case success(Value)
2021
case failure(Error)
2122
}
2223

24+
internal enum Defaults {
25+
static let host = "127.0.0.1"
26+
static let port = 8080
27+
}
28+
2329
internal enum Consts {
2430
static let hostPortEnvVariableName = "AWS_LAMBDA_RUNTIME_API"
2531

@@ -30,6 +36,7 @@ internal enum Consts {
3036
static let postErrorURLSuffix = "/error"
3137
}
3238

39+
/// AWS Lambda HTTP Headers, used to populate the `LambdaContext` object.
3340
internal enum AmazonHeaders {
3441
static let requestID = "Lambda-Runtime-Aws-Request-Id"
3542
static let traceID = "Lambda-Runtime-Trace-Id"
@@ -39,11 +46,7 @@ internal enum AmazonHeaders {
3946
static let invokedFunctionARN = "Lambda-Runtime-Invoked-Function-Arn"
4047
}
4148

42-
internal enum Defaults {
43-
static let host = "127.0.0.1"
44-
static let port = 8080
45-
}
46-
49+
/// Utility to read environment variables
4750
internal enum Environment {
4851
static func string(name: String, defaultValue: String) -> String {
4952
return self.string(name) ?? defaultValue
@@ -68,16 +71,7 @@ internal enum Environment {
6871
}
6972
}
7073

71-
internal enum Signal: Int32 {
72-
case HUP = 1
73-
case INT = 2
74-
case QUIT = 3
75-
case ABRT = 6
76-
case KILL = 9
77-
case ALRM = 14
78-
case TERM = 15
79-
}
80-
74+
/// Helper function to trap signals
8175
internal func trap(signal sig: Signal, handler: @escaping (Signal) -> Void) -> DispatchSourceSignal {
8276
let signalSource = DispatchSource.makeSignalSource(signal: sig.rawValue, queue: DispatchQueue.global())
8377
signal(sig.rawValue, SIG_IGN)
@@ -88,3 +82,13 @@ internal func trap(signal sig: Signal, handler: @escaping (Signal) -> Void) -> D
8882
signalSource.resume()
8983
return signalSource
9084
}
85+
86+
internal enum Signal: Int32 {
87+
case HUP = 1
88+
case INT = 2
89+
case QUIT = 3
90+
case ABRT = 6
91+
case KILL = 9
92+
case ALRM = 14
93+
case TERM = 15
94+
}

Tests/SwiftAwsLambdaTests/LambdaContextTest.swif

Whitespace-only changes.

readme.md

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Swift AWS Lambda
22

3-
This library is designed to allow writing AWS Lambdas using the Swift programming language.
3+
This library is designed to simplify implementing an AWS Lambda using the Swift programming language.
44

55
## Getting started
66

@@ -25,26 +25,45 @@ This library is designed to allow writing AWS Lambdas using the Swift programmin
2525
)
2626
```
2727

28-
2. Create a main.swift and implement you lambda. typically a lambda is implemented as a closure. For example, a simple lambda for a closure that receives a string payload and replies with the reverse version:
28+
2. Create a main.swift and implement your Lambda. Typically a Lambda is implemented as a closure. For example, a simple closure that receives a string payload and replies with the reverse version:
2929

3030
```
3131
import SwiftAwsLambda
3232
33-
_ = Lambda.run { (context: LambdaContext, payload: String, callback: LambdaStringCallback) in
33+
Lambda.run { (context: LambdaContext, payload: String, callback: LambdaStringCallback) in
3434
callback(.success(String(payload.reversed())))
3535
}
3636
```
3737

38-
note you can implement 3 types of lambdas:
38+
SwiftAwsLambda supports three types of Lambdas:
39+
1. `[UInt8]` (byte array) based (default): see `SwiftAwsLambdaExample`
40+
2. `String` based: see `SwiftAwsLambdaStringExample`
41+
3. `Codable` based: see `SwiftAwsLambdaCodableExample`. This is the most pragmatic mode of operation, since AWS Lambda is JSON based.
3942

40-
1. `[UInt8]` (byte array) based (default): see `SwiftAwsLambdaExample`
41-
2. `String` based: see `SwiftAwsLambdaStringExample`
42-
3. `Codable` based: see `SwiftAwsLambdaCodableExample`
43+
See more on [SwiftAwsLambda Sample](https://github.com/swift-server/swift-aws-lambda-sample).
44+
45+
3. Deploy to AWS Lambda. To do so, you need to compile your Application for EC2 Linux, package it as a Zip file, and upload to AWS. You can find sample build and deployment scripts in [SwiftAwsLambda Sample](https://github.com/swift-server/swift-aws-lambda-sample).
4346

4447
## Architecture
4548

46-
TODO
49+
The library is designed to integrate with AWS Lambda Runtime Engine, via the BYOL Native Runtime API.
50+
The latter is an HTTP server that exposes three main RESTful endpoint:
51+
* /runtime/invocation/next
52+
* /runtime/invocation/response
53+
* /runtime/invocation/error
54+
55+
The library encapsulates these endpoints and the expected lifecycle via `LambdaRuntimeClient` and `LambdaRunner` respectively.
56+
57+
**Single Lambda Execution Workflow**
58+
59+
1. The library calls AWS Lambda Runtime Engine `/next` endpoint to retrieve the next invocation request.
60+
2. The library parses the response HTTP headers and populate the `LambdaContext` object.
61+
3. The library reads the response body and attempt to decode it, if required. Typically it decodes to user provided type which extends `Decodable`, but users may choose to write Lambdas that receive the input as `String` or `[UInt8]` byte array which require less, or no decoding.
62+
4. The library hands off the `Context` and `Request` to the user provided handler on a dedicated `Dispatch` queue, providing isolation between user's and the library's code.
63+
5. User's code processes the request asynchronously, invoking a callback upon completion, which returns a result type with the `Response` or `Error` populated.
64+
6. In case of error, the library posts to AWS Lambda Runtime Engine `/error` endpoint to provide the error details, which will show up on AWS Lambda logs.
65+
7. In case of success, the library will attempt to encode the response, if required. Typically it encodes from user provided type which extends `Encodable`, but users may choose to write Lambdas that return a `String` or `[UInt8]` byte array, which require less, or no encoding. The library then posts to AWS Lambda Runtime Engine `/response` endpoint to provide the response.
4766

48-
## Deploying
67+
**Lifecycle Management**
4968

50-
TODO
69+
AWS Runtime Engine controls the Application lifecycle and in the happy case never terminates the application, only suspends it's execution when no work is avaialble. As such, the library main entry point is designed to run forever in a blocking fashion, performing the workflow described above in an endless loop. That loop is broken if/when an internal error occurs, such as a failure to communicate with AWS Runtime Engine API, or under other unexpected conditions.

0 commit comments

Comments
 (0)