@digitraffic/common 2026.9.4-1 → 2026.9.4-2
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 +82 -304
- package/dist/__test__/infra/acl-builder.test.js +18 -0
- package/dist/__test__/infra/stack/dt-function.test.d.ts +1 -0
- package/dist/__test__/infra/stack/dt-function.test.js +70 -0
- package/dist/__test__/infra/stack/stack.test.d.ts +1 -0
- package/dist/__test__/infra/stack/stack.test.js +33 -0
- package/dist/__test__/stack/dt-function.test.js +3 -121
- package/dist/__test__/utils/lead-time-logging.test.d.ts +1 -0
- package/dist/__test__/utils/lead-time-logging.test.js +153 -0
- package/dist/aws/infra/acl-builder.d.ts +21 -2
- package/dist/aws/infra/acl-builder.js +54 -6
- package/dist/aws/infra/canaries/database-checker.js +2 -3
- package/dist/aws/infra/stack/dt-function.d.ts +19 -12
- package/dist/aws/infra/stack/dt-function.js +31 -48
- package/dist/aws/infra/stack/stack-checking-aspect.js +1 -2
- package/dist/aws/infra/stack/stack.d.ts +2 -0
- package/dist/aws/infra/stack/stack.js +9 -8
- package/dist/aws/runtime/dt-logger.d.ts +1 -0
- package/dist/aws/runtime/dt-logger.js +5 -0
- package/dist/aws/runtime/environment.d.ts +1 -0
- package/dist/aws/runtime/environment.js +1 -0
- package/dist/aws/runtime/secrets/proxy-holder.d.ts +6 -1
- package/dist/aws/runtime/secrets/proxy-holder.js +6 -4
- package/dist/aws/runtime/secrets/rds-holder.d.ts +4 -1
- package/dist/aws/runtime/secrets/rds-holder.js +6 -4
- package/dist/database/database.d.ts +1 -1
- package/dist/database/database.js +1 -1
- package/dist/utils/lead-time-logging.d.ts +10 -0
- package/dist/utils/lead-time-logging.js +33 -0
- package/package.json +1 -1
|
@@ -61,6 +61,7 @@ describe("FunctionBuilder test", () => {
|
|
|
61
61
|
template.hasResourceProperties("AWS::Lambda::Function", {
|
|
62
62
|
Environment: {
|
|
63
63
|
Variables: {
|
|
64
|
+
[EnvKeys.APP_NAME]: "road-test",
|
|
64
65
|
[EnvKeys.SECRET_ID]: "testSecret",
|
|
65
66
|
[TEST_ENV_VAR]: TEST_ENV_VALUE,
|
|
66
67
|
},
|
|
@@ -139,11 +140,9 @@ describe("FunctionBuilder test", () => {
|
|
|
139
140
|
});
|
|
140
141
|
});
|
|
141
142
|
test("Lambda handler module is resolved from path last element", () => {
|
|
142
|
-
const template = createTemplate((_builder) => { }, false, "api/
|
|
143
|
-
// const lambdas = template.findResources("AWS::Lambda::Function");
|
|
144
|
-
// console.debug(JSON.stringify(lambdas, null, 2));
|
|
143
|
+
const template = createTemplate((_builder) => { }, false, "api/feature/v1/get-items");
|
|
145
144
|
template.hasResourceProperties("AWS::Lambda::Function", {
|
|
146
|
-
Handler: "
|
|
145
|
+
Handler: "get-items.handler",
|
|
147
146
|
});
|
|
148
147
|
});
|
|
149
148
|
test("Lambda handler module is same as lambda name", () => {
|
|
@@ -336,122 +335,5 @@ describe("FunctionBuilder test", () => {
|
|
|
336
335
|
.build();
|
|
337
336
|
}).toThrow('Lambda test-Test cannot use wildcard action "*" in withAllowedActions');
|
|
338
337
|
});
|
|
339
|
-
describe("withAdotTracing", () => {
|
|
340
|
-
test("given withAdotTracing is called / when lambda is built / then AWS_LAMBDA_EXEC_WRAPPER is set", () => {
|
|
341
|
-
// given/when
|
|
342
|
-
const template = createTemplate((builder) => {
|
|
343
|
-
builder.withAdotTracing();
|
|
344
|
-
});
|
|
345
|
-
// then
|
|
346
|
-
expectEnvironmentValue(template, "AWS_LAMBDA_EXEC_WRAPPER", "/opt/otel-instrument");
|
|
347
|
-
});
|
|
348
|
-
test("given withAdotTracing is called / when lambda is built / then OTEL_AWS_APPLICATION_SIGNALS_ENABLED is false", () => {
|
|
349
|
-
// given/when
|
|
350
|
-
const template = createTemplate((builder) => {
|
|
351
|
-
builder.withAdotTracing();
|
|
352
|
-
});
|
|
353
|
-
// then
|
|
354
|
-
expectEnvironmentValue(template, "OTEL_AWS_APPLICATION_SIGNALS_ENABLED", "false");
|
|
355
|
-
});
|
|
356
|
-
test("given withAdotTracing is called / when lambda is built / then X-Ray active tracing is enabled", () => {
|
|
357
|
-
// given/when
|
|
358
|
-
const template = createTemplate((builder) => {
|
|
359
|
-
builder.withAdotTracing();
|
|
360
|
-
});
|
|
361
|
-
// then
|
|
362
|
-
template.hasResourceProperties("AWS::Lambda::Function", {
|
|
363
|
-
TracingConfig: { Mode: "Active" },
|
|
364
|
-
});
|
|
365
|
-
});
|
|
366
|
-
test("given withAdotTracing is called / when lambda is built / then CloudWatch Lambda Application Signals policy is attached", () => {
|
|
367
|
-
// given/when
|
|
368
|
-
const template = createTemplate((builder) => {
|
|
369
|
-
builder.withAdotTracing();
|
|
370
|
-
});
|
|
371
|
-
// then — the managed policy ARN contains CloudWatchLambdaApplicationSignalsExecutionRolePolicy
|
|
372
|
-
const roles = template.findResources("AWS::IAM::Role");
|
|
373
|
-
const lambdaRoleKey = Object.keys(roles).find((key) => key.includes("ServiceRole"));
|
|
374
|
-
expect(lambdaRoleKey).toBeDefined();
|
|
375
|
-
const role = roles[lambdaRoleKey];
|
|
376
|
-
const managedPolicyArns = JSON.stringify(role["Properties"]?.["ManagedPolicyArns"]);
|
|
377
|
-
expect(managedPolicyArns).toContain("CloudWatchLambdaApplicationSignalsExecutionRolePolicy");
|
|
378
|
-
});
|
|
379
|
-
test("given withAdotTracing on Node.js runtime / when lambda is built / then JS ADOT layer is attached", () => {
|
|
380
|
-
// given/when — default runtime is NODEJS_24_X
|
|
381
|
-
const template = createTemplate((builder) => {
|
|
382
|
-
builder.withAdotTracing();
|
|
383
|
-
});
|
|
384
|
-
// then
|
|
385
|
-
template.hasResourceProperties("AWS::Lambda::Function", {
|
|
386
|
-
Layers: Match.arrayWith([
|
|
387
|
-
Match.stringLikeRegexp("AWSOpenTelemetryDistroJs"),
|
|
388
|
-
]),
|
|
389
|
-
});
|
|
390
|
-
});
|
|
391
|
-
test("given withAdotTracing on Python runtime / when lambda is built / then Python ADOT layer is attached", () => {
|
|
392
|
-
// given/when
|
|
393
|
-
const template = createTemplate((builder) => {
|
|
394
|
-
builder.withRuntime(Runtime.PYTHON_3_12).withAdotTracing();
|
|
395
|
-
});
|
|
396
|
-
// then
|
|
397
|
-
template.hasResourceProperties("AWS::Lambda::Function", {
|
|
398
|
-
Layers: Match.arrayWith([
|
|
399
|
-
Match.stringLikeRegexp("AWSOpenTelemetryDistroPython"),
|
|
400
|
-
]),
|
|
401
|
-
});
|
|
402
|
-
});
|
|
403
|
-
test("given withAdotTracing is NOT called / when lambda is built / then ADOT env vars are absent", () => {
|
|
404
|
-
// given/when
|
|
405
|
-
const template = createTemplate((_builder) => { });
|
|
406
|
-
// then
|
|
407
|
-
expectEnvironmentValueMissing(template, "AWS_LAMBDA_EXEC_WRAPPER");
|
|
408
|
-
expectEnvironmentValueMissing(template, "OTEL_AWS_APPLICATION_SIGNALS_ENABLED");
|
|
409
|
-
});
|
|
410
|
-
test("given withAdotTracing is NOT called / when lambda is built / then tracing is not Active", () => {
|
|
411
|
-
// given/when
|
|
412
|
-
const template = createTemplate((_builder) => { });
|
|
413
|
-
// then
|
|
414
|
-
template.hasResourceProperties("AWS::Lambda::Function", {
|
|
415
|
-
TracingConfig: Match.absent(),
|
|
416
|
-
});
|
|
417
|
-
});
|
|
418
|
-
test("given withAdotTracing combined with other builder methods / when lambda is built / then all properties are present", () => {
|
|
419
|
-
// given/when
|
|
420
|
-
const template = createTemplate((builder) => {
|
|
421
|
-
builder
|
|
422
|
-
.withAdotTracing()
|
|
423
|
-
.withMemorySize(512)
|
|
424
|
-
.withTimeout(Duration.seconds(30))
|
|
425
|
-
.withEnvironment({ CUSTOM_KEY: "custom_value" });
|
|
426
|
-
});
|
|
427
|
-
// then — ADOT config present
|
|
428
|
-
expectEnvironmentValue(template, "AWS_LAMBDA_EXEC_WRAPPER", "/opt/otel-instrument");
|
|
429
|
-
template.hasResourceProperties("AWS::Lambda::Function", {
|
|
430
|
-
TracingConfig: { Mode: "Active" },
|
|
431
|
-
});
|
|
432
|
-
// then — other builder settings also present
|
|
433
|
-
template.hasResourceProperties("AWS::Lambda::Function", {
|
|
434
|
-
MemorySize: 512,
|
|
435
|
-
Timeout: 30,
|
|
436
|
-
});
|
|
437
|
-
expectEnvironmentValue(template, "CUSTOM_KEY", "custom_value");
|
|
438
|
-
});
|
|
439
|
-
test("given withAdotTracing on plain builder / when lambda is built / then ADOT config is applied without database env vars", () => {
|
|
440
|
-
// given/when
|
|
441
|
-
const template = createTemplate((builder) => {
|
|
442
|
-
builder.withAdotTracing();
|
|
443
|
-
}, true);
|
|
444
|
-
// then — ADOT config present
|
|
445
|
-
expectEnvironmentValue(template, "AWS_LAMBDA_EXEC_WRAPPER", "/opt/otel-instrument");
|
|
446
|
-
template.hasResourceProperties("AWS::Lambda::Function", {
|
|
447
|
-
TracingConfig: { Mode: "Active" },
|
|
448
|
-
Layers: Match.arrayWith([
|
|
449
|
-
Match.stringLikeRegexp("AWSOpenTelemetryDistroJs"),
|
|
450
|
-
]),
|
|
451
|
-
});
|
|
452
|
-
// then — no database env vars
|
|
453
|
-
expectEnvironmentValueMissing(template, "DB_APPLICATION");
|
|
454
|
-
});
|
|
455
|
-
});
|
|
456
338
|
});
|
|
457
339
|
//# sourceMappingURL=dt-function.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test, vi } from "vitest";
|
|
2
|
+
import { logger } from "../../aws/runtime/dt-logger-default.js";
|
|
3
|
+
import { logLeadTime, logLeadTimes } from "../../utils/lead-time-logging.js";
|
|
4
|
+
describe("lead-time-logging", () => {
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
vi.restoreAllMocks();
|
|
7
|
+
});
|
|
8
|
+
test("logLeadTime logs expected payload", () => {
|
|
9
|
+
const infoSpy = vi
|
|
10
|
+
.spyOn(logger, "info")
|
|
11
|
+
.mockImplementation(() => undefined);
|
|
12
|
+
const errorSpy = vi
|
|
13
|
+
.spyOn(logger, "error")
|
|
14
|
+
.mockImplementation(() => undefined);
|
|
15
|
+
logLeadTime("lead-time", "test-target", 123);
|
|
16
|
+
expect(infoSpy).toHaveBeenCalledTimes(1);
|
|
17
|
+
expect(infoSpy).toHaveBeenCalledWith({
|
|
18
|
+
method: "LeadTimeLogging.logLeadTime",
|
|
19
|
+
customLeadTime: true,
|
|
20
|
+
customName: "lead-time",
|
|
21
|
+
customTarget: "test-target",
|
|
22
|
+
tookMs: 123,
|
|
23
|
+
});
|
|
24
|
+
expect(errorSpy).toHaveBeenCalledTimes(0);
|
|
25
|
+
});
|
|
26
|
+
test("logLeadTime merges extra fields", () => {
|
|
27
|
+
const infoSpy = vi
|
|
28
|
+
.spyOn(logger, "info")
|
|
29
|
+
.mockImplementation(() => undefined);
|
|
30
|
+
logLeadTime("mqtt-time", "mqtt", 88, {
|
|
31
|
+
customSource: "mqtt",
|
|
32
|
+
customRetriesCount: 2,
|
|
33
|
+
});
|
|
34
|
+
expect(infoSpy).toHaveBeenCalledTimes(1);
|
|
35
|
+
expect(infoSpy).toHaveBeenCalledWith({
|
|
36
|
+
method: "LeadTimeLogging.logLeadTime",
|
|
37
|
+
customLeadTime: true,
|
|
38
|
+
customName: "mqtt-time",
|
|
39
|
+
customTarget: "mqtt",
|
|
40
|
+
tookMs: 88,
|
|
41
|
+
customSource: "mqtt",
|
|
42
|
+
customRetriesCount: 2,
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
test("logLeadTime logs error if logger.info throws", () => {
|
|
46
|
+
const infoError = new Error("boom");
|
|
47
|
+
vi.spyOn(logger, "info").mockImplementation(() => {
|
|
48
|
+
throw infoError;
|
|
49
|
+
});
|
|
50
|
+
const errorSpy = vi
|
|
51
|
+
.spyOn(logger, "error")
|
|
52
|
+
.mockImplementation(() => undefined);
|
|
53
|
+
logLeadTime("process-time", "target", 10);
|
|
54
|
+
expect(errorSpy).toHaveBeenCalledTimes(1);
|
|
55
|
+
expect(errorSpy).toHaveBeenCalledWith({
|
|
56
|
+
method: "LeadTimeLogging.logLeadTime",
|
|
57
|
+
message: "Error logging lead time",
|
|
58
|
+
error: infoError,
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
test("logLeadTimes logs only lead-time when only leadTimeMs is safe", () => {
|
|
62
|
+
const infoSpy = vi
|
|
63
|
+
.spyOn(logger, "info")
|
|
64
|
+
.mockImplementation(() => undefined);
|
|
65
|
+
logLeadTimes({ target: "x", leadTimeMs: 10 });
|
|
66
|
+
expect(infoSpy).toHaveBeenCalledTimes(1);
|
|
67
|
+
expect(infoSpy).toHaveBeenNthCalledWith(1, {
|
|
68
|
+
method: "LeadTimeLogging.logLeadTime",
|
|
69
|
+
customLeadTime: true,
|
|
70
|
+
customName: "lead-time",
|
|
71
|
+
customTarget: "x",
|
|
72
|
+
tookMs: 10,
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
test("logLeadTimes logs only process-time when only processTimeMs is safe", () => {
|
|
76
|
+
const infoSpy = vi
|
|
77
|
+
.spyOn(logger, "info")
|
|
78
|
+
.mockImplementation(() => undefined);
|
|
79
|
+
logLeadTimes({ target: "x", processTimeMs: 20 });
|
|
80
|
+
expect(infoSpy).toHaveBeenCalledTimes(1);
|
|
81
|
+
expect(infoSpy).toHaveBeenNthCalledWith(1, {
|
|
82
|
+
method: "LeadTimeLogging.logLeadTime",
|
|
83
|
+
customLeadTime: true,
|
|
84
|
+
customName: "process-time",
|
|
85
|
+
customTarget: "x",
|
|
86
|
+
tookMs: 20,
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
test("logLeadTimes logs both in order with extra fields", () => {
|
|
90
|
+
const infoSpy = vi
|
|
91
|
+
.spyOn(logger, "info")
|
|
92
|
+
.mockImplementation(() => undefined);
|
|
93
|
+
const input = {
|
|
94
|
+
target: "device",
|
|
95
|
+
leadTimeMs: 11,
|
|
96
|
+
processTimeMs: 22,
|
|
97
|
+
extraFields: { customSource: "mqtt", customPartition: 3 },
|
|
98
|
+
};
|
|
99
|
+
logLeadTimes(input);
|
|
100
|
+
expect(infoSpy).toHaveBeenCalledTimes(2);
|
|
101
|
+
expect(infoSpy).toHaveBeenNthCalledWith(1, {
|
|
102
|
+
method: "LeadTimeLogging.logLeadTime",
|
|
103
|
+
customLeadTime: true,
|
|
104
|
+
customName: "lead-time",
|
|
105
|
+
customTarget: "device",
|
|
106
|
+
tookMs: 11,
|
|
107
|
+
customSource: "mqtt",
|
|
108
|
+
customPartition: 3,
|
|
109
|
+
});
|
|
110
|
+
expect(infoSpy).toHaveBeenNthCalledWith(2, {
|
|
111
|
+
method: "LeadTimeLogging.logLeadTime",
|
|
112
|
+
customLeadTime: true,
|
|
113
|
+
customName: "process-time",
|
|
114
|
+
customTarget: "device",
|
|
115
|
+
tookMs: 22,
|
|
116
|
+
customSource: "mqtt",
|
|
117
|
+
customPartition: 3,
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
test("logLeadTimes ignores non-safe values", () => {
|
|
121
|
+
const infoSpy = vi
|
|
122
|
+
.spyOn(logger, "info")
|
|
123
|
+
.mockImplementation(() => undefined);
|
|
124
|
+
logLeadTimes({
|
|
125
|
+
target: "x",
|
|
126
|
+
leadTimeMs: 1.5,
|
|
127
|
+
processTimeMs: Number.MAX_SAFE_INTEGER + 1,
|
|
128
|
+
});
|
|
129
|
+
logLeadTimes({
|
|
130
|
+
target: "x",
|
|
131
|
+
leadTimeMs: Number.NaN,
|
|
132
|
+
processTimeMs: Infinity,
|
|
133
|
+
});
|
|
134
|
+
logLeadTimes({ target: "x" });
|
|
135
|
+
expect(infoSpy).toHaveBeenCalledTimes(0);
|
|
136
|
+
});
|
|
137
|
+
test("logLeadTimes logs negative safe integer values", () => {
|
|
138
|
+
const infoSpy = vi
|
|
139
|
+
.spyOn(logger, "info")
|
|
140
|
+
.mockImplementation(() => undefined);
|
|
141
|
+
logLeadTimes({ target: "x", leadTimeMs: -5 });
|
|
142
|
+
expect(infoSpy).toHaveBeenCalledTimes(1);
|
|
143
|
+
expect(infoSpy).toHaveBeenNthCalledWith(1, {
|
|
144
|
+
method: "LeadTimeLogging.logLeadTime",
|
|
145
|
+
customLeadTime: true,
|
|
146
|
+
customName: "lead-time",
|
|
147
|
+
customTarget: "x",
|
|
148
|
+
tookMs: -5,
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
// clean comment
|
|
153
|
+
//# sourceMappingURL=lead-time-logging.test.js.map
|
|
@@ -32,13 +32,32 @@ export declare class AclBuilder {
|
|
|
32
32
|
* Allow access only from the given addresses
|
|
33
33
|
*/
|
|
34
34
|
withIpWhitelistRule(addresses: string[]): this;
|
|
35
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Adds a rate-based WAF rule for either blocking or counting requests.
|
|
37
|
+
*
|
|
38
|
+
* @param name WAF rule name and CloudWatch metric name.
|
|
39
|
+
* @param limit Maximum requests allowed in the WAF evaluation window.
|
|
40
|
+
* @param isHeaderRequired Whether the rule applies when the digitraffic-user header is present.
|
|
41
|
+
* @param isBasedOnIpAndUriPath Whether requests are aggregated by IP address and URI path.
|
|
42
|
+
* @param customResponseBodyKey Custom response body key; when provided, matching requests are blocked instead of counted.
|
|
43
|
+
* @param path Optional URI path pattern that limits which requests are included.
|
|
44
|
+
* @param excludedPath Optional URI path pattern that excludes matching requests from the rule.
|
|
45
|
+
*/
|
|
46
|
+
withThrottleRule(name: string, limit: number, isHeaderRequired: boolean, isBasedOnIpAndUriPath: boolean, customResponseBodyKey?: string, path?: RegExp, excludedPath?: RegExp): this;
|
|
36
47
|
withCustomResponseBody(key: string, customResponseBody: CfnWebACL.CustomResponseBodyProperty): this;
|
|
37
48
|
withThrottleDigitrafficUserIp(limit: number | undefined): this;
|
|
38
49
|
withThrottleDigitrafficUserIpAndUriPath(limit: number | undefined): this;
|
|
39
50
|
withThrottleAnonymousUserIp(limit: number | undefined): AclBuilder;
|
|
40
51
|
withThrottleAnonymousUserIpByUriPath(limit: number | undefined, path: RegExp | undefined): AclBuilder;
|
|
41
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Throttles anonymous requests by IP address and URI path.
|
|
54
|
+
*
|
|
55
|
+
* Existing callers that omit `excludedPath` retain the previous behavior.
|
|
56
|
+
*
|
|
57
|
+
* @param limit Maximum requests allowed for each IP and URI path combination.
|
|
58
|
+
* @param excludedPath Optional URI path pattern excluded from this throttle rule.
|
|
59
|
+
*/
|
|
60
|
+
withThrottleAnonymousUserIpAndUriPath(limit: number | undefined, excludedPath?: RegExp): this;
|
|
42
61
|
withCountDigitrafficUserIp(limit: number | undefined): this;
|
|
43
62
|
withCountDigitrafficUserIpAndUriPath(limit: number | undefined): this;
|
|
44
63
|
withCountAnonymousUserIp(limit: number | undefined): this;
|
|
@@ -91,7 +91,18 @@ export class AclBuilder {
|
|
|
91
91
|
});
|
|
92
92
|
return this;
|
|
93
93
|
}
|
|
94
|
-
|
|
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) {
|
|
95
106
|
const isBlockRule = !!customResponseBodyKey;
|
|
96
107
|
const rules = isBlockRule ? this._blockRules : this._countRules;
|
|
97
108
|
const action = isBlockRule
|
|
@@ -114,7 +125,7 @@ export class AclBuilder {
|
|
|
114
125
|
metricName: name,
|
|
115
126
|
},
|
|
116
127
|
action,
|
|
117
|
-
statement: createThrottleStatement(limit, isHeaderRequired, isBasedOnIpAndUriPath, path),
|
|
128
|
+
statement: createThrottleStatement(limit, isHeaderRequired, isBasedOnIpAndUriPath, path, excludedPath),
|
|
118
129
|
});
|
|
119
130
|
return this;
|
|
120
131
|
}
|
|
@@ -160,13 +171,21 @@ export class AclBuilder {
|
|
|
160
171
|
this._addThrottleResponseBody(customResponseBodyKey, limit);
|
|
161
172
|
return this.withThrottleRule("ThrottleRuleWithAnonymousUserByPath", limit, false, false, customResponseBodyKey, path);
|
|
162
173
|
}
|
|
163
|
-
|
|
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) {
|
|
164
183
|
if (limit === undefined) {
|
|
165
184
|
return this;
|
|
166
185
|
}
|
|
167
186
|
const customResponseBodyKey = `IP_PATH_THROTTLE_ANONYMOUS_USER_${limit}`;
|
|
168
187
|
this._addThrottleResponseBody(customResponseBodyKey, limit);
|
|
169
|
-
return this.withThrottleRule("ThrottleRuleIPQueryWithAnonymousUser", limit, false, true, customResponseBodyKey);
|
|
188
|
+
return this.withThrottleRule("ThrottleRuleIPQueryWithAnonymousUser", limit, false, true, customResponseBodyKey, undefined, excludedPath);
|
|
170
189
|
}
|
|
171
190
|
withCountDigitrafficUserIp(limit) {
|
|
172
191
|
if (limit === undefined) {
|
|
@@ -289,8 +308,18 @@ function notStatement(statement) {
|
|
|
289
308
|
},
|
|
290
309
|
};
|
|
291
310
|
}
|
|
292
|
-
|
|
293
|
-
|
|
311
|
+
/**
|
|
312
|
+
* Creates the scope-down and rate-based statement used by a throttle rule.
|
|
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.
|
|
294
323
|
const matchStatement = {
|
|
295
324
|
sizeConstraintStatement: {
|
|
296
325
|
comparisonOperator: isHeaderRequired ? "GT" : "GE",
|
|
@@ -324,6 +353,25 @@ function createThrottleStatement(limit, isHeaderRequired, isBasedOnIpAndUriPath,
|
|
|
324
353
|
},
|
|
325
354
|
};
|
|
326
355
|
}
|
|
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
|
+
}
|
|
327
375
|
if (isBasedOnIpAndUriPath) {
|
|
328
376
|
return {
|
|
329
377
|
rateBasedStatement: {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import synthetics from "Synthetics";
|
|
2
2
|
import { inDatabaseReadonly } from "../../../database/database.js";
|
|
3
|
-
import { getEnvVariable } from "../../../utils/utils.js";
|
|
4
3
|
import { logger } from "../../runtime/dt-logger-default.js";
|
|
5
4
|
import { ProxyHolder } from "../../runtime/secrets/proxy-holder.js";
|
|
6
5
|
import { RdsHolder } from "../../runtime/secrets/rds-holder.js";
|
|
@@ -66,10 +65,10 @@ export class DatabaseCountChecker {
|
|
|
66
65
|
synthetics.getConfiguration().withFailedCanaryMetric(true);
|
|
67
66
|
}
|
|
68
67
|
static createForProxy() {
|
|
69
|
-
return new DatabaseCountChecker(() =>
|
|
68
|
+
return new DatabaseCountChecker(() => ProxyHolder.create().setCredentials());
|
|
70
69
|
}
|
|
71
70
|
static createForRds() {
|
|
72
|
-
return new DatabaseCountChecker(() =>
|
|
71
|
+
return new DatabaseCountChecker(() => RdsHolder.create().setCredentials());
|
|
73
72
|
}
|
|
74
73
|
/**
|
|
75
74
|
* Expect that the count is 1
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Stack } from "aws-cdk-lib";
|
|
1
|
+
import type { Size, Stack } from "aws-cdk-lib";
|
|
2
2
|
import { Duration } from "aws-cdk-lib";
|
|
3
3
|
import type { ISecurityGroup } from "aws-cdk-lib/aws-ec2";
|
|
4
4
|
import type { IRole } from "aws-cdk-lib/aws-iam";
|
|
@@ -21,6 +21,7 @@ export declare class FunctionBuilder {
|
|
|
21
21
|
private functionName;
|
|
22
22
|
private environment;
|
|
23
23
|
private vpc?;
|
|
24
|
+
private storageSize?;
|
|
24
25
|
private alarms;
|
|
25
26
|
private code;
|
|
26
27
|
private handler;
|
|
@@ -28,7 +29,6 @@ export declare class FunctionBuilder {
|
|
|
28
29
|
private readonly securityGroups;
|
|
29
30
|
private readonly policyStatements;
|
|
30
31
|
private readonly allowedActions;
|
|
31
|
-
private _adotTracing;
|
|
32
32
|
private readonly _features;
|
|
33
33
|
constructor(stack: Stack & DigitrafficStackInterface, lambdaName: string);
|
|
34
34
|
/**
|
|
@@ -41,9 +41,19 @@ export declare class FunctionBuilder {
|
|
|
41
41
|
*/
|
|
42
42
|
static plain(stack: Stack & DigitrafficStackInterface, lambdaName: string): FunctionBuilder;
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
* Sets the Lambda deployment package to the directory `dist/lambda/${path}` and configures
|
|
45
|
+
* the handler to `basename(path).handler`.
|
|
46
|
+
* @param path - subdirectory under `dist/lambda/` that contains the compiled Lambda code.
|
|
47
|
+
* The handler module name is derived from `basename(path)` (e.g. `"locations.handler"`).
|
|
48
|
+
* Defaults to `lambdaName`.
|
|
49
|
+
* Example: `"dump/locations"` → asset dir `dist/lambda/dump/locations/`, handler `locations.handler`.
|
|
50
|
+
* @param exclude - optional glob patterns to exclude from the asset bundle.
|
|
51
|
+
* Patterns are relative to the asset root (`dist/lambda/${path}/`) — do NOT use a leading slash.
|
|
52
|
+
* Use `**` to match files recursively.
|
|
53
|
+
* Example: `["datex2-36", "datex2-37", "statuses"]` excludes sibling lambda subdirectories
|
|
54
|
+
* that would otherwise be bundled into this lambda's deployment package.
|
|
55
|
+
*/
|
|
56
|
+
withAssetCode(path?: string, exclude?: string[]): this;
|
|
47
57
|
withCode(code: Code): this;
|
|
48
58
|
/**
|
|
49
59
|
* Use given handler(${name}.handler) to run the lambda. Default value is lambdaName.
|
|
@@ -74,15 +84,13 @@ export declare class FunctionBuilder {
|
|
|
74
84
|
*/
|
|
75
85
|
withRuntime(runtime: Runtime): this;
|
|
76
86
|
/**
|
|
77
|
-
*
|
|
87
|
+
* Set storage size for the lambda. Defaults to AWS default (512MB).
|
|
78
88
|
*/
|
|
79
|
-
|
|
89
|
+
withStorageSize(storageSize: Size): this;
|
|
80
90
|
/**
|
|
81
|
-
*
|
|
82
|
-
* Adds the ADOT Lambda layer, enables X-Ray active tracing,
|
|
83
|
-
* sets required environment variables, and attaches IAM permissions.
|
|
91
|
+
* Add Lambda layers.
|
|
84
92
|
*/
|
|
85
|
-
|
|
93
|
+
withLayers(...layers: ILayerVersion[]): this;
|
|
86
94
|
/**
|
|
87
95
|
* Set architecture for the lambda. Default is Architecture.ARM_64.
|
|
88
96
|
*/
|
|
@@ -121,7 +129,6 @@ export declare class FunctionBuilder {
|
|
|
121
129
|
build(): AwsFunction;
|
|
122
130
|
private attachPolicies;
|
|
123
131
|
private validateNoWildcardActions;
|
|
124
|
-
private getLayers;
|
|
125
132
|
private getEnvironment;
|
|
126
133
|
private createAlarms;
|
|
127
134
|
private createAlarm;
|