Skip to content

Commit 5964fde

Browse files
committed
feat(cdk): enable API logging in tests
1 parent 041d248 commit 5964fde

File tree

2 files changed

+102
-11
lines changed

2 files changed

+102
-11
lines changed

cdk/resources/ApiLogging.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import {
2+
aws_apigateway as ApiGateway,
3+
aws_apigatewayv2 as ApiGatewayV2,
4+
aws_iam as IAM,
5+
aws_logs as Logs,
6+
RemovalPolicy,
7+
Stack,
8+
} from 'aws-cdk-lib'
9+
import { Construct } from 'constructs'
10+
11+
export class ApiLogging extends Construct {
12+
constructor(
13+
parent: Construct,
14+
api: ApiGatewayV2.CfnApi,
15+
stage: ApiGatewayV2.CfnStage,
16+
) {
17+
super(parent, 'apiLogging')
18+
19+
const role = new IAM.Role(this, 'CloudWatchRole', {
20+
assumedBy: new IAM.ServicePrincipal('apigateway.amazonaws.com'),
21+
managedPolicies: [
22+
IAM.ManagedPolicy.fromAwsManagedPolicyName(
23+
'service-role/AmazonAPIGatewayPushToCloudWatchLogs',
24+
),
25+
],
26+
})
27+
const cloudWatchAccount = new ApiGateway.CfnAccount(this, 'Account', {
28+
cloudWatchRoleArn: role.roleArn,
29+
})
30+
cloudWatchAccount.node.addDependency(api)
31+
const apiLogs = new Logs.LogGroup(this, `apiLogs`, {
32+
removalPolicy: RemovalPolicy.DESTROY,
33+
logGroupName: `/${Stack.of(this).stackName}/websocket`,
34+
retention: Logs.RetentionDays.ONE_DAY,
35+
})
36+
stage.accessLogSettings = {
37+
destinationArn: apiLogs.logGroupArn,
38+
format: JSON.stringify({
39+
requestId: '$context.requestId',
40+
awsEndpointRequestId: '$context.awsEndpointRequestId',
41+
requestTime: '$context.requestTime',
42+
ip: '$context.identity.sourceIp',
43+
protocol: '$context.protocol',
44+
routeKey: '$context.routeKey',
45+
status: '$context.status',
46+
responseLength: '$context.responseLength',
47+
responseLatency: '$context.responseLatency',
48+
integrationLatency: '$context.integrationLatency',
49+
integrationStatus: '$context.integrationStatus',
50+
integrationErrorMessage: '$context.integrationErrorMessage',
51+
integration: {
52+
error: '$context.integration.error',
53+
status: '$context.integration.status',
54+
requestId: '$context.integration.requestId',
55+
integrationStatus: '$context.integration.integrationStatus',
56+
latency: '$context.integration.latency', // The integration latency in ms. Equivalent to $context.integrationLatency.
57+
},
58+
authorize: {
59+
error: '$context.authorize.error',
60+
latency: '$context.authorize.latency',
61+
status: '$context.authorize.status', // The status code returned from an authorization attempt.
62+
},
63+
authorizer: {
64+
error: '$context.authorizer.error',
65+
integrationLatency: '$context.authorizer.integrationLatency',
66+
integrationStatus: '$context.authorizer.integrationStatus',
67+
latency: '$context.authorizer.latency',
68+
requestId: '$context.authorizer.requestId',
69+
status: '$context.authorizer.status', // The status code returned from an authorizer.
70+
},
71+
authenticate: {
72+
error: '$context.authenticate.error',
73+
latency: '$context.authenticate.latency',
74+
status: '$context.authenticate.status', // The status code returned from an authentication attempt.
75+
},
76+
xrayTraceId: '$context.xrayTraceId', // The trace ID for the X-Ray trace. For more information, see Setting up AWS X-Ray with API Gateway REST APIs.
77+
}),
78+
}
79+
stage.node.addDependency(apiLogs)
80+
}
81+
}

cdk/resources/WebsocketAPI.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
aws_apigatewayv2 as ApiGateway,
2+
aws_apigatewayv2 as ApiGatewayV2,
33
Duration,
44
aws_dynamodb as DynamoDB,
55
aws_events as Events,
@@ -12,6 +12,7 @@ import {
1212
} from 'aws-cdk-lib'
1313
import { Construct } from 'constructs'
1414
import type { PackedLambda } from '../helpers/lambdas/packLambda'
15+
import { ApiLogging } from './ApiLogging.js'
1516
import type { DeviceStorage } from './DeviceStorage.js'
1617

1718
export const integrationUri = (
@@ -159,12 +160,12 @@ export class WebsocketAPI extends Construct {
159160
})
160161

