@digitraffic/common 2023.1.18-1 → 2023.1.23-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/aws/infra/canaries/canary-alarm.js +11 -13
  2. package/dist/aws/infra/canaries/canary.js +2 -4
  3. package/dist/aws/infra/canaries/database-checker.js +4 -1
  4. package/dist/aws/infra/canaries/url-canary.js +1 -0
  5. package/dist/aws/infra/canaries/url-checker.d.ts +2 -2
  6. package/dist/aws/infra/canaries/url-checker.js +24 -5
  7. package/dist/aws/infra/sqs-integration.d.ts +1 -3
  8. package/dist/aws/infra/sqs-integration.js +28 -32
  9. package/dist/aws/infra/sqs-queue.d.ts +0 -2
  10. package/dist/aws/infra/sqs-queue.js +31 -24
  11. package/dist/aws/infra/stack/lambda-configs.d.ts +2 -31
  12. package/dist/aws/infra/stack/lambda-configs.js +5 -38
  13. package/dist/aws/infra/stack/monitoredfunction.js +3 -1
  14. package/dist/aws/infra/stacks/db-stack.js +1 -1
  15. package/dist/aws/infra/stacks/network-stack.d.ts +2 -1
  16. package/dist/aws/infra/stacks/network-stack.js +4 -2
  17. package/dist/aws/runtime/secrets/dbsecret.d.ts +0 -39
  18. package/dist/aws/runtime/secrets/dbsecret.js +1 -71
  19. package/dist/aws/runtime/secrets/proxy-holder.js +5 -4
  20. package/dist/aws/runtime/secrets/rds-holder.js +5 -4
  21. package/dist/aws/runtime/secrets/secret-holder.d.ts +0 -4
  22. package/dist/aws/runtime/secrets/secret-holder.js +6 -12
  23. package/dist/aws/runtime/secrets/secret.d.ts +0 -6
  24. package/dist/aws/runtime/secrets/secret.js +8 -17
  25. package/dist/database/database.d.ts +7 -0
  26. package/dist/database/database.js +19 -8
  27. package/dist/test/db-testutils.js +4 -5
  28. package/dist/utils/utils.d.ts +15 -0
  29. package/dist/utils/utils.js +3 -1
  30. package/package.json +1 -1
  31. package/src/aws/infra/canaries/canary-alarm.ts +26 -24
  32. package/src/aws/infra/canaries/canary.ts +2 -4
  33. package/src/aws/infra/canaries/database-checker.ts +4 -1
  34. package/src/aws/infra/canaries/url-canary.ts +2 -1
  35. package/src/aws/infra/canaries/url-checker.ts +28 -11
  36. package/src/aws/infra/sqs-integration.ts +51 -47
  37. package/src/aws/infra/sqs-queue.ts +85 -53
  38. package/src/aws/infra/stack/lambda-configs.ts +6 -69
  39. package/src/aws/infra/stack/monitoredfunction.ts +2 -1
  40. package/src/aws/infra/stacks/db-stack.ts +1 -1
  41. package/src/aws/infra/stacks/network-stack.ts +7 -3
  42. package/src/aws/runtime/secrets/dbsecret.ts +1 -117
  43. package/src/aws/runtime/secrets/proxy-holder.ts +2 -5
  44. package/src/aws/runtime/secrets/rds-holder.ts +2 -1
  45. package/src/aws/runtime/secrets/secret-holder.ts +8 -20
  46. package/src/aws/runtime/secrets/secret.ts +17 -22
  47. package/src/database/database.ts +14 -3
  48. package/src/test/db-testutils.ts +5 -2
  49. package/src/utils/utils.ts +2 -2
  50. package/dist/test/secret.d.ts +0 -3
  51. package/dist/test/secret.js +0 -25
  52. package/src/test/secret.ts +0 -23
@@ -1,7 +1,7 @@
1
1
  import { IncomingMessage, RequestOptions } from "http";
2
2
  import { Asserter } from "../../../test/asserter";
3
3
 
4
- // eslint-disable-next-line @typescript-eslint/no-var-requires
4
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires
5
5
  const synthetics = require("Synthetics");
