Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import * as cdk from 'aws-cdk-lib';
import { RemovalPolicies, Mixins } from 'aws-cdk-lib';
import * as sqs from 'aws-cdk-lib/aws-sqs';
import { BlocksBackend, SandboxDisableDeletionProtection } from '@aws-blocks/blocks/cdk';
import { BlocksBackend, SandboxDisableDeletionProtection, registerConfig } from '@aws-blocks/blocks/cdk';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
import { getSandboxId } from './scripts/sandbox-id.js';
Expand Down Expand Up @@ -53,9 +53,9 @@ class MyExistingStack extends cdk.Stack {
backendCDKPath: join(__dirname, 'index.ts'),
});

// Wire IAM + env on the BlocksBackend's handler — same surface as BlocksStack.
// Wire IAM and runtime configuration on the BlocksBackend — same surface as BlocksStack.
stack.externalQueue.grantSendMessages(stack.blocks.handler);
stack.blocks.handler.addEnvironment('EXTERNAL_QUEUE_URL', stack.externalQueue.queueUrl);
registerConfig(stack.blocks, 'EXTERNAL_QUEUE_URL', stack.externalQueue.queueUrl);

new cdk.CfnOutput(stack, 'ApiUrl', { value: stack.blocks.apiUrl });
new cdk.CfnOutput(stack, 'ExternalQueueUrl', { value: stack.externalQueue.queueUrl });
Expand Down
31 changes: 19 additions & 12 deletions test-apps/extending-blocks-guide-blocksbackend/test/synth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
* Synth-time validation for the BlocksBackend harness.
*
* Asserts that BlocksBackend.create produces the expected resources INSIDE
* the user-owned outer stack (not in a separate stack), and that env vars
* + IAM are wired correctly to the BlocksBackend's handler.
* the user-owned outer stack (not in a separate stack), and that runtime
* configuration + IAM are wired correctly to the BlocksBackend's handler.
*/
import { test, describe, before } from 'node:test';
import assert from 'node:assert';
Expand All @@ -29,15 +29,13 @@ describe('extending-blocks-guide-blocksbackend synth', () => {
template = JSON.parse(readFileSync(join(SYNTH_DIR, files[0]), 'utf-8'));
});

