@@ -25,72 +25,104 @@ struct User: Codable {
25
25
@main
26
26
struct LambdaFunction {
27
27
28
- static func main( ) async throws {
29
- try await LambdaFunction ( ) . main ( )
30
- }
31
-
32
28
private let pgClient : PostgresClient
33
- private var logger : Logger
29
+ private let logger : Logger
30
+
34
31
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
36
35
37
- self . pgClient = try LambdaFunction . preparePostgresClient (
36
+ self . pgClient = try LambdaFunction . createPostgresClient (
38
37
host: Lambda . env ( " DB_HOST " ) ?? " localhost " ,
39
38
user: Lambda . env ( " DB_USER " ) ?? " postgres " ,
40
39
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
43
42
)
44
43
}
44
+
45
+ /// Function entry point when the runtime environment is created
45
46
private func main( ) async throws {
46
47
47
48
// 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)
51
50
52
51
/// Use ServiceLifecycle to manage the initialization and termination
53
52
/// of the PGClient together with the LambdaRuntime
54
53
let serviceGroup = ServiceGroup (
55
- services: [ pgClient, runtime] ,
54
+ services: [ self . pgClient, runtime] ,
56
55
gracefulShutdownSignals: [ . sigterm] ,
57
56
cancellationSignals: [ . sigint] ,
58
- logger: logger
57
+ logger: self . logger
59
58
)
59
+
60
+ // launch the service groups
61
+ // this call will return upon termination or cancellation of all the services
60
62
try await serviceGroup. run ( )
61
63
62
64
// perform any cleanup here
63
-
64
65
}
65
66
67
+ /// Function handler. This code is called at each function invocation
68
+ /// input event is ignored in this demo.
66
69
private func handler( event: String , context: LambdaContext ) async -> [ User ] {
67
70
68
- // input event is ignored here
69
-
70
71
var result : [ User ] = [ ]
71
72
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
78
77
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 ( )
86
83
}
87
84
} catch {
88
- logger. error ( " PG Error: \( error) " )
85
+ logger. error ( " Database Error" , metadata : [ " cause " : " \( String ( reflecting : error) ) " ] )
89
86
}
87
+
90
88
return result
91
89
}
92
90
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(
94
126
host: String ,
95
127
user: String ,
96
128
password: String ,
@@ -125,6 +157,18 @@ struct LambdaFunction {
125
157
126
158
return PostgresClient ( configuration: config)
127
159
}
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
+
128
172
}
129
173
130
174
public enum LambdaErrors : Error {
0 commit comments