@digitraffic/common 2023.12.15-1 → 2024.1.10-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.
@@ -10,6 +10,7 @@ const aws_logs_1 = require("aws-cdk-lib/aws-logs");
10
10
  const aws_lambda_event_sources_1 = require("aws-cdk-lib/aws-lambda-event-sources");
11
11
  const aws_cloudwatch_1 = require("aws-cdk-lib/aws-cloudwatch");
12
12
  const aws_cloudwatch_actions_1 = require("aws-cdk-lib/aws-cloudwatch-actions");
13
+ const lib_storage_1 = require("@aws-sdk/lib-storage");
13
14
  const monitoredfunction_1 = require("./stack/monitoredfunction");
14
15
  /**
15
16
  * Construct for creating SQS-queues.
@@ -48,7 +49,7 @@ class DigitrafficDLQueue {
48
49
  });
49
50
  const dlqFunctionName = `${dlqName}-Function`;
50
51
  const lambda = monitoredfunction_1.MonitoredFunction.create(stack, dlqFunctionName, {
51
- runtime: aws_lambda_1.Runtime.NODEJS_16_X,
52
+ runtime: aws_lambda_1.Runtime.NODEJS_20_X,
52
53
  logRetention: aws_logs_1.RetentionDays.ONE_YEAR,
53
54
  functionName: dlqFunctionName,
54
55
  code: getDlqCode(dlqBucket.bucketName),
@@ -82,37 +83,40 @@ function addDLQAlarm(stack, dlqName, dlq) {
82
83
  })
83
84
  .addAlarmAction(new aws_cloudwatch_actions_1.SnsAction(stack.warningTopic));
84
85
  }
85
- function getDlqCode(bName) {
86
- const functionBody = DLQ_LAMBDA_CODE.replace("__bucketName__", bName)
86
+ function getDlqCode(Bucket) {
87
+ const functionBody = DLQ_LAMBDA_CODE.replace("__bucketName__", Bucket)
87
88
  .replace("__upload__", uploadToS3.toString())
88
89
  .replace("__doUpload__", doUpload.toString())
89
90
  .replace("__handler__", createHandler().toString().substring(23)); // remove function handler() from signature
90
91
  return new aws_lambda_1.InlineCode(functionBody);
91
92
  }
92
- async function uploadToS3(s3, bName, body, objectName) {
93
+ async function uploadToS3(s3, Bucket, Body, Key) {
93
94
  try {
94
- console.info("writing %s to %s", objectName, bName);
95
- await doUpload(s3, bName, body, objectName);
95
+ console.info("writing %s to %s", Key, Bucket);
96
+ await doUpload(s3, Bucket, Body, Key);
96
97
  }
97
98
  catch (error) {
98
99
  console.warn(error);
99
- console.warn("method=uploadToS3 retrying upload to bucket %s", bName);
100
+ console.warn("method=uploadToS3 retrying upload to bucket %s", Bucket);
100
101
  try {
101
- await doUpload(s3, bName, body, objectName);
102
+ await doUpload(s3, Bucket, Body, Key);
102
103
  }
103
104
  catch (e2) {
104
- console.error("method=uploadToS3 failed retrying upload to bucket %s", bName);
105
+ console.error("method=uploadToS3 failed retrying upload to bucket %s", Bucket);
105
106
  }
106
107
  }
107
108
  }
108
- function doUpload(s3, bName, Body, Key) {
109
- return s3
110
- .upload({
111
- Bucket: bName,
112
- Body,
113
- Key,
114
- })
115
- .promise();
109
+ async function doUpload(s3, Bucket, Body, Key) {
110
+ try {
111
+ const upload = new lib_storage_1.Upload({
112
+ client: s3,
113
+ params: { Bucket, Key, Body },
114
+ });
115
+ await upload.done();
116
+ }
117
+ catch (error) {
118
+ console.error(error);
119
+ }
116
120
  }
117
121
  // bucketName is unused, will be overridden in the actual lambda code below
118
122
  const bucketName = "";
@@ -126,7 +130,9 @@ function createHandler() {
126
130
  new AWS.S3(), bucketName, e.body, `dlq-${millis}-${idx}.json`)));
127
131
  };
128
132
  }
129
- const DLQ_LAMBDA_CODE = `const AWS = require('aws-sdk');
133
+ const DLQ_LAMBDA_CODE = `
134
+ import { S3, S3Client } from "@aws-sdk/client-s3";
135
+ import { Upload } from "@aws-sdk/lib-storage";
130
136
  const bucketName = "__bucketName__";
131
137
 
132
138
  __upload__
@@ -22,7 +22,7 @@ function databaseFunctionProps(stack, environment, lambdaName, simpleLambdaName,
22
22
  exports.databaseFunctionProps = databaseFunctionProps;
23
23
  function lambdaFunctionProps(stack, environment, lambdaName, simpleLambdaName, config) {
24
24
  return {
25
- runtime: config?.runtime ?? aws_lambda_1.Runtime.NODEJS_16_X,
25
+ runtime: config?.runtime ?? aws_lambda_1.Runtime.NODEJS_20_X,
26
26
  architecture: config?.architecture ?? aws_lambda_1.Architecture.ARM_64,
27
27
  memorySize: config?.memorySize ?? 128,
28
28
  functionName: lambdaName,
@@ -44,7 +44,7 @@ function getAssetCode(simpleLambdaName, isSingleLambda) {
44
44
  }
45
45
  function defaultLambdaConfiguration(config) {
46
46
  const props = {
47
- runtime: aws_lambda_1.Runtime.NODEJS_16_X,
47
+ runtime: aws_lambda_1.Runtime.NODEJS_20_X,
48
48
  memorySize: config.memorySize ?? 128,
49
49
  functionName: config.functionName,
50
50
  handler: config.handler,
@@ -14,7 +14,7 @@ const aws_logs_1 = require("aws-cdk-lib/aws-logs");
14
14
  const change_case_1 = require("change-case");
15
15
  const lodash_1 = __importDefault(require("lodash"));
16
16
  const MAX_CONCURRENCY_LIMIT = 100;
17
- const NODE_RUNTIMES = [aws_lambda_1.Runtime.NODEJS_16_X.name, aws_lambda_1.Runtime.NODEJS_18_X.name];
17
+ const NODE_RUNTIMES = [aws_lambda_1.Runtime.NODEJS_20_X.name, aws_lambda_1.Runtime.NODEJS_18_X.name];
18
18
  var ResourceType;
19
19
  (function (ResourceType) {
20
20
  ResourceType["stackName"] = "STACK_NAME";
@@ -67,13 +67,11 @@ class StackCheckingAspect {
67
67
  }
68
68
  checkStack(node) {
69
69
  if (node instanceof stack_1.DigitrafficStack) {
70
- if ((node.stackName.includes("Test") ||
71
- node.stackName.includes("Tst")) &&
70
+ if ((node.stackName.includes("Test") || node.stackName.includes("Tst")) &&
72
71
  node.configuration.production) {
73
72
  this.addAnnotation(node, ResourceType.stackName, "Production is set for Test-stack");
74
73
  }
75
- if ((node.stackName.includes("Prod") ||
76
- node.stackName.includes("Prd")) &&
74
+ if ((node.stackName.includes("Prod") || node.stackName.includes("Prd")) &&
77
75
  !node.configuration.production) {
78
76
  this.addAnnotation(node, ResourceType.stackName, "Production is not set for Production-stack");
79
77
  }
@@ -93,8 +91,7 @@ class StackCheckingAspect {
93
91
  if (!node.memorySize) {
94
92
  this.addAnnotation(node, ResourceType.functionMemorySize, "Function must have memorySize");
95
93
  }
96
- if (node.runtime !== undefined &&
97
- !NODE_RUNTIMES.includes(node.runtime)) {
94
+ if (node.runtime !== undefined && !NODE_RUNTIMES.includes(node.runtime)) {
98
95
  this.addAnnotation(node, ResourceType.functionRuntime, `Function has wrong runtime ${node.runtime}!`);
99
96
  }
100
97
  if (this.stackShortName &&
@@ -131,7 +128,7 @@ class StackCheckingAspect {
131
128
  if (path.includes("{")) {
132
129
  return this.isValidPath(path.split("{")[0]);
133
130
  }
134
- return (0, change_case_1.paramCase)(path) === path;
131
+ return (0, change_case_1.kebabCase)(path) === path;
135
132
  }
136
133
  static isValidQueryString(name) {
137
134
  return lodash_1.default.snakeCase(name) === name;
@@ -149,8 +146,7 @@ class StackCheckingAspect {
149
146
  const split = key.split(".");
150
147
  const type = split[2];
151
148
  const name = split[3];
152
- if (type === "querystring" &&
153
- !StackCheckingAspect.isValidQueryString(name)) {
149
+ if (type === "querystring" && !StackCheckingAspect.isValidQueryString(name)) {
154
150
  this.addAnnotation(node, name, "Querystring should be in snake_case");
155
151
  }
156
152
  });
@@ -8,8 +8,8 @@ import { Construct } from "constructs";
8
8
  import { TrafficType } from "../../../types/traffictype";
9
9
  import { DBLambdaEnvironment } from "./lambda-configs";
10
10
  export declare const SOLUTION_KEY = "Solution";
11
- export declare const SSM_KEY_WARNING_TOPIC: string;
12
- export declare const SSM_KEY_ALARM_TOPIC: string;
11
+ export declare const SSM_KEY_WARNING_TOPIC = "/digitraffic/monitoring/warning-topic";
12
+ export declare const SSM_KEY_ALARM_TOPIC = "/digitraffic/monitoring/alarm-topic";
13
13
  export interface StackConfiguration {
14
14
  readonly shortName: string;
15
15
  readonly secretId?: string;
@@ -21,7 +21,7 @@ class NetworkStack extends core_1.Stack {
21
21
  vpcName: configuration.vpcName,
22
22
  availabilityZones: core_1.Stack.of(this)
23
23
  .availabilityZones.sort()
24
- .slice(0, 2),
24
+ .slice(0, 2), // take two first azs
25
25
  enableDnsHostnames: true,
26
26
  enableDnsSupport: true,
27
27
  ipAddresses: aws_ec2_1.IpAddresses.cidr(configuration.cidr),
@@ -6,7 +6,7 @@ var EnvKeys;
6
6
  EnvKeys["AWS_REGION"] = "AWS_REGION";
7
7
  EnvKeys["SECRET_ID"] = "SECRET_ID";
8
8
  EnvKeys["SECRET_OVERRIDE_AWS_REGION"] = "SECRET_OVERRIDE_AWS_REGION";
9
- })(EnvKeys = exports.EnvKeys || (exports.EnvKeys = {}));
9
+ })(EnvKeys || (exports.EnvKeys = EnvKeys = {}));
10
10
  /**
11
11
  * @deprecated Use digitraffic/common/utils/utils#getEnvVariable
12
12
  */
