@digitraffic/common 2025.1.20-1 → 2025.2.4-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.
- package/README.md +28 -20
- package/dist/__test__/db-testutils.js +1 -1
- package/dist/__test__/infra/acl-builder.test.js +14 -6
- package/dist/__test__/infra/api/integration.test.js +21 -20
- package/dist/__test__/infra/api/static-integration.test.js +5 -4
- package/dist/__test__/marine/id_utils.test.js +2 -1
- package/dist/__test__/promise/promise.test.js +6 -3
- package/dist/__test__/runtime/dt-logger.test.js +1 -1
- package/dist/__test__/secrets/secret-holder.test.js +4 -1
- package/dist/__test__/secrets/secret.test.js +4 -1
- package/dist/__test__/stack/rest-apis.test.js +3 -1
- package/dist/__test__/types/lambda-response.test.js +2 -1
- package/dist/__test__/utils/utils.test.js +7 -2
- package/dist/aws/infra/acl-builder.js +7 -3
- package/dist/aws/infra/api/integration.js +5 -3
- package/dist/aws/infra/api/responses.js +3 -2
- package/dist/aws/infra/api/static-integration.js +7 -4
- package/dist/aws/infra/canaries/canary-role.js +3 -1
- package/dist/aws/infra/canaries/canary.js +1 -1
- package/dist/aws/infra/canaries/database-canary.js +6 -2
- package/dist/aws/infra/canaries/database-checker.js +5 -3
- package/dist/aws/infra/canaries/url-canary.js +6 -2
- package/dist/aws/infra/canaries/url-checker.js +2 -1
- package/dist/aws/infra/documentation.js +1 -1
- package/dist/aws/infra/sqs-integration.js +3 -1
- package/dist/aws/infra/sqs-queue.js +3 -3
- package/dist/aws/infra/stack/lambda-configs.js +4 -2
- package/dist/aws/infra/stack/monitoredfunction.js +6 -3
- package/dist/aws/infra/stack/rest_apis.js +3 -2
- package/dist/aws/infra/stack/stack-checking-aspect.js +2 -1
- package/dist/aws/infra/stack/stack.js +1 -1
- package/dist/aws/infra/stacks/db-dns-stack.js +1 -1
- package/dist/aws/infra/stacks/db-stack.d.ts +8 -5
- package/dist/aws/infra/stacks/db-stack.js +25 -23
- package/dist/aws/runtime/apikey.js +3 -3
- package/dist/aws/runtime/digitraffic-integration-response.js +4 -2
- package/dist/aws/runtime/dt-logger.js +4 -3
- package/dist/aws/runtime/secrets/secret-holder.js +3 -1
- package/dist/aws/runtime/secrets/secret.js +1 -1
- package/dist/database/cached.d.ts +0 -1
- package/dist/database/cached.js +0 -1
- package/dist/marine/id_utils.js +6 -2
- package/dist/types/openapi-schema.js +15 -3
- package/dist/types/traffictype.d.ts +1 -0
- package/dist/types/traffictype.js +1 -0
- package/dist/utils/logging.js +8 -2
- package/package.json +16 -14
package/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Digitraffic-common
|
2
2
|
|
3
|
-
This is a place for common utilities and classes that can be used in other
|
3
|
+
This is a place for common utilities and classes that can be used in other
|
4
|
+
cdk-projects.
|
4
5
|
|
5
6
|
## How to build
|
6
7
|
|
@@ -10,14 +11,19 @@ Use `pnpm` to build the code i.e.
|
|
10
11
|
pnpm build
|
11
12
|
pnpm test
|
12
13
|
|
14
|
+
Format code
|
15
|
+
|
16
|
+
pnpm run:format-changed # Formats stagged files
|
17
|
+
pnpm run:format # Format all files
|
18
|
+
|
13
19
|
## How to use
|
14
20
|
|
15
21
|
In package.json dependencies:
|
16
22
|
|
17
23
|
```
|
18
|
-
|
19
|
-
|
20
|
-
|
24
|
+
"dependencies": {
|
25
|
+
"@digitraffic/common": "*",
|
26
|
+
}
|
21
27
|
```
|
22
28
|
|
23
29
|
In code:
|
@@ -30,46 +36,48 @@ import {DigitrafficStack, StackConfiguration} from "@digitraffic/common/dist/aws
|
|
30
36
|
|
31
37
|
If you extend your stack from DigitrafficStack you get many benefits:
|
32
38
|
|
33
|
-
-
|
34
|
-
-
|
35
|
-
-
|
39
|
+
- Secret, VPC, Sg & alarmTopics automatically
|
40
|
+
- Stack validation with StackCheckingAspect
|
41
|
+
- Easier configuration with StackConfiguration
|
36
42
|
|
37
43
|
If you do not need those things, you should not use DigitrafficStack.
|
38
44
|
|
39
45
|
### StackConfiguration
|
40
46
|
|
41
|
-
Some commonly used parameters is predefined configuration. You can write
|
42
|
-
environments once and use across cdk-projects.
|
47
|
+
Some commonly used parameters is predefined configuration. You can write
|
48
|
+
configuration for your environments once and use across cdk-projects.
|
43
49
|
|
44
50
|
### StackCheckingAspect
|
45
51
|
|
46
52
|
Uses cdk aspects to do some sanity checking for your cdk stack:
|
47
53
|
|
48
|
-
-
|
49
|
-
-
|
50
|
-
-
|
51
|
-
-
|
52
|
-
-
|
53
|
-
-
|
54
|
-
-
|
54
|
+
- Stack naming check(Test/Prod in name)
|
55
|
+
- Function configuration(memory, timeout, runtime, reservedConcurrency)
|
56
|
+
- Tags, must have Solution tag defined
|
57
|
+
- S3 Buckets, no public access
|
58
|
+
- Api Gateway resource casing(kebabCase and snake_case)
|
59
|
+
- Queue encrypting
|
60
|
+
- LogGroup Retention
|
55
61
|
|
56
|
-
You can use StackCheckingAspect for any stack, DigitrafficStack does it
|
62
|
+
You can use StackCheckingAspect for any stack, DigitrafficStack does it
|
63
|
+
automatically, but you can call it manually:
|
57
64
|
|
58
65
|
```
|
59
66
|
Aspects.of(this).add(StackCheckingAspect.create(this));
|
60
67
|
```
|
61
68
|
|
62
|
-
Any resource can be whitelisted by giving it as a parameter or in the
|
69
|
+
Any resource can be whitelisted by giving it as a parameter or in the
|
70
|
+
StackConfiguration
|
63
71
|
|
64
72
|
### MonitoredFunction
|
65
73
|
|
66
74
|
MonitoredFunction extends Function with alarms on memory usage and timeouts.
|
67
75
|
|
68
|
-
If you need database access in your Function, you can use MonitoredDBFunction.
|
76
|
+
If you need database access in your Function, you can use MonitoredDBFunction.
|
77
|
+
Creating a Function with it is this easy:
|
69
78
|
|
70
79
|
```
|
71
80
|
const lambda = MonitoredDBFunction.create(stack, 'get-metadata');
|
72
81
|
```
|
73
82
|
|
74
83
|
See the documentation for more information.
|
75
|
-
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { DatabaseEnvironmentKeys, initDbConnection } from "../database/database.js";
|
1
|
+
import { DatabaseEnvironmentKeys, initDbConnection, } from "../database/database.js";
|
2
2
|
import { getEnvVariableOrElse } from "../utils/utils.js";
|
3
3
|
export async function assertCount(db, sql, count) {
|
4
4
|
await db.one(sql).then((x) => expect(x.count).toEqual(count));
|
@@ -15,11 +15,15 @@ describe("acl-builder tests", () => {
|
|
15
15
|
expect(acl.rules).toHaveLength(4);
|
16
16
|
});
|
17
17
|
test("two aws rules", () => {
|
18
|
-
const acl = createBuilder().withAWSManagedRules([
|
18
|
+
const acl = createBuilder().withAWSManagedRules([
|
19
|
+
"CommonRuleSet",
|
20
|
+
"AmazonIpReputationList",
|
21
|
+
]).build();
|
19
22
|
expect(acl.rules).toHaveLength(2);
|
20
23
|
});
|
21
24
|
test("ip restriction", () => {
|
22
|
-
const acl = createBuilder().withIpRestrictionRule(["1.2.3.4", "1.2.6.6"])
|
25
|
+
const acl = createBuilder().withIpRestrictionRule(["1.2.3.4", "1.2.6.6"])
|
26
|
+
.build();
|
23
27
|
expect(acl.rules).toHaveLength(1);
|
24
28
|
});
|
25
29
|
test("throttle rules", () => {
|
@@ -32,15 +36,19 @@ describe("acl-builder tests", () => {
|
|
32
36
|
const acl = aclBuilder.build();
|
33
37
|
// Check that the rule exists and a custom response is defined
|
34
38
|
expect(acl.rules).toHaveLength(1);
|
35
|
-
expect(Object.keys(acl.customResponseBodies))
|
39
|
+
expect(Object.keys(acl.customResponseBodies))
|
40
|
+
.toHaveLength(1);
|
36
41
|
// Check that the rule does throttle
|
37
42
|
const throttleRule = acl.rules[0];
|
38
|
-
expect(throttleRule.statement
|
39
|
-
|
43
|
+
expect(throttleRule.statement
|
44
|
+
.rateBasedStatement).toBeDefined();
|
45
|
+
expect(throttleRule.action.block)
|
46
|
+
.toBeDefined();
|
40
47
|
}
|
41
48
|
});
|
42
49
|
test("Cannot define two rules with the same name", () => {
|
43
|
-
expect(() => createBuilder().withThrottleAnonymousUserIp(10)
|
50
|
+
expect(() => createBuilder().withThrottleAnonymousUserIp(10)
|
51
|
+
.withThrottleAnonymousUserIp(200).build()).toThrow();
|
44
52
|
});
|
45
53
|
test("throtle rule without limit does nothing", () => {
|
46
54
|
for (const aclBuilder of [
|
@@ -5,7 +5,8 @@ import { MediaType } from "../../../aws/types/mediatypes.js";
|
|
5
5
|
import velocity from "velocityjs";
|
6
6
|
describe("integration tests", () => {
|
7
7
|
function createTemplate(i) {
|
8
|
-
const template = i.createRequestTemplates()[MediaType.APPLICATION_JSON]
|
8
|
+
const template = i.createRequestTemplates()[MediaType.APPLICATION_JSON]
|
9
|
+
.trim();
|
9
10
|
// assert template parses
|
10
11
|
const response = createResponseFromTemplate(template);
|
11
12
|
// assert response parses
|
@@ -19,9 +20,9 @@ describe("integration tests", () => {
|
|
19
20
|
method: {
|
20
21
|
request: {
|
21
22
|
multivaluequerystring: {
|
22
|
-
m1: ["multi1", "multi2"]
|
23
|
-
}
|
24
|
-
}
|
23
|
+
m1: ["multi1", "multi2"],
|
24
|
+
},
|
25
|
+
},
|
25
26
|
},
|
26
27
|
input: {
|
27
28
|
params: () => ({
|
@@ -30,24 +31,24 @@ describe("integration tests", () => {
|
|
30
31
|
},
|
31
32
|
querystring: {
|
32
33
|
q1: "querystring1",
|
33
|
-
q2: "querystring2"
|
34
|
+
q2: "querystring2",
|
34
35
|
},
|
35
36
|
path: {
|
36
|
-
p1: "path1"
|
37
|
+
p1: "path1",
|
37
38
|
},
|
38
|
-
})
|
39
|
+
}),
|
39
40
|
},
|
40
41
|
util: {
|
41
42
|
base64Decode: (data) => Buffer.from(data, "base64").toString(),
|
42
43
|
escapeJavaScript: (data) => encodeURIComponent(data),
|
43
|
-
parseJson: (data) => JSON.stringify(data)
|
44
|
+
parseJson: (data) => JSON.stringify(data),
|
44
45
|
},
|
45
46
|
context: {
|
46
47
|
c1: "context1",
|
47
48
|
authorizer: {
|
48
|
-
c2: "context2"
|
49
|
-
}
|
50
|
-
}
|
49
|
+
c2: "context2",
|
50
|
+
},
|
51
|
+
},
|
51
52
|
});
|
52
53
|
}
|
53
54
|
function createIntegration() {
|
@@ -70,7 +71,7 @@ describe("integration tests", () => {
|
|
70
71
|
.addQueryParameter("q1");
|
71
72
|
const t = createTemplate(i);
|
72
73
|
expect(t).toEqual({
|
73
|
-
q1: "querystring1"
|
74
|
+
q1: "querystring1",
|
74
75
|
});
|
75
76
|
});
|
76
77
|
test("two query parameters", () => {
|
@@ -80,7 +81,7 @@ describe("integration tests", () => {
|
|
80
81
|
const t = createTemplate(i);
|
81
82
|
expect(t).toEqual({
|
82
83
|
q1: "querystring1",
|
83
|
-
q2: "querystring2"
|
84
|
+
q2: "querystring2",
|
84
85
|
});
|
85
86
|
});
|
86
87
|
test("multivaluequery parameter", () => {
|
@@ -88,7 +89,7 @@ describe("integration tests", () => {
|
|
88
89
|
.addMultiValueQueryParameter("m1");
|
89
90
|
const t = createTemplate(i);
|
90
91
|
expect(t).toEqual({
|
91
|
-
m1: ["multi1", "multi2"]
|
92
|
+
m1: ["multi1", "multi2"],
|
92
93
|
});
|
93
94
|
});
|
94
95
|
test("all parameters", () => {
|
@@ -97,7 +98,7 @@ describe("integration tests", () => {
|
|
97
98
|
const t = createTemplate(i);
|
98
99
|
expect(t).toEqual({
|
99
100
|
q1: "querystring1",
|
100
|
-
q2: "querystring2"
|
101
|
+
q2: "querystring2",
|
101
102
|
});
|
102
103
|
});
|
103
104
|
test("path parameter", () => {
|
@@ -105,7 +106,7 @@ describe("integration tests", () => {
|
|
105
106
|
.addPathParameter("p1");
|
106
107
|
const t = createTemplate(i);
|
107
108
|
expect(t).toEqual({
|
108
|
-
p1: "path1"
|
109
|
+
p1: "path1",
|
109
110
|
});
|
110
111
|
});
|
111
112
|
test("context parameter", () => {
|
@@ -113,7 +114,7 @@ describe("integration tests", () => {
|
|
113
114
|
.addContextParameter("c1");
|
114
115
|
const t = createTemplate(i);
|
115
116
|
expect(t).toEqual({
|
116
|
-
c1: "context1"
|
117
|
+
c1: "context1",
|
117
118
|
});
|
118
119
|
});
|
119
120
|
test("context parameter authorizer", () => {
|
@@ -121,7 +122,7 @@ describe("integration tests", () => {
|
|
121
122
|
.addContextParameter("authorizer.c2");
|
122
123
|
const t = createTemplate(i);
|
123
124
|
expect(t).toEqual({
|
124
|
-
"authorizer.c2": "context2"
|
125
|
+
"authorizer.c2": "context2",
|
125
126
|
});
|
126
127
|
});
|
127
128
|
test("all parameters and header", () => {
|
@@ -132,7 +133,7 @@ describe("integration tests", () => {
|
|
132
133
|
expect(t).toEqual({
|
133
134
|
h1: "header1",
|
134
135
|
q1: "querystring1",
|
135
|
-
q2: "querystring2"
|
136
|
+
q2: "querystring2",
|
136
137
|
});
|
137
138
|
});
|
138
139
|
test("all parameters & parameter - fail", () => {
|
@@ -150,7 +151,7 @@ describe("integration tests", () => {
|
|
150
151
|
expect(t).toEqual({
|
151
152
|
p1: "path1",
|
152
153
|
q1: "querystring1",
|
153
|
-
q2: "querystring2"
|
154
|
+
q2: "querystring2",
|
154
155
|
});
|
155
156
|
});
|
156
157
|
});
|
@@ -3,7 +3,8 @@ import { DigitrafficStaticIntegration } from "../../../aws/infra/api/static-inte
|
|
3
3
|
import { MediaType } from "../../../aws/types/mediatypes.js";
|
4
4
|
describe("response tests", () => {
|
5
5
|
it("createIntegrationResponse works", () => {
|
6
|
-
const integrationResponse = DigitrafficStaticIntegration
|
6
|
+
const integrationResponse = DigitrafficStaticIntegration
|
7
|
+
.createIntegrationResponse("FakeResource", MediaType.APPLICATION_JSON, { "test-header": "test-value" });
|
7
8
|
expect(integrationResponse).toEqual({
|
8
9
|
responseParameters: {
|
9
10
|
"method.response.header.test-header": "'test-value'",
|
@@ -16,13 +17,13 @@ describe("response tests", () => {
|
|
16
17
|
});
|
17
18
|
it("createMethodResponse works", () => {
|
18
19
|
const methodResponse = DigitrafficStaticIntegration.createMethodResponse({
|
19
|
-
"test-header": "test-value"
|
20
|
+
"test-header": "test-value",
|
20
21
|
}, MediaType.TEXT_PLAIN, Model.EMPTY_MODEL);
|
21
22
|
expect(methodResponse).toEqual({
|
22
23
|
responseModels: {
|
23
24
|
"text/plain": {
|
24
|
-
modelId: "Empty"
|
25
|
-
}
|
25
|
+
modelId: "Empty",
|
26
|
+
},
|
26
27
|
},
|
27
28
|
responseParameters: {
|
28
29
|
"method.response.header.test-header": true,
|
@@ -38,7 +38,8 @@ describe("IdUtils tests", () => {
|
|
38
38
|
expect(IdUtils.isValidMMSI(getRandomNumber(0, 100000000 - 1))).toBe(false);
|
39
39
|
});
|
40
40
|
test("isValidMMSI - fail with number larger than 999999999", () => {
|
41
|
-
expect(IdUtils.isValidMMSI(getRandomNumber(999999999 + 1, 9999999999)))
|
41
|
+
expect(IdUtils.isValidMMSI(getRandomNumber(999999999 + 1, 9999999999)))
|
42
|
+
.toBe(false);
|
42
43
|
});
|
43
44
|
});
|
44
45
|
//# sourceMappingURL=id_utils.test.js.map
|
@@ -75,7 +75,8 @@ describe("Promise utils tests", () => {
|
|
75
75
|
});
|
76
76
|
test("retry - exceeded retry count throws error", async () => {
|
77
77
|
const fn = jest.fn(() => Promise.reject("error"));
|
78
|
-
await expect(() => retry(fn, 3, RetryLogError.LOG_ALL_AS_ERRORS)).rejects
|
78
|
+
await expect(() => retry(fn, 3, RetryLogError.LOG_ALL_AS_ERRORS)).rejects
|
79
|
+
.toThrow();
|
79
80
|
});
|
80
81
|
test("retry - defaults", async () => {
|
81
82
|
const fn = jest.fn(() => Promise.reject("error"));
|
@@ -93,11 +94,13 @@ describe("Promise utils tests", () => {
|
|
93
94
|
});
|
94
95
|
test("retry - NaN throws error", async () => {
|
95
96
|
const fn = jest.fn(() => Promise.resolve(NaN));
|
96
|
-
await expect(() => retry(fn, NaN, RetryLogError.NO_LOGGING)).rejects
|
97
|
+
await expect(() => retry(fn, NaN, RetryLogError.NO_LOGGING)).rejects
|
98
|
+
.toThrow();
|
97
99
|
});
|
98
100
|
test("retry - Infinity throws error", async () => {
|
99
101
|
const fn = jest.fn(() => Promise.resolve(NaN));
|
100
|
-
await expect(() => retry(fn, Infinity, RetryLogError.NO_LOGGING)).rejects
|
102
|
+
await expect(() => retry(fn, Infinity, RetryLogError.NO_LOGGING)).rejects
|
103
|
+
.toThrow();
|
101
104
|
});
|
102
105
|
test("retry - exceeded maximum retry count throws error", async () => {
|
103
106
|
const fn = jest.fn(() => Promise.resolve(NaN));
|
@@ -18,7 +18,10 @@ function mockSecret(secret) {
|
|
18
18
|
getSecretValueMock.mockImplementation(() => Promise.resolve(emptySecret));
|
19
19
|
}
|
20
20
|
else {
|
21
|
-
getSecretValueMock.mockImplementation(() => Promise.resolve({
|
21
|
+
getSecretValueMock.mockImplementation(() => Promise.resolve({
|
22
|
+
...emptySecret,
|
23
|
+
...{ SecretString: JSON.stringify(secret) },
|
24
|
+
}));
|
22
25
|
}
|
23
26
|
}
|
24
27
|
describe("SecretHolder - tests", () => {
|
@@ -16,7 +16,10 @@ function mockSecret(secret) {
|
|
16
16
|
getSecretValueMock.mockImplementation(() => Promise.resolve(emptySecret));
|
17
17
|
}
|
18
18
|
else {
|
19
|
-
getSecretValueMock.mockImplementation(() => Promise.resolve({
|
19
|
+
getSecretValueMock.mockImplementation(() => Promise.resolve({
|
20
|
+
...emptySecret,
|
21
|
+
...{ SecretString: JSON.stringify(secret) },
|
22
|
+
}));
|
20
23
|
}
|
21
24
|
}
|
22
25
|
// eslint-disable-next-line dot-notation
|
@@ -20,7 +20,9 @@ describe("Rest api test", () => {
|
|
20
20
|
const testsResource = publicApi.addResourceWithCorsOptionsSubTree(versionResource, "tests");
|
21
21
|
testsResource.addResource("{testId}");
|
22
22
|
const template = Template.fromStack(stack);
|
23
|
-
template.resourcePropertiesCountIs("AWS::ApiGateway::Method", {
|
23
|
+
template.resourcePropertiesCountIs("AWS::ApiGateway::Method", {
|
24
|
+
HttpMethod: "OPTIONS",
|
25
|
+
}, 2);
|
24
26
|
template.hasResource("AWS::ApiGateway::Method", {
|
25
27
|
Properties: {
|
26
28
|
HttpMethod: "OPTIONS",
|
@@ -31,7 +31,8 @@ describe("lambda-response", () => {
|
|
31
31
|
assertJson(response, TEST_JSON, 200, TEST_FILENAME);
|
32
32
|
});
|
33
33
|
test("okJson - with fileName and timestamp", () => {
|
34
|
-
const response = LambdaResponse.okJson(TEST_JSON, TEST_FILENAME)
|
34
|
+
const response = LambdaResponse.okJson(TEST_JSON, TEST_FILENAME)
|
35
|
+
.withTimestamp(TEST_TIMESTAMP);
|
35
36
|
assertJson(response, TEST_JSON, 200, TEST_FILENAME, TEST_TIMESTAMP);
|
36
37
|
});
|
37
38
|
test("okBinary - with fileName and timestamp", () => {
|
@@ -11,7 +11,8 @@ describe("ArrayUtils", () => {
|
|
11
11
|
expect(ArrayUtils.bothArraysHasSameValues(["a"], undefined)).toEqual(false);
|
12
12
|
expect(ArrayUtils.bothArraysHasSameValues(["a"], null)).toEqual(false);
|
13
13
|
expect(ArrayUtils.bothArraysHasSameValues(["a", "b"], ["a", "a"])).toEqual(false);
|
14
|
-
expect(ArrayUtils.bothArraysHasSameValues(["a", "a", "a"], ["a", "b", "c"]))
|
14
|
+
expect(ArrayUtils.bothArraysHasSameValues(["a", "a", "a"], ["a", "b", "c"]))
|
15
|
+
.toEqual(false);
|
15
16
|
const o1 = { a: 1, b: 2 };
|
16
17
|
const o2 = { a: 1, b: 2 };
|
17
18
|
// Objects are references to same
|
@@ -39,7 +40,11 @@ describe("ArrayUtils", () => {
|
|
39
40
|
expect(ArrayUtils.getLast([1, 2])).toEqual(2);
|
40
41
|
});
|
41
42
|
test("isDefined", () => {
|
42
|
-
expect([1, 2, undefined, null, 3].filter(ArrayUtils.isDefined)).toEqual([
|
43
|
+
expect([1, 2, undefined, null, 3].filter(ArrayUtils.isDefined)).toEqual([
|
44
|
+
1,
|
45
|
+
2,
|
46
|
+
3,
|
47
|
+
]);
|
43
48
|
});
|
44
49
|
});
|
45
50
|
//# sourceMappingURL=utils.test.js.map
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { CfnIPSet, CfnWebACL } from "aws-cdk-lib/aws-wafv2";
|
2
2
|
import { logger } from "../runtime/dt-logger-default.js";
|
3
|
-
import {
|
3
|
+
import { concat, range, zipWith } from "lodash-es";
|
4
4
|
/**
|
5
5
|
* Builder class for building CfnWebACL.
|
6
6
|
*
|
@@ -243,7 +243,9 @@ function createThrottleStatement(limit, isHeaderRequired, isBasedOnIpAndUriPath)
|
|
243
243
|
aggregateKeyType: "CUSTOM_KEYS",
|
244
244
|
customKeys: CUSTOM_KEYS_IP_AND_URI_PATH,
|
245
245
|
limit: limit,
|
246
|
-
scopeDownStatement: isHeaderRequired
|
246
|
+
scopeDownStatement: isHeaderRequired
|
247
|
+
? matchStatement
|
248
|
+
: notStatement(matchStatement),
|
247
249
|
},
|
248
250
|
};
|
249
251
|
}
|
@@ -251,7 +253,9 @@ function createThrottleStatement(limit, isHeaderRequired, isBasedOnIpAndUriPath)
|
|
251
253
|
rateBasedStatement: {
|
252
254
|
aggregateKeyType: "IP",
|
253
255
|
limit: limit,
|
254
|
-
scopeDownStatement: isHeaderRequired
|
256
|
+
scopeDownStatement: isHeaderRequired
|
257
|
+
? matchStatement
|
258
|
+
: notStatement(matchStatement),
|
255
259
|
},
|
256
260
|
};
|
257
261
|
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { LambdaIntegration, PassthroughBehavior } from "aws-cdk-lib/aws-apigateway";
|
1
|
+
import { LambdaIntegration, PassthroughBehavior, } from "aws-cdk-lib/aws-apigateway";
|
2
2
|
import { MediaType } from "../../types/mediatypes.js";
|
3
3
|
import { DigitrafficIntegrationResponse } from "../../runtime/digitraffic-integration-response.js";
|
4
4
|
const VELOCITY_ALL_PARAMS = `#foreach($paramName in $params.keySet())
|
@@ -20,8 +20,9 @@ export class DigitrafficIntegration {
|
|
20
20
|
this._passBody = false;
|
21
21
|
}
|
22
22
|
passAllQueryParameters() {
|
23
|
-
if (this.parameters.some((p) => p.type === "querystring"))
|
23
|
+
if (this.parameters.some((p) => p.type === "querystring")) {
|
24
24
|
throw new Error("Can't add query parameters with pass all");
|
25
|
+
}
|
25
26
|
this._passAllQueryParameters = true;
|
26
27
|
return this;
|
27
28
|
}
|
@@ -38,8 +39,9 @@ export class DigitrafficIntegration {
|
|
38
39
|
return this;
|
39
40
|
}
|
40
41
|
addQueryParameter(...names) {
|
41
|
-
if (this._passAllQueryParameters)
|
42
|
+
if (this._passAllQueryParameters) {
|
42
43
|
throw new Error("Can't add query parameters with pass all");
|
44
|
+
}
|
43
45
|
names.forEach((name) => this.parameters.push({ type: "querystring", name }));
|
44
46
|
return this;
|
45
47
|
}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { BadRequestResponseTemplate, InternalServerErrorResponseTemplate, NotFoundResponseTemplate, XmlResponseTemplate, } from "./response.js";
|
2
2
|
import { LambdaIntegration, PassthroughBehavior, } from "aws-cdk-lib/aws-apigateway";
|
3
|
-
import { BAD_REQUEST_MESSAGE, ERROR_MESSAGE, NOT_FOUND_MESSAGE } from "../../types/errors.js";
|
3
|
+
import { BAD_REQUEST_MESSAGE, ERROR_MESSAGE, NOT_FOUND_MESSAGE, } from "../../types/errors.js";
|
4
4
|
/// @deprecated
|
5
5
|
export const RESPONSE_200_OK = {
|
6
6
|
statusCode: "200",
|
@@ -63,7 +63,8 @@ export function defaultIntegration(lambdaFunction, options) {
|
|
63
63
|
],
|
64
64
|
requestParameters: options?.requestParameters ?? {},
|
65
65
|
requestTemplates: options?.requestTemplates ?? {},
|
66
|
-
passthroughBehavior: options?.passthroughBehavior ??
|
66
|
+
passthroughBehavior: options?.passthroughBehavior ??
|
67
|
+
PassthroughBehavior.WHEN_NO_MATCH,
|
67
68
|
});
|
68
69
|
}
|
69
70
|
export function getResponse(response, options) {
|
@@ -15,7 +15,8 @@ export class DigitrafficStaticIntegration extends MockIntegration {
|
|
15
15
|
if (enableCors) {
|
16
16
|
headers = { ...headers, "Access-Control-Allow-Origin": "*" };
|
17
17
|
}
|
18
|
-
const integrationResponse = DigitrafficStaticIntegration
|
18
|
+
const integrationResponse = DigitrafficStaticIntegration
|
19
|
+
.createIntegrationResponse(response, mediaType, headers);
|
19
20
|
super({
|
20
21
|
passthroughBehavior: PassthroughBehavior.WHEN_NO_TEMPLATES,
|
21
22
|
requestTemplates: {
|
@@ -26,7 +27,9 @@ export class DigitrafficStaticIntegration extends MockIntegration {
|
|
26
27
|
["GET", "HEAD"].forEach((httpMethod) => {
|
27
28
|
resource.addMethod(httpMethod, this, {
|
28
29
|
apiKeyRequired,
|
29
|
-
methodResponses: [
|
30
|
+
methodResponses: [
|
31
|
+
DigitrafficStaticIntegration.createMethodResponse(headers, mediaType, model),
|
32
|
+
],
|
30
33
|
});
|
31
34
|
});
|
32
35
|
}
|
@@ -50,8 +53,8 @@ export class DigitrafficStaticIntegration extends MockIntegration {
|
|
50
53
|
statusCode: "200",
|
51
54
|
responseParameters: prefixKeys("method.response.header.", entries),
|
52
55
|
responseModels: {
|
53
|
-
[mediaType]: model
|
54
|
-
}
|
56
|
+
[mediaType]: model,
|
57
|
+
},
|
55
58
|
};
|
56
59
|
}
|
57
60
|
}
|
@@ -22,7 +22,9 @@ export class DigitrafficCanaryRole extends Role {
|
|
22
22
|
constructor(stack, canaryName) {
|
23
23
|
super(stack, "canary-role-" + canaryName, {
|
24
24
|
assumedBy: new ServicePrincipal("lambda.amazonaws.com"),
|
25
|
-
managedPolicies: [
|
25
|
+
managedPolicies: [
|
26
|
+
ManagedPolicy.fromAwsManagedPolicyName("CloudWatchSyntheticsFullAccess"),
|
27
|
+
],
|
26
28
|
});
|
27
29
|
this.addToPolicy(new PolicyStatement(BASE_POLICY_STATEMENT_PROPS));
|
28
30
|
this.addToPolicy(new PolicyStatement(CLOUDWATCH_STATEMENT_PROPS));
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { Duration } from "aws-cdk-lib";
|
2
|
-
import { AssetCode, Canary, Runtime, Schedule, Test } from "aws-cdk-lib/aws-synthetics";
|
2
|
+
import { AssetCode, Canary, Runtime, Schedule, Test, } from "aws-cdk-lib/aws-synthetics";
|
3
3
|
import { CanaryAlarm } from "./canary-alarm.js";
|
4
4
|
export class DigitrafficCanary extends Canary {
|
5
5
|
constructor(scope, canaryName, role, params, environmentVariables) {
|
@@ -12,8 +12,12 @@ export class DatabaseCanary extends DigitrafficCanary {
|
|
12
12
|
secret.grantRead(this.role);
|
13
13
|
// need to override vpc and security group, can't do this with cdk
|
14
14
|
if (this.node.defaultChild instanceof CfnCanary) {
|
15
|
-
const subnetIds = stack.vpc === undefined
|
16
|
-
|
15
|
+
const subnetIds = stack.vpc === undefined
|
16
|
+
? []
|
17
|
+
: stack.vpc.privateSubnets.map((subnet) => subnet.subnetId);
|
18
|
+
const securityGroupIds = stack.lambdaDbSg === undefined
|
19
|
+
? []
|
20
|
+
: [stack.lambdaDbSg.securityGroupId];
|
17
21
|
this.node.defaultChild.vpcConfig = {
|
18
22
|
vpcId: stack.vpc?.vpcId,
|
19
23
|
securityGroupIds,
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { inDatabaseReadonly } from "../../../database/database.js";
|
1
|
+
import { inDatabaseReadonly, } from "../../../database/database.js";
|
2
2
|
import { ProxyHolder } from "../../runtime/secrets/proxy-holder.js";
|
3
3
|
import { RdsHolder } from "../../runtime/secrets/rds-holder.js";
|
4
4
|
import { getEnvVariable } from "../../../utils/utils.js";
|
@@ -25,10 +25,12 @@ class CountDatabaseCheck extends DatabaseCheck {
|
|
25
25
|
// eslint-disable-next-line @rushstack/no-new-null
|
26
26
|
maxCount) {
|
27
27
|
super(name, sql);
|
28
|
-
if (!sql.toLowerCase().includes("select") ||
|
28
|
+
if (!sql.toLowerCase().includes("select") ||
|
29
|
+
!sql.toLowerCase().includes("count")) {
|
29
30
|
throw new Error("sql must contain select count(*)");
|
30
31
|
}
|
31
|
-
if ((minCount === null || minCount === undefined) &&
|
32
|
+
if ((minCount === null || minCount === undefined) &&
|
33
|
+
(maxCount === null || maxCount === undefined)) {
|
32
34
|
throw new Error("no max or min given");
|
33
35
|
}
|
34
36
|
this.minCount = minCount;
|
@@ -18,8 +18,12 @@ export class UrlCanary extends DigitrafficCanary {
|
|
18
18
|
// the handler code is defined at the actual project using this
|
19
19
|
super(stack, canaryName, role, params, environmentVariables);
|
20
20
|
if (params.inVpc && this.node.defaultChild instanceof CfnCanary) {
|
21
|
-
const subnetIds = stack.vpc === undefined
|
22
|
-
|
21
|
+
const subnetIds = stack.vpc === undefined
|
22
|
+
? []
|
23
|
+
: stack.vpc.privateSubnets.map((subnet) => subnet.subnetId);
|
24
|
+
const securityGroupIds = stack.lambdaDbSg === undefined
|
25
|
+
? []
|
26
|
+
: [stack.lambdaDbSg.securityGroupId];
|
23
27
|
this.node.defaultChild.vpcConfig = {
|
24
28
|
vpcId: stack.vpc?.vpcId,
|
25
29
|
securityGroupIds,
|
@@ -77,7 +77,8 @@ export class UrlChecker {
|
|
77
77
|
return synthetics.executeHttpStep(`Verify 400 for ${url}`, requestOptions, validateStatusCodeAndContentType(400, MediaType.TEXT_PLAIN));
|
78
78
|
}
|
79
79
|
expect403WithoutApiKey(url, mediaType) {
|
80
|
-
if (!this.requestOptions.headers ||
|
80
|
+
if (!this.requestOptions.headers ||
|
81
|
+
!this.requestOptions.headers[API_KEY_HEADER]) {
|
81
82
|
logger.error({
|
82
83
|
method: "UrlChecker.expect403WithoutApiKey",
|
83
84
|
message: "No Api-key defined",
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { CfnDocumentationPart } from "aws-cdk-lib/aws-apigateway";
|
1
|
+
import { CfnDocumentationPart, } from "aws-cdk-lib/aws-apigateway";
|
2
2
|
// Documentation parts are objects that describe an API Gateway API or parts of an API
|
3
3
|
// https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-documenting-api.html
|
4
4
|
/**
|
@@ -25,7 +25,9 @@ export function attachQueueToApiGatewayResource(stack, queue, resource, requestV
|
|
25
25
|
],
|
26
26
|
}));
|
27
27
|
// create an integration between API Gateway and an SQS queue
|
28
|
-
const fifoMessageGroupId = queue.fifo
|
28
|
+
const fifoMessageGroupId = queue.fifo
|
29
|
+
? "&MessageGroupId=AlwaysSameFifoGroup"
|
30
|
+
: "";
|
29
31
|
const sqsIntegration = new AwsIntegration({
|
30
32
|
service: "sqs",
|
31
33
|
integrationHttpMethod: "POST",
|
@@ -5,10 +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 { ComparisonOperator, TreatMissingData } from "aws-cdk-lib/aws-cloudwatch";
|
8
|
+
import { ComparisonOperator, TreatMissingData, } from "aws-cdk-lib/aws-cloudwatch";
|
9
9
|
import { SnsAction } from "aws-cdk-lib/aws-cloudwatch-actions";
|
10
10
|
import { MonitoredFunction } from "./stack/monitoredfunction.js";
|
11
|
-
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
11
|
+
import { PutObjectCommand, S3Client, } from "@aws-sdk/client-s3";
|
12
12
|
import { logger } from "../runtime/dt-logger-default.js";
|
13
13
|
const DLQ_LAMBDA_CODE = `
|
14
14
|
import type { ObjectCannedACL } from "@aws-sdk/client-s3";
|
@@ -52,7 +52,7 @@ export class DigitrafficSqsQueue extends Queue {
|
|
52
52
|
const queueProps = {
|
53
53
|
...props,
|
54
54
|
encryption: QueueEncryption.KMS_MANAGED,
|
55
|
-
queueName
|
55
|
+
queueName,
|
56
56
|
};
|
57
57
|
return new DigitrafficSqsQueue(stack, queueName, queueProps);
|
58
58
|
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Architecture, AssetCode, Runtime } from "aws-cdk-lib/aws-lambda";
|
1
|
+
import { Architecture, AssetCode, Runtime, } from "aws-cdk-lib/aws-lambda";
|
2
2
|
import { Duration } from "aws-cdk-lib";
|
3
3
|
import { RetentionDays } from "aws-cdk-lib/aws-logs";
|
4
4
|
export function databaseFunctionProps(stack, environment, lambdaName, simpleLambdaName, config) {
|
@@ -32,7 +32,9 @@ export function lambdaFunctionProps(_, environment, lambdaName, simpleLambdaName
|
|
32
32
|
};
|
33
33
|
}
|
34
34
|
function getAssetCode(simpleLambdaName, isSingleLambda) {
|
35
|
-
const lambdaPath = isSingleLambda
|
35
|
+
const lambdaPath = isSingleLambda
|
36
|
+
? `dist/lambda/`
|
37
|
+
: `dist/lambda/${simpleLambdaName}`;
|
36
38
|
return new AssetCode(lambdaPath);
|
37
39
|
}
|
38
40
|
export function defaultLambdaConfiguration(config) {
|
@@ -79,10 +79,12 @@ export class MonitoredFunction extends Function {
|
|
79
79
|
* @param props Monitored function properties
|
80
80
|
*/
|
81
81
|
static create(stack, id, functionProps, props) {
|
82
|
-
if (props === MonitoredFunction.DISABLE_ALARMS &&
|
82
|
+
if (props === MonitoredFunction.DISABLE_ALARMS &&
|
83
|
+
stack.configuration.production) {
|
83
84
|
throw new Error(
|
84
85
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
85
|
-
`Function ${functionProps
|
86
|
+
`Function ${functionProps
|
87
|
+
.functionName} has DISABLE_ALARMS. Remove before installing to production or define your own properties!`);
|
86
88
|
}
|
87
89
|
return new MonitoredFunction(stack, id, functionProps, stack.alarmTopic, stack.warningTopic, stack.configuration.production, stack.configuration.trafficType, props);
|
88
90
|
}
|
@@ -114,7 +116,8 @@ export class MonitoredFunction extends Function {
|
|
114
116
|
threshold: alarmProps?.threshold ?? threshold,
|
115
117
|
evaluationPeriods: alarmProps?.evaluationPeriods ?? evaluationPeriods,
|
116
118
|
datapointsToAlarm: alarmProps?.datapointsToAlarm ?? datapointsToAlarm,
|
117
|
-
comparisonOperator: alarmProps?.comparisonOperator ??
|
119
|
+
comparisonOperator: alarmProps?.comparisonOperator ??
|
120
|
+
comparisonOperator,
|
118
121
|
})
|
119
122
|
.addAlarmAction(alarmSnsAction);
|
120
123
|
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { CfnDocumentationPart, Cors, EndpointType, GatewayResponse, MethodLoggingLevel, ResponseType, RestApi, } from "aws-cdk-lib/aws-apigateway";
|
2
|
-
import { AnyPrincipal, Effect, PolicyDocument, PolicyStatement } from "aws-cdk-lib/aws-iam";
|
2
|
+
import { AnyPrincipal, Effect, PolicyDocument, PolicyStatement, } from "aws-cdk-lib/aws-iam";
|
3
3
|
import { getModelReference } from "../../../utils/api-model.js";
|
4
4
|
import { MediaType } from "../../types/mediatypes.js";
|
5
5
|
import { createDefaultUsagePlan, createUsagePlan } from "../usage-plans.js";
|
@@ -39,7 +39,8 @@ export class DigitrafficRestApi extends RestApi {
|
|
39
39
|
};
|
40
40
|
super(stack, apiId, apiConfig);
|
41
41
|
this.apiKeyIds = [];
|
42
|
-
this.enableDocumentation =
|
42
|
+
this.enableDocumentation =
|
43
|
+
stack.configuration.stackFeatures?.enableDocumentation ?? true;
|
43
44
|
add404Support(this, stack);
|
44
45
|
}
|
45
46
|
hostname() {
|
@@ -146,7 +146,8 @@ export class StackCheckingAspect {
|
|
146
146
|
if (name === undefined) {
|
147
147
|
throw Error("Name should not be undefined");
|
148
148
|
}
|
149
|
-
if (type === "querystring" &&
|
149
|
+
if (type === "querystring" &&
|
150
|
+
!StackCheckingAspect.isValidQueryString(name)) {
|
150
151
|
this.addAnnotation(node, name, "Querystring should be in snake_case");
|
151
152
|
}
|
152
153
|
});
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { Aspects, Stack } from "aws-cdk-lib";
|
2
|
-
import { SecurityGroup, Vpc } from "aws-cdk-lib/aws-ec2";
|
2
|
+
import { SecurityGroup, Vpc, } from "aws-cdk-lib/aws-ec2";
|
3
3
|
import { Topic } from "aws-cdk-lib/aws-sns";
|
4
4
|
import { StringParameter } from "aws-cdk-lib/aws-ssm";
|
5
5
|
import { Secret } from "aws-cdk-lib/aws-secretsmanager";
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { Duration, RemovalPolicy, Stack } from "aws-cdk-lib";
|
2
2
|
import {} from "constructs";
|
3
|
-
import { PrivateHostedZone, RecordSet, RecordTarget, RecordType } from "aws-cdk-lib/aws-route53";
|
3
|
+
import { PrivateHostedZone, RecordSet, RecordTarget, RecordType, } from "aws-cdk-lib/aws-route53";
|
4
4
|
import { importVpc } from "../import-util.js";
|
5
5
|
import { getParameterValue } from "../stack/parameters.js";
|
6
6
|
const DEFAULT_RECORD_TTL = Duration.seconds(30);
|
@@ -23,18 +23,21 @@ export interface ClusterConfiguration {
|
|
23
23
|
readonly securityGroupId: string;
|
24
24
|
readonly snapshotIdentifier?: string;
|
25
25
|
readonly dbVersion: AuroraPostgresEngineVersion;
|
26
|
+
readonly storageEncrypted?: boolean;
|
26
27
|
readonly writer: ClusterDbInstanceConfiguration;
|
27
28
|
readonly readers: ClusterDbInstanceConfiguration[];
|
28
29
|
}
|
29
30
|
export interface ClusterImportConfiguration {
|
30
31
|
readonly clusterReadEndpoint: string;
|
31
32
|
readonly clusterWriteEndpoint: string;
|
32
|
-
/** Override clusterIdentifier if clusterWriteEndpoint name doesn't contain
|
33
|
-
* clusterIdentifier before '.cluster' substring.
|
34
|
-
* clusterWriteEndpoint name that is normally formed stackenv-stackenvxxx-xxx.cluster-xxx.region.rds.amazonaws.com
|
35
|
-
* and we can parse clusterIdentifier from it. */
|
36
|
-
readonly clusterIdentifier?: string;
|
37
33
|
}
|
34
|
+
/**
|
35
|
+
* Parse cluster identifier from cluster writer endpoint.
|
36
|
+
* i.e. <i>stackenv-stackenvxxx-xxx.cluster-xxx.region.rds.amazonaws.com</i>
|
37
|
+
* -> <i>stackenv-stackenvxxx-xxx</i>
|
38
|
+
* @param clusterImport
|
39
|
+
*/
|
40
|
+
export declare function parseClusterIdentifier(clusterImport: ClusterImportConfiguration): string;
|
38
41
|
/**
|
39
42
|
* Stack that creates DatabaseCluster.
|
40
43
|
*
|
@@ -4,6 +4,22 @@ import { Secret } from "aws-cdk-lib/aws-secretsmanager";
|
|
4
4
|
import { Duration, RemovalPolicy, Stack } from "aws-cdk-lib/core";
|
5
5
|
import { exportValue, importVpc } from "../import-util.js";
|
6
6
|
import { createParameter } from "../stack/parameters.js";
|
7
|
+
/**
|
8
|
+
* Parse cluster identifier from cluster writer endpoint.
|
9
|
+
* i.e. <i>stackenv-stackenvxxx-xxx.cluster-xxx.region.rds.amazonaws.com</i>
|
10
|
+
* -> <i>stackenv-stackenvxxx-xxx</i>
|
11
|
+
* @param clusterImport
|
12
|
+
*/
|
13
|
+
export function parseClusterIdentifier(clusterImport) {
|
14
|
+
if (clusterImport.clusterWriteEndpoint.includes(".cluster") &&
|
15
|
+
clusterImport.clusterWriteEndpoint.split(".cluster")[0]) {
|
16
|
+
// @ts-ignore this is checked above
|
17
|
+
return clusterImport.clusterWriteEndpoint.split(".cluster")[0];
|
18
|
+
}
|
19
|
+
throw new Error("Could not resolve 'clusterIdentifier' from 'configuration.clusterImport': " +
|
20
|
+
clusterImport.clusterWriteEndpoint +
|
21
|
+
". 'configuration.clusterImport.clusterWriteEndpoint' didn't contain '.cluster'");
|
22
|
+
}
|
7
23
|
/**
|
8
24
|
* Stack that creates DatabaseCluster.
|
9
25
|
*
|
@@ -40,24 +56,7 @@ export class DbStack extends Stack {
|
|
40
56
|
this.clusterIdentifier = cluster.clusterIdentifier;
|
41
57
|
}
|
42
58
|
if (configuration.clusterImport) {
|
43
|
-
|
44
|
-
// from clusterWriteEndpoint name that is normally formed stackenv-stackenvxxx-xxx.cluster-xxx.region.rds.amazonaws.com
|
45
|
-
// and part before .cluster is clusterIdentifier.
|
46
|
-
if (configuration.clusterImport.clusterIdentifier) {
|
47
|
-
this.clusterIdentifier = configuration.clusterImport.clusterIdentifier;
|
48
|
-
}
|
49
|
-
else if (configuration.clusterImport.clusterWriteEndpoint !== undefined &&
|
50
|
-
configuration.clusterImport.clusterWriteEndpoint.split('.cluster')[0] !== undefined &&
|
51
|
-
configuration.clusterImport.clusterWriteEndpoint.split('.cluster')[0] !== configuration.clusterImport.clusterWriteEndpoint) {
|
52
|
-
// @ts-ignore We check that this is defined
|
53
|
-
this.clusterIdentifier = configuration.clusterImport.clusterWriteEndpoint.split('.cluster')[0];
|
54
|
-
}
|
55
|
-
else {
|
56
|
-
throw new Error("Could not resolve 'clusterIdentifier' from 'configuration.clusterImport': " +
|
57
|
-
configuration.clusterImport.clusterWriteEndpoint +
|
58
|
-
" Either 'configuration.clusterImport.clusterReadEndpoint' didn't contain '.cluster' or " +
|
59
|
-
"configuration.clusterImport.clusterIdentifier was not defined to override default value.");
|
60
|
-
}
|
59
|
+
this.clusterIdentifier = parseClusterIdentifier(configuration.clusterImport);
|
61
60
|
createParameter(this, "cluster.reader", configuration.clusterImport.clusterReadEndpoint);
|
62
61
|
createParameter(this, "cluster.writer", configuration.clusterImport.clusterWriteEndpoint);
|
63
62
|
createParameter(this, "cluster.identifier", this.clusterIdentifier);
|
@@ -87,21 +86,21 @@ export class DbStack extends Stack {
|
|
87
86
|
autoMinorVersionUpgrade: true,
|
88
87
|
allowMajorVersionUpgrade: false,
|
89
88
|
enablePerformanceInsights: true,
|
90
|
-
parameterGroup: parameterGroup
|
89
|
+
parameterGroup: parameterGroup,
|
91
90
|
};
|
92
91
|
const writer = ClusterInstance.provisioned("WriterInstance", {
|
93
92
|
...{
|
94
93
|
instanceType: clusterConfiguration.writer.instanceType,
|
95
|
-
isFromLegacyInstanceProps: clusterConfiguration.writer.isFromLegacyInstanceProps
|
94
|
+
isFromLegacyInstanceProps: clusterConfiguration.writer.isFromLegacyInstanceProps,
|
96
95
|
},
|
97
|
-
...defaultDbInstanceProps
|
96
|
+
...defaultDbInstanceProps,
|
98
97
|
});
|
99
98
|
const readers = clusterConfiguration.readers.map((reader, index) => ClusterInstance.provisioned(`ReaderInstance${index}`, {
|
100
99
|
...{
|
101
100
|
instanceType: reader.instanceType,
|
102
101
|
isFromLegacyInstanceProps: reader.isFromLegacyInstanceProps,
|
103
102
|
},
|
104
|
-
...defaultDbInstanceProps
|
103
|
+
...defaultDbInstanceProps,
|
105
104
|
}));
|
106
105
|
return {
|
107
106
|
engine: DatabaseClusterEngine.auroraPostgres({
|
@@ -128,12 +127,15 @@ export class DbStack extends Stack {
|
|
128
127
|
credentials: Credentials.fromPassword(secret.secretValueFromJson("db.superuser").unsafeUnwrap(), secret.secretValueFromJson("db.superuser.password")),
|
129
128
|
parameterGroup,
|
130
129
|
monitoringInterval: Duration.seconds(30),
|
130
|
+
storageEncrypted: clusterConfiguration.storageEncrypted ?? true,
|
131
131
|
};
|
132
132
|
}
|
133
133
|
createAuroraCluster(isc, configuration, clusterConfiguration, parameterGroups) {
|
134
134
|
const instanceName = isc.environmentName + "-db";
|
135
135
|
const securityGroup = SecurityGroup.fromSecurityGroupId(this, "securitygroup", clusterConfiguration.securityGroupId);
|
136
|
-
const vpc = configuration.vpc
|
136
|
+
const vpc = configuration.vpc
|
137
|
+
? configuration.vpc
|
138
|
+
: importVpc(this, isc.environmentName);
|
137
139
|
if (parameterGroups[0] === undefined) {
|
138
140
|
throw Error("ParameterGroups should not be empty");
|
139
141
|
}
|
@@ -1,12 +1,12 @@
|
|
1
|
-
import { APIGatewayClient, GetApiKeyCommand } from "@aws-sdk/client-api-gateway";
|
1
|
+
import { APIGatewayClient, GetApiKeyCommand, } from "@aws-sdk/client-api-gateway";
|
2
2
|
import { FetchHttpHandler } from "@smithy/fetch-http-handler";
|
3
3
|
export async function getApiKeyFromAPIGateway(keyId) {
|
4
4
|
const client = new APIGatewayClient({
|
5
|
-
requestHandler: new FetchHttpHandler()
|
5
|
+
requestHandler: new FetchHttpHandler(),
|
6
6
|
});
|
7
7
|
const command = new GetApiKeyCommand({
|
8
8
|
apiKey: keyId,
|
9
|
-
includeValue: true
|
9
|
+
includeValue: true,
|
10
10
|
});
|
11
11
|
return (await client.send(command));
|
12
12
|
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { MediaType } from "../types/mediatypes.js";
|
2
|
-
import { getDeprecatedDefaultLambdaResponse, RESPONSE_DEFAULT_LAMBDA } from "../infra/api/response.js";
|
2
|
+
import { getDeprecatedDefaultLambdaResponse, RESPONSE_DEFAULT_LAMBDA, } from "../infra/api/response.js";
|
3
3
|
export class DigitrafficIntegrationResponse {
|
4
4
|
static ok(mediaType, sunset) {
|
5
5
|
return this.create("200", mediaType, sunset);
|
@@ -14,7 +14,9 @@ export class DigitrafficIntegrationResponse {
|
|
14
14
|
return {
|
15
15
|
statusCode,
|
16
16
|
responseTemplates: {
|
17
|
-
[mediaType]: sunset
|
17
|
+
[mediaType]: sunset
|
18
|
+
? getDeprecatedDefaultLambdaResponse(sunset)
|
19
|
+
: RESPONSE_DEFAULT_LAMBDA,
|
18
20
|
},
|
19
21
|
};
|
20
22
|
}
|
@@ -19,9 +19,10 @@ export class DtLogger {
|
|
19
19
|
* @param {LoggerConfiguration?} [config] - Accepts configuration options @see {@link LoggerConfiguration}
|
20
20
|
*/
|
21
21
|
constructor(config) {
|
22
|
-
this.lambdaName =
|
23
|
-
|
24
|
-
this.runtime = config?.runTime ??
|
22
|
+
this.lambdaName = config?.lambdaName ??
|
23
|
+
getEnvVariableOrElse("AWS_LAMBDA_FUNCTION_NAME", "unknown lambda name");
|
24
|
+
this.runtime = config?.runTime ??
|
25
|
+
getEnvVariableOrElse("AWS_EXECUTION_ENV", "unknown runtime");
|
25
26
|
this.writeStream = config?.writeStream ?? process.stdout;
|
26
27
|
}
|
27
28
|
/**
|
@@ -43,7 +43,9 @@ export class SecretHolder {
|
|
43
43
|
}
|
44
44
|
async get() {
|
45
45
|
const secret = await this.getSecret();
|
46
|
-
const parsedSecret = this.prefix === DEFAULT_PREFIX
|
46
|
+
const parsedSecret = this.prefix === DEFAULT_PREFIX
|
47
|
+
? secret
|
48
|
+
: this.parseSecret(secret, `${this.prefix}.`);
|
47
49
|
if (this.expectedKeys.length > 0) {
|
48
50
|
checkExpectedSecretKeys(this.expectedKeys, parsedSecret);
|
49
51
|
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { SecretsManager } from "@aws-sdk/client-secrets-manager";
|
2
|
-
import { getEnvVariable, getEnvVariableOrElse, getEnvVariableSafe } from "../../../utils/utils.js";
|
2
|
+
import { getEnvVariable, getEnvVariableOrElse, getEnvVariableSafe, } from "../../../utils/utils.js";
|
3
3
|
import { EnvKeys } from "../environment.js";
|
4
4
|
// SECRET_OVERRIDE_AWS_REGION might not have been set before import of
|
5
5
|
// secret, so we need to lazy initialize SecretsManager
|
package/dist/database/cached.js
CHANGED
package/dist/marine/id_utils.js
CHANGED
@@ -11,10 +11,14 @@ function imoChecksumIsValid(imo) {
|
|
11
11
|
const imoDigit5 = Number(imoStr[4]);
|
12
12
|
const imoDigit6 = Number(imoStr[5]);
|
13
13
|
const checkDigit = Number(imoStr[6]);
|
14
|
-
const checkCalculation = Number(imoDigit1 * 7 + imoDigit2 * 6 + imoDigit3 * 5 + imoDigit4 * 4 +
|
14
|
+
const checkCalculation = Number(imoDigit1 * 7 + imoDigit2 * 6 + imoDigit3 * 5 + imoDigit4 * 4 +
|
15
|
+
imoDigit5 * 3 + imoDigit6 * 2);
|
15
16
|
const checkResult = checkCalculation % 10 === checkDigit;
|
16
17
|
if (!checkResult) {
|
17
|
-
logger.warn({
|
18
|
+
logger.warn({
|
19
|
+
method: "idUtils.imoChecksumIsValid",
|
20
|
+
message: `IMO checksum failed ${imo}`,
|
21
|
+
});
|
18
22
|
}
|
19
23
|
return checkResult;
|
20
24
|
}
|
@@ -12,7 +12,15 @@ export const parameterObject = z.object({
|
|
12
12
|
deprecated: z.boolean().optional(),
|
13
13
|
allowEmptyValue: z.boolean().optional(),
|
14
14
|
style: z
|
15
|
-
.enum([
|
15
|
+
.enum([
|
16
|
+
"matrix",
|
17
|
+
"label",
|
18
|
+
"form",
|
19
|
+
"simple",
|
20
|
+
"spaceDelimited",
|
21
|
+
"pipeDelimited",
|
22
|
+
"deepObject",
|
23
|
+
])
|
16
24
|
.optional(),
|
17
25
|
explode: z.string().optional(),
|
18
26
|
allowReserved: z.boolean().optional(),
|
@@ -76,11 +84,15 @@ export const openapiSchema = z
|
|
76
84
|
})
|
77
85
|
.strict()
|
78
86
|
.optional(),
|
79
|
-
license: z.object({ name: z.string(), url: z.string().optional() })
|
87
|
+
license: z.object({ name: z.string(), url: z.string().optional() })
|
88
|
+
.strict().optional(),
|
80
89
|
version: z.string(),
|
81
90
|
})
|
82
91
|
.strict(),
|
83
|
-
externalDocs: z.object({
|
92
|
+
externalDocs: z.object({
|
93
|
+
description: z.string().optional(),
|
94
|
+
url: z.string(),
|
95
|
+
}).strict().optional(),
|
84
96
|
servers: z
|
85
97
|
.array(z
|
86
98
|
.object({
|
package/dist/utils/logging.js
CHANGED
@@ -42,8 +42,14 @@ export function createExceptionLogger(logger = undefined, includeStack = false)
|
|
42
42
|
* @see {@link createExceptionLogger} for a curried setup
|
43
43
|
*/
|
44
44
|
export function logException(logger, error, includeStack = false) {
|
45
|
-
const message = error instanceof Error
|
46
|
-
|
45
|
+
const message = error instanceof Error
|
46
|
+
? error.message
|
47
|
+
: typeof error === "string"
|
48
|
+
? error
|
49
|
+
: JSON.stringify(error);
|
50
|
+
const stack = error instanceof Error && includeStack
|
51
|
+
? error.stack
|
52
|
+
: undefined;
|
47
53
|
// In case error is AxiosError, log the custom code property.
|
48
54
|
// eslint-disable-next-line dot-notation
|
49
55
|
const customCode = error["code"];
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@digitraffic/common",
|
3
|
-
"version": "2025.
|
3
|
+
"version": "2025.2.4-1",
|
4
4
|
"description": "",
|
5
5
|
"type": "module",
|
6
6
|
"repository": {
|
@@ -111,7 +111,7 @@
|
|
111
111
|
"@aws-sdk/lib-storage": "^3.721.0",
|
112
112
|
"@date-fns/tz": "1.2.0",
|
113
113
|
"@smithy/fetch-http-handler": "4.1.3",
|
114
|
-
"aws-cdk-lib": "^2.
|
114
|
+
"aws-cdk-lib": "^2.177.0",
|
115
115
|
"change-case": "^5.4.4",
|
116
116
|
"constructs": "~10.4.2",
|
117
117
|
"date-fns": "~4.1.0",
|
@@ -126,19 +126,19 @@
|
|
126
126
|
"zod": "^3.24.1"
|
127
127
|
},
|
128
128
|
"devDependencies": {
|
129
|
-
"@aws-sdk/client-api-gateway": "^3.
|
130
|
-
"@aws-sdk/client-s3": "^3.
|
131
|
-
"@aws-sdk/client-secrets-manager": "^3.
|
132
|
-
"@aws-sdk/client-sns": "^3.
|
133
|
-
"@aws-sdk/lib-storage": "^3.
|
129
|
+
"@aws-sdk/client-api-gateway": "^3.738.0",
|
130
|
+
"@aws-sdk/client-s3": "^3.738.0",
|
131
|
+
"@aws-sdk/client-secrets-manager": "^3.738.0",
|
132
|
+
"@aws-sdk/client-sns": "^3.738.0",
|
133
|
+
"@aws-sdk/lib-storage": "^3.738.0",
|
134
134
|
"@date-fns/tz": "1.2.0",
|
135
135
|
"@digitraffic/eslint-config": "^3.1.1",
|
136
136
|
"@jest/globals": "^29.7.0",
|
137
137
|
"@rushstack/eslint-config": "^3.7.1",
|
138
|
-
"@rushstack/heft": "^0.68.
|
139
|
-
"@rushstack/heft-jest-plugin": "^0.14.
|
140
|
-
"@rushstack/heft-lint-plugin": "^0.5.
|
141
|
-
"@rushstack/heft-typescript-plugin": "^0.6.
|
138
|
+
"@rushstack/heft": "^0.68.15",
|
139
|
+
"@rushstack/heft-jest-plugin": "^0.14.5",
|
140
|
+
"@rushstack/heft-lint-plugin": "^0.5.14",
|
141
|
+
"@rushstack/heft-typescript-plugin": "^0.6.8",
|
142
142
|
"@smithy/fetch-http-handler": "4.1.3",
|
143
143
|
"@smithy/types": "^3.7.2",
|
144
144
|
"@types/aws-lambda": "8.10.147",
|
@@ -151,7 +151,7 @@
|
|
151
151
|
"@types/node": "20.17.6",
|
152
152
|
"@typescript-eslint/eslint-plugin": "~7.14.1",
|
153
153
|
"@typescript-eslint/parser": "^7.18.0",
|
154
|
-
"aws-cdk-lib": "^2.
|
154
|
+
"aws-cdk-lib": "^2.177.0",
|
155
155
|
"aws-sdk": "^2.1692.0",
|
156
156
|
"change-case": "^5.4.4",
|
157
157
|
"constructs": "~10.4.2",
|
@@ -164,6 +164,7 @@
|
|
164
164
|
"jest": "^29.7.0",
|
165
165
|
"jest-junit": "^16.0.0",
|
166
166
|
"ky": "^1.7.4",
|
167
|
+
"lefthook": "^1.10.10",
|
167
168
|
"lodash": "^4.17.21",
|
168
169
|
"lodash-es": "~4.17.21",
|
169
170
|
"madge": "^8.0.0",
|
@@ -171,7 +172,6 @@
|
|
171
172
|
"pg-native": "^3.2.0",
|
172
173
|
"pg-promise": "^11.10.2",
|
173
174
|
"pg-query-stream": "4.7.1",
|
174
|
-
"prettier": "^3.4.2",
|
175
175
|
"rimraf": "^6.0.1",
|
176
176
|
"ts-jest": "^29.2.5",
|
177
177
|
"typescript": "~5.6.3",
|
@@ -188,6 +188,8 @@
|
|
188
188
|
"test:jest": "node --trace-warnings --experimental-vm-modules --max-old-space-size=1536 --expose-gc ./node_modules/jest/bin/jest.js --detectOpenHandles --forceExit --coverage --coverageDirectory=output/coverage/jest --logHeapUsage --runInBand --verbose",
|
189
189
|
"test:inspect": "node --inspect-brk --expose-gc ./node_modules/jest/bin/jest.js --detectOpenHandles --forceExit --coverage --coverageDirectory=output/coverage/jest --logHeapUsage --runInBand",
|
190
190
|
"test:watch": "NODE_OPTIONS='--experimental-vm-modules' heft test-watch --max-workers=1",
|
191
|
-
"test:watch:no-coverage": "NODE_OPTIONS='--experimental-vm-modules' heft test-watch --disable-code-coverage --max-workers=1"
|
191
|
+
"test:watch:no-coverage": "NODE_OPTIONS='--experimental-vm-modules' heft test-watch --disable-code-coverage --max-workers=1",
|
192
|
+
"run:format-changed": "lefthook run pre-commit",
|
193
|
+
"run:format": "deno fmt"
|
192
194
|
}
|
193
195
|
}
|