161162
// API
162-
const api = new ApiGateway.CfnApi(this, 'api', {
163+
const api = new ApiGatewayV2.CfnApi(this, 'api', {
163164
name: 'websocketGateway',
164165
protocolType: 'WEBSOCKET',
165166
routeSelectionExpression: '$request.body.message',
166167
})
167-
const authorizer = new ApiGateway.CfnAuthorizer(this, 'authorizer', {
168+
const authorizer = new ApiGatewayV2.CfnAuthorizer(this, 'authorizer', {
168169
apiId: api.ref,
169170
authorizerType: 'REQUEST',
170171
name: `authorizer`,
@@ -174,7 +175,7 @@ export class WebsocketAPI extends Construct {
174175
principal: new IAM.ServicePrincipal('apigateway.amazonaws.com'),
175176
})
176177
// API on connect
177-
const connectIntegration = new ApiGateway.CfnIntegration(
178+
const connectIntegration = new ApiGatewayV2.CfnIntegration(
178179
this,
179180
'connectIntegration',
180181
{
@@ -184,7 +185,7 @@ export class WebsocketAPI extends Construct {
184185
integrationUri: integrationUri(this, onConnect),
185186
},
186187
)
187-
const connectRoute = new ApiGateway.CfnRoute(this, 'connectRoute', {
188+
const connectRoute = new ApiGatewayV2.CfnRoute(this, 'connectRoute', {
188189
apiId: api.ref,
189190
routeKey: '$connect',
190191
authorizationType: 'CUSTOM',
@@ -193,7 +194,7 @@ export class WebsocketAPI extends Construct {
193194
authorizerId: authorizer?.ref,
194195
})
195196
// API on message
196-
const onMessageIntegration = new ApiGateway.CfnIntegration(
197+
const onMessageIntegration = new ApiGatewayV2.CfnIntegration(
197198
this,
198199
'onMessageIntegration',
199200
{
@@ -203,15 +204,15 @@ export class WebsocketAPI extends Construct {
203204
integrationUri: integrationUri(this, onMessage),
204205
},
205206
)
206-
const onMessageRoute = new ApiGateway.CfnRoute(this, 'onMessageRoute', {
207+
const onMessageRoute = new ApiGatewayV2.CfnRoute(this, 'onMessageRoute', {
207208
apiId: api.ref,
208209
routeKey: 'message',
209210
authorizationType: 'NONE',
210211
operationName: 'OnMessageRoute',
211212
target: `integrations/${onMessageIntegration.ref}`,
212213
})
213214
// API on disconnect
214-
const disconnectIntegration = new ApiGateway.CfnIntegration(
215+
const disconnectIntegration = new ApiGatewayV2.CfnIntegration(
215216
this,
216217
'disconnectIntegration',
217218
{
@@ -221,21 +222,21 @@ export class WebsocketAPI extends Construct {
221222
integrationUri: integrationUri(this, onDisconnect),
222223
},
223224
)
224-
const disconnectRoute = new ApiGateway.CfnRoute(this, 'disconnectRoute', {
225+
const disconnectRoute = new ApiGatewayV2.CfnRoute(this, 'disconnectRoute', {
225226
apiId: api.ref,
226227
routeKey: '$disconnect',
227228
authorizationType: 'NONE',
228229
operationName: 'DisconnectRoute',
229230
target: `integrations/${disconnectIntegration.ref}`,
230231
})
231232
// API deploy
232-
const deployment = new ApiGateway.CfnDeployment(this, 'apiDeployment', {
233+
const deployment = new ApiGatewayV2.CfnDeployment(this, 'apiDeployment', {
233234
apiId: api.ref,
234235
})
235236
deployment.node.addDependency(connectRoute)
236237
deployment.node.addDependency(onMessageRoute)
237238
deployment.node.addDependency(disconnectRoute)
238-
const prodStage = new ApiGateway.CfnStage(this, 'prodStage', {
239+
const prodStage = new ApiGatewayV2.CfnStage(this, 'prodStage', {
239240
stageName: '2023-06-22',
240241
deploymentId: deployment.ref,
241242
apiId: api.ref,
@@ -269,13 +270,22 @@ export class WebsocketAPI extends Construct {
269270
}:${api.ref}/${prodStage.stageName}/$disconnect`,
270271
})
271272

273+
// Construct URLs
274+
this.websocketURI = `wss://${api.ref}.execute-api.${
275+
Stack.of(this).region
276+
}.amazonaws.com/${prodStage.ref}`
272277
this.websocketAPIArn = `arn:aws:execute-api:${Stack.of(this).region}:${
273278
Stack.of(this).account
274279
}:${api.ref}/${prodStage.stageName}/POST/@connections/*`
275280
this.websocketManagementAPIURL = `https://${api.ref}.execute-api.${
276281
Stack.of(this).region
277282
}.amazonaws.com/${prodStage.stageName}`
278283

284+
// Logging
285+
if (this.node.tryGetContext('isTest') === true) {
286+
new ApiLogging(this, api, prodStage)
287+
}
288+
279289
// Publish event to sockets
280290
const publishToWebsocketClients = new Lambda.Function(
281291
this,

0 commit comments

Comments
 (0)