@@ -7,14 +7,14 @@ var RdsProxySecretKey;
7
7
  RdsProxySecretKey["password"] = "password";
8
8
  RdsProxySecretKey["proxy_host"] = "proxy_host";
9
9
  RdsProxySecretKey["proxy_ro_host"] = "proxy_ro_host";
10
- })(RdsProxySecretKey = exports.RdsProxySecretKey || (exports.RdsProxySecretKey = {}));
10
+ })(RdsProxySecretKey || (exports.RdsProxySecretKey = RdsProxySecretKey = {}));
11
11
  var RdsSecretKey;
12
12
  (function (RdsSecretKey) {
13
13
  RdsSecretKey["username"] = "username";
14
14
  RdsSecretKey["password"] = "password";
15
15
  RdsSecretKey["host"] = "host";
16
16
  RdsSecretKey["ro_host"] = "ro_host";
17
- })(RdsSecretKey = exports.RdsSecretKey || (exports.RdsSecretKey = {}));
17
+ })(RdsSecretKey || (exports.RdsSecretKey = RdsSecretKey = {}));
18
18
  function checkExpectedSecretKeys(keys, secret) {
19
19
  const missingKeys = keys.filter((key) => !(key in secret));
20
20
  if (missingKeys.length) {
@@ -13,5 +13,5 @@ var MediaType;
13
13
  MediaType["TEXT_HTML"] = "text/html";
14
14
  MediaType["TEXT_CSV"] = "text/csv";
15
15
  MediaType["APPLICATION_JSON_UTF8"] = "application/json;charset=UTF-8";
16
- })(MediaType = exports.MediaType || (exports.MediaType = {}));
16
+ })(MediaType || (exports.MediaType = MediaType = {}));
17
17
  //# sourceMappingURL=mediatypes.js.map