test('exactly one Lambda function (the BlocksBackend handler)', () => {
const lambdas = Object.values(template.Resources).filter(
(r: any) => r.Type === 'AWS::Lambda::Function'
);
assert.strictEqual(
lambdas.length,
1,
`expected 1 Lambda (BlocksBackend handler); got ${lambdas.length}.`
test('contains the BlocksBackend handler', () => {
const handler = Object.values(template.Resources).find(
(r: any) =>
r.Type === 'AWS::Lambda::Function' &&
r.Properties?.Environment?.Variables?.NODE_ENV === 'production'
);
assert.ok(handler, 'could not find BlocksBackend handler Lambda');
});

test('exactly one API Gateway (managed by BlocksBackend)', () => {
Expand All @@ -54,14 +52,23 @@ describe('extending-blocks-guide-blocksbackend synth', () => {
assert.strictEqual(queues.length, 1);
});

test('BlocksBackend handler has EXTERNAL_QUEUE_URL injected by the outer stack', () => {
test('registers EXTERNAL_QUEUE_URL in the runtime config', () => {
const handler: any = Object.values(template.Resources).find(
(r: any) =>
r.Type === 'AWS::Lambda::Function' &&
r.Properties?.Environment?.Variables?.NODE_ENV === 'production'
);
assert.ok(handler, 'could not find Blocks handler Lambda');
const envVars = handler.Properties.Environment.Variables;
assert.ok('EXTERNAL_QUEUE_URL' in envVars, 'missing EXTERNAL_QUEUE_URL on BlocksBackend handler');
assert.ok('BLOCKS_CONFIG_BUCKET' in envVars, 'missing BLOCKS_CONFIG_BUCKET on handler');
assert.ok('BLOCKS_CONFIG_KEY' in envVars, 'missing BLOCKS_CONFIG_KEY on handler');
assert.ok(!('EXTERNAL_QUEUE_URL' in envVars), 'EXTERNAL_QUEUE_URL must use runtime config');

const configDeployment: any = Object.values(template.Resources).find(
(r: any) => r.Type === 'Custom::CDKBucketDeployment'
);
assert.ok(configDeployment, 'missing runtime config deployment');
const config = JSON.stringify(configDeployment.Properties.SourceMarkers);
assert.ok(config.includes('EXTERNAL_QUEUE_URL'), 'missing EXTERNAL_QUEUE_URL in runtime config');
});
});
8 changes: 4 additions & 4 deletions test-apps/extending-blocks-guide/aws-blocks/index.cdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as cdk from 'aws-cdk-lib';
import { RemovalPolicies, Mixins } from 'aws-cdk-lib';
import * as sqs from 'aws-cdk-lib/aws-sqs';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import { BlocksStack, SandboxDisableDeletionProtection } from '@aws-blocks/blocks/cdk';
import { BlocksStack, SandboxDisableDeletionProtection, registerConfig } from '@aws-blocks/blocks/cdk';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
import { getSandboxId } from './scripts/sandbox-id.js';
Expand Down Expand Up @@ -38,7 +38,7 @@ Mixins.of(blocksStack).apply(new SandboxDisableDeletionProtection());
// Pattern 1 fixture: an SQS queue we'll send to via raw SDK.
const externalQueue = new sqs.Queue(blocksStack, 'external-queue');
externalQueue.grantSendMessages(blocksStack.handler);
blocksStack.handler.addEnvironment('EXTERNAL_QUEUE_URL', externalQueue.queueUrl);
registerConfig(blocksStack, 'EXTERNAL_QUEUE_URL', externalQueue.queueUrl);

// Pattern 2 fixtures: two DynamoDB tables that pretend to predate Blocks.
// We pre-pinned the physical names above so KVStore.fromExisting and
Expand Down Expand Up @@ -67,7 +67,7 @@ new cdk.CfnOutput(blocksStack, 'StackNameOut', { value: stackName });

// Surface the table names to runtime — *.fromExisting on the runtime side
// reads them from process.env.
blocksStack.handler.addEnvironment('BLOCKS_LEGACY_SESSIONS_TABLE', legacyTableName);
blocksStack.handler.addEnvironment('BLOCKS_LEGACY_USERS_TABLE', legacyUsersTableName);
registerConfig(blocksStack, 'BLOCKS_LEGACY_SESSIONS_TABLE', legacyTableName);
registerConfig(blocksStack, 'BLOCKS_LEGACY_USERS_TABLE', legacyUsersTableName);

cdk.Tags.of(blocksStack).add('blocks:purpose', 'extending-guide-validation');
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import { Duration } from 'aws-cdk-lib';
import * as sqs from 'aws-cdk-lib/aws-sqs';
import { Scope } from '@aws-blocks/core/cdk';
import { Scope, registerConfig } from '@aws-blocks/core/cdk';
import type { ScopeParent } from '@aws-blocks/core';

export interface QueueOptions {
Expand All @@ -21,7 +21,7 @@ export class Queue extends Scope {
});

queue.grantSendMessages(this.handler);
this.handler.addEnvironment(`${envSafe(this.fullId)}_URL`, queue.queueUrl);
registerConfig(this, `${envSafe(this.fullId)}_URL`, queue.queueUrl);
}
}

Expand Down
23 changes: 15 additions & 8 deletions test-apps/extending-blocks-guide/test/synth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
* template to assert each pattern produced (or *didn't* produce) the
* expected resources.
*
* Pattern 1: external SQS queue + handler env var EXTERNAL_QUEUE_URL.
* Pattern 1: external SQS queue + runtime config EXTERNAL_QUEUE_URL.
* Pattern 2: legacy DynamoDB table is created by us; KVStore.fromExisting
* does NOT add a second table (this asserts the bb-kv-store fix).
* Pattern 3: bb-queue creates its own SQS queue, env var WORK_URL on handler.
* Pattern 3: bb-queue creates its own SQS queue, runtime config WORK_URL.
*/
import { test, describe, before } from 'node:test';
import assert from 'node:assert';
Expand Down Expand Up @@ -70,18 +70,25 @@ describe('extending-blocks-guide synth', () => {
assert.strictEqual(queues.length, 2, `expected 2 SQS queues; got ${queues.length}`);
});

test('handler has EXTERNAL_QUEUE_URL and BLOCKS_LEGACY_SESSIONS_TABLE env vars', () => {
test('registers external resource identifiers in the runtime config', () => {
const handler: any = Object.values(template.Resources).find(
(r: any) =>
r.Type === 'AWS::Lambda::Function' &&
r.Properties?.Environment?.Variables?.NODE_ENV === 'production'
);
assert.ok(handler, 'could not find Blocks handler Lambda');
const envVars = handler.Properties.Environment.Variables;
assert.ok('EXTERNAL_QUEUE_URL' in envVars, 'missing EXTERNAL_QUEUE_URL on handler');
assert.ok('BLOCKS_LEGACY_SESSIONS_TABLE' in envVars, 'missing BLOCKS_LEGACY_SESSIONS_TABLE on handler');
// Pattern 3: bb-queue injects EXTENDING_GUIDE_WORK_URL.
const workKey = Object.keys(envVars).find(k => k.endsWith('_WORK_URL'));
assert.ok(workKey, 'missing *_WORK_URL on handler (Pattern 3 custom BB)');
assert.ok('BLOCKS_CONFIG_BUCKET' in envVars, 'missing BLOCKS_CONFIG_BUCKET on handler');
assert.ok('BLOCKS_CONFIG_KEY' in envVars, 'missing BLOCKS_CONFIG_KEY on handler');
assert.ok(!('EXTERNAL_QUEUE_URL' in envVars), 'EXTERNAL_QUEUE_URL must use runtime config');

const configDeployment: any = Object.values(template.Resources).find(
(r: any) => r.Type === 'Custom::CDKBucketDeployment'
);
assert.ok(configDeployment, 'missing runtime config deployment');
const config = JSON.stringify(configDeployment.Properties.SourceMarkers);
assert.ok(config.includes('EXTERNAL_QUEUE_URL'), 'missing EXTERNAL_QUEUE_URL in runtime config');
assert.ok(config.includes('BLOCKS_LEGACY_SESSIONS_TABLE'), 'missing legacy sessions table in runtime config');
assert.ok(config.includes('WORK_URL'), 'missing custom BB queue URL in runtime config');
});
});