@digitraffic/common 2026.8.31-2 → 2026.9.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/dist/__test__/imports.test.js +304 -82
- package/dist/__test__/infra/acl-builder.test.js +0 -18
- package/dist/__test__/stack/dt-function.test.js +121 -3
- package/dist/aws/infra/acl-builder.d.ts +2 -21
- package/dist/aws/infra/acl-builder.js +6 -54
- package/dist/aws/infra/canaries/database-checker.js +3 -2
- package/dist/aws/infra/stack/dt-function.d.ts +11 -13
- package/dist/aws/infra/stack/dt-function.js +49 -23
- package/dist/aws/infra/stack/stack-checking-aspect.js +2 -1
- package/dist/aws/infra/stack/stack.d.ts +0 -2
- package/dist/aws/infra/stack/stack.js +8 -9
- package/dist/aws/runtime/dt-logger.d.ts +0 -1
- package/dist/aws/runtime/dt-logger.js +0 -5
- package/dist/aws/runtime/environment.d.ts +0 -1
- package/dist/aws/runtime/environment.js +0 -1
- package/dist/aws/runtime/secrets/proxy-holder.d.ts +1 -6
- package/dist/aws/runtime/secrets/proxy-holder.js +4 -6
- package/dist/aws/runtime/secrets/rds-holder.d.ts +1 -4
- package/dist/aws/runtime/secrets/rds-holder.js +4 -6
- package/dist/database/database.d.ts +1 -1
- package/dist/database/database.js +1 -1
- package/package.json +1 -1
- package/dist/__test__/infra/stack/dt-function.test.d.ts +0 -1
- package/dist/__test__/infra/stack/dt-function.test.js +0 -70
- package/dist/__test__/infra/stack/stack.test.d.ts +0 -1
- package/dist/__test__/infra/stack/stack.test.js +0 -33
- package/dist/__test__/utils/lead-time-logging.test.d.ts +0 -1
- package/dist/__test__/utils/lead-time-logging.test.js +0 -153
- package/dist/utils/lead-time-logging.d.ts +0 -10
- package/dist/utils/lead-time-logging.js +0 -33
|
@@ -91,18 +91,7 @@ export class AclBuilder {
|
|
|
91
91
|
});
|
|
92
92
|
return this;
|
|
93
93
|
}
|
|
94
|
-
|
|
95
|
-
* Adds a rate-based WAF rule for either blocking or counting requests.
|
|
96
|
-
*
|
|
97
|
-
* @param name WAF rule name and CloudWatch metric name.
|
|
98
|
-
* @param limit Maximum requests allowed in the WAF evaluation window.
|
|
99
|
-
* @param isHeaderRequired Whether the rule applies when the digitraffic-user header is present.
|
|
100
|
-
* @param isBasedOnIpAndUriPath Whether requests are aggregated by IP address and URI path.
|
|
101
|
-
* @param customResponseBodyKey Custom response body key; when provided, matching requests are blocked instead of counted.
|
|
102
|
-
* @param path Optional URI path pattern that limits which requests are included.
|
|
103
|
-
* @param excludedPath Optional URI path pattern that excludes matching requests from the rule.
|
|
104
|
-
*/
|
|
105
|
-
withThrottleRule(name, limit, isHeaderRequired, isBasedOnIpAndUriPath, customResponseBodyKey, path, excludedPath) {
|
|
94
|
+
withThrottleRule(name, limit, isHeaderRequired, isBasedOnIpAndUriPath, customResponseBodyKey, path) {
|
|
106
95
|
const isBlockRule = !!customResponseBodyKey;
|
|
107
96
|
const rules = isBlockRule ? this._blockRules : this._countRules;
|
|
108
97
|
const action = isBlockRule
|
|
@@ -125,7 +114,7 @@ export class AclBuilder {
|
|
|
125
114
|
metricName: name,
|
|
126
115
|
},
|
|
127
116
|
action,
|
|
128
|
-
statement: createThrottleStatement(limit, isHeaderRequired, isBasedOnIpAndUriPath, path
|
|
117
|
+
statement: createThrottleStatement(limit, isHeaderRequired, isBasedOnIpAndUriPath, path),
|
|
129
118
|
});
|
|
130
119
|
return this;
|
|
131
120
|
}
|
|
@@ -171,21 +160,13 @@ export class AclBuilder {
|
|
|
171
160
|
this._addThrottleResponseBody(customResponseBodyKey, limit);
|
|
172
161
|
return this.withThrottleRule("ThrottleRuleWithAnonymousUserByPath", limit, false, false, customResponseBodyKey, path);
|
|
173
162
|
}
|
|
174
|
-
|
|
175
|
-
* Throttles anonymous requests by IP address and URI path.
|
|
176
|
-
*
|
|
177
|
-
* Existing callers that omit `excludedPath` retain the previous behavior.
|
|
178
|
-
*
|
|
179
|
-
* @param limit Maximum requests allowed for each IP and URI path combination.
|
|
180
|
-
* @param excludedPath Optional URI path pattern excluded from this throttle rule.
|
|
181
|
-
*/
|
|
182
|
-
withThrottleAnonymousUserIpAndUriPath(limit, excludedPath) {
|
|
163
|
+
withThrottleAnonymousUserIpAndUriPath(limit) {
|
|
183
164
|
if (limit === undefined) {
|
|
184
165
|
return this;
|
|
185
166
|
}
|
|
186
167
|
const customResponseBodyKey = `IP_PATH_THROTTLE_ANONYMOUS_USER_${limit}`;
|
|
187
168
|
this._addThrottleResponseBody(customResponseBodyKey, limit);
|
|
188
|
-
return this.withThrottleRule("ThrottleRuleIPQueryWithAnonymousUser", limit, false, true, customResponseBodyKey
|
|
169
|
+
return this.withThrottleRule("ThrottleRuleIPQueryWithAnonymousUser", limit, false, true, customResponseBodyKey);
|
|
189
170
|
}
|
|
190
171
|
withCountDigitrafficUserIp(limit) {
|
|
191
172
|
if (limit === undefined) {
|
|
@@ -308,18 +289,8 @@ function notStatement(statement) {
|
|
|
308
289
|
},
|
|
309
290
|
};
|
|
310
291
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
*
|
|
314
|
-
* @param limit Maximum requests allowed in the WAF evaluation window.
|
|
315
|
-
* @param isHeaderRequired Whether the statement matches requests with the digitraffic-user header.
|
|
316
|
-
* @param isBasedOnIpAndUriPath Whether requests are aggregated by IP address and URI path.
|
|
317
|
-
* @param path Optional URI path pattern that limits which requests are included.
|
|
318
|
-
* @param excludedPath Optional URI path pattern that is explicitly excluded from the statement.
|
|
319
|
-
*/
|
|
320
|
-
function createThrottleStatement(limit, isHeaderRequired, isBasedOnIpAndUriPath, path, excludedPath) {
|
|
321
|
-
// Matches requests with a non-empty digitraffic-user header. For anonymous
|
|
322
|
-
// requests, the inverted statement matches requests without the header.
|
|
292
|
+
function createThrottleStatement(limit, isHeaderRequired, isBasedOnIpAndUriPath, path) {
|
|
293
|
+
// this statement matches empty digitraffic-user -header
|
|
323
294
|
const matchStatement = {
|
|
324
295
|
sizeConstraintStatement: {
|
|
325
296
|
comparisonOperator: isHeaderRequired ? "GT" : "GE",
|
|
@@ -353,25 +324,6 @@ function createThrottleStatement(limit, isHeaderRequired, isBasedOnIpAndUriPath,
|
|
|
353
324
|
},
|
|
354
325
|
};
|
|
355
326
|
}
|
|
356
|
-
if (excludedPath) {
|
|
357
|
-
const excludedPathMatchStatement = {
|
|
358
|
-
regexMatchStatement: {
|
|
359
|
-
fieldToMatch: {
|
|
360
|
-
uriPath: {},
|
|
361
|
-
},
|
|
362
|
-
regexString: excludedPath.source,
|
|
363
|
-
textTransformations: [{ priority: 0, type: "NONE" }],
|
|
364
|
-
},
|
|
365
|
-
};
|
|
366
|
-
scopeDownStatement = {
|
|
367
|
-
andStatement: {
|
|
368
|
-
statements: [
|
|
369
|
-
scopeDownStatement,
|
|
370
|
-
notStatement(excludedPathMatchStatement),
|
|
371
|
-
],
|
|
372
|
-
},
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
327
|
if (isBasedOnIpAndUriPath) {
|
|
376
328
|
return {
|
|
377
329
|
rateBasedStatement: {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import synthetics from "Synthetics";
|
|
2
2
|
import { inDatabaseReadonly } from "../../../database/database.js";
|
|
3
|
+
import { getEnvVariable } from "../../../utils/utils.js";
|
|
3
4
|
import { logger } from "../../runtime/dt-logger-default.js";
|
|
4
5
|
import { ProxyHolder } from "../../runtime/secrets/proxy-holder.js";
|
|
5
6
|
import { RdsHolder } from "../../runtime/secrets/rds-holder.js";
|
|
@@ -65,10 +66,10 @@ export class DatabaseCountChecker {
|
|
|
65
66
|
synthetics.getConfiguration().withFailedCanaryMetric(true);
|
|
66
67
|
}
|
|
67
68
|
static createForProxy() {
|
|
68
|
-
return new DatabaseCountChecker(() => ProxyHolder
|
|
69
|
+
return new DatabaseCountChecker(() => new ProxyHolder(getEnvVariable("SECRET_ID")).setCredentials());
|
|
69
70
|
}
|
|
70
71
|
static createForRds() {
|
|
71
|
-
return new DatabaseCountChecker(() => RdsHolder
|
|
72
|
+
return new DatabaseCountChecker(() => new RdsHolder(getEnvVariable("SECRET_ID")).setCredentials());
|
|
72
73
|
}
|
|
73
74
|
/**
|
|
74
75
|
* Expect that the count is 1
|
|
@@ -28,6 +28,7 @@ export declare class FunctionBuilder {
|
|
|
28
28
|
private readonly securityGroups;
|
|
29
29
|
private readonly policyStatements;
|
|
30
30
|
private readonly allowedActions;
|
|
31
|
+
private _adotTracing;
|
|
31
32
|
private readonly _features;
|
|
32
33
|
constructor(stack: Stack & DigitrafficStackInterface, lambdaName: string);
|
|
33
34
|
/**
|
|
@@ -40,19 +41,9 @@ export declare class FunctionBuilder {
|
|
|
40
41
|
*/
|
|
41
42
|
static plain(stack: Stack & DigitrafficStackInterface, lambdaName: string): FunctionBuilder;
|
|
42
43
|
/**
|
|
43
|
-
*
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
* The handler module name is derived from `basename(path)` (e.g. `"locations.handler"`).
|
|
47
|
-
* Defaults to `lambdaName`.
|
|
48
|
-
* Example: `"dump/locations"` → asset dir `dist/lambda/dump/locations/`, handler `locations.handler`.
|
|
49
|
-
* @param exclude - optional glob patterns to exclude from the asset bundle.
|
|
50
|
-
* Patterns are relative to the asset root (`dist/lambda/${path}/`) — do NOT use a leading slash.
|
|
51
|
-
* Use `**` to match files recursively.
|
|
52
|
-
* Example: `["datex2-36", "datex2-37", "statuses"]` excludes sibling lambda subdirectories
|
|
53
|
-
* that would otherwise be bundled into this lambda's deployment package.
|
|
54
|
-
*/
|
|
55
|
-
withAssetCode(path?: string, exclude?: string[]): this;
|
|
44
|
+
* Use AssetCode from given path(dist/lambda/${path}). Default path is lambdaName. Also calls withHandler with the same value.
|
|
45
|
+
*/
|
|
46
|
+
withAssetCode(path?: string): this;
|
|
56
47
|
withCode(code: Code): this;
|
|
57
48
|
/**
|
|
58
49
|
* Use given handler(${name}.handler) to run the lambda. Default value is lambdaName.
|
|
@@ -86,6 +77,12 @@ export declare class FunctionBuilder {
|
|
|
86
77
|
* Add Lambda layers.
|
|
87
78
|
*/
|
|
88
79
|
withLayers(...layers: ILayerVersion[]): this;
|
|
80
|
+
/**
|
|
81
|
+
* Enable ADOT (AWS Distro for OpenTelemetry) tracing for this Lambda.
|
|
82
|
+
* Adds the ADOT Lambda layer, enables X-Ray active tracing,
|
|
83
|
+
* sets required environment variables, and attaches IAM permissions.
|
|
84
|
+
*/
|
|
85
|
+
withAdotTracing(): this;
|
|
89
86
|
/**
|
|
90
87
|
* Set architecture for the lambda. Default is Architecture.ARM_64.
|
|
91
88
|
*/
|
|
@@ -124,6 +121,7 @@ export declare class FunctionBuilder {
|
|
|
124
121
|
build(): AwsFunction;
|
|
125
122
|
private attachPolicies;
|
|
126
123
|
private validateNoWildcardActions;
|
|
124
|
+
private getLayers;
|
|
127
125
|
private getEnvironment;
|
|
128
126
|
private createAlarms;
|
|
129
127
|
private createAlarm;
|
|
@@ -2,8 +2,9 @@ import { basename } from "node:path";
|
|
|
2
2
|
import { Duration } from "aws-cdk-lib";
|
|
3
3
|
import { SnsAction } from "aws-cdk-lib/aws-cloudwatch-actions";
|
|
4
4
|
import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam";
|
|
5
|
-
import { ApplicationLogLevel, Architecture, AssetCode, Function as AwsFunction, Code, LoggingFormat, Runtime, SystemLogLevel, } from "aws-cdk-lib/aws-lambda";
|
|
6
|
-
import {
|
|
5
|
+
import { ApplicationLogLevel, Architecture, AssetCode, Function as AwsFunction, Code, LayerVersion, LoggingFormat, Runtime, SystemLogLevel, Tracing, } from "aws-cdk-lib/aws-lambda";
|
|
6
|
+
import { ManagedPolicy } from "aws-cdk-lib/aws-iam";
|
|
7
|
+
import { camelCase, startCase } from "es-toolkit";
|
|
7
8
|
import { DatabaseEnvironmentKeys } from "../../../database/database.js";
|
|
8
9
|
import { EnvKeys } from "../../runtime/environment.js";
|
|
9
10
|
import { DtFunctionAlarms } from "./dt-function-alarms.js";
|
|
@@ -29,6 +30,7 @@ export class FunctionBuilder {
|
|
|
29
30
|
securityGroups = [];
|
|
30
31
|
policyStatements = [];
|
|
31
32
|
allowedActions = [];
|
|
33
|
+
_adotTracing = false;
|
|
32
34
|
_features = {
|
|
33
35
|
databaseAccess: true,
|
|
34
36
|
secretAccess: true,
|
|
@@ -39,7 +41,13 @@ export class FunctionBuilder {
|
|
|
39
41
|
this.functionName = `${stack.configuration.shortName}-${startCase(camelCase(lambdaName)).replace(/\s/g, "")}`;
|
|
40
42
|
this.environment = {};
|
|
41
43
|
this.vpc = stack.vpc;
|
|
44
|
+
// this calls withHandler as well but with full path
|
|
42
45
|
this.withAssetCode(lambdaName);
|
|
46
|
+
// Remove path from lambda to get module name.
|
|
47
|
+
// e.g. for lambdaName "api/charging-network/v1/operators",
|
|
48
|
+
// moduleName becomes "operators" and handler becomes "operators.handler"
|
|
49
|
+
const moduleName = basename(lambdaName);
|
|
50
|
+
this.withHandler(moduleName);
|
|
43
51
|
}
|
|
44
52
|
/**
|
|
45
53
|
* Creates a new builder with defaults, using the lambdaName as a source for the lambda implementation (dist/lambdaName/lambdaName.js).
|
|
@@ -57,22 +65,11 @@ export class FunctionBuilder {
|
|
|
57
65
|
.withoutSecretAccess();
|
|
58
66
|
}
|
|
59
67
|
/**
|
|
60
|
-
*
|
|
61
|
-
* the handler to `basename(path).handler`.
|
|
62
|
-
* @param path - subdirectory under `dist/lambda/` that contains the compiled Lambda code.
|
|
63
|
-
* The handler module name is derived from `basename(path)` (e.g. `"locations.handler"`).
|
|
64
|
-
* Defaults to `lambdaName`.
|
|
65
|
-
* Example: `"dump/locations"` → asset dir `dist/lambda/dump/locations/`, handler `locations.handler`.
|
|
66
|
-
* @param exclude - optional glob patterns to exclude from the asset bundle.
|
|
67
|
-
* Patterns are relative to the asset root (`dist/lambda/${path}/`) — do NOT use a leading slash.
|
|
68
|
-
* Use `**` to match files recursively.
|
|
69
|
-
* Example: `["datex2-36", "datex2-37", "statuses"]` excludes sibling lambda subdirectories
|
|
70
|
-
* that would otherwise be bundled into this lambda's deployment package.
|
|
68
|
+
* Use AssetCode from given path(dist/lambda/${path}). Default path is lambdaName. Also calls withHandler with the same value.
|
|
71
69
|
*/
|
|
72
|
-
withAssetCode(path = this._name
|
|
73
|
-
this.code = new AssetCode(`dist/lambda/${path}
|
|
74
|
-
|
|
75
|
-
this.withHandler(moduleName);
|
|
70
|
+
withAssetCode(path = this._name) {
|
|
71
|
+
this.code = new AssetCode(`dist/lambda/${path}`);
|
|
72
|
+
this.withHandler(path);
|
|
76
73
|
return this;
|
|
77
74
|
}
|
|
78
75
|
withCode(code) {
|
|
@@ -135,6 +132,15 @@ export class FunctionBuilder {
|
|
|
135
132
|
this.layers.push(...layers);
|
|
136
133
|
return this;
|
|
137
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Enable ADOT (AWS Distro for OpenTelemetry) tracing for this Lambda.
|
|
137
|
+
* Adds the ADOT Lambda layer, enables X-Ray active tracing,
|
|
138
|
+
* sets required environment variables, and attaches IAM permissions.
|
|
139
|
+
*/
|
|
140
|
+
withAdotTracing() {
|
|
141
|
+
this._adotTracing = true;
|
|
142
|
+
return this;
|
|
143
|
+
}
|
|
138
144
|
/**
|
|
139
145
|
* Set architecture for the lambda. Default is Architecture.ARM_64.
|
|
140
146
|
*/
|
|
@@ -213,6 +219,7 @@ export class FunctionBuilder {
|
|
|
213
219
|
}
|
|
214
220
|
: undefined;
|
|
215
221
|
const securityGroups = this.getSecurityGroups();
|
|
222
|
+
const layers = this.getLayers();
|
|
216
223
|
const createdFunction = new AwsFunction(this._stack, this.functionName, {
|
|
217
224
|
vpc: this.vpc,
|
|
218
225
|
vpcSubnets,
|
|
@@ -232,11 +239,15 @@ export class FunctionBuilder {
|
|
|
232
239
|
securityGroups,
|
|
233
240
|
environment: this.getEnvironment(),
|
|
234
241
|
description: this.description,
|
|
235
|
-
layers:
|
|
242
|
+
layers: layers.length > 0 ? layers : undefined,
|
|
243
|
+
tracing: this._adotTracing ? Tracing.ACTIVE : undefined,
|
|
236
244
|
});
|
|
237
245
|
if (this._features.secretAccess) {
|
|
238
246
|
this._stack.grantSecret(createdFunction);
|
|
239
247
|
}
|
|
248
|
+
if (this._adotTracing) {
|
|
249
|
+
createdFunction.role?.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName("CloudWatchLambdaApplicationSignalsExecutionRolePolicy"));
|
|
250
|
+
}
|
|
240
251
|
this.attachPolicies(createdFunction);
|
|
241
252
|
this.createAlarms(createdFunction);
|
|
242
253
|
return createdFunction;
|
|
@@ -259,22 +270,37 @@ export class FunctionBuilder {
|
|
|
259
270
|
throw new Error(`Lambda ${this.functionName} cannot use wildcard action "*" in withAllowedActions. Please specify explicit actions.`);
|
|
260
271
|
}
|
|
261
272
|
}
|
|
273
|
+
getLayers() {
|
|
274
|
+
const layers = [...this.layers];
|
|
275
|
+
if (this._adotTracing) {
|
|
276
|
+
const layerName = this.runtime.name.startsWith("python")
|
|
277
|
+
? "AWSOpenTelemetryDistroPython"
|
|
278
|
+
: "AWSOpenTelemetryDistroJs";
|
|
279
|
+
layers.push(LayerVersion.fromLayerVersionArn(this._stack, `${this.functionName}-AdotLayer`, `arn:aws:lambda:eu-west-1:615299751070:layer:${layerName}:1`));
|
|
280
|
+
}
|
|
281
|
+
return layers;
|
|
282
|
+
}
|
|
262
283
|
getEnvironment() {
|
|
263
|
-
let environment = {
|
|
264
|
-
|
|
265
|
-
};
|
|
266
|
-
if (this._features.secretAccess && this._stack.configuration.secretId) {
|
|
284
|
+
let environment = {};
|
|
285
|
+
if (this._features.secretAccess) {
|
|
267
286
|
environment = {
|
|
268
287
|
...environment,
|
|
269
288
|
[EnvKeys.SECRET_ID]: this._stack.configuration.secretId,
|
|
270
289
|
};
|
|
271
290
|
}
|
|
272
|
-
if (this._features.databaseAccess
|
|
291
|
+
if (this._features.databaseAccess) {
|
|
273
292
|
environment = {
|
|
274
293
|
...environment,
|
|
275
294
|
[DatabaseEnvironmentKeys.DB_APPLICATION]: this._stack.configuration.shortName,
|
|
276
295
|
};
|
|
277
296
|
}
|
|
297
|
+
if (this._adotTracing) {
|
|
298
|
+
environment = {
|
|
299
|
+
...environment,
|
|
300
|
+
AWS_LAMBDA_EXEC_WRAPPER: "/opt/otel-instrument",
|
|
301
|
+
OTEL_AWS_APPLICATION_SIGNALS_ENABLED: "false",
|
|
302
|
+
};
|
|
303
|
+
}
|
|
278
304
|
return { ...environment, ...this.environment };
|
|
279
305
|
}
|
|
280
306
|
createAlarms(lambda) {
|
|
@@ -4,7 +4,8 @@ import { CfnFunction, Runtime } from "aws-cdk-lib/aws-lambda";
|
|
|
4
4
|
import { LogRetention } from "aws-cdk-lib/aws-logs";
|
|
5
5
|
import { CfnBucket } from "aws-cdk-lib/aws-s3";
|
|
6
6
|
import { CfnQueue } from "aws-cdk-lib/aws-sqs";
|
|
7
|
-
import { kebabCase
|
|
7
|
+
import { kebabCase } from "change-case";
|
|
8
|
+
import { snakeCase } from "es-toolkit";
|
|
8
9
|
import { DigitrafficStack, SOLUTION_KEY } from "./stack.js";
|
|
9
10
|
const MAX_CONCURRENCY_LIMIT = 100;
|
|
10
11
|
const ALLOWED_RUNTIMES = [
|
|
@@ -20,8 +20,6 @@ export interface StackConfiguration {
|
|
|
20
20
|
readonly lambdaDbSgId?: string;
|
|
21
21
|
readonly privateSubnetIds?: string[];
|
|
22
22
|
readonly availabilityZones?: string[];
|
|
23
|
-
readonly dbWriterHost?: string;
|
|
24
|
-
readonly dbReaderHost?: string;
|
|
25
23
|
readonly trafficType: TrafficType;
|
|
26
24
|
readonly production: boolean;
|
|
27
25
|
readonly stackProps: StackProps;
|
|
@@ -3,8 +3,6 @@ import { SecurityGroup, Vpc } from "aws-cdk-lib/aws-ec2";
|
|
|
3
3
|
import { Secret } from "aws-cdk-lib/aws-secretsmanager";
|
|
4
4
|
import { Topic } from "aws-cdk-lib/aws-sns";
|
|
5
5
|
import { StringParameter } from "aws-cdk-lib/aws-ssm";
|
|
6
|
-
import { kebabCase } from "es-toolkit";
|
|
7
|
-
import { EnvKeys } from "../../runtime/environment.js";
|
|
8
6
|
import { StackCheckingAspect } from "./stack-checking-aspect.js";
|
|
9
7
|
const SSM_ROOT = "/digitraffic";
|
|
10
8
|
export const SOLUTION_KEY = "Solution";
|
|
@@ -48,13 +46,14 @@ export class DigitrafficStack extends Stack {
|
|
|
48
46
|
return this.createDefaultLambdaEnvironment(this.configuration.shortName);
|
|
49
47
|
}
|
|
50
48
|
createDefaultLambdaEnvironment(dbApplication) {
|
|
51
|
-
return
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
49
|
+
return this.configuration.secretId
|
|
50
|
+
? {
|
|
51
|
+
SECRET_ID: this.configuration.secretId,
|
|
52
|
+
DB_APPLICATION: dbApplication,
|
|
53
|
+
}
|
|
54
|
+
: {
|
|
55
|
+
DB_APPLICATION: dbApplication,
|
|
56
|
+
};
|
|
58
57
|
}
|
|
59
58
|
getSecret() {
|
|
60
59
|
if (this.secret === undefined) {
|
|
@@ -61,7 +61,6 @@ export interface LoggableType extends CustomParams {
|
|
|
61
61
|
* * the actual message (as json or as string)
|
|
62
62
|
*/
|
|
63
63
|
export declare class DtLogger {
|
|
64
|
-
readonly appName?: string;
|
|
65
64
|
readonly lambdaName?: string;
|
|
66
65
|
readonly runtime?: string;
|
|
67
66
|
readonly writeStream: Writable;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { lowerFirst, mapKeys } from "es-toolkit";
|
|
2
2
|
import { getEnvVariableOrElse } from "../../utils/utils.js";
|
|
3
|
-
import { EnvKeys } from "./environment.js";
|
|
4
3
|
/**
|
|
5
4
|
* Helper class for json-logging.
|
|
6
5
|
*
|
|
@@ -11,7 +10,6 @@ import { EnvKeys } from "./environment.js";
|
|
|
11
10
|
* * the actual message (as json or as string)
|
|
12
11
|
*/
|
|
13
12
|
export class DtLogger {
|
|
14
|
-
appName;
|
|
15
13
|
lambdaName;
|
|
16
14
|
runtime;
|
|
17
15
|
writeStream;
|
|
@@ -21,7 +19,6 @@ export class DtLogger {
|
|
|
21
19
|
* @param {LoggerConfiguration?} [config] - Accepts configuration options @see {@link LoggerConfiguration}
|
|
22
20
|
*/
|
|
23
21
|
constructor(config) {
|
|
24
|
-
this.appName = getEnvVariableOrElse(EnvKeys.APP_NAME, undefined);
|
|
25
22
|
this.lambdaName =
|
|
26
23
|
config?.lambdaName ??
|
|
27
24
|
getEnvVariableOrElse("AWS_LAMBDA_FUNCTION_NAME", "unknown lambda name");
|
|
@@ -41,7 +38,6 @@ export class DtLogger {
|
|
|
41
38
|
const logMessage = {
|
|
42
39
|
message,
|
|
43
40
|
level: "DEBUG",
|
|
44
|
-
app: this.appName,
|
|
45
41
|
lambdaName: this.lambdaName,
|
|
46
42
|
runtime: this.runtime,
|
|
47
43
|
};
|
|
@@ -116,7 +112,6 @@ export class DtLogger {
|
|
|
116
112
|
...messageFields,
|
|
117
113
|
error,
|
|
118
114
|
stack,
|
|
119
|
-
app: this.appName,
|
|
120
115
|
lambdaName: this.lambdaName,
|
|
121
116
|
runtime: this.runtime,
|
|
122
117
|
};
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import type { RdsProxySecret } from "./dbsecret.js";
|
|
2
|
-
import { SecretHolder } from "./secret-holder.js";
|
|
3
1
|
/**
|
|
4
2
|
* Holds credentials for RDS Proxy access.
|
|
5
3
|
*/
|
|
6
4
|
export declare class ProxyHolder {
|
|
7
5
|
private readonly secretHolder;
|
|
8
|
-
|
|
9
|
-
/** Creates a new instance of ProxyHolder from given SecretHolder. */
|
|
10
|
-
static create(secretHolder: SecretHolder<RdsProxySecret>): ProxyHolder;
|
|
11
|
-
/** Creates a new instance of ProxyHolder with a new default SecretHolder(using env variable SECRET_ID). */
|
|
6
|
+
constructor(secretId: string);
|
|
12
7
|
static create(): ProxyHolder;
|
|
13
8
|
setCredentials(): Promise<void>;
|
|
14
9
|
}
|
|
@@ -8,13 +8,11 @@ const RDS_PROXY_SECRET_KEYS = Object.values(RdsProxySecretKey);
|
|
|
8
8
|
*/
|
|
9
9
|
export class ProxyHolder {
|
|
10
10
|
secretHolder;
|
|
11
|
-
constructor(
|
|
12
|
-
this.secretHolder =
|
|
11
|
+
constructor(secretId) {
|
|
12
|
+
this.secretHolder = new SecretHolder(secretId, "", RDS_PROXY_SECRET_KEYS);
|
|
13
13
|
}
|
|
14
|
-
static create(
|
|
15
|
-
|
|
16
|
-
new SecretHolder(getEnvVariable("SECRET_ID"), "", RDS_PROXY_SECRET_KEYS);
|
|
17
|
-
return new ProxyHolder(holder);
|
|
14
|
+
static create() {
|
|
15
|
+
return new ProxyHolder(getEnvVariable("SECRET_ID"));
|
|
18
16
|
}
|
|
19
17
|
async setCredentials() {
|
|
20
18
|
const secret = await this.secretHolder.get();
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import type { RdsSecret } from "./dbsecret.js";
|
|
2
|
-
import { SecretHolder } from "./secret-holder.js";
|
|
3
1
|
/**
|
|
4
2
|
* Holds credentials for RDS access.
|
|
5
3
|
*/
|
|
6
4
|
export declare class RdsHolder {
|
|
7
5
|
private readonly secretHolder;
|
|
8
|
-
|
|
9
|
-
static create(rdsHolder: SecretHolder<RdsSecret>): RdsHolder;
|
|
6
|
+
constructor(secretId: string);
|
|
10
7
|
static create(): RdsHolder;
|
|
11
8
|
setCredentials(): Promise<void>;
|
|
12
9
|
}
|
|
@@ -8,13 +8,11 @@ const RDS_SECRET_KEYS = Object.values(RdsSecretKey);
|
|
|
8
8
|
*/
|
|
9
9
|
export class RdsHolder {
|
|
10
10
|
secretHolder;
|
|
11
|
-
constructor(
|
|
12
|
-
this.secretHolder =
|
|
11
|
+
constructor(secretId) {
|
|
12
|
+
this.secretHolder = new SecretHolder(secretId, "", RDS_SECRET_KEYS);
|
|
13
13
|
}
|
|
14
|
-
static create(
|
|
15
|
-
|
|
16
|
-
new SecretHolder(getEnvVariable("SECRET_ID"), "", RDS_SECRET_KEYS);
|
|
17
|
-
return new RdsHolder(holder);
|
|
14
|
+
static create() {
|
|
15
|
+
return new RdsHolder(getEnvVariable("SECRET_ID"));
|
|
18
16
|
}
|
|
19
17
|
async setCredentials() {
|
|
20
18
|
const secret = await this.secretHolder.get();
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { App } from "aws-cdk-lib";
|
|
2
|
-
import { AssetCode } from "aws-cdk-lib/aws-lambda";
|
|
3
|
-
import { describe, expect, test } from "vitest";
|
|
4
|
-
import { FunctionBuilder } from "../../../aws/infra/stack/dt-function.js";
|
|
5
|
-
import { DigitrafficStack } from "../../../aws/infra/stack/stack.js";
|
|
6
|
-
function createStack() {
|
|
7
|
-
const app = new App();
|
|
8
|
-
return new DigitrafficStack(app, "TestStack", {
|
|
9
|
-
shortName: "TEST",
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
function getCode(builder) {
|
|
13
|
-
return builder.code;
|
|
14
|
-
}
|
|
15
|
-
function getHandler(builder) {
|
|
16
|
-
return builder.handler;
|
|
17
|
-
}
|
|
18
|
-
describe("FunctionBuilder.withAssetCode", () => {
|
|
19
|
-
test("uses lambdaName as default path", () => {
|
|
20
|
-
const stack = createStack();
|
|
21
|
-
const builder = FunctionBuilder.create(stack, "feature/subfeature/lambda-implementation");
|
|
22
|
-
const code = getCode(builder);
|
|
23
|
-
expect(code).toBeInstanceOf(AssetCode);
|
|
24
|
-
expect(code.path).toBe("dist/lambda/feature/subfeature/lambda-implementation");
|
|
25
|
-
});
|
|
26
|
-
test("uses custom path when provided", () => {
|
|
27
|
-
const stack = createStack();
|
|
28
|
-
const builder = FunctionBuilder.create(stack, "feature/subfeature/lambda-implementation").withAssetCode("feature/subfeature/lambda-implementation");
|
|
29
|
-
const code = getCode(builder);
|
|
30
|
-
expect(code.path).toBe("dist/lambda/feature/subfeature/lambda-implementation");
|
|
31
|
-
});
|
|
32
|
-
test("returns this for chaining when called without exclude", () => {
|
|
33
|
-
const stack = createStack();
|
|
34
|
-
const builder = FunctionBuilder.create(stack, "feature/subfeature/lambda-implementation");
|
|
35
|
-
expect(builder.withAssetCode("feature/subfeature/lambda-implementation")).toBe(builder);
|
|
36
|
-
});
|
|
37
|
-
test("accepts exclude list and uses correct path", () => {
|
|
38
|
-
const stack = createStack();
|
|
39
|
-
const builder = FunctionBuilder.create(stack, "feature/shared").withAssetCode("feature/shared", [
|
|
40
|
-
"handler-a/**",
|
|
41
|
-
"handler-b/**",
|
|
42
|
-
"handler-c/**",
|
|
43
|
-
]);
|
|
44
|
-
const code = getCode(builder);
|
|
45
|
-
expect(code).toBeInstanceOf(AssetCode);
|
|
46
|
-
expect(code.path).toBe("dist/lambda/feature/shared");
|
|
47
|
-
});
|
|
48
|
-
test("returns this for chaining when called with exclude", () => {
|
|
49
|
-
const stack = createStack();
|
|
50
|
-
const builder = FunctionBuilder.create(stack, "feature/shared");
|
|
51
|
-
expect(builder.withAssetCode("feature/shared", ["handler-a/**"])).toBe(builder);
|
|
52
|
-
});
|
|
53
|
-
test("exclude is optional — undefined behaves same as no exclude", () => {
|
|
54
|
-
const stack = createStack();
|
|
55
|
-
const builderWithout = FunctionBuilder.create(stack, "test/lambda");
|
|
56
|
-
const builderWithUndefined = FunctionBuilder.create(stack, "test/lambda2").withAssetCode("test/lambda2", undefined);
|
|
57
|
-
expect(getCode(builderWithout).path).toBe("dist/lambda/test/lambda");
|
|
58
|
-
expect(getCode(builderWithUndefined).path).toBe("dist/lambda/test/lambda2");
|
|
59
|
-
});
|
|
60
|
-
test("handler uses basename(path), not the full path", () => {
|
|
61
|
-
const stack = createStack();
|
|
62
|
-
// Without explicit withAssetCode — constructor default
|
|
63
|
-
const builderDefault = FunctionBuilder.create(stack, "feature/shared");
|
|
64
|
-
expect(getHandler(builderDefault)).toBe("shared.handler");
|
|
65
|
-
// With explicit withAssetCode (e.g. to pass exclude) — must also use basename
|
|
66
|
-
const builderExplicit = FunctionBuilder.create(stack, "feature/shared2").withAssetCode("feature/shared2", ["handler-a/**"]);
|
|
67
|
-
expect(getHandler(builderExplicit)).toBe("shared2.handler");
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
//# sourceMappingURL=dt-function.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { App } from "aws-cdk-lib";
|
|
2
|
-
import { describe, expect, test } from "vitest";
|
|
3
|
-
import { DigitrafficStack } from "../../../aws/infra/stack/stack.js";
|
|
4
|
-
import { EnvKeys } from "../../../aws/runtime/environment.js";
|
|
5
|
-
import { TrafficType } from "../../../types/traffictype.js";
|
|
6
|
-
function createStack(secretId) {
|
|
7
|
-
const configuration = {
|
|
8
|
-
shortName: "VS",
|
|
9
|
-
trafficType: TrafficType.ROAD,
|
|
10
|
-
secretId,
|
|
11
|
-
alarmTopicArn: "",
|
|
12
|
-
warningTopicArn: "",
|
|
13
|
-
production: false,
|
|
14
|
-
stackProps: {},
|
|
15
|
-
};
|
|
16
|
-
return new DigitrafficStack(new App(), "TestStack", configuration);
|
|
17
|
-
}
|
|
18
|
-
describe("DigitrafficStack.createDefaultLambdaEnvironment", () => {
|
|
19
|
-
test("includes the application name without a secret", () => {
|
|
20
|
-
expect(createStack().createDefaultLambdaEnvironment("variable-signs")).toEqual({
|
|
21
|
-
[EnvKeys.APP_NAME]: "road-vs",
|
|
22
|
-
DB_APPLICATION: "variable-signs",
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
test("preserves the secret configuration", () => {
|
|
26
|
-
expect(createStack("test-secret").createDefaultLambdaEnvironment("variable-signs")).toEqual({
|
|
27
|
-
[EnvKeys.APP_NAME]: "road-vs",
|
|
28
|
-
DB_APPLICATION: "variable-signs",
|
|
29
|
-
SECRET_ID: "test-secret",
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
//# sourceMappingURL=stack.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|