@@ -17,7 +17,7 @@ var JSON_CACHE_KEY;
17
17
  (function (JSON_CACHE_KEY) {
18
18
  JSON_CACHE_KEY["NAUTICAL_WARNINGS_ACTIVE"] = "nautical-warnings-active";
19
19
  JSON_CACHE_KEY["NAUTICAL_WARNINGS_ARCHIVED"] = "nautical-warnings-archived";
20
- })(JSON_CACHE_KEY = exports.JSON_CACHE_KEY || (exports.JSON_CACHE_KEY = {}));
20
+ })(JSON_CACHE_KEY || (exports.JSON_CACHE_KEY = JSON_CACHE_KEY = {}));
21
21
  /**
22
22
  *
23
23
  * @param db
@@ -11,7 +11,7 @@ var DatabaseEnvironmentKeys;
11
11
  DatabaseEnvironmentKeys["DB_URI"] = "DB_URI";
12
12
  DatabaseEnvironmentKeys["DB_RO_URI"] = "DB_RO_URI";
13
13
  DatabaseEnvironmentKeys["DB_APPLICATION"] = "DB_APPLICATION";
14
- })(DatabaseEnvironmentKeys = exports.DatabaseEnvironmentKeys || (exports.DatabaseEnvironmentKeys = {}));
14
+ })(DatabaseEnvironmentKeys || (exports.DatabaseEnvironmentKeys = DatabaseEnvironmentKeys = {}));
15
15
  // eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
16
16
  const pgp = require("pg-promise")();
17
17
  // convert numeric types to number instead of string
@@ -10,7 +10,7 @@ var DataType;
10
10
  DataType["MAINTENANCE_TRACKING_DATA_CHECKED"] = "MAINTENANCE_TRACKING_DATA_CHECKED";
11
11
  DataType["PERMIT_DATA"] = "PERMIT_DATA";
12
12
  DataType["PERMIT_DATA_CHECK"] = "PERMIT_DATA_CHECK";
13
- })(DataType = exports.DataType || (exports.DataType = {}));
13
+ })(DataType || (exports.DataType = DataType = {}));
14
14
  const UNSET_SUBTYPE = "-";
15
15
  function getLastUpdated(db, datatype) {
16
16
  return db.oneOrNone("select updated from data_updated where data_type=$(datatype) and subtype=$(subtype)", {
@@ -11,7 +11,7 @@ export declare class TestHttpServer {
11
11
  getCallCount(): number;
12
12
  getRequestBody(callNumber: number): string;
13
13
  listen(port: number, props: ListenProperties, debug?: boolean, statusCode?: number): void;
14
- close(): void;
14
+ close(): Promise<boolean>;
15
15
  private debuglog;
16
16
  }
17
17
  export type ListenProperties = Record<string, (url?: string, data?: string) => string>;
@@ -65,10 +65,15 @@ class TestHttpServer {
65
65
  this.server.listen(port);
66
66
  }
67
67
  close() {
68
- this.debuglog("Closing test server");
69
- if (this.server !== undefined) {
70
- this.server.close();
71
- }
68
+ return new Promise((resolve, reject) => {
69
+ this.debuglog("Closing test server");
70
+ if (this.server !== undefined) {
71
+ this.server.close((error) => error != null ? reject(false) : resolve(true));
72
+ }
73
+ else {
74
+ resolve(true);
75
+ }
76
+ });
72
77
  }
73
78
  debuglog(str) {
74
79
  if (this.debug) {
@@ -6,5 +6,5 @@ var Language;
6
6
  Language["FI"] = "fi";
7
7
  Language["EN"] = "en";
8
8
  Language["SV"] = "sv";
9
- })(Language = exports.Language || (exports.Language = {}));
9
+ })(Language || (exports.Language = Language = {}));
10
10
  //# sourceMappingURL=language.js.map
@@ -9,5 +9,5 @@ var TrafficType;
9
9
  TrafficType["AVIATION"] = "Aviation";
10
10
  TrafficType["MCP"] = "MCP";
11
11
  TrafficType["OTHER"] = "Other";
12
- })(TrafficType = exports.TrafficType || (exports.TrafficType = {}));
12
+ })(TrafficType || (exports.TrafficType = TrafficType = {}));
13
13
  //# sourceMappingURL=traffictype.js.map
@@ -9,7 +9,7 @@ var RetryLogError;
9
9
  RetryLogError[RetryLogError["LOG_ALL_AS_ERRORS"] = 0] = "LOG_ALL_AS_ERRORS";
10
10
  RetryLogError[RetryLogError["LOG_LAST_RETRY_AS_ERROR_OTHERS_AS_WARNS"] = 1] = "LOG_LAST_RETRY_AS_ERROR_OTHERS_AS_WARNS";
11
11
  RetryLogError[RetryLogError["NO_LOGGING"] = 2] = "NO_LOGGING";
12
- })(RetryLogError = exports.RetryLogError || (exports.RetryLogError = {}));
12
+ })(RetryLogError || (exports.RetryLogError = RetryLogError = {}));
13
13
  /**
14
14
  * Utility timeout functions for "retry" function.
15
15
  */
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@digitraffic/common",
3
- "version": "2023.12.15-1",
3
+ "version": "2024.1.10-1",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/tmfg/digitraffic-common.git"
8
8
  },
