@composurecdk/budgets 0.5.1 → 0.6.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 +21 -12
- package/dist/budget-alarms.d.ts.map +1 -1
- package/dist/budget-alarms.js +2 -0
- package/dist/budget-alarms.js.map +1 -1
- package/dist/budget-builder.d.ts +22 -10
- package/dist/budget-builder.d.ts.map +1 -1
- package/dist/budget-builder.js +33 -11
- package/dist/budget-builder.js.map +1 -1
- package/dist/currency.d.ts +21 -0
- package/dist/currency.d.ts.map +1 -0
- package/dist/currency.js +35 -0
- package/dist/currency.js.map +1 -0
- package/dist/defaults.d.ts +14 -0
- package/dist/defaults.d.ts.map +1 -1
- package/dist/defaults.js +48 -0
- package/dist/defaults.js.map +1 -1
- package/dist/email.d.ts +28 -0
- package/dist/email.d.ts.map +1 -0
- package/dist/email.js +30 -0
- package/dist/email.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/notifications.d.ts +41 -13
- package/dist/notifications.d.ts.map +1 -1
- package/dist/notifications.js +12 -9
- package/dist/notifications.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -7,12 +7,12 @@ This package provides a fluent builder for `AWS::Budgets::Budget` with well-arch
|
|
|
7
7
|
## Budget Builder
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
|
-
import { createBudgetBuilder } from "@composurecdk/budgets";
|
|
10
|
+
import { createBudgetBuilder, email } from "@composurecdk/budgets";
|
|
11
11
|
|
|
12
12
|
const budget = createBudgetBuilder()
|
|
13
13
|
.budgetName("AgentBudget")
|
|
14
|
-
.limit({ amount: 50
|
|
15
|
-
.notifyOnActual(100, "ops@example.com")
|
|
14
|
+
.limit({ amount: 50 })
|
|
15
|
+
.notifyOnActual(100, { emails: [email("ops@example.com")] })
|
|
16
16
|
.build(stack, "AgentBudget");
|
|
17
17
|
```
|
|
18
18
|
|
|
@@ -31,34 +31,43 @@ Every field on [CfnBudget.BudgetDataProperty](https://docs.aws.amazon.com/cdk/ap
|
|
|
31
31
|
|
|
32
32
|
### Notifications
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Each notification takes a `NotifySubscribers` object with **at most one** `sns` topic and a list of validated `emails` — AWS Budgets caps every notification at 1 SNS subscriber plus up to 10 EMAIL subscribers. The shape encodes that constraint in the type system: passing two SNS topics is unrepresentable.
|
|
35
35
|
|
|
36
36
|
```ts
|
|
37
|
+
import { email } from "@composurecdk/budgets";
|
|
38
|
+
|
|
37
39
|
createBudgetBuilder()
|
|
38
40
|
.limit({ amount: 100 })
|
|
39
|
-
.notifyOnActual(80, "ops@example.com") // 80% ACTUAL → email
|
|
40
|
-
.notifyOnForecasted(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
.notifyOnActual(80, { emails: [email("ops@example.com")] }) // 80% ACTUAL → email
|
|
42
|
+
.notifyOnForecasted(100, { sns: ref("alerts", (r) => r.topic) }) // 100% FORECASTED → SNS topic
|
|
43
|
+
.notifyOnActual(100, {
|
|
44
|
+
sns: killSwitchTopic,
|
|
45
|
+
emails: [email("oncall@example.com")],
|
|
46
|
+
}) // hard breach → automation + human
|
|
44
47
|
.addNotification({
|
|
45
48
|
notificationType: "ACTUAL",
|
|
46
49
|
threshold: 120,
|
|
47
50
|
thresholdType: "ABSOLUTE_VALUE",
|
|
48
|
-
subscribers: ["oncall@example.com"],
|
|
51
|
+
subscribers: { emails: [email("oncall@example.com")] },
|
|
49
52
|
});
|
|
50
53
|
```
|
|
51
54
|
|
|
52
|
-
|
|
55
|
+
Email addresses must be constructed via `email(string)`, which validates and brands the value — bare strings are rejected at compile time. The `sns` slot accepts an `ITopic` instance or a `Resolvable<ITopic>` reference to a topic owned by a sibling component.
|
|
53
56
|
|
|
54
57
|
### Recommended Thresholds
|
|
55
58
|
|
|
56
59
|
```ts
|
|
57
|
-
createBudgetBuilder()
|
|
60
|
+
createBudgetBuilder()
|
|
61
|
+
.limit({ amount: 50 })
|
|
62
|
+
.withRecommendedThresholds({ emails: [email("ops@example.com")] });
|
|
58
63
|
```
|
|
59
64
|
|
|
60
65
|
Applies the AWS Cost Optimization pillar defaults: `ACTUAL` at 80% and `FORECASTED` at 100%.
|
|
61
66
|
|
|
67
|
+
### Currency
|
|
68
|
+
|
|
69
|
+
`limit({ amount, unit })` validates `unit` against the AWS-Budgets-supported ISO 4217 set (`DEFAULT_BUDGET_CURRENCIES`). Typos like `"ZZZ"` throw at synth instead of mid-deploy. Because the synth context cannot see an account's billing currency, anything other than `"USD"` also emits a non-fatal warning (`@composurecdk/budgets:limit-currency`) — verify the configured unit matches your billing currency before deploying.
|
|
70
|
+
|
|
62
71
|
## Defaults
|
|
63
72
|
|
|
64
73
|
| Property | Default | Rationale |
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"budget-alarms.d.ts","sourceRoot":"","sources":["../src/budget-alarms.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"budget-alarms.d.ts","sourceRoot":"","sources":["../src/budget-alarms.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAK3D;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,iBAAiB,GAAG,SAAS,GACpC,eAAe,EAAE,CA0BnB"}
|
package/dist/budget-alarms.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Duration } from "aws-cdk-lib";
|
|
2
2
|
import { ComparisonOperator, Metric, TreatMissingData } from "aws-cdk-lib/aws-cloudwatch";
|
|
3
|
+
import { assertValidBudgetCurrency } from "./currency.js";
|
|
3
4
|
const BILLING_METRIC_PERIOD = Duration.hours(6);
|
|
4
5
|
/**
|
|
5
6
|
* Resolves the recommended alarm configuration into fully-resolved
|
|
@@ -26,6 +27,7 @@ export function resolveBudgetAlarmDefinitions(config) {
|
|
|
26
27
|
return [];
|
|
27
28
|
const cfg = config.estimatedCharges;
|
|
28
29
|
const currency = cfg.currency ?? "USD";
|
|
30
|
+
assertValidBudgetCurrency(currency, `EstimatedChargesAlarmConfig: currency`);
|
|
29
31
|
return [
|
|
30
32
|
{
|
|
31
33
|
key: "estimatedCharges",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"budget-alarms.js","sourceRoot":"","sources":["../src/budget-alarms.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"budget-alarms.js","sourceRoot":"","sources":["../src/budget-alarms.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAG1F,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAE1D,MAAM,qBAAqB,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEhD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,6BAA6B,CAC3C,MAAqC;IAErC,IAAI,MAAM,EAAE,OAAO,KAAK,KAAK;QAAE,OAAO,EAAE,CAAC;IACzC,IAAI,CAAC,MAAM,EAAE,gBAAgB;QAAE,OAAO,EAAE,CAAC;IAEzC,MAAM,GAAG,GAAG,MAAM,CAAC,gBAAgB,CAAC;IACpC,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,IAAI,KAAK,CAAC;IACvC,yBAAyB,CAAC,QAAQ,EAAE,uCAAuC,CAAC,CAAC;IAE7E,OAAO;QACL;YACE,GAAG,EAAE,kBAAkB;YACvB,MAAM,EAAE,IAAI,MAAM,CAAC;gBACjB,SAAS,EAAE,aAAa;gBACxB,UAAU,EAAE,kBAAkB;gBAC9B,aAAa,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE;gBACrC,SAAS,EAAE,SAAS;gBACpB,MAAM,EAAE,qBAAqB;aAC9B,CAAC;YACF,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,kBAAkB,EAAE,kBAAkB,CAAC,sBAAsB;YAC7D,iBAAiB,EAAE,GAAG,CAAC,iBAAiB,IAAI,CAAC;YAC7C,iBAAiB,EAAE,GAAG,CAAC,iBAAiB,IAAI,CAAC;YAC7C,gBAAgB,EAAE,GAAG,CAAC,gBAAgB,IAAI,gBAAgB,CAAC,aAAa;YACxE,WAAW,EAAE,4CAA4C,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,QAAQ,kDAAkD;SAC7I;KACF,CAAC;AACJ,CAAC"}
|
package/dist/budget-builder.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { IConstruct } from "constructs";
|
|
|
5
5
|
import { type IBuilder, type Lifecycle } from "@composurecdk/core";
|
|
6
6
|
import { AlarmDefinitionBuilder } from "@composurecdk/cloudwatch";
|
|
7
7
|
import type { BudgetAlarmConfig } from "./alarm-config.js";
|
|
8
|
-
import { type
|
|
8
|
+
import { type NotificationEntry, type NotifySubscribers } from "./notifications.js";
|
|
9
9
|
/**
|
|
10
10
|
* Spend limit for a cost or usage budget.
|
|
11
11
|
*/
|
|
@@ -15,6 +15,12 @@ export interface BudgetLimit {
|
|
|
15
15
|
/**
|
|
16
16
|
* Currency or usage unit. Defaults to
|
|
17
17
|
* {@link BUDGET_DEFAULTS.limitUnit} when omitted.
|
|
18
|
+
*
|
|
19
|
+
* For `COST` budgets, must be a recognised AWS Budgets currency
|
|
20
|
+
* (validated at synth — see {@link DEFAULT_BUDGET_CURRENCIES}). The
|
|
21
|
+
* builder also emits a non-fatal warning when this is anything other
|
|
22
|
+
* than `"USD"`, since the account's billing currency isn't visible
|
|
23
|
+
* at synth and a mismatch is rejected at deploy time.
|
|
18
24
|
*/
|
|
19
25
|
unit?: string;
|
|
20
26
|
}
|
|
@@ -108,9 +114,12 @@ export interface BudgetBuilderResult {
|
|
|
108
114
|
* ```ts
|
|
109
115
|
* createBudgetBuilder()
|
|
110
116
|
* .budgetName("AgentBudget")
|
|
111
|
-
* .limit({ amount: 50
|
|
112
|
-
* .notifyOnActual(100,
|
|
113
|
-
*
|
|
117
|
+
* .limit({ amount: 50 })
|
|
118
|
+
* .notifyOnActual(100, {
|
|
119
|
+
* emails: [email("ops@example.com")],
|
|
120
|
+
* sns: ref("alerts", r => r.topic),
|
|
121
|
+
* })
|
|
122
|
+
* .withRecommendedThresholds({ emails: [email("ops@example.com")] })
|
|
114
123
|
* .build(stack, "AgentBudget");
|
|
115
124
|
* ```
|
|
116
125
|
*/
|
|
@@ -124,18 +133,21 @@ declare class BudgetBuilder implements Lifecycle<BudgetBuilderResult> {
|
|
|
124
133
|
*
|
|
125
134
|
* @param thresholdPercent - Percentage of the budget limit (e.g. `80`).
|
|
126
135
|
* For absolute-value thresholds, use {@link addNotification} directly.
|
|
127
|
-
* @param subscribers -
|
|
128
|
-
*
|
|
136
|
+
* @param subscribers - Up to one SNS topic plus up to ten validated
|
|
137
|
+
* email addresses. AWS Budgets caps each notification at 1 SNS + up
|
|
138
|
+
* to 10 EMAIL subscribers.
|
|
129
139
|
*/
|
|
130
|
-
notifyOnActual(thresholdPercent: number,
|
|
140
|
+
notifyOnActual(thresholdPercent: number, subscribers: NotifySubscribers): this;
|
|
131
141
|
/**
|
|
132
142
|
* Add a notification that fires when FORECASTED spend crosses the
|
|
133
143
|
* given percentage of the budget limit.
|
|
134
144
|
*
|
|
135
145
|
* @param thresholdPercent - Percentage of the budget limit (e.g. `100`).
|
|
136
146
|
* For absolute-value thresholds, use {@link addNotification} directly.
|
|
147
|
+
* @param subscribers - Up to one SNS topic plus up to ten validated
|
|
148
|
+
* email addresses.
|
|
137
149
|
*/
|
|
138
|
-
notifyOnForecasted(thresholdPercent: number,
|
|
150
|
+
notifyOnForecasted(thresholdPercent: number, subscribers: NotifySubscribers): this;
|
|
139
151
|
/**
|
|
140
152
|
* Raw notification passthrough for callers that need the full
|
|
141
153
|
* CloudFormation surface (e.g. absolute-value thresholds).
|
|
@@ -148,11 +160,11 @@ declare class BudgetBuilder implements Lifecycle<BudgetBuilderResult> {
|
|
|
148
160
|
* - `FORECASTED` at 100% — trending-over-budget alert.
|
|
149
161
|
*
|
|
150
162
|
* Must be called with at least one subscriber; the same subscriber
|
|
151
|
-
*
|
|
163
|
+
* set is used for both thresholds.
|
|
152
164
|
*
|
|
153
165
|
* @see https://docs.aws.amazon.com/cost-management/latest/userguide/budgets-best-practices.html
|
|
154
166
|
*/
|
|
155
|
-
withRecommendedThresholds(
|
|
167
|
+
withRecommendedThresholds(subscribers: NotifySubscribers): this;
|
|
156
168
|
/**
|
|
157
169
|
* Adds a custom CloudWatch alarm against the budget. The configure
|
|
158
170
|
* callback receives a fresh {@link AlarmDefinitionBuilder} pre-set with
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"budget-builder.d.ts","sourceRoot":"","sources":["../src/budget-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAuB,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,KAAK,EAAU,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAW,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"budget-builder.d.ts","sourceRoot":"","sources":["../src/budget-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAuB,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,KAAK,EAAU,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAW,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAI3D,OAAO,EACL,KAAK,iBAAiB,EAEtB,KAAK,iBAAiB,EAGvB,MAAM,oBAAoB,CAAC;AAK5B;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,qBAAqB;IACrB,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yBAAyB;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACvC,8CAA8C;IAC9C,SAAS,CAAC,EAAE,SAAS,CAAC,iBAAiB,CAAC;IACxC;;;;;;;;;;;;;;;;;OAiBG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,KAAK,CAAC;CAC/C;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,4CAA4C;IAC5C,MAAM,EAAE,SAAS,CAAC;IAClB;;;;;;;OAOG;IACH,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC3C;;;;;;;;;;OAUG;IACH,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC;AAEzE,cAAM,aAAc,YAAW,SAAS,CAAC,mBAAmB,CAAC;;IAC3D,KAAK,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAM;IAIxC;;;;;;;;;OASG;IACH,cAAc,CAAC,gBAAgB,EAAE,MAAM,EAAE,WAAW,EAAE,iBAAiB,GAAG,IAAI;IAI9E;;;;;;;;OAQG;IACH,kBAAkB,CAAC,gBAAgB,EAAE,MAAM,EAAE,WAAW,EAAE,iBAAiB,GAAG,IAAI;IAIlF;;;OAGG;IACH,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI;IAK/C;;;;;;;;;;OAUG;IACH,yBAAyB,CAAC,WAAW,EAAE,iBAAiB,GAAG,IAAI;IAc/D;;;;;;;;;;;;;OAaG;IACH,QAAQ,CACN,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,CAAC,KAAK,EAAE,sBAAsB,CAAC,SAAS,CAAC,KAAK,sBAAsB,CAAC,SAAS,CAAC,GACzF,IAAI;IAKP,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAAG,mBAAmB;CAkGhG;AAWD;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,cAAc,CAEpD"}
|
package/dist/budget-builder.js
CHANGED
|
@@ -2,9 +2,11 @@ import { CfnBudget } from "aws-cdk-lib/aws-budgets";
|
|
|
2
2
|
import { Builder } from "@composurecdk/core";
|
|
3
3
|
import { AlarmDefinitionBuilder } from "@composurecdk/cloudwatch";
|
|
4
4
|
import { buildBudgetAlarms } from "./budget-alarm-builder.js";
|
|
5
|
+
import { assertValidBudgetCurrency, warnIfNonUsdCurrency } from "./currency.js";
|
|
5
6
|
import { BUDGET_DEFAULTS } from "./defaults.js";
|
|
6
7
|
import { resolveSubscribers, toCfnNotificationWithSubscribers, } from "./notifications.js";
|
|
7
8
|
import { createBudgetsTopicPolicies } from "./topic-policy.js";
|
|
9
|
+
const MAX_EMAILS_PER_NOTIFICATION = 10;
|
|
8
10
|
class BudgetBuilder {
|
|
9
11
|
props = {};
|
|
10
12
|
#notifications = [];
|
|
@@ -15,10 +17,11 @@ class BudgetBuilder {
|
|
|
15
17
|
*
|
|
16
18
|
* @param thresholdPercent - Percentage of the budget limit (e.g. `80`).
|
|
17
19
|
* For absolute-value thresholds, use {@link addNotification} directly.
|
|
18
|
-
* @param subscribers -
|
|
19
|
-
*
|
|
20
|
+
* @param subscribers - Up to one SNS topic plus up to ten validated
|
|
21
|
+
* email addresses. AWS Budgets caps each notification at 1 SNS + up
|
|
22
|
+
* to 10 EMAIL subscribers.
|
|
20
23
|
*/
|
|
21
|
-
notifyOnActual(thresholdPercent,
|
|
24
|
+
notifyOnActual(thresholdPercent, subscribers) {
|
|
22
25
|
return this.#addPercentageNotification("ACTUAL", thresholdPercent, subscribers);
|
|
23
26
|
}
|
|
24
27
|
/**
|
|
@@ -27,8 +30,10 @@ class BudgetBuilder {
|
|
|
27
30
|
*
|
|
28
31
|
* @param thresholdPercent - Percentage of the budget limit (e.g. `100`).
|
|
29
32
|
* For absolute-value thresholds, use {@link addNotification} directly.
|
|
33
|
+
* @param subscribers - Up to one SNS topic plus up to ten validated
|
|
34
|
+
* email addresses.
|
|
30
35
|
*/
|
|
31
|
-
notifyOnForecasted(thresholdPercent,
|
|
36
|
+
notifyOnForecasted(thresholdPercent, subscribers) {
|
|
32
37
|
return this.#addPercentageNotification("FORECASTED", thresholdPercent, subscribers);
|
|
33
38
|
}
|
|
34
39
|
/**
|
|
@@ -46,12 +51,12 @@ class BudgetBuilder {
|
|
|
46
51
|
* - `FORECASTED` at 100% — trending-over-budget alert.
|
|
47
52
|
*
|
|
48
53
|
* Must be called with at least one subscriber; the same subscriber
|
|
49
|
-
*
|
|
54
|
+
* set is used for both thresholds.
|
|
50
55
|
*
|
|
51
56
|
* @see https://docs.aws.amazon.com/cost-management/latest/userguide/budgets-best-practices.html
|
|
52
57
|
*/
|
|
53
|
-
withRecommendedThresholds(
|
|
54
|
-
if (subscribers
|
|
58
|
+
withRecommendedThresholds(subscribers) {
|
|
59
|
+
if (!hasAnySubscriber(subscribers)) {
|
|
55
60
|
throw new Error(`BudgetBuilder: withRecommendedThresholds(...) must be called with at least one subscriber.`);
|
|
56
61
|
}
|
|
57
62
|
const { actualPercent, forecastedPercent } = BUDGET_DEFAULTS.recommendedThresholds;
|
|
@@ -84,7 +89,12 @@ class BudgetBuilder {
|
|
|
84
89
|
if (requiresLimit && !budgetProps.limit) {
|
|
85
90
|
throw new Error(`BudgetBuilder "${id}": limit({ amount, unit }) must be set for ${budgetType} budgets.`);
|
|
86
91
|
}
|
|
87
|
-
const
|
|
92
|
+
const limitUnit = budgetProps.limit?.unit ?? BUDGET_DEFAULTS.limitUnit;
|
|
93
|
+
if (budgetType === "COST" && budgetProps.limit) {
|
|
94
|
+
assertValidBudgetCurrency(limitUnit, `BudgetBuilder "${id}": limit unit`);
|
|
95
|
+
warnIfNonUsdCurrency(scope, limitUnit, `BudgetBuilder "${id}": limit unit`);
|
|
96
|
+
}
|
|
97
|
+
const { notificationsWithSubscribers, snsTopics } = this.#buildNotifications(id, context);
|
|
88
98
|
const budgetData = {
|
|
89
99
|
budgetName: budgetProps.budgetName,
|
|
90
100
|
budgetType,
|
|
@@ -93,7 +103,7 @@ class BudgetBuilder {
|
|
|
93
103
|
? {
|
|
94
104
|
budgetLimit: {
|
|
95
105
|
amount: budgetProps.limit.amount,
|
|
96
|
-
unit:
|
|
106
|
+
unit: limitUnit,
|
|
97
107
|
},
|
|
98
108
|
}
|
|
99
109
|
: {}),
|
|
@@ -113,16 +123,21 @@ class BudgetBuilder {
|
|
|
113
123
|
return { budget, topicPolicies, alarms };
|
|
114
124
|
}
|
|
115
125
|
#addPercentageNotification(notificationType, thresholdPercent, subscribers) {
|
|
116
|
-
if (subscribers
|
|
126
|
+
if (!hasAnySubscriber(subscribers)) {
|
|
117
127
|
throw new Error(`BudgetBuilder: ${notificationType} notification at ${String(thresholdPercent)}% requires at least one subscriber.`);
|
|
118
128
|
}
|
|
119
129
|
this.#notifications.push({ notificationType, threshold: thresholdPercent, subscribers });
|
|
120
130
|
return this;
|
|
121
131
|
}
|
|
122
|
-
#buildNotifications(context) {
|
|
132
|
+
#buildNotifications(id, context) {
|
|
123
133
|
const notificationsWithSubscribers = [];
|
|
124
134
|
const allSnsTopics = [];
|
|
125
135
|
for (const entry of this.#notifications) {
|
|
136
|
+
const emailCount = entry.subscribers.emails?.length ?? 0;
|
|
137
|
+
if (emailCount > MAX_EMAILS_PER_NOTIFICATION) {
|
|
138
|
+
throw new Error(`BudgetBuilder "${id}": ${describeNotification(entry)} has ${String(emailCount)} email subscribers; ` +
|
|
139
|
+
`AWS Budgets allows at most ${String(MAX_EMAILS_PER_NOTIFICATION)} email subscribers per notification (plus up to 1 SNS topic).`);
|
|
140
|
+
}
|
|
126
141
|
const resolved = resolveSubscribers(entry.subscribers, context);
|
|
127
142
|
notificationsWithSubscribers.push(toCfnNotificationWithSubscribers(entry, resolved.cfn));
|
|
128
143
|
allSnsTopics.push(...resolved.snsTopics);
|
|
@@ -130,6 +145,13 @@ class BudgetBuilder {
|
|
|
130
145
|
return { notificationsWithSubscribers, snsTopics: allSnsTopics };
|
|
131
146
|
}
|
|
132
147
|
}
|
|
148
|
+
function hasAnySubscriber(subscribers) {
|
|
149
|
+
return subscribers.sns !== undefined || (subscribers.emails?.length ?? 0) > 0;
|
|
150
|
+
}
|
|
151
|
+
function describeNotification(entry) {
|
|
152
|
+
const unit = (entry.thresholdType ?? "PERCENTAGE") === "PERCENTAGE" ? "%" : "";
|
|
153
|
+
return `notification ${entry.notificationType} @ ${String(entry.threshold)}${unit}`;
|
|
154
|
+
}
|
|
133
155
|
/**
|
|
134
156
|
* Creates a new {@link IBudgetBuilder} for configuring an AWS Budget.
|
|
135
157
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"budget-builder.js","sourceRoot":"","sources":["../src/budget-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAuB,MAAM,yBAAyB,CAAC;AAIzE,OAAO,EAAE,OAAO,EAAiC,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAIL,kBAAkB,EAClB,gCAAgC,GACjC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"budget-builder.js","sourceRoot":"","sources":["../src/budget-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { IConstruct } from "constructs";
|
|
2
|
+
/**
|
|
3
|
+
* Throws if `unit` is not in {@link DEFAULT_BUDGET_CURRENCIES}.
|
|
4
|
+
*
|
|
5
|
+
* `context` is woven into the message (e.g. `BudgetBuilder "X": limit
|
|
6
|
+
* unit ...`) so callers can blame the right field. Catches typos like
|
|
7
|
+
* `"USDD"` or `"ZZZ"` at synth instead of mid-deploy.
|
|
8
|
+
*/
|
|
9
|
+
export declare function assertValidBudgetCurrency(unit: string, context: string): void;
|
|
10
|
+
/**
|
|
11
|
+
* Annotates `scope` with a non-fatal warning when `unit` is anything
|
|
12
|
+
* other than `USD`. The synth context cannot see an account's billing
|
|
13
|
+
* currency, and AWS Budgets rejects `BudgetLimit.Unit` values that
|
|
14
|
+
* don't match it — so a non-USD configuration deserves a "make sure
|
|
15
|
+
* this matches your billing currency" nudge.
|
|
16
|
+
*
|
|
17
|
+
* Short-circuits on unresolved tokens so env-agnostic stacks aren't
|
|
18
|
+
* spammed.
|
|
19
|
+
*/
|
|
20
|
+
export declare function warnIfNonUsdCurrency(scope: IConstruct, unit: string, context: string): void;
|
|
21
|
+
//# sourceMappingURL=currency.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"currency.d.ts","sourceRoot":"","sources":["../src/currency.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7C;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAM7E;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAS3F"}
|
package/dist/currency.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Annotations, Token } from "aws-cdk-lib";
|
|
2
|
+
import { DEFAULT_BUDGET_CURRENCIES } from "./defaults.js";
|
|
3
|
+
/**
|
|
4
|
+
* Throws if `unit` is not in {@link DEFAULT_BUDGET_CURRENCIES}.
|
|
5
|
+
*
|
|
6
|
+
* `context` is woven into the message (e.g. `BudgetBuilder "X": limit
|
|
7
|
+
* unit ...`) so callers can blame the right field. Catches typos like
|
|
8
|
+
* `"USDD"` or `"ZZZ"` at synth instead of mid-deploy.
|
|
9
|
+
*/
|
|
10
|
+
export function assertValidBudgetCurrency(unit, context) {
|
|
11
|
+
if (DEFAULT_BUDGET_CURRENCIES.includes(unit))
|
|
12
|
+
return;
|
|
13
|
+
throw new Error(`${context}: "${unit}" is not a recognised AWS Budgets currency code. ` +
|
|
14
|
+
`Expected one of: ${DEFAULT_BUDGET_CURRENCIES.join(", ")}.`);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Annotates `scope` with a non-fatal warning when `unit` is anything
|
|
18
|
+
* other than `USD`. The synth context cannot see an account's billing
|
|
19
|
+
* currency, and AWS Budgets rejects `BudgetLimit.Unit` values that
|
|
20
|
+
* don't match it — so a non-USD configuration deserves a "make sure
|
|
21
|
+
* this matches your billing currency" nudge.
|
|
22
|
+
*
|
|
23
|
+
* Short-circuits on unresolved tokens so env-agnostic stacks aren't
|
|
24
|
+
* spammed.
|
|
25
|
+
*/
|
|
26
|
+
export function warnIfNonUsdCurrency(scope, unit, context) {
|
|
27
|
+
if (Token.isUnresolved(unit))
|
|
28
|
+
return;
|
|
29
|
+
if (unit === "USD")
|
|
30
|
+
return;
|
|
31
|
+
Annotations.of(scope).addWarningV2("@composurecdk/budgets:limit-currency", `${context}: currency "${unit}" must match the account's billing currency or AWS Budgets ` +
|
|
32
|
+
`will reject the request at deploy time. Most accounts default to USD; verify yours and ` +
|
|
33
|
+
`suppress this warning if intentional.`);
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=currency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"currency.js","sourceRoot":"","sources":["../src/currency.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAE1D;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,IAAY,EAAE,OAAe;IACrE,IAAI,yBAAyB,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO;IACrD,MAAM,IAAI,KAAK,CACb,GAAG,OAAO,MAAM,IAAI,mDAAmD;QACrE,oBAAoB,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAC9D,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAiB,EAAE,IAAY,EAAE,OAAe;IACnF,IAAI,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC;QAAE,OAAO;IACrC,IAAI,IAAI,KAAK,KAAK;QAAE,OAAO;IAC3B,WAAW,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,YAAY,CAChC,sCAAsC,EACtC,GAAG,OAAO,eAAe,IAAI,6DAA6D;QACxF,yFAAyF;QACzF,uCAAuC,CAC1C,CAAC;AACJ,CAAC"}
|
package/dist/defaults.d.ts
CHANGED
|
@@ -40,4 +40,18 @@ export declare const BUDGET_DEFAULTS: {
|
|
|
40
40
|
forecastedPercent: number;
|
|
41
41
|
};
|
|
42
42
|
};
|
|
43
|
+
/**
|
|
44
|
+
* ISO 4217 currency codes accepted by AWS Budgets for `COST` budgets'
|
|
45
|
+
* `BudgetLimit.Unit` and the `EstimatedCharges` alarm's `Currency`
|
|
46
|
+
* dimension. Sourced from the AWS Billing supported-currencies list.
|
|
47
|
+
*
|
|
48
|
+
* The synth context cannot see an account's billing currency, so the
|
|
49
|
+
* builder uses this set for shape validation only — a hard error on
|
|
50
|
+
* anything outside it (catches typos like `"ZZZ"`/`"USDD"`) — and emits
|
|
51
|
+
* a soft warning when the configured unit is anything other than `USD`,
|
|
52
|
+
* since most accounts default to USD billing.
|
|
53
|
+
*
|
|
54
|
+
* @see https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/manage-account-payment.html
|
|
55
|
+
*/
|
|
56
|
+
export declare const DEFAULT_BUDGET_CURRENCIES: readonly string[];
|
|
43
57
|
//# sourceMappingURL=defaults.d.ts.map
|
package/dist/defaults.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,eAAe;IAC1B;;;;OAIG;;IAGH;;;;;OAKG;;IAGH;;;;;OAKG;;IAGH;;;;;;;;;OASG;;;;;CAKJ,CAAC"}
|
|
1
|
+
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,eAAe;IAC1B;;;;OAIG;;IAGH;;;;;OAKG;;IAGH;;;;;OAKG;;IAGH;;;;;;;;;OASG;;;;;CAKJ,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,yBAAyB,EAAE,SAAS,MAAM,EAkCtD,CAAC"}
|
package/dist/defaults.js
CHANGED
|
@@ -40,4 +40,52 @@ export const BUDGET_DEFAULTS = {
|
|
|
40
40
|
forecastedPercent: 100,
|
|
41
41
|
},
|
|
42
42
|
};
|
|
43
|
+
/**
|
|
44
|
+
* ISO 4217 currency codes accepted by AWS Budgets for `COST` budgets'
|
|
45
|
+
* `BudgetLimit.Unit` and the `EstimatedCharges` alarm's `Currency`
|
|
46
|
+
* dimension. Sourced from the AWS Billing supported-currencies list.
|
|
47
|
+
*
|
|
48
|
+
* The synth context cannot see an account's billing currency, so the
|
|
49
|
+
* builder uses this set for shape validation only — a hard error on
|
|
50
|
+
* anything outside it (catches typos like `"ZZZ"`/`"USDD"`) — and emits
|
|
51
|
+
* a soft warning when the configured unit is anything other than `USD`,
|
|
52
|
+
* since most accounts default to USD billing.
|
|
53
|
+
*
|
|
54
|
+
* @see https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/manage-account-payment.html
|
|
55
|
+
*/
|
|
56
|
+
export const DEFAULT_BUDGET_CURRENCIES = [
|
|
57
|
+
"AED",
|
|
58
|
+
"ARS",
|
|
59
|
+
"AUD",
|
|
60
|
+
"BRL",
|
|
61
|
+
"CAD",
|
|
62
|
+
"CHF",
|
|
63
|
+
"CLP",
|
|
64
|
+
"CNY",
|
|
65
|
+
"COP",
|
|
66
|
+
"CZK",
|
|
67
|
+
"DKK",
|
|
68
|
+
"EUR",
|
|
69
|
+
"GBP",
|
|
70
|
+
"HKD",
|
|
71
|
+
"IDR",
|
|
72
|
+
"ILS",
|
|
73
|
+
"INR",
|
|
74
|
+
"JPY",
|
|
75
|
+
"KRW",
|
|
76
|
+
"MXN",
|
|
77
|
+
"MYR",
|
|
78
|
+
"NOK",
|
|
79
|
+
"NZD",
|
|
80
|
+
"PLN",
|
|
81
|
+
"RUB",
|
|
82
|
+
"SAR",
|
|
83
|
+
"SEK",
|
|
84
|
+
"SGD",
|
|
85
|
+
"THB",
|
|
86
|
+
"TRY",
|
|
87
|
+
"TWD",
|
|
88
|
+
"USD",
|
|
89
|
+
"ZAR",
|
|
90
|
+
];
|
|
43
91
|
//# sourceMappingURL=defaults.js.map
|
package/dist/defaults.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B;;;;OAIG;IACH,UAAU,EAAE,MAAe;IAE3B;;;;;OAKG;IACH,QAAQ,EAAE,SAAkB;IAE5B;;;;;OAKG;IACH,SAAS,EAAE,KAAK;IAEhB;;;;;;;;;OASG;IACH,qBAAqB,EAAE;QACrB,aAAa,EAAE,EAAE;QACjB,iBAAiB,EAAE,GAAG;KACvB;CACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B;;;;OAIG;IACH,UAAU,EAAE,MAAe;IAE3B;;;;;OAKG;IACH,QAAQ,EAAE,SAAkB;IAE5B;;;;;OAKG;IACH,SAAS,EAAE,KAAK;IAEhB;;;;;;;;;OASG;IACH,qBAAqB,EAAE;QACrB,aAAa,EAAE,EAAE;QACjB,iBAAiB,EAAE,GAAG;KACvB;CACF,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAsB;IAC1D,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;CACN,CAAC"}
|
package/dist/email.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
declare const emailBrand: unique symbol;
|
|
2
|
+
/**
|
|
3
|
+
* A validated email address suitable for use as a budget notification
|
|
4
|
+
* subscriber. Construct via {@link email}; the brand prevents bare
|
|
5
|
+
* strings from being passed where an `Email` is required, ensuring the
|
|
6
|
+
* value has been syntactically validated and length-checked against
|
|
7
|
+
* AWS Budgets' per-subscriber limit.
|
|
8
|
+
*/
|
|
9
|
+
export type Email = string & {
|
|
10
|
+
readonly [emailBrand]: true;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Validates and brands a string as an {@link Email}.
|
|
14
|
+
*
|
|
15
|
+
* The pattern intentionally errs on the side of acceptance — anything
|
|
16
|
+
* obviously not an email (whitespace, missing `@`, missing TLD) is
|
|
17
|
+
* rejected, but any address AWS Budgets will plausibly accept passes.
|
|
18
|
+
* The 50-char cap matches the Budgets API's documented per-subscriber
|
|
19
|
+
* limit.
|
|
20
|
+
*
|
|
21
|
+
* @throws If the input is empty, exceeds 50 characters, or doesn't
|
|
22
|
+
* contain `local@domain.tld`.
|
|
23
|
+
*
|
|
24
|
+
* @see https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_budgets_Subscriber.html
|
|
25
|
+
*/
|
|
26
|
+
export declare function email(input: string): Email;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=email.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,UAAU,EAAE,OAAO,MAAM,CAAC;AAExC;;;;;;GAMG;AACH,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,UAAU,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC;AAK7D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,CAc1C"}
|
package/dist/email.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const MAX_LEN = 50;
|
|
2
|
+
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
3
|
+
/**
|
|
4
|
+
* Validates and brands a string as an {@link Email}.
|
|
5
|
+
*
|
|
6
|
+
* The pattern intentionally errs on the side of acceptance — anything
|
|
7
|
+
* obviously not an email (whitespace, missing `@`, missing TLD) is
|
|
8
|
+
* rejected, but any address AWS Budgets will plausibly accept passes.
|
|
9
|
+
* The 50-char cap matches the Budgets API's documented per-subscriber
|
|
10
|
+
* limit.
|
|
11
|
+
*
|
|
12
|
+
* @throws If the input is empty, exceeds 50 characters, or doesn't
|
|
13
|
+
* contain `local@domain.tld`.
|
|
14
|
+
*
|
|
15
|
+
* @see https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_budgets_Subscriber.html
|
|
16
|
+
*/
|
|
17
|
+
export function email(input) {
|
|
18
|
+
const trimmed = input.trim();
|
|
19
|
+
if (trimmed.length === 0) {
|
|
20
|
+
throw new Error("email cannot be empty");
|
|
21
|
+
}
|
|
22
|
+
if (trimmed.length > MAX_LEN) {
|
|
23
|
+
throw new Error(`email exceeds ${String(MAX_LEN)} chars (AWS Budgets per-subscriber limit): "${trimmed}"`);
|
|
24
|
+
}
|
|
25
|
+
if (!EMAIL_REGEX.test(trimmed)) {
|
|
26
|
+
throw new Error(`invalid email address: "${trimmed}"`);
|
|
27
|
+
}
|
|
28
|
+
return trimmed;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=email.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.js","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"AAWA,MAAM,OAAO,GAAG,EAAE,CAAC;AACnB,MAAM,WAAW,GAAG,4BAA4B,CAAC;AAEjD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,KAAK,CAAC,KAAa;IACjC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAC3C,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,OAAO,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,iBAAiB,MAAM,CAAC,OAAO,CAAC,+CAA+C,OAAO,GAAG,CAC1F,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,2BAA2B,OAAO,GAAG,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,OAAgB,CAAC;AAC1B,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export { createBudgetBuilder, type IBudgetBuilder, type BudgetBuilderProps, type BudgetBuilderResult, type BudgetLimit, } from "./budget-builder.js";
|
|
2
2
|
export { createBudgetAlarmBuilder, type IBudgetAlarmBuilder, type BudgetAlarmBuilderProps, type BudgetAlarmBuilderResult, } from "./budget-alarm-builder.js";
|
|
3
|
-
export { BUDGET_DEFAULTS } from "./defaults.js";
|
|
3
|
+
export { BUDGET_DEFAULTS, DEFAULT_BUDGET_CURRENCIES } from "./defaults.js";
|
|
4
4
|
export { type BudgetAlarmConfig, type EstimatedChargesAlarmConfig } from "./alarm-config.js";
|
|
5
5
|
export { createBudgetsTopicPolicies } from "./topic-policy.js";
|
|
6
|
-
export {
|
|
6
|
+
export { email, type Email } from "./email.js";
|
|
7
|
+
export { resolveSubscribers, toCfnNotificationWithSubscribers, type NotificationEntry, type NotificationType, type NotifySubscribers, type ResolvedSubscribers, } from "./notifications.js";
|
|
7
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,WAAW,GACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,wBAAwB,EACxB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,GAC9B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,WAAW,GACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,wBAAwB,EACxB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,GAC9B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAC3E,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAC7F,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EACL,kBAAkB,EAClB,gCAAgC,EAChC,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,GACzB,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { createBudgetBuilder, } from "./budget-builder.js";
|
|
2
2
|
export { createBudgetAlarmBuilder, } from "./budget-alarm-builder.js";
|
|
3
|
-
export { BUDGET_DEFAULTS } from "./defaults.js";
|
|
3
|
+
export { BUDGET_DEFAULTS, DEFAULT_BUDGET_CURRENCIES } from "./defaults.js";
|
|
4
4
|
export { createBudgetsTopicPolicies } from "./topic-policy.js";
|
|
5
|
+
export { email } from "./email.js";
|
|
5
6
|
export { resolveSubscribers, toCfnNotificationWithSubscribers, } from "./notifications.js";
|
|
6
7
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,GAKpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,wBAAwB,GAIzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,GAKpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,wBAAwB,GAIzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAE3E,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,KAAK,EAAc,MAAM,YAAY,CAAC;AAC/C,OAAO,EACL,kBAAkB,EAClB,gCAAgC,GAKjC,MAAM,oBAAoB,CAAC"}
|
package/dist/notifications.d.ts
CHANGED
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
import { CfnBudget } from "aws-cdk-lib/aws-budgets";
|
|
2
2
|
import type { ITopic } from "aws-cdk-lib/aws-sns";
|
|
3
3
|
import { type Resolvable } from "@composurecdk/core";
|
|
4
|
+
import type { Email } from "./email.js";
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
6
|
+
* Subscribers attached to a budget notification.
|
|
7
|
+
*
|
|
8
|
+
* AWS Budgets enforces an asymmetric per-notification subscriber rule
|
|
9
|
+
* that CloudFormation does not model:
|
|
10
|
+
*
|
|
11
|
+
* - up to 10 subscribers per notification
|
|
12
|
+
* - **at most one** with `SubscriptionType=SNS`
|
|
13
|
+
* - the remainder must be `EMAIL`
|
|
14
|
+
*
|
|
15
|
+
* This shape encodes that constraint in the type system: `sns` is
|
|
16
|
+
* singular, so passing two SNS topics is unrepresentable. The
|
|
17
|
+
* combined-count cap is enforced at synth time.
|
|
18
|
+
*
|
|
19
|
+
* @see https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_budgets_NotificationWithSubscribers.html
|
|
7
20
|
*/
|
|
8
|
-
export
|
|
21
|
+
export interface NotifySubscribers {
|
|
22
|
+
/**
|
|
23
|
+
* Optional SNS topic to publish notifications to. AWS Budgets allows
|
|
24
|
+
* at most one SNS subscriber per notification; route fan-out by
|
|
25
|
+
* adding additional subscriptions to this single topic.
|
|
26
|
+
*/
|
|
27
|
+
sns?: ITopic | Resolvable<ITopic>;
|
|
28
|
+
/**
|
|
29
|
+
* Optional list of validated email addresses. Construct each value
|
|
30
|
+
* via {@link email}; bare strings are rejected at compile time. The
|
|
31
|
+
* combined `sns` + `emails` count must be ≤ 10.
|
|
32
|
+
*/
|
|
33
|
+
emails?: Email[];
|
|
34
|
+
}
|
|
9
35
|
/**
|
|
10
36
|
* Which side of spend a notification triggers on.
|
|
11
37
|
*
|
|
@@ -31,27 +57,29 @@ export interface NotificationEntry {
|
|
|
31
57
|
* absolute amount when `thresholdType` is `ABSOLUTE_VALUE`.
|
|
32
58
|
*/
|
|
33
59
|
threshold: number;
|
|
34
|
-
subscribers:
|
|
60
|
+
subscribers: NotifySubscribers;
|
|
35
61
|
comparisonOperator?: "GREATER_THAN" | "LESS_THAN" | "EQUAL_TO";
|
|
36
62
|
thresholdType?: "PERCENTAGE" | "ABSOLUTE_VALUE";
|
|
37
63
|
}
|
|
38
64
|
/**
|
|
39
|
-
* Resolved
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* `AWS::SNS::TopicPolicy` entries granting `budgets.amazonaws.com`
|
|
43
|
-
* permission to publish.
|
|
65
|
+
* Resolved subscribers in the CloudFormation shape required by
|
|
66
|
+
* `AWS::Budgets::Budget`, plus any SNS topic referenced (so the caller
|
|
67
|
+
* can create matching `AWS::SNS::TopicPolicy` grants).
|
|
44
68
|
*/
|
|
45
69
|
export interface ResolvedSubscribers {
|
|
46
70
|
cfn: CfnBudget.SubscriberProperty[];
|
|
47
71
|
snsTopics: ITopic[];
|
|
48
72
|
}
|
|
49
73
|
/**
|
|
50
|
-
* Resolve a
|
|
51
|
-
*
|
|
52
|
-
*
|
|
74
|
+
* Resolve a {@link NotifySubscribers} into the CloudFormation shape for
|
|
75
|
+
* `AWS::Budgets::Budget`'s `Subscribers` array, plus any SNS topic
|
|
76
|
+
* referenced so the caller can create a matching topic policy.
|
|
77
|
+
*
|
|
78
|
+
* The SNS subscriber (if any) is emitted first, followed by emails in
|
|
79
|
+
* declaration order — the order is not load-bearing, but stable output
|
|
80
|
+
* keeps test snapshots steady.
|
|
53
81
|
*/
|
|
54
|
-
export declare function resolveSubscribers(subscribers:
|
|
82
|
+
export declare function resolveSubscribers(subscribers: NotifySubscribers, context: Record<string, object>): ResolvedSubscribers;
|
|
55
83
|
/**
|
|
56
84
|
* Convert a {@link NotificationEntry} plus resolved subscribers into the
|
|
57
85
|
* CloudFormation `NotificationWithSubscribersProperty` shape.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../src/notifications.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../src/notifications.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAClC;;;;OAIG;IACH,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;CAClB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,YAAY,CAAC;AAEvD;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IAChC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,iBAAiB,CAAC;IAC/B,kBAAkB,CAAC,EAAE,cAAc,GAAG,WAAW,GAAG,UAAU,CAAC;IAC/D,aAAa,CAAC,EAAE,YAAY,GAAG,gBAAgB,CAAC;CACjD;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,SAAS,CAAC,kBAAkB,EAAE,CAAC;IACpC,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,iBAAiB,EAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,mBAAmB,CAerB;AAED;;;GAGG;AACH,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,iBAAiB,EACxB,mBAAmB,EAAE,SAAS,CAAC,kBAAkB,EAAE,GAClD,SAAS,CAAC,mCAAmC,CAU/C"}
|
package/dist/notifications.js
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import { resolve } from "@composurecdk/core";
|
|
2
2
|
/**
|
|
3
|
-
* Resolve a
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* Resolve a {@link NotifySubscribers} into the CloudFormation shape for
|
|
4
|
+
* `AWS::Budgets::Budget`'s `Subscribers` array, plus any SNS topic
|
|
5
|
+
* referenced so the caller can create a matching topic policy.
|
|
6
|
+
*
|
|
7
|
+
* The SNS subscriber (if any) is emitted first, followed by emails in
|
|
8
|
+
* declaration order — the order is not load-bearing, but stable output
|
|
9
|
+
* keeps test snapshots steady.
|
|
6
10
|
*/
|
|
7
11
|
export function resolveSubscribers(subscribers, context) {
|
|
8
12
|
const cfn = [];
|
|
9
13
|
const snsTopics = [];
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
cfn.push({ address: subscriber, subscriptionType: "EMAIL" });
|
|
13
|
-
continue;
|
|
14
|
-
}
|
|
15
|
-
const topic = resolve(subscriber, context);
|
|
14
|
+
if (subscribers.sns !== undefined) {
|
|
15
|
+
const topic = resolve(subscribers.sns, context);
|
|
16
16
|
cfn.push({ address: topic.topicArn, subscriptionType: "SNS" });
|
|
17
17
|
snsTopics.push(topic);
|
|
18
18
|
}
|
|
19
|
+
for (const address of subscribers.emails ?? []) {
|
|
20
|
+
cfn.push({ address, subscriptionType: "EMAIL" });
|
|
21
|
+
}
|
|
19
22
|
return { cfn, snsTopics };
|
|
20
23
|
}
|
|
21
24
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifications.js","sourceRoot":"","sources":["../src/notifications.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAmB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"notifications.js","sourceRoot":"","sources":["../src/notifications.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAmB,MAAM,oBAAoB,CAAC;AA2E9D;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAChC,WAA8B,EAC9B,OAA+B;IAE/B,MAAM,GAAG,GAAmC,EAAE,CAAC;IAC/C,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,IAAI,WAAW,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAChD,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,QAAQ,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAAC,CAAC;QAC/D,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC;IAED,KAAK,MAAM,OAAO,IAAI,WAAW,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;QAC/C,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gCAAgC,CAC9C,KAAwB,EACxB,mBAAmD;IAEnD,OAAO;QACL,YAAY,EAAE;YACZ,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;YACxC,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,kBAAkB,EAAE,KAAK,CAAC,kBAAkB,IAAI,cAAc;YAC9D,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,YAAY;SACnD;QACD,WAAW,EAAE,mBAAmB;KACjC,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@composurecdk/budgets",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Composable AWS Budgets builder with well-architected defaults and automatic SNS topic policies",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
},
|
|
36
36
|
"type": "module",
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"@composurecdk/cloudwatch": "^0.
|
|
39
|
-
"@composurecdk/core": "^0.
|
|
38
|
+
"@composurecdk/cloudwatch": "^0.6.0",
|
|
39
|
+
"@composurecdk/core": "^0.6.0",
|
|
40
40
|
"aws-cdk-lib": "^2.0.0",
|
|
41
41
|
"constructs": "^10.0.0"
|
|
42
42
|
},
|