6
6
  import zlib = require("zlib");
7
7
  import { MediaType } from "../../types/mediatypes";
@@ -19,12 +19,12 @@ const baseHeaders = {
19
19
  Accept: "*/*",
20
20
  } as Record<string, string>;
21
21
 
22
- type CheckerFunction = (Res: IncomingMessage) => void;
22
+ type CheckerFunction = (Res: IncomingMessage) => Promise<void>;
23
23
  type JsonCheckerFunction<T> = (
24
24
  json: T,
25
25
  body: string,
26
26
  message: IncomingMessage
27
- ) => void;
27
+ ) => Promise<void>;
28
28
 
29
29
  export class UrlChecker {
30
30
  private readonly requestOptions: RequestOptions;
@@ -43,8 +43,10 @@ export class UrlChecker {
43
43
  headers,
44
44
  };
45
45
 
46
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
46
47
  synthetics.getConfiguration().disableRequestMetrics();
47
48
 
49
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
48
50
  synthetics
49
51
  .getConfiguration()
50
52
  .withIncludeRequestBody(false)
@@ -79,6 +81,7 @@ export class UrlChecker {
79
81
  },
80
82
  };
81
83
 
84
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-return
82
85
  return synthetics.executeHttpStep(
83
86
  `Verify ${statusCode} for ${url.replace(/auth=.*/, "")}`,
84
87
  requestOptions,
@@ -109,6 +112,7 @@ export class UrlChecker {
109
112
  },
110
113
  };
111
114
 
115
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-return
112
116
  return synthetics.executeHttpStep(
113
117
  `Verify 404 for ${url}`,
114
118
  requestOptions,
@@ -124,6 +128,7 @@ export class UrlChecker {
124
128
  },
125
129
  };
126
130
 