9
9
  "engines": {
10
- "node": ">=14 <17 || >=18 <19"
10
+ "node": ">=20 <21"
11
11
  },
12
12
  "license": "EUPL-1.2",
13
13
  "private": false,
@@ -21,10 +21,10 @@
21
21
  "aws-cdk-lib": "^2.103.0",
22
22
  "aws-sdk": "^2.1481.0",
23
23
  "axios": "^1.6.2",
24
- "change-case": "4.1.2",
24
+ "change-case": "5.0.0",
25
25
  "constructs": "^10.3.0",
26
- "date-fns-tz": "~2.0.0",
27
26
  "date-fns": "~2.30.0",
27
+ "date-fns-tz": "~2.0.0",
28
28
  "etag": "^1.8.1",
29
29
  "geojson-validation": "^1.0.2",
30
30
  "node-ttl": "^0.2.0",
@@ -32,51 +32,48 @@
32
32
  "pg-promise": "^11.5.4"
33
33
  },
34
34
  "devDependencies": {
35
- "@types/aws-lambda": "8.10.125",
36
- "@types/geojson": "7946.0.12",
37
- "@types/etag": "1.8.2",
38
- "@types/jest": "29.5.6",
39
- "@types/lodash": "4.14.200",
40
- "@types/node": "20.8.9",
41
- "@types/sinon": "10.0.20",
42
- "@typescript-eslint/eslint-plugin": "~6.9.0",
43
- "@typescript-eslint/parser": "^6.9.0",
44
- "aws-cdk-lib": "~2.103.1",
45
- "aws-sdk": "~2.1482.0",
46
- "axios": "^1.6.0",
47
- "change-case": "4.1.2",
35
+ "@types/aws-lambda": "8.10.131",
36
+ "@types/etag": "1.8.3",
37
+ "@types/geojson": "7946.0.13",
38
+ "@types/jest": "29.5.11",
39
+ "@types/lodash": "4.14.202",
40
+ "@types/node": "20.10.7",
41
+ "@types/sinon": "17.0.2",
42
+ "@typescript-eslint/eslint-plugin": "~6.18.1",
43
+ "@typescript-eslint/parser": "^6.18.1",
44
+ "aws-cdk-lib": "~2.118.0",
45
+ "aws-sdk": "~2.1531.0",
46
+ "axios": "^1.6.5",
47
+ "change-case": "5.3.0",
48
48
  "constructs": "10.3.0",
49
- "date-fns-tz": "~2.0.0",
50
49
  "date-fns": "~2.30.0",
51
- "eslint": "~8.52.0",
52
- "eslint-config-prettier": "^9.0.0",
50
+ "date-fns-tz": "~2.0.0",
51
+ "eslint": "~8.56.0",
52
+ "eslint-config-prettier": "^9.1.0",
53
53
  "eslint-plugin-deprecation": "~2.0.0",
54
54
  "etag": "^1.8.1",
55
55
  "geojson-validation": "^1.0.2",
56
- "husky": "^8.0.3",
57
56
  "jest": "^29.7.0",
58
57
  "jest-junit": "^16.0.0",
59
- "lint-staged": "^15.0.2",
58
+ "lint-staged": "^15.2.0",
60
59
  "lodash": "~4.17.21",
61
60
  "node-ttl": "^0.2.0",
62
61
  "pg-native": "^3.0.1",
63
62
  "pg-promise": "^11.5.4",
64
- "prettier": "^2.8.8",
63
+ "prettier": "^3.1.1",
65
64
  "rimraf": "^5.0.5",
66
- "sinon": "17.0.0",
65
+ "sinon": "17.0.1",
67
66
  "ts-jest": "^29.1.1",
68
- "typescript": "~4.9.5",
67
+ "typescript": "~5.3.3",
69
68
  "velocityjs": "2.0.6"
70
69
  },
