@composurecdk/sqs 0.8.7 → 0.9.0
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 +99 -13
- package/dist/commonjs/dlq-alarm-defaults.d.ts +53 -0
- package/dist/commonjs/dlq-alarm-defaults.d.ts.map +1 -0
- package/dist/commonjs/dlq-alarm-defaults.js +53 -0
- package/dist/commonjs/dlq-alarm-defaults.js.map +1 -0
- package/dist/commonjs/dlq-defaults.d.ts +19 -0
- package/dist/commonjs/dlq-defaults.d.ts.map +1 -0
- package/dist/commonjs/dlq-defaults.js +22 -0
- package/dist/commonjs/dlq-defaults.js.map +1 -0
- package/dist/commonjs/grants.d.ts +19 -0
- package/dist/commonjs/grants.d.ts.map +1 -0
- package/dist/commonjs/grants.js +28 -0
- package/dist/commonjs/grants.js.map +1 -0
- package/dist/commonjs/index.d.ts +6 -1
- package/dist/commonjs/index.d.ts.map +1 -1
- package/dist/commonjs/index.js +7 -1
- package/dist/commonjs/index.js.map +1 -1
- package/dist/commonjs/queue-alarm-config.d.ts +47 -11
- package/dist/commonjs/queue-alarm-config.d.ts.map +1 -1
- package/dist/commonjs/queue-alarm-defaults.d.ts +46 -11
- package/dist/commonjs/queue-alarm-defaults.d.ts.map +1 -1
- package/dist/commonjs/queue-alarm-defaults.js +11 -5
- package/dist/commonjs/queue-alarm-defaults.js.map +1 -1
- package/dist/commonjs/queue-alarm-profiles.d.ts +37 -0
- package/dist/commonjs/queue-alarm-profiles.d.ts.map +1 -0
- package/dist/commonjs/queue-alarm-profiles.js +96 -0
- package/dist/commonjs/queue-alarm-profiles.js.map +1 -0
- package/dist/commonjs/queue-alarms.d.ts +8 -2
- package/dist/commonjs/queue-alarms.d.ts.map +1 -1
- package/dist/commonjs/queue-alarms.js +49 -42
- package/dist/commonjs/queue-alarms.js.map +1 -1
- package/dist/commonjs/queue-builder.d.ts +70 -53
- package/dist/commonjs/queue-builder.d.ts.map +1 -1
- package/dist/commonjs/queue-builder.js +27 -68
- package/dist/commonjs/queue-builder.js.map +1 -1
- package/dist/commonjs/queue-props.d.ts +93 -0
- package/dist/commonjs/queue-props.d.ts.map +1 -0
- package/dist/commonjs/queue-props.js +16 -0
- package/dist/commonjs/queue-props.js.map +1 -0
- package/dist/commonjs/queue-role.d.ts +28 -0
- package/dist/commonjs/queue-role.d.ts.map +1 -0
- package/dist/commonjs/queue-role.js +13 -0
- package/dist/commonjs/queue-role.js.map +1 -0
- package/dist/commonjs/queue-validation.d.ts +12 -0
- package/dist/commonjs/queue-validation.d.ts.map +1 -0
- package/dist/commonjs/queue-validation.js +142 -0
- package/dist/commonjs/queue-validation.js.map +1 -0
- package/dist/esm/dlq-alarm-defaults.d.ts +53 -0
- package/dist/esm/dlq-alarm-defaults.d.ts.map +1 -0
- package/dist/esm/dlq-alarm-defaults.js +50 -0
- package/dist/esm/dlq-alarm-defaults.js.map +1 -0
- package/dist/esm/dlq-defaults.d.ts +19 -0
- package/dist/esm/dlq-defaults.d.ts.map +1 -0
- package/dist/esm/dlq-defaults.js +19 -0
- package/dist/esm/dlq-defaults.js.map +1 -0
- package/dist/esm/grants.d.ts +19 -0
- package/dist/esm/grants.d.ts.map +1 -0
- package/dist/esm/grants.js +25 -0
- package/dist/esm/grants.js.map +1 -0
- package/dist/esm/index.d.ts +6 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +3 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/queue-alarm-config.d.ts +47 -11
- package/dist/esm/queue-alarm-config.d.ts.map +1 -1
- package/dist/esm/queue-alarm-defaults.d.ts +46 -11
- package/dist/esm/queue-alarm-defaults.d.ts.map +1 -1
- package/dist/esm/queue-alarm-defaults.js +11 -5
- package/dist/esm/queue-alarm-defaults.js.map +1 -1
- package/dist/esm/queue-alarm-profiles.d.ts +37 -0
- package/dist/esm/queue-alarm-profiles.d.ts.map +1 -0
- package/dist/esm/queue-alarm-profiles.js +92 -0
- package/dist/esm/queue-alarm-profiles.js.map +1 -0
- package/dist/esm/queue-alarms.d.ts +8 -2
- package/dist/esm/queue-alarms.d.ts.map +1 -1
- package/dist/esm/queue-alarms.js +50 -43
- package/dist/esm/queue-alarms.js.map +1 -1
- package/dist/esm/queue-builder.d.ts +70 -53
- package/dist/esm/queue-builder.d.ts.map +1 -1
- package/dist/esm/queue-builder.js +27 -68
- package/dist/esm/queue-builder.js.map +1 -1
- package/dist/esm/queue-props.d.ts +93 -0
- package/dist/esm/queue-props.d.ts.map +1 -0
- package/dist/esm/queue-props.js +13 -0
- package/dist/esm/queue-props.js.map +1 -0
- package/dist/esm/queue-role.d.ts +28 -0
- package/dist/esm/queue-role.d.ts.map +1 -0
- package/dist/esm/queue-role.js +9 -0
- package/dist/esm/queue-role.js.map +1 -0
- package/dist/esm/queue-validation.d.ts +12 -0
- package/dist/esm/queue-validation.d.ts.map +1 -0
- package/dist/esm/queue-validation.js +139 -0
- package/dist/esm/queue-validation.js.map +1 -0
- package/package.json +4 -4
|
@@ -4,31 +4,12 @@ import { type IConstruct } from "constructs";
|
|
|
4
4
|
import { COPY_STATE, type Lifecycle } from "@composurecdk/core";
|
|
5
5
|
import { type ITaggedBuilder } from "@composurecdk/cloudformation";
|
|
6
6
|
import { AlarmDefinitionBuilder } from "@composurecdk/cloudwatch";
|
|
7
|
-
import type {
|
|
7
|
+
import type { QueueBuilderExtensionProps, QueueBuilderPropsByRole } from "./queue-props.js";
|
|
8
|
+
import { type QueueRole } from "./queue-role.js";
|
|
8
9
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*/
|
|
13
|
-
export interface QueueBuilderProps extends QueueProps {
|
|
14
|
-
/**
|
|
15
|
-
* Configuration for AWS-recommended CloudWatch alarms.
|
|
16
|
-
*
|
|
17
|
-
* By default, the builder creates recommended alarms with sensible
|
|
18
|
-
* thresholds for every applicable metric. Individual alarms can be
|
|
19
|
-
* customized or disabled. Set to `false` to disable all alarms.
|
|
20
|
-
*
|
|
21
|
-
* No alarm actions are configured by default since notification
|
|
22
|
-
* methods are user-specific. Access alarms from the build result
|
|
23
|
-
* or use an `afterBuild` hook to apply actions.
|
|
24
|
-
*
|
|
25
|
-
* @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#SQS
|
|
26
|
-
*/
|
|
27
|
-
recommendedAlarms?: QueueAlarmConfig | false;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* The build output of an {@link IQueueBuilder}. Contains the CDK constructs
|
|
31
|
-
* created during {@link Lifecycle.build}, keyed by role.
|
|
10
|
+
* The build output of {@link createQueueBuilder}, identical across every
|
|
11
|
+
* {@link QueueRole}. Contains the CDK constructs created during
|
|
12
|
+
* {@link Lifecycle.build}, keyed by role.
|
|
32
13
|
*/
|
|
33
14
|
export interface QueueBuilderResult {
|
|
34
15
|
/** The SQS queue construct created by the builder. */
|
|
@@ -37,8 +18,8 @@ export interface QueueBuilderResult {
|
|
|
37
18
|
* CloudWatch alarms created for the queue, keyed by alarm name.
|
|
38
19
|
*
|
|
39
20
|
* Includes both AWS-recommended alarms and any custom alarms added
|
|
40
|
-
* via
|
|
41
|
-
*
|
|
21
|
+
* via `addAlarm`. Access individual alarms by key (e.g.,
|
|
22
|
+
* `result.alarms.approximateAgeOfOldestMessage`).
|
|
42
23
|
*
|
|
43
24
|
* No alarm actions are configured — apply them via the result or an
|
|
44
25
|
* `afterBuild` hook.
|
|
@@ -48,21 +29,37 @@ export interface QueueBuilderResult {
|
|
|
48
29
|
alarms: Record<string, Alarm>;
|
|
49
30
|
}
|
|
50
31
|
/**
|
|
51
|
-
*
|
|
32
|
+
* The builder's full internal prop store: the CDK {@link QueueProps},
|
|
33
|
+
* the builder-only extensions, and the queue's role. The public surface
|
|
34
|
+
* per role is the narrowed view in {@link QueueBuilderPropsByRole} —
|
|
35
|
+
* this type exists so one `QueueBuilder` implementation can back every
|
|
36
|
+
* role.
|
|
37
|
+
*/
|
|
38
|
+
interface InternalQueueBuilderProps extends QueueProps, QueueBuilderExtensionProps {
|
|
39
|
+
/**
|
|
40
|
+
* The queue's role, set by `createQueueBuilder(role)`. Stored as a
|
|
41
|
+
* prop (not a private field) so it is data: `.copy()` preserves it
|
|
42
|
+
* for free and it is inspectable on the builder.
|
|
43
|
+
*/
|
|
44
|
+
queueRole?: QueueRole;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A fluent builder for configuring and creating an AWS SQS queue in a
|
|
48
|
+
* given {@link QueueRole}.
|
|
52
49
|
*
|
|
53
|
-
* Each configuration property
|
|
54
|
-
*
|
|
50
|
+
* Each configuration property applicable to the role is exposed as an
|
|
51
|
+
* overloaded method: call with a value to set it (returns the builder
|
|
55
52
|
* for chaining), or call with no arguments to read the current value.
|
|
53
|
+
* The prop surface is exact per role — FIFO-only props exist only on
|
|
54
|
+
* the FIFO roles, and `deadLetterQueue` does not exist on the
|
|
55
|
+
* dead-letter roles.
|
|
56
56
|
*
|
|
57
|
-
* The builder implements {@link Lifecycle}, so it can be used directly as
|
|
58
|
-
* component in a {@link compose | composed system}. When built, it
|
|
59
|
-
* an SQS queue with the configured properties
|
|
57
|
+
* The builder implements {@link Lifecycle}, so it can be used directly as
|
|
58
|
+
* a component in a {@link compose | composed system}. When built, it
|
|
59
|
+
* creates an SQS queue with the configured properties, the role's
|
|
60
|
+
* defaults, and the role's recommended CloudWatch alarms, returning a
|
|
60
61
|
* {@link QueueBuilderResult}.
|
|
61
62
|
*
|
|
62
|
-
* The builder also creates AWS-recommended CloudWatch alarms by default.
|
|
63
|
-
* Alarms can be customized or disabled via the `recommendedAlarms` property.
|
|
64
|
-
* Custom alarms can be added via the {@link addAlarm} method.
|
|
65
|
-
*
|
|
66
63
|
* @see https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_sqs.Queue.html
|
|
67
64
|
*
|
|
68
65
|
* @example
|
|
@@ -70,42 +67,62 @@ export interface QueueBuilderResult {
|
|
|
70
67
|
* const orders = createQueueBuilder()
|
|
71
68
|
* .queueName("orders")
|
|
72
69
|
* .visibilityTimeout(Duration.seconds(60));
|
|
70
|
+
*
|
|
71
|
+
* const orderEvents = createQueueBuilder("fifo")
|
|
72
|
+
* .queueName("order-events.fifo")
|
|
73
|
+
* .contentBasedDeduplication(true);
|
|
74
|
+
*
|
|
75
|
+
* const ordersDlq = createQueueBuilder("dlq").queueName("orders-dlq");
|
|
73
76
|
* ```
|
|
74
77
|
*/
|
|
75
|
-
export type IQueueBuilder = ITaggedBuilder<
|
|
78
|
+
export type IQueueBuilder<R extends QueueRole = "standard"> = ITaggedBuilder<QueueBuilderPropsByRole[R], QueueBuilder>;
|
|
76
79
|
declare class QueueBuilder implements Lifecycle<QueueBuilderResult> {
|
|
77
80
|
#private;
|
|
78
|
-
props: Partial<
|
|
81
|
+
props: Partial<InternalQueueBuilderProps>;
|
|
79
82
|
addAlarm(key: string, configure: (alarm: AlarmDefinitionBuilder<IQueue>) => AlarmDefinitionBuilder<IQueue>): this;
|
|
80
83
|
/** @internal — see ADR-0005. */
|
|
81
84
|
[COPY_STATE](target: QueueBuilder): void;
|
|
82
85
|
build(scope: IConstruct, id: string): QueueBuilderResult;
|
|
83
86
|
}
|
|
84
87
|
/**
|
|
85
|
-
* Creates a new {@link IQueueBuilder} for configuring an AWS SQS queue
|
|
88
|
+
* Creates a new {@link IQueueBuilder} for configuring an AWS SQS queue
|
|
89
|
+
* in the given role.
|
|
86
90
|
*
|
|
87
|
-
* This is the entry point for
|
|
88
|
-
*
|
|
89
|
-
*
|
|
91
|
+
* This is the single entry point for every queue type. The role — see
|
|
92
|
+
* {@link QueueRole} for the full catalogue — selects the builder's
|
|
93
|
+
* typed prop surface, its Well-Architected defaults, its
|
|
94
|
+
* recommended-alarm profile, and its build-time validation:
|
|
90
95
|
*
|
|
91
|
-
*
|
|
96
|
+
* - `createQueueBuilder()` / `createQueueBuilder("standard")` — primary
|
|
97
|
+
* standard queue.
|
|
98
|
+
* - `createQueueBuilder("fifo")` — primary FIFO queue.
|
|
99
|
+
* - `createQueueBuilder("dlq")` — standard dead-letter queue.
|
|
100
|
+
* - `createQueueBuilder("fifo-dlq")` — FIFO dead-letter queue (the DLQ
|
|
101
|
+
* for a FIFO source must itself be FIFO).
|
|
92
102
|
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* .queueName("orders")
|
|
97
|
-
* .visibilityTimeout(Duration.seconds(60));
|
|
103
|
+
* The returned builder exposes every prop applicable to the role as a
|
|
104
|
+
* fluent setter/getter and implements {@link Lifecycle} for use with
|
|
105
|
+
* {@link compose}.
|
|
98
106
|
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
107
|
+
* @param role - The queue's role. Defaults to `"standard"`.
|
|
108
|
+
* @returns A fluent builder for an AWS SQS queue in that role.
|
|
101
109
|
*
|
|
102
|
-
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* // A FIFO primary queue redriving to a FIFO dead-letter queue:
|
|
103
113
|
* const system = compose(
|
|
104
|
-
* {
|
|
105
|
-
*
|
|
114
|
+
* {
|
|
115
|
+
* orderEventsDlq: createQueueBuilder("fifo-dlq").queueName("order-events-dlq.fifo"),
|
|
116
|
+
* orderEvents: createQueueBuilder("fifo")
|
|
117
|
+
* .queueName("order-events.fifo")
|
|
118
|
+
* .contentBasedDeduplication(true)
|
|
119
|
+
* .deadLetterQueue(ref("orderEventsDlq", (r) => ({ queue: r.queue, maxReceiveCount: 5 }))),
|
|
120
|
+
* },
|
|
121
|
+
* { orderEventsDlq: [], orderEvents: ["orderEventsDlq"] },
|
|
106
122
|
* );
|
|
107
123
|
* ```
|
|
108
124
|
*/
|
|
109
125
|
export declare function createQueueBuilder(): IQueueBuilder;
|
|
126
|
+
export declare function createQueueBuilder<R extends QueueRole>(role: R): IQueueBuilder<R>;
|
|
110
127
|
export {};
|
|
111
128
|
//# sourceMappingURL=queue-builder.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue-builder.d.ts","sourceRoot":"","sources":["../../src/queue-builder.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"queue-builder.d.ts","sourceRoot":"","sources":["../../src/queue-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,KAAK,cAAc,EAAiB,MAAM,8BAA8B,CAAC;AAClF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAC5F,OAAO,EAAyB,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAOxE;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,sDAAsD;IACtD,KAAK,EAAE,KAAK,CAAC;IAEb;;;;;;;;;;;OAWG;IACH,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,UAAU,yBAA0B,SAAQ,UAAU,EAAE,0BAA0B;IAChF;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,SAAS,GAAG,UAAU,IAAI,cAAc,CAC1E,uBAAuB,CAAC,CAAC,CAAC,EAC1B,YAAY,CACb,CAAC;AAEF,cAAM,YAAa,YAAW,SAAS,CAAC,kBAAkB,CAAC;;IACzD,KAAK,EAAE,OAAO,CAAC,yBAAyB,CAAC,CAAM;IAG/C,QAAQ,CACN,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,CAAC,KAAK,EAAE,sBAAsB,CAAC,MAAM,CAAC,KAAK,sBAAsB,CAAC,MAAM,CAAC,GACnF,IAAI;IAKP,gCAAgC;IAChC,CAAC,UAAU,CAAC,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAIxC,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,kBAAkB;CA+BzD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,kBAAkB,IAAI,aAAa,CAAC;AACpD,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,SAAS,EAAE,IAAI,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -1,20 +1,13 @@
|
|
|
1
|
-
import { Annotations, Token } from "aws-cdk-lib";
|
|
2
1
|
import { Queue } from "aws-cdk-lib/aws-sqs";
|
|
3
2
|
import { COPY_STATE } from "@composurecdk/core";
|
|
4
3
|
import { taggedBuilder } from "@composurecdk/cloudformation";
|
|
5
4
|
import { AlarmDefinitionBuilder } from "@composurecdk/cloudwatch";
|
|
6
|
-
import {
|
|
5
|
+
import { isDlqRole, isFifoRole } from "./queue-role.js";
|
|
7
6
|
import { QUEUE_DEFAULTS } from "./defaults.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* tends to surface as a flood of "poison" messages from transient
|
|
13
|
-
* errors that would have succeeded on retry.
|
|
14
|
-
*
|
|
15
|
-
* @see https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html
|
|
16
|
-
*/
|
|
17
|
-
const RECOMMENDED_MIN_MAX_RECEIVE_COUNT = 5;
|
|
7
|
+
import { DLQ_QUEUE_DEFAULTS } from "./dlq-defaults.js";
|
|
8
|
+
import { dlqAlarmProfile, PRIMARY_ALARM_PROFILE } from "./queue-alarm-profiles.js";
|
|
9
|
+
import { createQueueAlarms } from "./queue-alarms.js";
|
|
10
|
+
import { validateQueueProps } from "./queue-validation.js";
|
|
18
11
|
class QueueBuilder {
|
|
19
12
|
props = {};
|
|
20
13
|
#customAlarms = [];
|
|
@@ -27,67 +20,33 @@ class QueueBuilder {
|
|
|
27
20
|
target.#customAlarms.push(...this.#customAlarms);
|
|
28
21
|
}
|
|
29
22
|
build(scope, id) {
|
|
30
|
-
const {
|
|
23
|
+
const { queueRole: role = "standard", ...configured } = this.props;
|
|
24
|
+
const fifo = isFifoRole(role);
|
|
25
|
+
const dlq = isDlqRole(role);
|
|
31
26
|
const mergedProps = {
|
|
32
27
|
...QUEUE_DEFAULTS,
|
|
33
|
-
...
|
|
28
|
+
...(dlq ? DLQ_QUEUE_DEFAULTS : {}),
|
|
29
|
+
...configured,
|
|
30
|
+
...(fifo ? { fifo: true } : {}),
|
|
34
31
|
};
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
const
|
|
32
|
+
validateQueueProps(scope, id, role, mergedProps);
|
|
33
|
+
const { recommendedAlarms, ...queueProps } = mergedProps;
|
|
34
|
+
const queue = new Queue(scope, id, queueProps);
|
|
35
|
+
const profile = dlq
|
|
36
|
+
? dlqAlarmProfile(scope, mergedProps.retentionPeriod)
|
|
37
|
+
: PRIMARY_ALARM_PROFILE;
|
|
38
|
+
const alarms = createQueueAlarms(scope, id, queue, recommendedAlarms, this.#customAlarms, profile);
|
|
38
39
|
return { queue, alarms };
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
* @example
|
|
51
|
-
* ```ts
|
|
52
|
-
* const orders = createQueueBuilder()
|
|
53
|
-
* .queueName("orders")
|
|
54
|
-
* .visibilityTimeout(Duration.seconds(60));
|
|
55
|
-
*
|
|
56
|
-
* // Use standalone:
|
|
57
|
-
* const result = orders.build(stack, "Orders");
|
|
58
|
-
*
|
|
59
|
-
* // Or compose into a system:
|
|
60
|
-
* const system = compose(
|
|
61
|
-
* { orders, alerts: createTopicBuilder() },
|
|
62
|
-
* { orders: [], alerts: [] },
|
|
63
|
-
* );
|
|
64
|
-
* ```
|
|
65
|
-
*/
|
|
66
|
-
export function createQueueBuilder() {
|
|
67
|
-
return taggedBuilder(QueueBuilder);
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Annotates `scope` with a non-fatal warning when a redrive policy is
|
|
71
|
-
* configured with `maxReceiveCount` below the AWS-recommended floor
|
|
72
|
-
* of {@link RECOMMENDED_MIN_MAX_RECEIVE_COUNT}.
|
|
73
|
-
*
|
|
74
|
-
* The builder owns the redrive policy directly, so this is a true
|
|
75
|
-
* check rather than a contextual reminder — the actual configured
|
|
76
|
-
* value is compared. Short-circuits on unresolved tokens so stacks
|
|
77
|
-
* that thread `maxReceiveCount` through CFN parameters aren't spammed.
|
|
78
|
-
*/
|
|
79
|
-
function warnIfLowMaxReceiveCount(scope, id, props) {
|
|
80
|
-
const dlq = props.deadLetterQueue;
|
|
81
|
-
if (!dlq)
|
|
82
|
-
return;
|
|
83
|
-
const maxReceiveCount = dlq.maxReceiveCount;
|
|
84
|
-
if (Token.isUnresolved(maxReceiveCount))
|
|
85
|
-
return;
|
|
86
|
-
if (maxReceiveCount >= RECOMMENDED_MIN_MAX_RECEIVE_COUNT)
|
|
87
|
-
return;
|
|
88
|
-
Annotations.of(scope).addWarningV2("@composurecdk/sqs:redrive-low-max-receive-count", `QueueBuilder "${id}": redrive policy maxReceiveCount is ${String(maxReceiveCount)}; ` +
|
|
89
|
-
`AWS recommends >= ${String(RECOMMENDED_MIN_MAX_RECEIVE_COUNT)} so the consumer ` +
|
|
90
|
-
`has room to retry before messages hit the dead-letter queue. ` +
|
|
91
|
-
`See https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html`);
|
|
42
|
+
// Callers only ever see the overloads above, which return the exact
|
|
43
|
+
// per-role `IQueueBuilder<R>`. The `unknown` here types the implementation
|
|
44
|
+
// body alone (the internal builder carries the full prop surface, not the
|
|
45
|
+
// narrowed per-role one) and is never observed at a call site — so it does
|
|
46
|
+
// not weaken the type of a builder passed to `compose`.
|
|
47
|
+
export function createQueueBuilder(role = "standard") {
|
|
48
|
+
const builder = taggedBuilder(QueueBuilder);
|
|
49
|
+
builder.queueRole(role);
|
|
50
|
+
return builder;
|
|
92
51
|
}
|
|
93
52
|
//# sourceMappingURL=queue-builder.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue-builder.js","sourceRoot":"","sources":["../../src/queue-builder.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"queue-builder.js","sourceRoot":"","sources":["../../src/queue-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,KAAK,EAAmB,MAAM,qBAAqB,CAAC;AAE1E,OAAO,EAAE,UAAU,EAAkB,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAuB,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAkB,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACnF,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AA+E3D,MAAM,YAAY;IAChB,KAAK,GAAuC,EAAE,CAAC;IACtC,aAAa,GAAqC,EAAE,CAAC;IAE9D,QAAQ,CACN,GAAW,EACX,SAAoF;QAEpF,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,sBAAsB,CAAS,GAAG,CAAC,CAAC,CAAC,CAAC;QAC5E,OAAO,IAAI,CAAC;IACd,CAAC;IAED,gCAAgC;IAChC,CAAC,UAAU,CAAC,CAAC,MAAoB;QAC/B,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC;IACnD,CAAC;IAED,KAAK,CAAC,KAAiB,EAAE,EAAU;QACjC,MAAM,EAAE,SAAS,EAAE,IAAI,GAAG,UAAU,EAAE,GAAG,UAAU,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACnE,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;QAC9B,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QAE5B,MAAM,WAAW,GAAG;YAClB,GAAG,cAAc;YACjB,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,CAAC;YAClC,GAAG,UAAU;YACb,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAChC,CAAC;QAEF,kBAAkB,CAAC,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;QAEjD,MAAM,EAAE,iBAAiB,EAAE,GAAG,UAAU,EAAE,GAAG,WAAW,CAAC;QACzD,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,UAAU,CAAC,CAAC;QAE/C,MAAM,OAAO,GAAG,GAAG;YACjB,CAAC,CAAC,eAAe,CAAC,KAAK,EAAE,WAAW,CAAC,eAAe,CAAC;YACrD,CAAC,CAAC,qBAAqB,CAAC;QAC1B,MAAM,MAAM,GAAG,iBAAiB,CAC9B,KAAK,EACL,EAAE,EACF,KAAK,EACL,iBAAiB,EACjB,IAAI,CAAC,aAAa,EAClB,OAAO,CACR,CAAC;QAEF,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAC3B,CAAC;CACF;AA0CD,oEAAoE;AACpE,2EAA2E;AAC3E,0EAA0E;AAC1E,2EAA2E;AAC3E,wDAAwD;AACxD,MAAM,UAAU,kBAAkB,CAAC,OAAkB,UAAU;IAC7D,MAAM,OAAO,GAAG,aAAa,CAA0C,YAAY,CAAC,CAAC;IACpF,OAA8D,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAChF,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { QueueProps } from "aws-cdk-lib/aws-sqs";
|
|
2
|
+
import type { QueueAlarmConfig } from "./queue-alarm-config.js";
|
|
3
|
+
/**
|
|
4
|
+
* A queue name for a FIFO queue. AWS requires the name of a FIFO queue to
|
|
5
|
+
* end in `.fifo`, so the constraint is expressed in the type — a
|
|
6
|
+
* non-conforming literal is a compile error at the call site rather than
|
|
7
|
+
* a synth failure. (Unresolved tokens are not supported as FIFO queue
|
|
8
|
+
* names by the CDK `Queue` construct itself.)
|
|
9
|
+
*
|
|
10
|
+
* @see https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/FIFO-queues.html
|
|
11
|
+
*/
|
|
12
|
+
export type FifoQueueName = `${string}.fifo`;
|
|
13
|
+
/**
|
|
14
|
+
* The {@link QueueProps} keys that only have meaning on a FIFO queue.
|
|
15
|
+
* These are omitted from the standard queue builder's prop surface and
|
|
16
|
+
* guarded at build time, so a queue with FIFO behaviour is always an
|
|
17
|
+
* explicit choice of the FIFO-aware entry points.
|
|
18
|
+
*/
|
|
19
|
+
export declare const FIFO_ONLY_PROP_KEYS: readonly ["fifo", "contentBasedDeduplication", "deduplicationScope", "fifoThroughputLimit"];
|
|
20
|
+
/** Union of the FIFO-only {@link QueueProps} keys. */
|
|
21
|
+
export type FifoOnlyPropKey = (typeof FIFO_ONLY_PROP_KEYS)[number];
|
|
22
|
+
/**
|
|
23
|
+
* Builder-only props every queue builder in this package layers on top
|
|
24
|
+
* of the CDK {@link QueueProps}. Defined once so the builders and the
|
|
25
|
+
* shared build core agree on what must be stripped before the props
|
|
26
|
+
* reach the `Queue` construct.
|
|
27
|
+
*/
|
|
28
|
+
export interface QueueBuilderExtensionProps {
|
|
29
|
+
/**
|
|
30
|
+
* Configuration for AWS-recommended CloudWatch alarms.
|
|
31
|
+
*
|
|
32
|
+
* By default, the builder creates recommended alarms with sensible
|
|
33
|
+
* thresholds for every applicable metric — which alarms apply depends
|
|
34
|
+
* on the queue's role (primary vs. dead-letter). Individual alarms can
|
|
35
|
+
* be customized or disabled. Set to `false` to disable all alarms.
|
|
36
|
+
*
|
|
37
|
+
* No alarm actions are configured by default since notification
|
|
38
|
+
* methods are user-specific. Access alarms from the build result
|
|
39
|
+
* or use an `afterBuild` hook to apply actions.
|
|
40
|
+
*
|
|
41
|
+
* @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#SQS
|
|
42
|
+
*/
|
|
43
|
+
recommendedAlarms?: QueueAlarmConfig | false;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Prop surface of `createQueueBuilder()` / `createQueueBuilder("standard")`:
|
|
47
|
+
* every CDK {@link QueueProps} property except the FIFO-only ones — a
|
|
48
|
+
* queue with FIFO behaviour is an explicit role choice
|
|
49
|
+
* (`createQueueBuilder("fifo")`), not a prop.
|
|
50
|
+
*/
|
|
51
|
+
export interface QueueBuilderProps extends Omit<QueueProps, FifoOnlyPropKey>, QueueBuilderExtensionProps {
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Prop surface of `createQueueBuilder("fifo")`. `fifo` itself is not
|
|
55
|
+
* settable — the role is the switch — and `queueName` is typed as
|
|
56
|
+
* {@link FifoQueueName}, so a name missing the AWS-required `.fifo`
|
|
57
|
+
* suffix is a compile error rather than a synth failure.
|
|
58
|
+
*/
|
|
59
|
+
export interface FifoQueueBuilderProps extends Omit<QueueProps, "fifo" | "queueName">, QueueBuilderExtensionProps {
|
|
60
|
+
/**
|
|
61
|
+
* Physical name of the FIFO queue. Must end in `.fifo` (AWS
|
|
62
|
+
* requirement, enforced by the type and validated at build for
|
|
63
|
+
* untyped callers). Omit to let CloudFormation generate a valid name.
|
|
64
|
+
*/
|
|
65
|
+
queueName?: FifoQueueName;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Prop surface of `createQueueBuilder("dlq")`: the standard surface (no
|
|
69
|
+
* FIFO-only props — a FIFO dead-letter queue is its own role,
|
|
70
|
+
* `"fifo-dlq"`) minus `deadLetterQueue`, because a dead-letter queue is
|
|
71
|
+
* the terminal destination for failed messages and must not carry its
|
|
72
|
+
* own redrive policy.
|
|
73
|
+
*/
|
|
74
|
+
export type DlqQueueBuilderProps = Omit<QueueBuilderProps, "deadLetterQueue">;
|
|
75
|
+
/**
|
|
76
|
+
* Prop surface of `createQueueBuilder("fifo-dlq")`: the FIFO surface
|
|
77
|
+
* (suffix-typed `queueName`, no settable `fifo`) minus
|
|
78
|
+
* `deadLetterQueue`, for the dead-letter queue of a FIFO source — AWS
|
|
79
|
+
* requires it to itself be FIFO.
|
|
80
|
+
*/
|
|
81
|
+
export type FifoDlqQueueBuilderProps = Omit<FifoQueueBuilderProps, "deadLetterQueue">;
|
|
82
|
+
/**
|
|
83
|
+
* Maps each {@link QueueRole} to its prop surface. `createQueueBuilder`
|
|
84
|
+
* uses this to give every role an exact fluent API — props that don't
|
|
85
|
+
* apply to a role simply don't exist on its builder type.
|
|
86
|
+
*/
|
|
87
|
+
export interface QueueBuilderPropsByRole {
|
|
88
|
+
standard: QueueBuilderProps;
|
|
89
|
+
fifo: FifoQueueBuilderProps;
|
|
90
|
+
dlq: DlqQueueBuilderProps;
|
|
91
|
+
"fifo-dlq": FifoDlqQueueBuilderProps;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=queue-props.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-props.d.ts","sourceRoot":"","sources":["../../src/queue-props.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GAAG,GAAG,MAAM,OAAO,CAAC;AAE7C;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,6FAKkB,CAAC;AAEnD,sDAAsD;AACtD,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnE;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;;;;;;;;;;;OAaG;IACH,iBAAiB,CAAC,EAAE,gBAAgB,GAAG,KAAK,CAAC;CAC9C;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBACf,SAAQ,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,EAAE,0BAA0B;CAAG;AAE1E;;;;;GAKG;AACH,MAAM,WAAW,qBACf,SAAQ,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,CAAC,EAAE,0BAA0B;IAC1E;;;;OAIG;IACH,SAAS,CAAC,EAAE,aAAa,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,iBAAiB,EAAE,iBAAiB,CAAC,CAAC;AAE9E;;;;;GAKG;AACH,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,qBAAqB,EAAE,iBAAiB,CAAC,CAAC;AAEtF;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,IAAI,EAAE,qBAAqB,CAAC;IAC5B,GAAG,EAAE,oBAAoB,CAAC;IAC1B,UAAU,EAAE,wBAAwB,CAAC;CACtC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The {@link QueueProps} keys that only have meaning on a FIFO queue.
|
|
3
|
+
* These are omitted from the standard queue builder's prop surface and
|
|
4
|
+
* guarded at build time, so a queue with FIFO behaviour is always an
|
|
5
|
+
* explicit choice of the FIFO-aware entry points.
|
|
6
|
+
*/
|
|
7
|
+
export const FIFO_ONLY_PROP_KEYS = [
|
|
8
|
+
"fifo",
|
|
9
|
+
"contentBasedDeduplication",
|
|
10
|
+
"deduplicationScope",
|
|
11
|
+
"fifoThroughputLimit",
|
|
12
|
+
];
|
|
13
|
+
//# sourceMappingURL=queue-props.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-props.js","sourceRoot":"","sources":["../../src/queue-props.ts"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,MAAM;IACN,2BAA2B;IAC3B,oBAAoB;IACpB,qBAAqB;CAC2B,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The role a queue plays in its system, chosen at the factory:
|
|
3
|
+
* `createQueueBuilder(role)`. The role selects the builder's prop
|
|
4
|
+
* surface (each role exposes only the props that apply to it), its
|
|
5
|
+
* defaults, its recommended-alarm profile, and its validation:
|
|
6
|
+
*
|
|
7
|
+
* - `"standard"` — a primary, consumer-fed standard queue (the
|
|
8
|
+
* default). FIFO-only props are excluded.
|
|
9
|
+
* - `"fifo"` — a primary FIFO queue: `fifo: true` always, `queueName`
|
|
10
|
+
* typed to require the `.fifo` suffix, high-throughput coherence
|
|
11
|
+
* validated.
|
|
12
|
+
* - `"dlq"` — a standard dead-letter queue: 14-day retention, inverted
|
|
13
|
+
* alarm set (any visible message alerts), no `deadLetterQueue` — a
|
|
14
|
+
* DLQ is a terminal destination.
|
|
15
|
+
* - `"fifo-dlq"` — a FIFO dead-letter queue for a FIFO source (AWS
|
|
16
|
+
* requires the DLQ of a FIFO queue to be FIFO): combines the FIFO
|
|
17
|
+
* surface with the DLQ defaults and alarms.
|
|
18
|
+
*
|
|
19
|
+
* Modeled as data rather than separate factories so that roles compose
|
|
20
|
+
* (`"fifo-dlq"` is a role, not a fourth entry point) and the choice is
|
|
21
|
+
* copy-safe, inspectable state on the builder.
|
|
22
|
+
*/
|
|
23
|
+
export type QueueRole = "standard" | "fifo" | "dlq" | "fifo-dlq";
|
|
24
|
+
/** Whether the role builds a FIFO queue. */
|
|
25
|
+
export declare function isFifoRole(role: QueueRole): boolean;
|
|
26
|
+
/** Whether the role builds a dead-letter queue. */
|
|
27
|
+
export declare function isDlqRole(role: QueueRole): boolean;
|
|
28
|
+
//# sourceMappingURL=queue-role.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-role.d.ts","sourceRoot":"","sources":["../../src/queue-role.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,KAAK,GAAG,UAAU,CAAC;AAEjE,4CAA4C;AAC5C,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAEnD;AAED,mDAAmD;AACnD,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAElD"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Whether the role builds a FIFO queue. */
|
|
2
|
+
export function isFifoRole(role) {
|
|
3
|
+
return role === "fifo" || role === "fifo-dlq";
|
|
4
|
+
}
|
|
5
|
+
/** Whether the role builds a dead-letter queue. */
|
|
6
|
+
export function isDlqRole(role) {
|
|
7
|
+
return role === "dlq" || role === "fifo-dlq";
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=queue-role.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-role.js","sourceRoot":"","sources":["../../src/queue-role.ts"],"names":[],"mappings":"AAwBA,4CAA4C;AAC5C,MAAM,UAAU,UAAU,CAAC,IAAe;IACxC,OAAO,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,UAAU,CAAC;AAChD,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,SAAS,CAAC,IAAe;IACvC,OAAO,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,UAAU,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { QueueProps } from "aws-cdk-lib/aws-sqs";
|
|
2
|
+
import type { IConstruct } from "constructs";
|
|
3
|
+
import { type QueueRole } from "./queue-role.js";
|
|
4
|
+
/**
|
|
5
|
+
* Runs every build-time check for a queue in the given role against its
|
|
6
|
+
* merged props. This is the single validation entry point — each guard
|
|
7
|
+
* owns its own applicability (self-gating on the role or on the props),
|
|
8
|
+
* so a new role or a new invariant is entirely a change to this module,
|
|
9
|
+
* never new branching in the builder.
|
|
10
|
+
*/
|
|
11
|
+
export declare function validateQueueProps(scope: IConstruct, id: string, role: QueueRole, props: Partial<QueueProps>): void;
|
|
12
|
+
//# sourceMappingURL=queue-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-validation.d.ts","sourceRoot":"","sources":["../../src/queue-validation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAmB,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,EAAyB,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAExE;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,UAAU,EACjB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,SAAS,EACf,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,GACzB,IAAI,CASN"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { Annotations, Token } from "aws-cdk-lib";
|
|
2
|
+
import { DeduplicationScope, FifoThroughputLimit } from "aws-cdk-lib/aws-sqs";
|
|
3
|
+
import { FIFO_ONLY_PROP_KEYS } from "./queue-props.js";
|
|
4
|
+
import { isDlqRole, isFifoRole } from "./queue-role.js";
|
|
5
|
+
/**
|
|
6
|
+
* Runs every build-time check for a queue in the given role against its
|
|
7
|
+
* merged props. This is the single validation entry point — each guard
|
|
8
|
+
* owns its own applicability (self-gating on the role or on the props),
|
|
9
|
+
* so a new role or a new invariant is entirely a change to this module,
|
|
10
|
+
* never new branching in the builder.
|
|
11
|
+
*/
|
|
12
|
+
export function validateQueueProps(scope, id, role, props) {
|
|
13
|
+
if (!isFifoRole(role))
|
|
14
|
+
throwIfFifoPropsOnNonFifoRole(id, role, props);
|
|
15
|
+
validateFifoQueueProps(id, props);
|
|
16
|
+
if (isDlqRole(role)) {
|
|
17
|
+
throwIfRedriveOnDlqRole(id, role, props);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
throwIfRedriveTargetFifoMismatch(id, isFifoRole(role), props.deadLetterQueue);
|
|
21
|
+
warnIfLowMaxReceiveCount(scope, id, props);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* AWS-recommended minimum for `maxReceiveCount` on an SQS redrive
|
|
26
|
+
* policy. A consumer needs a few retries before SQS gives up and
|
|
27
|
+
* forwards the message to the dead-letter queue; anything below this
|
|
28
|
+
* tends to surface as a flood of "poison" messages from transient
|
|
29
|
+
* errors that would have succeeded on retry.
|
|
30
|
+
*
|
|
31
|
+
* @see https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html
|
|
32
|
+
*/
|
|
33
|
+
const RECOMMENDED_MIN_MAX_RECEIVE_COUNT = 5;
|
|
34
|
+
/**
|
|
35
|
+
* Annotates `scope` with a non-fatal warning when a redrive policy is
|
|
36
|
+
* configured with `maxReceiveCount` below the AWS-recommended floor
|
|
37
|
+
* of {@link RECOMMENDED_MIN_MAX_RECEIVE_COUNT}.
|
|
38
|
+
*
|
|
39
|
+
* The builder owns the redrive policy directly, so this is a true
|
|
40
|
+
* check rather than a contextual reminder — the actual configured
|
|
41
|
+
* value is compared. Short-circuits on unresolved tokens so stacks
|
|
42
|
+
* that thread `maxReceiveCount` through CFN parameters aren't spammed.
|
|
43
|
+
*/
|
|
44
|
+
function warnIfLowMaxReceiveCount(scope, id, props) {
|
|
45
|
+
const dlq = props.deadLetterQueue;
|
|
46
|
+
if (!dlq)
|
|
47
|
+
return;
|
|
48
|
+
const maxReceiveCount = dlq.maxReceiveCount;
|
|
49
|
+
if (Token.isUnresolved(maxReceiveCount))
|
|
50
|
+
return;
|
|
51
|
+
if (maxReceiveCount >= RECOMMENDED_MIN_MAX_RECEIVE_COUNT)
|
|
52
|
+
return;
|
|
53
|
+
Annotations.of(scope).addWarningV2("@composurecdk/sqs:redrive-low-max-receive-count", `QueueBuilder "${id}": redrive policy maxReceiveCount is ${String(maxReceiveCount)}; ` +
|
|
54
|
+
`AWS recommends >= ${String(RECOMMENDED_MIN_MAX_RECEIVE_COUNT)} so the consumer ` +
|
|
55
|
+
`has room to retry before messages hit the dead-letter queue. ` +
|
|
56
|
+
`See https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html`);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Throws when a FIFO-only prop reaches a non-FIFO role — the role's
|
|
60
|
+
* typed surface omits them, so this catches untyped (JavaScript)
|
|
61
|
+
* callers and points them at the FIFO roles instead of letting the
|
|
62
|
+
* queue synth with primary-queue alarm defaults that don't fit a FIFO
|
|
63
|
+
* queue.
|
|
64
|
+
*/
|
|
65
|
+
function throwIfFifoPropsOnNonFifoRole(id, role, props) {
|
|
66
|
+
for (const key of FIFO_ONLY_PROP_KEYS) {
|
|
67
|
+
if (props[key] === undefined)
|
|
68
|
+
continue;
|
|
69
|
+
throw new Error(`QueueBuilder "${id}": "${key}" is FIFO-specific and not supported on role ` +
|
|
70
|
+
`"${role}". Create the builder with ` +
|
|
71
|
+
`createQueueBuilder("${isDlqRole(role) ? "fifo-dlq" : "fifo"}") instead.`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Validates the FIFO-specific configuration invariants that would
|
|
76
|
+
* otherwise surface as a synth or deploy failure:
|
|
77
|
+
*
|
|
78
|
+
* - A non-token `queueName` must end in `.fifo` (AWS requirement).
|
|
79
|
+
* - High-throughput mode (`fifoThroughputLimit: PER_MESSAGE_GROUP_ID`)
|
|
80
|
+
* requires `deduplicationScope: MESSAGE_GROUP`.
|
|
81
|
+
*
|
|
82
|
+
* No-op unless `props.fifo` is set (the FIFO roles merge `fifo: true`
|
|
83
|
+
* before calling), so every role calls it unconditionally — the
|
|
84
|
+
* validator owns both the invariants and their applicability.
|
|
85
|
+
*
|
|
86
|
+
* These are configuration errors, not advisory best practice, so they
|
|
87
|
+
* throw (unlike the `maxReceiveCount` warning).
|
|
88
|
+
*
|
|
89
|
+
* @see https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/high-throughput-fifo.html
|
|
90
|
+
*/
|
|
91
|
+
function validateFifoQueueProps(id, props) {
|
|
92
|
+
const { fifo, queueName, fifoThroughputLimit, deduplicationScope } = props;
|
|
93
|
+
if (!fifo)
|
|
94
|
+
return;
|
|
95
|
+
if (queueName !== undefined && !Token.isUnresolved(queueName) && !queueName.endsWith(".fifo")) {
|
|
96
|
+
throw new Error(`QueueBuilder "${id}": FIFO queues require a queueName ending in ".fifo"; ` +
|
|
97
|
+
`got "${queueName}". Omit queueName to let CloudFormation generate one.`);
|
|
98
|
+
}
|
|
99
|
+
if (fifoThroughputLimit === FifoThroughputLimit.PER_MESSAGE_GROUP_ID &&
|
|
100
|
+
deduplicationScope !== DeduplicationScope.MESSAGE_GROUP) {
|
|
101
|
+
throw new Error(`QueueBuilder "${id}": fifoThroughputLimit=PER_MESSAGE_GROUP_ID (high-throughput ` +
|
|
102
|
+
`FIFO) requires deduplicationScope=MESSAGE_GROUP. ` +
|
|
103
|
+
`See https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/high-throughput-fifo.html`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Throws when a queue's redrive target does not match its own FIFO-ness.
|
|
108
|
+
* AWS requires the dead-letter queue of a FIFO queue to be FIFO and the
|
|
109
|
+
* dead-letter queue of a standard queue to be standard; the mismatch
|
|
110
|
+
* otherwise fails at deploy time, after a successful synth.
|
|
111
|
+
*
|
|
112
|
+
* @see https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html
|
|
113
|
+
*/
|
|
114
|
+
function throwIfRedriveTargetFifoMismatch(id, fifo, deadLetterQueue) {
|
|
115
|
+
if (!deadLetterQueue)
|
|
116
|
+
return;
|
|
117
|
+
if (deadLetterQueue.queue.fifo === fifo)
|
|
118
|
+
return;
|
|
119
|
+
const [queueKind, targetKind] = fifo ? ["FIFO", "standard"] : ["standard", "FIFO"];
|
|
120
|
+
throw new Error(`QueueBuilder "${id}": a ${queueKind} queue cannot redrive to the ${targetKind} ` +
|
|
121
|
+
`dead-letter queue "${deadLetterQueue.queue.node.id}" — AWS requires the ` +
|
|
122
|
+
`dead-letter queue to match the source queue's type. ` +
|
|
123
|
+
`See https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html`);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Throws when a dead-letter-role queue is given its own
|
|
127
|
+
* `deadLetterQueue` redrive policy — a DLQ is the terminal destination
|
|
128
|
+
* for failed messages; a queue that redrives elsewhere is a primary
|
|
129
|
+
* queue and belongs to the `"standard"` or `"fifo"` role.
|
|
130
|
+
*/
|
|
131
|
+
function throwIfRedriveOnDlqRole(id, role, props) {
|
|
132
|
+
if (!props.deadLetterQueue)
|
|
133
|
+
return;
|
|
134
|
+
throw new Error(`QueueBuilder "${id}": deadLetterQueue is not supported on role "${role}" — a ` +
|
|
135
|
+
`dead-letter queue is the terminal destination for failed messages. If this queue ` +
|
|
136
|
+
`needs its own redrive policy it is a primary queue: create the builder with ` +
|
|
137
|
+
`createQueueBuilder("${isFifoRole(role) ? "fifo" : "standard"}").`);
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=queue-validation.js.map
|