131
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-return
127
132
  return synthetics.executeHttpStep(
128
133
  `Verify 400 for ${url}`,
129
134
  requestOptions,
@@ -133,6 +138,7 @@ export class UrlChecker {
133
138
 
134
139
  expect403WithoutApiKey(url: string, mediaType?: MediaType): Promise<void> {
135
140
  if (
141
+ // eslint-disable-next-line @typescript-eslint/prefer-optional-chain
136
142
  !this.requestOptions.headers ||
137
143
  !this.requestOptions.headers[API_KEY_HEADER]
138
144
  ) {
@@ -147,6 +153,7 @@ export class UrlChecker {
147
153
  },
148
154
  };
149
155
 
156
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-return
150
157
  return synthetics.executeHttpStep(
151
158
  `Verify 403 for ${url}`,
152
159
  requestOptions,
@@ -202,12 +209,14 @@ function validateStatusCodeAndContentType(
202
209
  return (res: IncomingMessage) => {
203
210
  return new Promise((resolve) => {
204
211
  if (res.statusCode !== statusCode) {
205
- throw new Error(`${res.statusCode} ${res.statusMessage}`);
212
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
213
+ throw new Error(`${res.statusCode!} ${res.statusMessage!}`);
206
214
  }
207
215
 
208
216
  if (res.headers["content-type"] !== contentType) {
209
217
  throw new Error(
210
- "Wrong content-type " + res.headers["content-type"]
218
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
219
+ `Wrong content-type ${res.headers["content-type"]!}`
211
220
  );
212
221
  }
213
222
 
@@ -251,7 +260,7 @@ export class ResponseChecker {
251
260
  fn: (json: T, body: string, res: IncomingMessage) => void
252
261
  ): CheckerFunction {
253
262
  return this.responseChecker((body: string, res: IncomingMessage) => {
254
- fn(JSON.parse(body), body, res);
263
+ fn(JSON.parse(body) as unknown as T, body, res);
255
264
  });
256
265
  }
257
266
 
@@ -264,7 +273,8 @@ export class ResponseChecker {
264
273
  }
265
274
 
266
275
  if (res.statusCode < 200 || res.statusCode > 299) {
267
- throw new Error(`${res.statusCode} ${res.statusMessage}`);
276
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
277
+ throw new Error(`${res.statusCode} ${res.statusMessage!}`);
268
278
  }
269
279
 
270
280
  if (this.checkCors && !res.headers["access-control-allow-origin"]) {
@@ -273,7 +283,8 @@ export class ResponseChecker {
273
283
 
274
284
  if (res.headers["content-type"] !== this.contentType) {
275
285
  throw new Error(
276
- "Wrong content-type " + res.headers["content-type"]
286
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
287
+ `Wrong content-type ${res.headers["content-type"]!}`
277
288
  );
278
289
  }
279
290
 
@@ -291,7 +302,7 @@ export class ContentChecker {
291
302
  return async (res: IncomingMessage): Promise<void> => {
292
303
  const body = await getResponseBody(res);
293
304
 
294
- fn(JSON.parse(body), body, res);
305
+ fn(JSON.parse(body) as unknown as T, body, res);
295
306
  };
296
307
  }
297
308
 
@@ -314,7 +325,8 @@ export class ContentTypeChecker {
314
325
  }
315
326
 
316
327
  if (res.statusCode < 200 || res.statusCode > 299) {
317
- throw new Error(`${res.statusCode} ${res.statusMessage}`);
328
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
329
+ throw new Error(`${res.statusCode} ${res.statusMessage!}`);
318
330
  }
319
331
 
320
332
  if (!res.headers["access-control-allow-origin"]) {
@@ -323,7 +335,8 @@ export class ContentTypeChecker {
323
335
 
324
336
  if (res.headers["content-type"] !== contentType) {
325
337
  throw new Error(
326
- "Wrong content-type " + res.headers["content-type"]
338
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
339
+ `Wrong content-type ${res.headers["content-type"]!}`
327
340
  );
328
341
  }
329
342
  };
@@ -353,6 +366,8 @@ export class HeaderChecker {
353
366
  if (!res.headers[headerName]) {
354
367
  throw new Error("Missing header: " + headerName);
355
368
  }
369
+
370
+ return Promise.resolve();
356
371
  };
357
372
  }
358
373
 
@@ -361,6 +376,8 @@ export class HeaderChecker {
361
376
  if (res.headers[headerName]) {
362
377
  throw new Error("Header should not exist: " + headerName);
363
378
  }
379
+
380
+ return Promise.resolve();
364
381
  };
365
382
  }
366
383
  }
@@ -1,9 +1,14 @@
1
- import {Aws} from "aws-cdk-lib";
2
- import {AwsIntegration, PassthroughBehavior, RequestValidator, Resource} from "aws-cdk-lib/aws-apigateway";
3
- import {Queue} from "aws-cdk-lib/aws-sqs";
4
- import {PolicyStatement, Role, ServicePrincipal} from "aws-cdk-lib/aws-iam";
5
- import {IModel} from "aws-cdk-lib/aws-apigateway/lib/model";
6
- import {Construct} from "constructs";
1
+ import { Aws } from "aws-cdk-lib";
2
+ import {
3
+ AwsIntegration,
4
+ PassthroughBehavior,
5
+ RequestValidator,
6
+ Resource,
7
+ } from "aws-cdk-lib/aws-apigateway";
8
+ import { Queue } from "aws-cdk-lib/aws-sqs";
9
+ import { PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam";
10
+ import { IModel } from "aws-cdk-lib/aws-apigateway/lib/model";
11
+ import { Construct } from "constructs";
7
12
 
8
13
  export function attachQueueToApiGatewayResource(
9
14
  stack: Construct,
@@ -12,89 +17,88 @@ export function attachQueueToApiGatewayResource(
12
17
  requestValidator: RequestValidator,
13
18
  resourceName: string,
14
19
  apiKeyRequired: boolean,
15
- requestModels?: {[param: string]: IModel},
20
+ requestModels?: Record<string, IModel>
16
21
  ) {
17
22
  // role for API Gateway
18
23
  const apiGwRole = new Role(stack, `${resourceName}APIGatewayToSQSRole`, {
19
- assumedBy: new ServicePrincipal('apigateway.amazonaws.com'),
24
+ assumedBy: new ServicePrincipal("apigateway.amazonaws.com"),
20
25
  });
21
26
  // grants API Gateway the right to send SQS messages
22
- apiGwRole.addToPolicy(new PolicyStatement({
23
- resources: [
24
- queue.queueArn,
25
- ],
26
- actions: [
27
- 'sqs:SendMessage',
28
- ],
29
- }));
27
+ apiGwRole.addToPolicy(
28
+ new PolicyStatement({
29
+ resources: [queue.queueArn],
30
+ actions: ["sqs:SendMessage"],
31
+ })
32
+ );
30
33
  // grants API Gateway the right write CloudWatch Logs
31
- apiGwRole.addToPolicy(new PolicyStatement({
32
- resources: [
33
- '*',
34
- ],
35
- actions: [
36
- 'logs:CreateLogGroup',
37
- 'logs:CreateLogStream',
38
- 'logs:DescribeLogGroups',
39
- 'logs:DescribeLogStreams',
40
- 'logs:PutLogEvents',
41
- 'logs:GetLogEvents',
42
- 'logs:FilterLogEvents',
43
- ],
44
- }));
34
+ apiGwRole.addToPolicy(
35
+ new PolicyStatement({
36
+ resources: ["*"],
37
+ actions: [
38
+ "logs:CreateLogGroup",
39
+ "logs:CreateLogStream",
40
+ "logs:DescribeLogGroups",
41
+ "logs:DescribeLogStreams",
42
+ "logs:PutLogEvents",
43
+ "logs:GetLogEvents",
44
+ "logs:FilterLogEvents",
45
+ ],
46
+ })
47
+ );
45
48
  // create an integration between API Gateway and an SQS queue
46
- const fifoMessageGroupId = queue.fifo ? '&MessageGroupId=AlwaysSameFifoGroup' : '';
49
+ const fifoMessageGroupId = queue.fifo
50
+ ? "&MessageGroupId=AlwaysSameFifoGroup"
51
+ : "";
47
52
  const sqsIntegration = new AwsIntegration({
48
- service: 'sqs',
49
- integrationHttpMethod: 'POST',
53
+ service: "sqs",
54
+ integrationHttpMethod: "POST",
50
55
  options: {
51
56
  passthroughBehavior: PassthroughBehavior.NEVER,
52
57
  credentialsRole: apiGwRole,
53
58
  requestParameters: {
54
59
  // SQS requires the Content-Type of the HTTP request to be application/x-www-form-urlencoded
55
- 'integration.request.header.Content-Type': "'application/x-www-form-urlencoded'",
60
+ "integration.request.header.Content-Type":
61
+ "'application/x-www-form-urlencoded'",
56
62
  },
57
63
  requestTemplates: {
58
64
  // map the JSON request to a form parameter, FIFO needs also MessageGroupId
59
65
  // https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html
60
- 'application/json': `Action=SendMessage${fifoMessageGroupId}&MessageBody=$util.urlEncode($input.body)`,
66
+ "application/json": `Action=SendMessage${fifoMessageGroupId}&MessageBody=$util.urlEncode($input.body)`,
61
67
  },
62
68
  // these are required by SQS
63
69
  integrationResponses: [
64
70
  {
65
- statusCode: '200',
71
+ statusCode: "200",
66
72
  responseTemplates: {
67
- 'text/html': 'Success',
73
+ "text/html": "Success",
68
74
  },
69
75
  },
70
76
  {
71
- statusCode: '500',
77
+ statusCode: "500",
72
78
  responseTemplates: {
73
- 'text/html': 'Error',
79
+ "text/html": "Error",
74
80
  },
75
- selectionPattern: '500',
81
+ selectionPattern: "500",
76
82
  },
77
-
78
83
  ],
79
-
80
84
  },
81
85
  path: `${Aws.ACCOUNT_ID}/${queue.queueName}`,
82
86
  });
83
- resource.addMethod('POST', sqsIntegration, {
87
+ resource.addMethod("POST", sqsIntegration, {
84
88
  requestValidator,
85
89
  apiKeyRequired,
86
90
  requestModels: requestModels ?? {},
87
91
  methodResponses: [
88
92
  {
89
- statusCode: '200',
93
+ statusCode: "200",
90
94
  responseParameters: {
91
- 'method.response.header.Content-Type': true,
95
+ "method.response.header.Content-Type": true,
92
96
  },
93
97
  },
94
98
  {
95
- statusCode: '500',
99
+ statusCode: "500",
96
100
  responseParameters: {
97
- 'method.response.header.Content-Type': true,
101
+ "method.response.header.Content-Type": true,
98
102
  },
99
103
  },
100
104
  ],
@@ -1,18 +1,20 @@
1
- import {Queue, QueueEncryption, QueueProps} from "aws-cdk-lib/aws-sqs";
2
- import {Duration} from "aws-cdk-lib";
3
- import {BlockPublicAccess, Bucket} from "aws-cdk-lib/aws-s3";
4
- import {PolicyStatement} from "aws-cdk-lib/aws-iam";
5
- import {InlineCode, Runtime} from "aws-cdk-lib/aws-lambda";
6
- import {RetentionDays} from "aws-cdk-lib/aws-logs";
7
- import {SqsEventSource} from "aws-cdk-lib/aws-lambda-event-sources";
8
- import {ComparisonOperator, TreatMissingData} from "aws-cdk-lib/aws-cloudwatch";
9
- import {SnsAction} from "aws-cdk-lib/aws-cloudwatch-actions";
10
- import {ManagedUpload} from "aws-sdk/clients/s3";
11
- import {S3} from "aws-sdk";
12
- import {SQSEvent, SQSHandler, SQSRecord} from "aws-lambda";
13
- import {Construct} from "constructs";
14
- import {DigitrafficStack} from "./stack/stack";
15
- import {MonitoredFunction} from "./stack/monitoredfunction";
1
+ import { Queue, QueueEncryption, QueueProps } from "aws-cdk-lib/aws-sqs";
2
+ import { Duration } from "aws-cdk-lib";
3
+ import { BlockPublicAccess, Bucket } from "aws-cdk-lib/aws-s3";
4
+ import { PolicyStatement } from "aws-cdk-lib/aws-iam";
5
+ import { InlineCode, Runtime } from "aws-cdk-lib/aws-lambda";
6
+ import { RetentionDays } from "aws-cdk-lib/aws-logs";
7
+ import { SqsEventSource } from "aws-cdk-lib/aws-lambda-event-sources";
8
+ import {
9
+ ComparisonOperator,
10
+ TreatMissingData,
11
+ } from "aws-cdk-lib/aws-cloudwatch";
12
+ import { SnsAction } from "aws-cdk-lib/aws-cloudwatch-actions";
13
+ import { ManagedUpload } from "aws-sdk/clients/s3";
14
+ import { S3 } from "aws-sdk";
15
+ import { SQSEvent, SQSHandler, SQSRecord } from "aws-lambda";
16
+ import { DigitrafficStack } from "./stack/stack";
17
+ import { MonitoredFunction } from "./stack/monitoredfunction";
16
18
 
17
19
  /**
18
20
  * Construct for creating SQS-queues.
@@ -21,20 +23,23 @@ import {MonitoredFunction} from "./stack/monitoredfunction";
21
23
  * and an alarm for the queue. Anything that goes to the dlq will be written into the bucket and the alarm is activated.
22
24
  */
23
25
  export class DigitrafficSqsQueue extends Queue {
24
- constructor(scope: Construct, name: string, props: QueueProps) {
25
- super(scope, name, props);
26
- }
27
-
28
- static create(stack: DigitrafficStack, name: string, props: QueueProps): DigitrafficSqsQueue {
26
+ static create(
27
+ stack: DigitrafficStack,
28
+ name: string,
29
+ props: QueueProps
30
+ ): DigitrafficSqsQueue {
29
31
  const queueName = `${stack.configuration.shortName}-${name}-Queue`;
30
- const queueProps = {...props, ...{
31
- encryption: QueueEncryption.KMS_MANAGED,
32
- queueName,
33
- deadLetterQueue: props.deadLetterQueue || {
34
- maxReceiveCount: 2,
35
- queue: DigitrafficDLQueue.create(stack, name),
32
+ const queueProps = {
33
+ ...props,
34
+ ...{
35
+ encryption: QueueEncryption.KMS_MANAGED,
36
+ queueName,
37
+ deadLetterQueue: props.deadLetterQueue ?? {
38
+ maxReceiveCount: 2,
39
+ queue: DigitrafficDLQueue.create(stack, name),
40
+ },
36
41
  },
37
- }};
42
+ };
38
43
 
39
44
  return new DigitrafficSqsQueue(stack, queueName, queueProps);
40
45
  }
@@ -61,15 +66,15 @@ export class DigitrafficDLQueue {
61
66
  functionName: dlqFunctionName,
62
67
  code: getDlqCode(dlqBucket.bucketName),
63
68
  timeout: Duration.seconds(10),
64
- handler: 'index.handler',
69
+ handler: "index.handler",
65
70
  memorySize: 128,
66
71
  reservedConcurrentExecutions: 1,
67
72
  });
68
73
 
69
74
  const statement = new PolicyStatement();
70
- statement.addActions('s3:PutObject');
71
- statement.addActions('s3:PutObjectAcl');
72
- statement.addResources(dlqBucket.bucketArn + '/*');
75
+ statement.addActions("s3:PutObject");
76
+ statement.addActions("s3:PutObjectAcl");
77
+ statement.addResources(dlqBucket.bucketArn + "/*");
73
78
 
74
79
  lambda.addToRolePolicy(statement);
75
80
  lambda.addEventSource(new SqsEventSource(dlq));
@@ -84,18 +89,19 @@ function addDLQAlarm(stack: DigitrafficStack, dlqName: string, dlq: Queue) {
84
89
  const alarmName = `${dlqName}-Alarm`;
85
90
  dlq.metricNumberOfMessagesReceived({
86
91
  period: Duration.minutes(5),
87
- }).createAlarm(stack, alarmName, {
88
- alarmName,
89
- threshold: 0,
90
- evaluationPeriods: 1,
91
- treatMissingData: TreatMissingData.NOT_BREACHING,
92
- comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD,
93
- }).addAlarmAction(new SnsAction(stack.warningTopic));
92
+ })
93
+ .createAlarm(stack, alarmName, {
94
+ alarmName,
95
+ threshold: 0,
96
+ evaluationPeriods: 1,
97
+ treatMissingData: TreatMissingData.NOT_BREACHING,
98
+ comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD,
99
+ })
100
+ .addAlarmAction(new SnsAction(stack.warningTopic));
94
101
  }
95
102
 
96
103
  function getDlqCode(bName: string): InlineCode {
97
- const functionBody = DLQ_LAMBDA_CODE
98
- .replace("__bucketName__", bName)
104
+ const functionBody = DLQ_LAMBDA_CODE.replace("__bucketName__", bName)
99
105
  .replace("__upload__", uploadToS3.toString())
100
106
  .replace("__doUpload__", doUpload.toString())
101
107
  .replace("__handler__", createHandler().toString().substring(23)); // remove function handler() from signature
@@ -103,38 +109,64 @@ function getDlqCode(bName: string): InlineCode {
103
109
  return new InlineCode(functionBody);
104
110
  }
105
111
 
106
- async function uploadToS3(s3: S3, bName: string, body: string, objectName: string): Promise<void> {
112
+ async function uploadToS3(
113
+ s3: S3,
114
+ bName: string,
115
+ body: string,
116
+ objectName: string
117
+ ): Promise<void> {
107
118
  try {
108
- console.info('writing %s to %s', objectName, bName);
119
+ console.info("writing %s to %s", objectName, bName);
109
120
  await doUpload(s3, bName, body, objectName);
110
121
  } catch (error) {
111
122
  console.warn(error);
112
- console.warn('method=uploadToS3 retrying upload to bucket %s', bName);
123
+ console.warn("method=uploadToS3 retrying upload to bucket %s", bName);
113
124
  try {
114
125
  await doUpload(s3, bName, body, objectName);
115
126
  } catch (e2) {
116
- console.error('method=uploadToS3 failed retrying upload to bucket %s', bName);
127
+ console.error(
128
+ "method=uploadToS3 failed retrying upload to bucket %s",
129
+ bName
130
+ );
117
131
  }
118
132
  }
119
133
  }
120
134
 
121
- function doUpload(s3: S3, bName: string, Body: string, Key: string): Promise<ManagedUpload.SendData> {
122
- return s3.upload({
123
- Bucket: bName, Body, Key,
124
- }).promise();
135
+ function doUpload(
136
+ s3: S3,
137
+ bName: string,
138
+ Body: string,
139
+ Key: string
140
+ ): Promise<ManagedUpload.SendData> {
141
+ return s3
142
+ .upload({
143
+ Bucket: bName,
144
+ Body,
145
+ Key,
146
+ })
147
+ .promise();
125
148
  }
126
149
 
127
150
  // bucketName is unused, will be overridden in the actual lambda code below
128
- const bucketName = '';
151
+ const bucketName = "";
129
152
 
130
153
  function createHandler(): SQSHandler {
131
154
  return async function handler(event: SQSEvent): Promise<void> {
132
- // eslint-disable-next-line @typescript-eslint/no-var-requires
133
- const AWS = require('aws-sdk');
155
+ // eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-assignment
156
+ const AWS = require("aws-sdk");
134
157
 
135
158
  const millis = new Date().getTime();
136
- await Promise.all(event.Records.map((e: SQSRecord, idx: number) =>
137
- uploadToS3(new AWS.S3(), bucketName, e.body, `dlq-${millis}-${idx}.json`)));
159
+ await Promise.all(
160
+ event.Records.map((e: SQSRecord, idx: number) =>
161
+ uploadToS3(
162
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
163
+ new AWS.S3(),
164
+ bucketName,
165
+ e.body,
166
+ `dlq-${millis}-${idx}.json`
167
+ )
168
+ )
169
+ );
138
170
  };
139
171
  }
140
172
 
@@ -6,7 +6,7 @@ import {
6
6
  Runtime,
7
7
  } from "aws-cdk-lib/aws-lambda";
8
8
  import { Duration } from "aws-cdk-lib";
9
- import { ISecurityGroup, IVpc, SubnetSelection } from "aws-cdk-lib/aws-ec2";
9
+ import { IVpc, SubnetSelection } from "aws-cdk-lib/aws-ec2";
10
10
  import { RetentionDays } from "aws-cdk-lib/aws-logs";
11
11
  import { Role } from "aws-cdk-lib/aws-iam";
12
12
  import { DigitrafficStack } from "./stack";
@@ -19,26 +19,6 @@ export type DBLambdaEnvironment = LambdaEnvironment & {
19
19
  DB_APPLICATION: string;
20
20
  };
21
21
 
22
- export interface LambdaConfiguration {
23
- vpcId: string;
24
- allowFromIpAddresses?: string[];
25
- privateSubnetIds: string[];
26
- availabilityZones: string[];
27
- lambdaDbSgId: string;
28
- dbProps?: DbProps;
29
- defaultLambdaDurationSeconds?: number;
30
- logsDestinationArn: string;
31
- memorySize?: number;
32
- runtime?: Runtime;
33
- }
34
-
35
- declare interface DbProps {
36
- username: string;
37
- password: string;
38
- uri?: string;
39
- ro_uri?: string;
40
- }
41
-
42
22
  export function databaseFunctionProps(
43
23
  stack: DigitrafficStack,
44
24
  environment: LambdaEnvironment,
@@ -61,9 +41,9 @@ export function databaseFunctionProps(
61
41
  config
62
42
  ),
63
43
  ...{
64
- vpc: stack.vpc || undefined,
44
+ vpc: stack.vpc ?? undefined,
65
45
  vpcSubnets,
66
- securityGroup: stack.lambdaDbSg || undefined,
46
+ securityGroup: stack.lambdaDbSg ?? undefined,
67
47
  },
68
48
  };
69
49
  }
@@ -101,47 +81,6 @@ function getAssetCode(
101
81
  return new AssetCode(lambdaPath);
102
82
  }
103
83
 
104
- /**
105
- * Creates a base configuration for a Lambda that uses an RDS database
106
- * @param vpc "Private" Lambdas are associated with a VPC
107
- * @param lambdaDbSg Security Group shared by Lambda and RDS
108
- * @param props Database connection properties for the Lambda
109
- * @param config Lambda configuration
110
- */
111
- export function dbLambdaConfiguration(
112
- vpc: IVpc,
113
- lambdaDbSg: ISecurityGroup,
114
- props: LambdaConfiguration,
115
- config: FunctionParameters
116
- ): FunctionProps {
117
- return {
118
- runtime: props.runtime ?? Runtime.NODEJS_16_X,
119
- memorySize: props.memorySize ?? config.memorySize ?? 1024,
120
- functionName: config.functionName,
121
- code: config.code,
122
- role: config.role,
123
- handler: config.handler,
124
- timeout: Duration.seconds(
125
- config.timeout ?? props.defaultLambdaDurationSeconds ?? 60
126
- ),
127
- environment: config.environment ?? {
128
- DB_USER: props.dbProps?.username ?? "",
129
- DB_PASS: props.dbProps?.password ?? "",
130
- DB_URI:
131
- (config.readOnly
132
- ? props.dbProps?.ro_uri
133
- : props.dbProps?.uri) ?? "",
134
- },
135
- logRetention: RetentionDays.ONE_YEAR,
136
- vpc: vpc,
137
- vpcSubnets: {
138
- subnets: vpc.privateSubnets,
139
- },
140
- securityGroups: [lambdaDbSg],
141
- reservedConcurrentExecutions: config.reservedConcurrentExecutions ?? 3,
142
- };
143
- }
144
-
145
84
  export function defaultLambdaConfiguration(
146
85
  config: FunctionParameters
147
86
  ): FunctionProps {
@@ -155,7 +94,7 @@ export function defaultLambdaConfiguration(
155
94
  reservedConcurrentExecutions: config.reservedConcurrentExecutions,
156
95
  code: config.code,
157
96
  role: config.role,
158
- timeout: Duration.seconds(config.timeout || 10),
97
+ timeout: Duration.seconds(config.timeout ?? 10),
159
98
  };
160
99
  if (config.vpc) {
161
100
  return {
@@ -163,7 +102,7 @@ export function defaultLambdaConfiguration(
163
102
  ...{
164
103
  vpc: config.vpc,
165
104
  vpcSubnets: {
166
- subnets: config.vpc?.privateSubnets,
105
+ subnets: config.vpc.privateSubnets,
167
106
  },
168
107
  },
169
108
  };
@@ -178,9 +117,7 @@ export interface FunctionParameters {
178
117
  code: Code;
179
118
  handler: string;
180
119
  readOnly?: boolean;
181
- environment?: {
182
- [key: string]: string;
183
- };
120
+ environment?: Record<string, string>;
184
121
  reservedConcurrentExecutions?: number;
185
122
  role?: Role;
186
123
  vpc?: IVpc;
@@ -82,7 +82,8 @@ export class MonitoredFunction extends Function {
82
82
  stack.configuration.production
83
83
  ) {
84
84
  throw new Error(
85
- `Function ${functionProps.functionName} has DISABLE_ALARMS. Remove before installing to production or define your own properties!`
85
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
86
+ `Function ${functionProps.functionName!} has DISABLE_ALARMS. Remove before installing to production or define your own properties!`
86
87
  );
87
88
  }
88
89
 
@@ -139,7 +139,7 @@ export class DbStack extends Stack {
139
139
  vpc,
140
140
  securityGroups: [securityGroup],
141
141
  vpcSubnets: {
142
- subnetType: SubnetType.PRIVATE_WITH_NAT,
142
+ subnetType: SubnetType.PRIVATE_WITH_EGRESS,
143
143
  },
144
144
  instanceType: configuration.dbInstanceType,
145
145
  parameterGroup,