Skip to content

Commit d4b5ebe

Browse files
committed
automatically populate the table at first usage
1 parent c661cbe commit d4b5ebe

File tree

1 file changed

+76
-32
lines changed

1 file changed

+76
-32
lines changed

Examples/ServiceLifecycle/Sources/Lambda.swift

Lines changed: 76 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -25,72 +25,104 @@ struct User: Codable {
2525
@main
2626
struct LambdaFunction {
2727

28-
static func main() async throws {
29-
try await LambdaFunction().main()
30-
}
31-
3228
private let pgClient: PostgresClient
33-
private var logger: Logger
29+
private let logger: Logger
30+
3431
private init() throws {
35-
self.logger = Logger(label: "ServiceLifecycleExample")
32+
var logger = Logger(label: "ServiceLifecycleExample")
33+
logger.logLevel = Lambda.env("LOG_LEVEL").flatMap(Logger.Level.init) ?? .info
34+
self.logger = logger
3635

37-
self.pgClient = try LambdaFunction.preparePostgresClient(
36+
self.pgClient = try LambdaFunction.createPostgresClient(
3837
host: Lambda.env("DB_HOST") ?? "localhost",
3938
user: Lambda.env("DB_USER") ?? "postgres",
4039
password: Lambda.env("DB_PASSWORD") ?? "secret",
41-
dbName: Lambda.env("DB_NAME") ?? "test",
42-
logger: logger
40+
dbName: Lambda.env("DB_NAME") ?? "servicelifecycle",
41+
logger: self.logger
4342
)
4443
}
44+
45+
/// Function entry point when the runtime environment is created
4546
private func main() async throws {
4647

4748
// Instantiate LambdaRuntime with a handler implementing the business logic of the Lambda function
48-
// ok when https://github.com/swift-server/swift-aws-lambda-runtime/pull/523 will be merged
49-
//let runtime = LambdaRuntime(logger: logger, body: handler)
50-
let runtime = LambdaRuntime(body: handler)
49+
let runtime = LambdaRuntime(logger: self.logger, body: self.handler)
5150

5251
/// Use ServiceLifecycle to manage the initialization and termination
5352
/// of the PGClient together with the LambdaRuntime
5453
let serviceGroup = ServiceGroup(
55-
services: [pgClient, runtime],
54+
services: [self.pgClient, runtime],
5655
gracefulShutdownSignals: [.sigterm],
5756
cancellationSignals: [.sigint],
58-
logger: logger
57+
logger: self.logger
5958
)
59+
60+
// launch the service groups
61+
// this call will return upon termination or cancellation of all the services
6062
try await serviceGroup.run()
6163

6264
// perform any cleanup here
63-
6465
}
6566

67+
/// Function handler. This code is called at each function invocation
68+
/// input event is ignored in this demo.
6669
private func handler(event: String, context: LambdaContext) async -> [User] {
6770

68-
// input event is ignored here
69-
7071
var result: [User] = []
7172
do {
72-
// Use initialized service within the handler
73-
// IMPORTANT - CURRENTLY WHEN THERE IS AN ERROR, THIS CALL HANGS WHEN DB IS NOT REACHABLE
74-
// https://github.com/vapor/postgres-nio/issues/489
75-
// this is why there is a timeout, as suggested by
76-
// https://github.com/vapor/postgres-nio/issues/489#issuecomment-2186509773
77-
logger.info("Connecting to the database")
73+
// IMPORTANT - CURRENTLY, THIS CALL STOPS WHEN DB IS NOT REACHABLE
74+
// See: https://github.com/vapor/postgres-nio/issues/489
75+
// This is why there is a timeout, as suggested Fabian
76+
// See: https://github.com/vapor/postgres-nio/issues/489#issuecomment-2186509773
7877
result = try await timeout(deadline: .seconds(3)) {
79-
let rows = try await pgClient.query("SELECT id, username FROM users")
80-
var users: [User] = []
81-
for try await (id, username) in rows.decode((Int, String).self) {
82-
logger.info("Adding \(id) : \(username)")
83-
users.append(User(id: id, username: username))
84-
}
85-
return users
78+
// check if table exists
79+
try await prepareDatabase()
80+
81+
// query users
82+
return try await self.queryUsers()
8683
}
8784
} catch {
88-
logger.error("PG Error: \(error)")
85+
logger.error("Database Error", metadata: ["cause": "\(String(reflecting: error))"])
8986
}
87+
9088
return result
9189
}
9290

93-
private static func preparePostgresClient(
91+
/// Prepare the database
92+
/// At first run, this functions checks the database exist and is populated.
93+
/// This is useful for demo purposes. In real life, the database will contain data already.
94+
private func prepareDatabase() async throws {
95+
logger.trace("Preparing to the database")
96+
do {
97+
// initial creation of the table. This will fails if it already exists
98+
try await self.pgClient.query(SQLStatements.createTable)
99+
// it did not fail, it means the table is new and empty
100+
try await self.pgClient.query(SQLStatements.populateTable)
101+
} catch is PSQLError {
102+
// when there is a database error, it means the table or values already existed
103+
// ignore this error
104+
} catch {
105+
// propagate other errors
106+
throw error
107+
}
108+
}
109+
110+
/// Query the database
111+
private func queryUsers() async throws -> [User] {
112+
logger.trace("Querying to the database")
113+
var users: [User] = []
114+
let query = SQLStatements.queryAllUsers
115+
let rows = try await self.pgClient.query(query)
116+
for try await (id, username) in rows.decode((Int, String).self) {
117+
self.logger.trace("Adding \(id) : \(username)")
118+
users.append(User(id: id, username: username))
119+
}
120+
return users
121+
}
122+
123+
/// Create a postgres client
124+
/// ...TODO
125+
private static func createPostgresClient(
94126
host: String,
95127
user: String,
96128
password: String,
@@ -125,6 +157,18 @@ struct LambdaFunction {
125157

126158
return PostgresClient(configuration: config)
127159
}
160+
161+
private struct SQLStatements {
162+
static let createTable: PostgresQuery =
163+
"CREATE TABLE users (id SERIAL PRIMARY KEY, username VARCHAR(50) NOT NULL);"
164+
static let populateTable: PostgresQuery = "INSERT INTO users (username) VALUES ('alice'), ('bob'), ('charlie');"
165+
static let queryAllUsers: PostgresQuery = "SELECT id, username FROM users"
166+
}
167+
168+
static func main() async throws {
169+
try await LambdaFunction().main()
170+
}
171+
128172
}
129173

130174
public enum LambdaErrors: Error {

0 commit comments

Comments
 (0)