71
- "externals": [
72
- "aws-sdk",
73
- "Synthetics"
74
- ],
75
- "lint-staged": {
76
- "*.{js,ts,css,md,yml,yaml,json}": "prettier --write"
70
+ "dependencies": {
71
+ "@aws-sdk/client-s3": "~3.472.0",
72
+ "@aws-sdk/lib-storage": "~3.472.0"
77
73
  },
78
74
  "scripts": {
79
75
  "build": "tsc",
76
+ "build:watch": "tsc --watch",
80
77
  "lint": "eslint --cache .",
81
78
  "eslint-report": "eslint . --format html",
82
79
  "ci:eslint-report": "eslint . --format html -o report.html || true",
@@ -5,13 +5,10 @@ import { PolicyStatement } from "aws-cdk-lib/aws-iam";
5
5
  import { InlineCode, Runtime } from "aws-cdk-lib/aws-lambda";
6
6
  import { RetentionDays } from "aws-cdk-lib/aws-logs";
7
7
  import { SqsEventSource } from "aws-cdk-lib/aws-lambda-event-sources";
8
- import {
9
- ComparisonOperator,
10
- TreatMissingData,
11
- } from "aws-cdk-lib/aws-cloudwatch";
8
+ import { ComparisonOperator, TreatMissingData } from "aws-cdk-lib/aws-cloudwatch";
12
9
  import { SnsAction } from "aws-cdk-lib/aws-cloudwatch-actions";
13
- import { ManagedUpload } from "aws-sdk/clients/s3";
14
- import { S3 } from "aws-sdk";
10
+ import { S3, S3Client } from "@aws-sdk/client-s3";
11
+ import { Upload } from "@aws-sdk/lib-storage";
15
12
  import { SQSEvent, SQSHandler, SQSRecord } from "aws-lambda";
16
13
  import { DigitrafficStack } from "./stack/stack";
17
14
  import { MonitoredFunction } from "./stack/monitoredfunction";
@@ -23,11 +20,7 @@ import { MonitoredFunction } from "./stack/monitoredfunction";
23
20
  * and an alarm for the queue. Anything that goes to the dlq will be written into the bucket and the alarm is activated.
24
21
  */
25
22
  export class DigitrafficSqsQueue extends Queue {
26
- static create(
27
- stack: DigitrafficStack,
28
- name: string,
29
- props: QueueProps
30
- ): DigitrafficSqsQueue {
23
+ static create(stack: DigitrafficStack, name: string, props: QueueProps): DigitrafficSqsQueue {
31
24
  const queueName = `${stack.configuration.shortName}-${name}-Queue`;
32
25
  const queueProps = {
33
26
  ...props,
@@ -61,7 +54,7 @@ export class DigitrafficDLQueue {
61
54
 
62
55
  const dlqFunctionName = `${dlqName}-Function`;
63
56
  const lambda = MonitoredFunction.create(stack, dlqFunctionName, {
64
- runtime: Runtime.NODEJS_16_X,
57
+ runtime: Runtime.NODEJS_20_X,
65
58
  logRetention: RetentionDays.ONE_YEAR,
66
59
  functionName: dlqFunctionName,
67
60
  code: getDlqCode(dlqBucket.bucketName),
@@ -100,8 +93,8 @@ function addDLQAlarm(stack: DigitrafficStack, dlqName: string, dlq: Queue) {
100
93
  .addAlarmAction(new SnsAction(stack.warningTopic));
101
94
  }
102
95
 
103
- function getDlqCode(bName: string): InlineCode {
104
- const functionBody = DLQ_LAMBDA_CODE.replace("__bucketName__", bName)
96
+ function getDlqCode(Bucket: string): InlineCode {
97
+ const functionBody = DLQ_LAMBDA_CODE.replace("__bucketName__", Bucket)
105
98
  .replace("__upload__", uploadToS3.toString())
106
99
  .replace("__doUpload__", doUpload.toString())
107
100
  .replace("__handler__", createHandler().toString().substring(23)); // remove function handler() from signature
@@ -109,42 +102,32 @@ function getDlqCode(bName: string): InlineCode {
109
102
  return new InlineCode(functionBody);
110
103
  }
111
104
 
112
- async function uploadToS3(
113
- s3: S3,
114
- bName: string,
115
- body: string,
116
- objectName: string
117
- ): Promise<void> {
105
+ async function uploadToS3(s3: S3 | S3Client, Bucket: string, Body: string, Key: string): Promise<void> {
118
106
  try {
119
- console.info("writing %s to %s", objectName, bName);
120
- await doUpload(s3, bName, body, objectName);
107
+ console.info("writing %s to %s", Key, Bucket);
108
+ await doUpload(s3, Bucket, Body, Key);
121
109
  } catch (error) {
122
110
  console.warn(error);
123
- console.warn("method=uploadToS3 retrying upload to bucket %s", bName);
111
+ console.warn("method=uploadToS3 retrying upload to bucket %s", Bucket);
124
112
  try {
125
- await doUpload(s3, bName, body, objectName);
113
+ await doUpload(s3, Bucket, Body, Key);
126
114
  } catch (e2) {
127
- console.error(
128
- "method=uploadToS3 failed retrying upload to bucket %s",
129
- bName
130
- );
115
+ console.error("method=uploadToS3 failed retrying upload to bucket %s", Bucket);
131
116
  }
132
117
  }
133
118
  }
134
119
 
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();
120
+ async function doUpload(s3: S3 | S3Client, Bucket: string, Body: string, Key: string) {
121
+ try {
122
+ const upload = new Upload({
123
+ client: s3,
124
+ params: { Bucket, Key, Body },
125
+ });
126
+
127
+ await upload.done();
128
+ } catch (error) {
129
+ console.error(error);
130
+ }
148
131
  }
149
132
 
150
133
  // bucketName is unused, will be overridden in the actual lambda code below
@@ -170,7 +153,9 @@ function createHandler(): SQSHandler {
170
153
  };
171
154
  }
172
155
 
173
- const DLQ_LAMBDA_CODE = `const AWS = require('aws-sdk');
156
+ const DLQ_LAMBDA_CODE = `
157
+ import { S3, S3Client } from "@aws-sdk/client-s3";
158
+ import { Upload } from "@aws-sdk/lib-storage";
174
159
  const bucketName = "__bucketName__";
175
160
 
176
161
  __upload__
@@ -24,7 +24,7 @@ export function databaseFunctionProps(
24
24
  environment: LambdaEnvironment,
25
25
  lambdaName: string,
26
26
  simpleLambdaName: string,
27
- config?: Partial<FunctionParameters>
27
+ config?: Partial<FunctionParameters>,
28
28
  ): FunctionProps {
29
29
  const vpcSubnets = stack.vpc
30
30
  ? {
@@ -38,7 +38,7 @@ export function databaseFunctionProps(
38
38
  environment,
39
39
  lambdaName,
40
40
  simpleLambdaName,
41
- config
41
+ config,
42
42
  ),
43
43
  ...{
44
44
  vpc: stack.vpc ?? undefined,
@@ -53,10 +53,10 @@ export function lambdaFunctionProps(
53
53
  environment: LambdaEnvironment,
54
54
  lambdaName: string,
55
55
  simpleLambdaName: string,
56
- config?: Partial<FunctionParameters>
56
+ config?: Partial<FunctionParameters>,
57
57
  ): FunctionProps {
58
58
  return {
59
- runtime: config?.runtime ?? Runtime.NODEJS_16_X,
59
+ runtime: config?.runtime ?? Runtime.NODEJS_20_X,
60
60
  architecture: config?.architecture ?? Architecture.ARM_64,
61
61
  memorySize: config?.memorySize ?? 128,
62
62
  functionName: lambdaName,
@@ -72,7 +72,7 @@ export function lambdaFunctionProps(
72
72
 
73
73
  function getAssetCode(
74
74
  simpleLambdaName: string,
75
- isSingleLambda: boolean
75
+ isSingleLambda: boolean,
76
76
  ): AssetCode {
77
77
  const lambdaPath = isSingleLambda
78
78
  ? `dist/lambda/`
@@ -82,10 +82,10 @@ function getAssetCode(
82
82
  }
83
83
 
84
84
  export function defaultLambdaConfiguration(
85
- config: FunctionParameters
85
+ config: FunctionParameters,
86
86
  ): FunctionProps {
87
87
  const props: FunctionProps = {
88
- runtime: Runtime.NODEJS_16_X,
88
+ runtime: Runtime.NODEJS_20_X,
89
89
  memorySize: config.memorySize ?? 128,
90
90
  functionName: config.functionName,
91
91
  handler: config.handler,
@@ -6,12 +6,12 @@ import { IConstruct } from "constructs";
6
6
  import { CfnMethod, CfnResource } from "aws-cdk-lib/aws-apigateway";
7
7
  import { CfnQueue } from "aws-cdk-lib/aws-sqs";
8
8
  import { LogRetention } from "aws-cdk-lib/aws-logs";
9
- import { paramCase } from "change-case";
9
+ import { kebabCase } from "change-case";
10
10
  import _ from "lodash";
11
11
  import IntegrationProperty = CfnMethod.IntegrationProperty;
12
12
 
13
13
  const MAX_CONCURRENCY_LIMIT = 100;
14
- const NODE_RUNTIMES = [Runtime.NODEJS_16_X.name, Runtime.NODEJS_18_X.name];
14
+ const NODE_RUNTIMES = [Runtime.NODEJS_20_X.name, Runtime.NODEJS_18_X.name];
15
15
 
16
16
  enum ResourceType {
17
17
  stackName = "STACK_NAME",
@@ -61,12 +61,7 @@ export class StackCheckingAspect implements IAspect {
61
61
  });
62
62
  }
63
63
 
64
- private addAnnotation(
65
- node: IConstruct,
66
- key: ResourceType | string,
67
- message: string,
68
- isError = true
69
- ) {
64
+ private addAnnotation(node: IConstruct, key: ResourceType | string, message: string, isError = true) {
70
65
  const resourceKey = `${node.node.path}/${key}`;
71
66
  const isWhiteListed = this.isWhitelisted(resourceKey);
72
67
  const annotationMessage = `${resourceKey}:${message}`;
@@ -83,20 +78,14 @@ export class StackCheckingAspect implements IAspect {
83
78
  private checkStack(node: IConstruct) {
84
79
  if (node instanceof DigitrafficStack) {
85
80
  if (
86
- (node.stackName.includes("Test") ||
87
- node.stackName.includes("Tst")) &&
81
+ (node.stackName.includes("Test") || node.stackName.includes("Tst")) &&
88
82
  node.configuration.production
89
83
  ) {
90
- this.addAnnotation(
91
- node,
92
- ResourceType.stackName,
93
- "Production is set for Test-stack"
94
- );
84
+ this.addAnnotation(node, ResourceType.stackName, "Production is set for Test-stack");
95
85
  }
96
86
 
97
87
  if (
98
- (node.stackName.includes("Prod") ||
99
- node.stackName.includes("Prd")) &&
88
+ (node.stackName.includes("Prod") || node.stackName.includes("Prd")) &&
100
89
  !node.configuration.production
101
90
  ) {
102
91
  this.addAnnotation(
@@ -116,9 +105,7 @@ export class StackCheckingAspect implements IAspect {
116
105
  ResourceType.reservedConcurrentConcurrency,
117
106
  "Function must have reservedConcurrentConcurrency"
118
107
  );
119
- } else if (
120
- node.reservedConcurrentExecutions > MAX_CONCURRENCY_LIMIT
121
- ) {
108
+ } else if (node.reservedConcurrentExecutions > MAX_CONCURRENCY_LIMIT) {
122
109
  this.addAnnotation(
123
110
  node,
124
111
  ResourceType.reservedConcurrentConcurrency,
@@ -127,25 +114,14 @@ export class StackCheckingAspect implements IAspect {
127
114
  }
128
115
 
129
116
  if (!node.timeout) {
130
- this.addAnnotation(
131
- node,
132
- ResourceType.functionTimeout,
133
- "Function must have timeout"
134
- );
117
+ this.addAnnotation(node, ResourceType.functionTimeout, "Function must have timeout");
135
118
  }
136
119
 
137
120
  if (!node.memorySize) {
138
- this.addAnnotation(
139
- node,
140
- ResourceType.functionMemorySize,
141
- "Function must have memorySize"
142
- );
121
+ this.addAnnotation(node, ResourceType.functionMemorySize, "Function must have memorySize");
143
122
  }
144
123
 
145
- if (
146
- node.runtime !== undefined &&
147
- !NODE_RUNTIMES.includes(node.runtime)
148
- ) {
124
+ if (node.runtime !== undefined && !NODE_RUNTIMES.includes(node.runtime)) {
149
125
  this.addAnnotation(
150
126
  node,
151
127
  ResourceType.functionRuntime,
@@ -170,11 +146,7 @@ export class StackCheckingAspect implements IAspect {
170
146
  private checkTags(node: IConstruct) {
171
147
  if (node instanceof Stack) {
172
148
  if (!node.tags.tagValues()[SOLUTION_KEY]) {
173
- this.addAnnotation(
174
- node,
175
- ResourceType.tagSolution,
176
- "Solution tag is missing"
177
- );
149
+ this.addAnnotation(node, ResourceType.tagSolution, "Solution tag is missing");
178
150
  }
179
151
  }
180
152
  }
@@ -192,11 +164,7 @@ export class StackCheckingAspect implements IAspect {
192
164
  !c.ignorePublicAcls ||
193
165
  !c.restrictPublicBuckets)
194
166
  ) {
195
- this.addAnnotation(
196
- node,
197
- ResourceType.bucketPublicity,
198
- "Check bucket publicity"
199
- );
167
+ this.addAnnotation(node, ResourceType.bucketPublicity, "Check bucket publicity");
200
168
  }
201
169
  }
202
170
  }
@@ -211,7 +179,7 @@ export class StackCheckingAspect implements IAspect {
211
179
  return this.isValidPath(path.split("{")[0]);
212
180
  }
213
181
 
214
- return paramCase(path) === path;
182
+ return kebabCase(path) === path;
215
183
  }
216
184
 
217
185
  private static isValidQueryString(name: string) {
@@ -221,16 +189,10 @@ export class StackCheckingAspect implements IAspect {
221
189
  private checkResourceCasing(node: IConstruct) {
222
190
  if (node instanceof CfnResource) {
223
191
  if (!StackCheckingAspect.isValidPath(node.pathPart)) {
224
- this.addAnnotation(
225
- node,
226
- ResourceType.resourcePath,
227
- "Path part should be in kebab-case"
228
- );
192
+ this.addAnnotation(node, ResourceType.resourcePath, "Path part should be in kebab-case");
229
193
  }
230
194
  } else if (node instanceof CfnMethod) {
231
- const integration = node.integration as
232
- | IntegrationProperty
233
- | undefined;
195
+ const integration = node.integration as IntegrationProperty | undefined;
234
196
 
235
197
  if (integration?.requestParameters) {
236
198
  Object.keys(integration.requestParameters).forEach((key) => {
@@ -238,15 +200,8 @@ export class StackCheckingAspect implements IAspect {
238
200
  const type = split[2];
239
201
  const name = split[3];
240
202
 
241
- if (
242
- type === "querystring" &&
243
- !StackCheckingAspect.isValidQueryString(name)
244
- ) {
245
- this.addAnnotation(
246
- node,
247
- name,
248
- "Querystring should be in snake_case"
249
- );
203
+ if (type === "querystring" && !StackCheckingAspect.isValidQueryString(name)) {
204
+ this.addAnnotation(node, name, "Querystring should be in snake_case");
250
205
  }
251
206
  });
252
207
  }
@@ -256,21 +211,14 @@ export class StackCheckingAspect implements IAspect {
256
211
  private checkQueueEncryption(node: IConstruct) {
257
212
  if (node instanceof CfnQueue) {
258
213
  if (!node.kmsMasterKeyId) {
259
- this.addAnnotation(
260
- node,
261
- ResourceType.queueEncryption,
262
- "Queue must have encryption enabled"
263
- );
214
+ this.addAnnotation(node, ResourceType.queueEncryption, "Queue must have encryption enabled");
264
215
  }
265
216
  }
266
217
  }
267
218
 
268
219
  private checkLogGroupRetention(node: IConstruct) {
269
220
  if (node instanceof LogRetention) {
270
- const child = node.node.defaultChild as unknown as Record<
271
- string,
272
- Record<string, string>
273
- >;
221
+ const child = node.node.defaultChild as unknown as Record<string, Record<string, string>>;
274
222
  const retention = child._cfnProperties.RetentionInDays;
275
223
 
276
224
  if (!retention) {
@@ -30,7 +30,7 @@ export class TestHttpServer {
30
30
  port: number,
31
31
  props: ListenProperties,
32
32
  debug = false,
33
- statusCode = 200
33
+ statusCode = 200,
34
34
  ) {
35
35
  this.debug = debug;
36
36
  this.messageStack = [];
@@ -62,7 +62,7 @@ export class TestHttpServer {
62
62
  res.setHeader("Access-Control-Allow-Origin", "*");
63
63
  res.setHeader(
64
64
  "Access-Control-Allow-Headers",
65
- "Authorization,X-User-Id,X-Auth-Token"
65
+ "Authorization,X-User-Id,X-Auth-Token",
66
66
  );
67
67
  res.writeHead(statusCode);
68
68
 
@@ -85,11 +85,17 @@ export class TestHttpServer {
85
85
  this.server.listen(port);
86
86
  }
87
87
 
88
- close() {
89
- this.debuglog("Closing test server");
90
- if (this.server !== undefined) {
91
- this.server.close();
92
- }
88
+ close(): Promise<boolean> {
89
+ return new Promise((resolve, reject) => {
90
+ this.debuglog("Closing test server");
91
+ if (this.server !== undefined) {
92
+ this.server.close((error) =>
93
+ error != null ? reject(false) : resolve(true),
94
+ );
95
+ } else {
96
+ resolve(true);
97
+ }
98
+ });
93
99
  }
94
100
 
95
101
  private debuglog(str: string) {