@@ -18,69 +18,82 @@ import Foundation
18
18
#endif
19
19
20
20
@main
21
- struct GHHooksHandler : LambdaHandler {
22
- typealias Event = APIGatewayV2Request
23
- typealias Output = APIGatewayV2Response
21
+ @dynamicMemberLookup
22
+ struct GHHooksHandler {
23
+ struct SharedContext {
24
+ let httpClient : HTTPClient
25
+ let githubClient : Client
26
+ let awsClient : AWSClient
27
+ let secretsRetriever : SecretsRetriever
28
+ }
29
+
30
+ subscript< T> ( dynamicMember keyPath: KeyPath < SharedContext , T > ) -> T {
31
+ get {
32
+ sharedContext [ keyPath: keyPath]
33
+ }
34
+ }
24
35
25
- let httpClient : HTTPClient
26
- let githubClient : Client
27
- let secretsRetriever : SecretsRetriever
36
+ let sharedContext : SharedContext
28
37
let messageLookupRepo : any MessageLookupRepo
29
38
let logger : Logger
30
39
31
40
/// We don't do this in the initializer to avoid a possible unnecessary
32
41
/// `secretsRetriever.getSecret()` call which costs $$$.
33
42
var discordClient : any DiscordClient {
34
43
get async throws {
35
- let botToken = try await secretsRetriever. getSecret ( arnEnvVarKey: " BOT_TOKEN_ARN " )
36
- return await DefaultDiscordClient ( httpClient: httpClient, token: botToken)
44
+ let botToken = try await self . secretsRetriever. getSecret ( arnEnvVarKey: " BOT_TOKEN_ARN " )
45
+ return await DefaultDiscordClient ( httpClient: self . httpClient, token: botToken)
37
46
}
38
47
}
39
48
40
- init ( context: LambdaInitializationContext ) async throws {
41
- self . logger = context. logger
42
- /// We can remove this if/when the lambda runtime gets support for
43
- /// bootstrapping the logging system which it appears to not have.
44
- DiscordGlobalConfiguration . makeLogger = { _ in context. logger }
45
-
46
- self . httpClient = HTTPClient (
47
- eventLoopGroupProvider: . shared( context. eventLoop) ,
49
+ static func main( ) async throws {
50
+ let httpClient = HTTPClient (
51
+ eventLoopGroupProvider: . shared( Lambda . defaultEventLoop) ,
48
52
configuration: . forPenny
49
53
)
50
-
51
- let awsClient = AWSClient ( httpClient: self . httpClient)
52
- self . secretsRetriever = SecretsRetriever ( awsClient: awsClient, logger: logger)
53
-
54
+ let awsClient = AWSClient ( httpClient: httpClient)
55
+ let secretsRetriever = SecretsRetriever ( awsClient: awsClient, logger: Logger ( label: " GHHooksHandler " ) )
54
56
let authenticator = Authenticator (
55
57
secretsRetriever: secretsRetriever,
56
58
httpClient: httpClient,
57
- logger: logger
59
+ logger: Logger ( label : " GitHubClient.Authenticator " )
58
60
)
59
61
60
- self . githubClient = try . makeForGitHub(
62
+ let githubClient = try Client . makeForGitHub (
61
63
httpClient: httpClient,
62
64
authorization: . computedBearer { isRetry in
63
65
try await authenticator. generateAccessToken (
64
66
forceRefreshToken: isRetry
65
67
)
66
68
} ,
67
- logger: logger
69
+ logger: Logger ( label: " GitHubClient " )
70
+ )
71
+ let sharedContext = SharedContext (
72
+ httpClient: httpClient,
73
+ githubClient: githubClient,
74
+ awsClient: awsClient,
75
+ secretsRetriever: secretsRetriever
68
76
)
77
+ try await LambdaRuntime { ( event: APIGatewayV2Request , context: LambdaContext ) in
78
+ let handler = try GHHooksHandler ( context: context, sharedContext: sharedContext)
79
+ return try await handler. handle ( event)
80
+ } . run ( )
81
+ }
69
82
83
+ init ( context: LambdaContext , sharedContext: SharedContext ) throws {
84
+ self . sharedContext = sharedContext
85
+ self . logger = context. logger
70
86
self . messageLookupRepo = DynamoMessageRepo (
71
- awsClient: awsClient,
87
+ awsClient: sharedContext . awsClient,
72
88
logger: logger
73
89
)
74
90
75
- context . logger. trace ( " Handler did initialize " )
91
+ self . logger. trace ( " Handler did initialize " )
76
92
}
77
93
78
- func handle(
79
- _ request: APIGatewayV2Request ,
80
- context: LambdaContext
81
- ) async throws -> APIGatewayV2Response {
94
+ func handle( _ request: APIGatewayV2Request ) async throws -> APIGatewayV2Response {
82
95
do {
83
- return try await handleThrowing ( request, context : context )
96
+ return try await handleThrowing ( request)
84
97
} catch {
85
98
do {
86
99
/// Report to Discord server for easier notification of maintainers
@@ -130,10 +143,7 @@ struct GHHooksHandler: LambdaHandler {
130
143
}
131
144
}
132
145
133
- func handleThrowing(
134
- _ request: APIGatewayV2Request ,
135
- context: LambdaContext
136
- ) async throws -> APIGatewayV2Response {
146
+ func handleThrowing( _ request: APIGatewayV2Request ) async throws -> APIGatewayV2Response {
137
147
logger. debug (
138
148
" Got request " ,
139
149
metadata: [
@@ -172,21 +182,21 @@ struct GHHooksHandler: LambdaHandler {
172
182
context: . init(
173
183
eventName: eventName,
174
184
event: event,
175
- httpClient: httpClient,
176
- discordClient: discordClient,
177
- githubClient: githubClient,
185
+ httpClient: self . httpClient,
186
+ discordClient: self . discordClient,
187
+ githubClient: self . githubClient,
178
188
renderClient: RenderClient (
179
189
renderer: try . forGHHooks(
180
- httpClient: httpClient,
181
- logger: logger
190
+ httpClient: self . httpClient,
191
+ logger: self . logger
182
192
)
183
193
) ,
184
194
messageLookupRepo: self . messageLookupRepo,
185
195
usersService: ServiceFactory . makeUsersService (
186
- httpClient: httpClient,
196
+ httpClient: self . httpClient,
187
197
apiBaseURL: apiBaseURL
188
198
) ,
189
- logger: logger
199
+ logger: self . logger
190
200
)
191
201
) . handle ( )
192
202
@@ -201,7 +211,7 @@ struct GHHooksHandler: LambdaHandler {
201
211
throw Errors . headerNotFound ( name: " x-hub-signature-256 " , headers: request. headers)
202
212
}
203
213
let body = Data ( ( request. body ?? " " ) . utf8)
204
- let secret = try await secretsRetriever. getSecret ( arnEnvVarKey: " WH_SECRET_ARN " )
214
+ let secret = try await self . secretsRetriever. getSecret ( arnEnvVarKey: " WH_SECRET_ARN " )
205
215
try Verifier . verifyWebhookSignature (
206
216
signatureHeader: signature,
207
217
requestBody: body,
0 commit comments