@mettlecast/domain-cdk-packer 0.2.102 → 0.2.104
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/DomainStack.js
CHANGED
|
@@ -175,6 +175,11 @@ export class DomainStack extends cdk.Stack {
|
|
|
175
175
|
const environment = {
|
|
176
176
|
TIB_DOMAIN_ID: domainId,
|
|
177
177
|
TIB_EVENT_BUS_ARN: eventBusArn,
|
|
178
|
+
// #5282: the runtime resolves the `ctx.publish` bus from
|
|
179
|
+
// EVENT_BUS_NAME (falling back to 'default'). Inject the shared bus
|
|
180
|
+
// name so published domain events land on the project-scoped bus.
|
|
181
|
+
// Prefer the supplied name; derive from the ARN when it is omitted.
|
|
182
|
+
EVENT_BUS_NAME: eventBusName ?? cdk.Fn.select(1, cdk.Fn.split('/', eventBusArn)),
|
|
178
183
|
};
|
|
179
184
|
if (databaseUrl) {
|
|
180
185
|
environment.DATABASE_URL = databaseUrl;
|
|
@@ -248,6 +253,7 @@ export class DomainStack extends cdk.Stack {
|
|
|
248
253
|
apiEntries,
|
|
249
254
|
environment,
|
|
250
255
|
eventBusArn,
|
|
256
|
+
eventBusName,
|
|
251
257
|
reservedConcurrency,
|
|
252
258
|
logRetentionDays: logRetentionDays ?? 30,
|
|
253
259
|
lambdaGroupId: group.outboundAccess,
|
|
@@ -278,6 +284,7 @@ export class DomainStack extends cdk.Stack {
|
|
|
278
284
|
}),
|
|
279
285
|
environment,
|
|
280
286
|
eventBusArn,
|
|
287
|
+
eventBusName,
|
|
281
288
|
dedicated: internalDedicated,
|
|
282
289
|
// Deterministic physical name so OTHER domain stacks can resolve
|
|
283
290
|
// this Lambda's ARN for cross-domain `ctx.actions` dispatch.
|
|
@@ -319,6 +326,7 @@ export class DomainStack extends cdk.Stack {
|
|
|
319
326
|
}),
|
|
320
327
|
environment,
|
|
321
328
|
eventBusArn,
|
|
329
|
+
eventBusName,
|
|
322
330
|
dedicated,
|
|
323
331
|
reservedConcurrency,
|
|
324
332
|
logRetentionDays: logRetentionDays ?? 30,
|
|
@@ -620,6 +628,12 @@ export class DomainStack extends cdk.Stack {
|
|
|
620
628
|
actionLambdas.forEach((fn) => {
|
|
621
629
|
iamPolicies.forEach(statement => fn.addToRolePolicy(statement));
|
|
622
630
|
});
|
|
631
|
+
// #5282: actions publish domain events via `ctx.publish`. The runtime
|
|
632
|
+
// targets the bus named by EVENT_BUS_NAME, so the execution role needs
|
|
633
|
+
// events:PutEvents scoped to the shared bus ARN.
|
|
634
|
+
actionLambdas.forEach((fn) => {
|
|
635
|
+
eventBus.grantPutEventsTo(fn);
|
|
636
|
+
});
|
|
623
637
|
// Add dbSecretArn grant if provided
|
|
624
638
|
if (dbSecretArn) {
|
|
625
639
|
actionLambdas.forEach(fn => fn.addToRolePolicy(new iam.PolicyStatement({
|
|
@@ -91,6 +91,14 @@ describe('auth registry — bootstrap-invite registration (#5226)', () => {
|
|
|
91
91
|
expect(bootstrapInviteSource).toContain("VALUES ($1, 'system', NULL, $2, 'sys_admin')");
|
|
92
92
|
expect(bootstrapInviteSource).not.toContain("00000000-0000-0000-0000-000000000000");
|
|
93
93
|
});
|
|
94
|
+
it('runs SQL through the raw pg client (ctx.db.client.query), not the Drizzle builder', () => {
|
|
95
|
+
// Regression for #5277: `DbContext.query` is the Drizzle query builder —
|
|
96
|
+
// an object, not a function. Calling it (`ctx.db.query(sql, params)`)
|
|
97
|
+
// throws `TypeError: e.db.query is not a function` at runtime. Raw SQL
|
|
98
|
+
// must go through the raw pg client exposed as `DbContext.client`.
|
|
99
|
+
expect(bootstrapInviteSource).toContain('ctx.db.client.query(');
|
|
100
|
+
expect(bootstrapInviteSource).not.toContain('ctx.db.query(');
|
|
101
|
+
});
|
|
94
102
|
});
|
|
95
103
|
describe('full documented build process — all current domains', () => {
|
|
96
104
|
it('generates a registry file for EVERY domain under domains/ (auth, data-management, email, orgs)', () => {
|
|
@@ -568,4 +568,69 @@ describe('DomainStack', () => {
|
|
|
568
568
|
expect(actionRoute.Properties.AuthorizerId).toBeDefined();
|
|
569
569
|
});
|
|
570
570
|
});
|
|
571
|
+
/**
|
|
572
|
+
* Issue #5282 — API action event publishing.
|
|
573
|
+
*
|
|
574
|
+
* Production evidence: the auth API-action Lambda inserted a bootstrap
|
|
575
|
+
* invitation but `ctx.publish('auth.member.invited')` failed with
|
|
576
|
+
* `AccessDeniedException: events:PutEvents` on the EventBridge default bus.
|
|
577
|
+
* The runtime resolves the `ctx.publish` bus from the `EVENT_BUS_NAME` env
|
|
578
|
+
* var (falling back to 'default') and the grouped API-action Lambda's
|
|
579
|
+
* execution role previously had no `events:PutEvents`. These synth tests
|
|
580
|
+
* pin both halves of the packer fix.
|
|
581
|
+
*/
|
|
582
|
+
describe('action event publishing (#5282)', () => {
|
|
583
|
+
const busName = 'tib-event-bus';
|
|
584
|
+
it('injects EVENT_BUS_NAME into the grouped API-action Lambda', () => {
|
|
585
|
+
const app = new cdk.App();
|
|
586
|
+
const stack = new DomainStack(app, 'TestDomainStackEventBusName', {
|
|
587
|
+
registry: minimalRegistry,
|
|
588
|
+
eventBusArn,
|
|
589
|
+
eventBusName: busName,
|
|
590
|
+
projectId: 'Test',
|
|
591
|
+
envCode: 'Dev',
|
|
592
|
+
});
|
|
593
|
+
const template = Template.fromStack(stack);
|
|
594
|
+
const lambdas = template.findResources('AWS::Lambda::Function');
|
|
595
|
+
const actionApiLambdas = Object.values(lambdas).filter(l => l.Properties.Environment?.Variables?.POWERTOOLS_SERVICE_NAME === 'test-domain-action-api');
|
|
596
|
+
expect(actionApiLambdas.length).toBeGreaterThan(0);
|
|
597
|
+
for (const fn of actionApiLambdas) {
|
|
598
|
+
expect(fn.Properties.Environment?.Variables?.EVENT_BUS_NAME).toBe(busName);
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
it('derives EVENT_BUS_NAME from the bus ARN when eventBusName is omitted', () => {
|
|
602
|
+
const app = new cdk.App();
|
|
603
|
+
const stack = new DomainStack(app, 'TestDomainStackEventBusDerived', {
|
|
604
|
+
registry: minimalRegistry,
|
|
605
|
+
eventBusArn,
|
|
606
|
+
projectId: 'Test',
|
|
607
|
+
envCode: 'Dev',
|
|
608
|
+
});
|
|
609
|
+
const template = Template.fromStack(stack);
|
|
610
|
+
const lambdas = template.findResources('AWS::Lambda::Function');
|
|
611
|
+
const actionApiLambdas = Object.values(lambdas).filter(l => l.Properties.Environment?.Variables?.POWERTOOLS_SERVICE_NAME === 'test-domain-action-api');
|
|
612
|
+
expect(actionApiLambdas.length).toBeGreaterThan(0);
|
|
613
|
+
for (const fn of actionApiLambdas) {
|
|
614
|
+
// Without an explicit name the packer derives it from the supplied
|
|
615
|
+
// bus ARN tail (`...:event-bus/tib-event-bus` → `tib-event-bus`) so
|
|
616
|
+
// published events target the shared bus, not the 'default' one.
|
|
617
|
+
expect(fn.Properties.Environment?.Variables?.EVENT_BUS_NAME).toBe('tib-event-bus');
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
it('grants action Lambda roles events:PutEvents scoped to the shared bus', () => {
|
|
621
|
+
const app = new cdk.App();
|
|
622
|
+
const stack = new DomainStack(app, 'TestDomainStackPutEvents', {
|
|
623
|
+
registry: minimalRegistry,
|
|
624
|
+
eventBusArn,
|
|
625
|
+
eventBusName: busName,
|
|
626
|
+
projectId: 'Test',
|
|
627
|
+
envCode: 'Dev',
|
|
628
|
+
});
|
|
629
|
+
const template = Template.fromStack(stack);
|
|
630
|
+
const policies = Object.values(template.findResources('AWS::IAM::Policy'));
|
|
631
|
+
const hasScopedPutEvents = policies.some(resource => resource.Properties.PolicyDocument.Statement.some(statement => statement.Action?.includes('events:PutEvents')
|
|
632
|
+
&& JSON.stringify(statement.Resource).includes('event-bus/tib-event-bus')));
|
|
633
|
+
expect(hasScopedPutEvents).toBe(true);
|
|
634
|
+
});
|
|
635
|
+
});
|
|
571
636
|
});
|
|
@@ -45,6 +45,13 @@ export interface GroupedLambdaProps {
|
|
|
45
45
|
environment: Record<string, string>;
|
|
46
46
|
/** ARN of the project-scoped EventBridge bus. */
|
|
47
47
|
eventBusArn: string;
|
|
48
|
+
/**
|
|
49
|
+
* Name of the project-scoped EventBridge bus. Injected as `EVENT_BUS_NAME`
|
|
50
|
+
* so the runtime's `ctx.publish` targets the shared bus instead of falling
|
|
51
|
+
* back to the AWS 'default' bus (#5282). When omitted, the name is derived
|
|
52
|
+
* from `eventBusArn` (e.g. `arn:...:event-bus/my-bus` → `my-bus`).
|
|
53
|
+
*/
|
|
54
|
+
eventBusName?: string;
|
|
48
55
|
/** VPC to place Lambdas in — required for Aurora connectivity. */
|
|
49
56
|
vpc?: ec2.IVpc;
|
|
50
57
|
/** Security groups to attach to the Lambda(s). */
|
|
@@ -200,6 +200,7 @@ export function createGroupedLambdas(scope, props) {
|
|
|
200
200
|
POWERTOOLS_LOG_LEVEL: 'INFO',
|
|
201
201
|
TIB_HANDLER_ID: entry.id,
|
|
202
202
|
TIB_EVENT_BUS_ARN: props.eventBusArn,
|
|
203
|
+
EVENT_BUS_NAME: resolveEventBusName(props.eventBusName, props.eventBusArn),
|
|
203
204
|
},
|
|
204
205
|
timeout: cdk.Duration.seconds(30),
|
|
205
206
|
memorySize: 256,
|
|
@@ -226,6 +227,7 @@ export function createGroupedLambdas(scope, props) {
|
|
|
226
227
|
POWERTOOLS_LOG_LEVEL: 'INFO',
|
|
227
228
|
TIB_HANDLER_MAP: JSON.stringify(props.handlerEntries.map(e => e.id)),
|
|
228
229
|
TIB_EVENT_BUS_ARN: props.eventBusArn,
|
|
230
|
+
EVENT_BUS_NAME: resolveEventBusName(props.eventBusName, props.eventBusArn),
|
|
229
231
|
},
|
|
230
232
|
timeout: cdk.Duration.seconds(30),
|
|
231
233
|
memorySize: 256,
|
|
@@ -235,6 +237,15 @@ export function createGroupedLambdas(scope, props) {
|
|
|
235
237
|
}),
|
|
236
238
|
];
|
|
237
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* Resolves the EventBridge bus name injected as `EVENT_BUS_NAME`.
|
|
242
|
+
* Prefers the explicitly supplied name (avoids ARN parsing when the ARN is a
|
|
243
|
+
* dynamic reference); falls back to deriving the name from the ARN tail
|
|
244
|
+
* (`arn:...:event-bus/{name}` → `{name}`).
|
|
245
|
+
*/
|
|
246
|
+
function resolveEventBusName(eventBusName, eventBusArn) {
|
|
247
|
+
return eventBusName ?? cdk.Fn.select(1, cdk.Fn.split('/', eventBusArn));
|
|
248
|
+
}
|
|
238
249
|
function toLogRetention(days) {
|
|
239
250
|
const map = {
|
|
240
251
|
30: logs.RetentionDays.ONE_MONTH,
|
|
@@ -329,6 +340,7 @@ export function createGroupedApiActionLambda(scope, props) {
|
|
|
329
340
|
POWERTOOLS_SERVICE_NAME: serviceName,
|
|
330
341
|
POWERTOOLS_LOG_LEVEL: 'INFO',
|
|
331
342
|
TIB_EVENT_BUS_ARN: props.eventBusArn,
|
|
343
|
+
EVENT_BUS_NAME: resolveEventBusName(props.eventBusName, props.eventBusArn),
|
|
332
344
|
},
|
|
333
345
|
timeout: cdk.Duration.seconds(30),
|
|
334
346
|
memorySize: 256,
|