@aws-blocks/core 0.2.0 → 0.4.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 +13 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +32 -0
- package/dist/api.test.js +22 -0
- package/dist/bb-utils.d.ts +1 -0
- package/dist/bb-utils.d.ts.map +1 -1
- package/dist/bb-utils.js +3 -0
- package/dist/cdk/apigateway-account.d.ts +33 -0
- package/dist/cdk/apigateway-account.d.ts.map +1 -0
- package/dist/cdk/apigateway-account.js +60 -0
- package/dist/cdk/blocks-backend.d.ts +43 -9
- package/dist/cdk/blocks-backend.d.ts.map +1 -1
- package/dist/cdk/blocks-backend.js +69 -73
- package/dist/cdk/blocks-backend.test.js +78 -69
- package/dist/cdk/blocks-defaults.d.ts +92 -2
- package/dist/cdk/blocks-defaults.d.ts.map +1 -1
- package/dist/cdk/blocks-defaults.js +22 -2
- package/dist/cdk/blocks-defaults.test.js +21 -2
- package/dist/cdk/blocks-stack.test.js +94 -37
- package/dist/cdk/compute/compute-registry.d.ts +19 -0
- package/dist/cdk/compute/compute-registry.d.ts.map +1 -0
- package/dist/cdk/compute/compute-registry.js +38 -0
- package/dist/cdk/compute/compute.d.ts +2 -0
- package/dist/cdk/compute/compute.d.ts.map +1 -1
- package/dist/cdk/compute/compute.js +8 -0
- package/dist/cdk/compute/default-compute-factory.d.ts +34 -0
- package/dist/cdk/compute/default-compute-factory.d.ts.map +1 -0
- package/dist/cdk/compute/default-compute-factory.js +3 -0
- package/dist/cdk/config-registry.d.ts +34 -4
- package/dist/cdk/config-registry.d.ts.map +1 -1
- package/dist/cdk/config-registry.js +83 -25
- package/dist/cdk/config-registry.test.d.ts +2 -0
- package/dist/cdk/config-registry.test.d.ts.map +1 -0
- package/dist/cdk/config-registry.test.js +115 -0
- package/dist/cdk/index.d.ts +76 -7
- package/dist/cdk/index.d.ts.map +1 -1
- package/dist/cdk/index.js +99 -9
- package/dist/cdk/internal.d.ts +1 -0
- package/dist/cdk/internal.d.ts.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/common/config.d.ts +34 -0
- package/dist/common/config.d.ts.map +1 -1
- package/dist/common/config.js +45 -3
- package/dist/common/config.test.js +19 -0
- package/dist/common/index.d.ts +8 -0
- package/dist/common/index.d.ts.map +1 -1
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +20 -0
- package/dist/hosting-secrets.d.ts +48 -0
- package/dist/hosting-secrets.d.ts.map +1 -0
- package/dist/hosting-secrets.js +57 -0
- package/dist/hosting-secrets.test.d.ts +2 -0
- package/dist/hosting-secrets.test.d.ts.map +1 -0
- package/dist/hosting-secrets.test.js +81 -0
- package/dist/hosting.d.ts +97 -6
- package/dist/hosting.d.ts.map +1 -1
- package/dist/hosting.js +91 -23
- package/dist/hosting.test.js +182 -10
- package/dist/index.cdk.d.ts +13 -12
- package/dist/index.cdk.d.ts.map +1 -1
- package/dist/index.cdk.js +13 -9
- package/dist/index.d.ts +8 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -5
- package/dist/lambda-handler.d.ts +17 -0
- package/dist/lambda-handler.d.ts.map +1 -1
- package/dist/lambda-handler.js +52 -3
- package/dist/lambda-handler.test.js +196 -1
- package/dist/pipeline/index.d.ts +24 -2
- package/dist/pipeline/index.d.ts.map +1 -1
- package/dist/pipeline/index.js +41 -1
- package/dist/pipeline.test.d.ts +2 -0
- package/dist/pipeline.test.d.ts.map +1 -0
- package/dist/pipeline.test.js +47 -0
- package/dist/rpc.d.ts +21 -0
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +34 -0
- package/dist/rpc.test.js +38 -1
- package/dist/scripts/config.d.ts +15 -0
- package/dist/scripts/config.d.ts.map +1 -0
- package/dist/scripts/config.js +29 -0
- package/dist/scripts/deploy.d.ts.map +1 -1
- package/dist/scripts/deploy.js +4 -0
- package/dist/scripts/dev-server.d.ts +8 -0
- package/dist/scripts/dev-server.d.ts.map +1 -1
- package/dist/scripts/dev-server.js +31 -0
- package/dist/scripts/generate-client.d.ts.map +1 -1
- package/dist/scripts/generate-client.js +9 -4
- package/dist/scripts/generate-client.test.js +19 -4
- package/dist/scripts/index.d.ts +13 -10
- package/dist/scripts/index.d.ts.map +1 -1
- package/dist/scripts/index.js +11 -8
- package/dist/scripts/preflight-credentials.d.ts +30 -0
- package/dist/scripts/preflight-credentials.d.ts.map +1 -0
- package/dist/scripts/preflight-credentials.js +108 -0
- package/dist/scripts/preflight-credentials.test.d.ts +2 -0
- package/dist/scripts/preflight-credentials.test.d.ts.map +1 -0
- package/dist/scripts/preflight-credentials.test.js +77 -0
- package/dist/scripts/sandbox-args.test.d.ts +2 -0
- package/dist/scripts/sandbox-args.test.d.ts.map +1 -0
- package/dist/scripts/sandbox-args.test.js +77 -0
- package/dist/scripts/sandbox-empty-buckets.test.d.ts +2 -0
- package/dist/scripts/sandbox-empty-buckets.test.d.ts.map +1 -0
- package/dist/scripts/sandbox-empty-buckets.test.js +171 -0
- package/dist/scripts/sandbox.d.ts +106 -0
- package/dist/scripts/sandbox.d.ts.map +1 -1
- package/dist/scripts/sandbox.js +255 -40
- package/dist/scripts/secret.d.ts +15 -0
- package/dist/scripts/secret.d.ts.map +1 -0
- package/dist/scripts/secret.js +29 -0
- package/dist/scripts/secret.test.d.ts +2 -0
- package/dist/scripts/secret.test.d.ts.map +1 -0
- package/dist/scripts/secret.test.js +28 -0
- package/dist/scripts/typegen.d.ts +15 -0
- package/dist/scripts/typegen.d.ts.map +1 -0
- package/dist/scripts/typegen.js +16 -0
- package/dist/secret-naming.d.ts +58 -0
- package/dist/secret-naming.d.ts.map +1 -0
- package/dist/secret-naming.js +67 -0
- package/dist/secret-naming.test.d.ts +2 -0
- package/dist/secret-naming.test.d.ts.map +1 -0
- package/dist/secret-naming.test.js +40 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -1
- package/src/api.test.ts +25 -0
- package/src/api.ts +39 -0
- package/src/bb-utils.ts +3 -0
- package/src/cdk/apigateway-account.ts +66 -0
- package/src/cdk/blocks-backend.test.ts +291 -272
- package/src/cdk/blocks-backend.ts +88 -83
- package/src/cdk/blocks-defaults.test.ts +25 -2
- package/src/cdk/blocks-defaults.ts +94 -2
- package/src/cdk/blocks-stack.test.ts +186 -118
- package/src/cdk/compute/compute-registry.ts +45 -0
- package/src/cdk/compute/compute.ts +10 -0
- package/src/cdk/compute/default-compute-factory.ts +38 -0
- package/src/cdk/config-registry.test.ts +135 -0
- package/src/cdk/config-registry.ts +92 -34
- package/src/cdk/index.ts +129 -10
- package/src/cdk/internal.ts +1 -0
- package/src/client/index.ts +1 -1
- package/src/common/config.test.ts +21 -0
- package/src/common/config.ts +47 -3
- package/src/common/index.ts +8 -0
- package/src/errors.ts +21 -0
- package/src/hosting-secrets.test.ts +106 -0
- package/src/hosting-secrets.ts +106 -0
- package/src/hosting.test.ts +272 -63
- package/src/hosting.ts +200 -68
- package/src/index.cdk.ts +80 -38
- package/src/index.ts +35 -16
- package/src/lambda-handler.test.ts +215 -1
- package/src/lambda-handler.ts +54 -2
- package/src/pipeline/index.ts +64 -11
- package/src/pipeline.test.ts +55 -0
- package/src/rpc.test.ts +44 -1
- package/src/rpc.ts +41 -0
- package/src/scripts/config.ts +35 -0
- package/src/scripts/deploy.ts +5 -0
- package/src/scripts/dev-server.ts +40 -0
- package/src/scripts/generate-client.test.ts +43 -8
- package/src/scripts/generate-client.ts +70 -65
- package/src/scripts/index.ts +20 -17
- package/src/scripts/preflight-credentials.test.ts +110 -0
- package/src/scripts/preflight-credentials.ts +131 -0
- package/src/scripts/sandbox-args.test.ts +92 -0
- package/src/scripts/sandbox-empty-buckets.test.ts +191 -0
- package/src/scripts/sandbox.ts +287 -39
- package/src/scripts/secret.test.ts +35 -0
- package/src/scripts/secret.ts +35 -0
- package/src/scripts/typegen.ts +18 -0
- package/src/secret-naming.test.ts +59 -0
- package/src/secret-naming.ts +99 -0
- package/src/version.ts +1 -1
|
@@ -1,15 +1,57 @@
|
|
|
1
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
import { test, describe, before } from 'node:test';
|
|
4
3
|
import assert from 'node:assert';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
4
|
import { dirname, join } from 'node:path';
|
|
5
|
+
import { before, describe, test } from 'node:test';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import * as cdk from 'aws-cdk-lib';
|
|
8
|
-
import {
|
|
8
|
+
import { Match, Template } from 'aws-cdk-lib/assertions';
|
|
9
|
+
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
|
|
9
10
|
import { PolicyStatement } from 'aws-cdk-lib/aws-iam';
|
|
11
|
+
import * as lambda from 'aws-cdk-lib/aws-lambda-nodejs';
|
|
12
|
+
import { BLOCKS_RPC_PREFIX } from '../constants.js';
|
|
10
13
|
import { BlocksBackend } from './blocks-backend.js';
|
|
11
14
|
import { BlocksPresets } from './blocks-defaults.js';
|
|
15
|
+
import { Compute } from './compute/compute.js';
|
|
12
16
|
import { Scope } from './index.js';
|
|
17
|
+
// A real app gets its default compute from @aws-blocks/bb-lambda-compute (via
|
|
18
|
+
// @aws-blocks/blocks), which core's own tests can't depend on. Use an
|
|
19
|
+
// equivalent inline stub: a Compute that owns a NodejsFunction + API Gateway,
|
|
20
|
+
// so create() can build the default and the handler/gateway/apiUrl accessors
|
|
21
|
+
// and synth-shape assertions have something real to resolve to. It is passed to
|
|
22
|
+
// each create() via the internal `defaultComputeFactory` option (see
|
|
23
|
+
// makeBackend), exactly as @aws-blocks/blocks injects LambdaCompute.
|
|
24
|
+
class StubLambdaCompute extends Compute {
|
|
25
|
+
fn;
|
|
26
|
+
apiGateway;
|
|
27
|
+
apiUrl;
|
|
28
|
+
logGroup;
|
|
29
|
+
constructor(scope, id) {
|
|
30
|
+
super(id, { parent: scope });
|
|
31
|
+
this.logGroup = new cdk.aws_logs.LogGroup(this, 'HandlerLogGroup', {
|
|
32
|
+
removalPolicy: cdk.RemovalPolicy.DESTROY,
|
|
33
|
+
});
|
|
34
|
+
this.fn = new lambda.NodejsFunction(this, 'Handler', {
|
|
35
|
+
entry: this.backendHandlerPath,
|
|
36
|
+
runtime: cdk.aws_lambda.Runtime.NODEJS_22_X,
|
|
37
|
+
handler: 'handler',
|
|
38
|
+
role: this.executionRole,
|
|
39
|
+
logGroup: this.logGroup,
|
|
40
|
+
environment: { BLOCKS_STACK_NAME: this.backendStackName },
|
|
41
|
+
bundling: { minify: true, esbuildArgs: { '--conditions': 'aws-runtime' } },
|
|
42
|
+
});
|
|
43
|
+
this.apiGateway = new apigateway.RestApi(this, 'API', { restApiName: 'Blocks API' });
|
|
44
|
+
this.apiGateway.root.addProxy({
|
|
45
|
+
defaultIntegration: new apigateway.LambdaIntegration(this.fn),
|
|
46
|
+
anyMethod: true,
|
|
47
|
+
});
|
|
48
|
+
this.apiUrl = `${this.apiGateway.url}${BLOCKS_RPC_PREFIX.slice(1)}`;
|
|
49
|
+
}
|
|
50
|
+
setEnv(key, value) {
|
|
51
|
+
this.fn.addEnvironment(key, value);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const stubComputeFactory = (root) => new StubLambdaCompute(root, 'DefaultCompute');
|
|
13
55
|
// Simulate the CDK condition being active (tests import CDK files directly)
|
|
14
56
|
before(() => {
|
|
15
57
|
process.env.NODE_OPTIONS = (process.env.NODE_OPTIONS ?? '') + ' --conditions=cdk';
|
|
@@ -21,20 +63,15 @@ const factoryBackendPath = join(__dirname, '__fixtures__', 'factory-backend.js')
|
|
|
21
63
|
const fullIdConstructBackendPath = join(__dirname, '__fixtures__', 'fullid-construct-backend.js');
|
|
22
64
|
const EXECUTION_ROLE_MARKER_ACTION = 'blocks-test:MarkerAction';
|
|
23
65
|
const importMetaHandlerPath = join(__dirname, '__fixtures__', 'import-meta-handler.js');
|
|
66
|
+
// Wraps BlocksBackend.create, injecting the stub default-compute factory the way
|
|
67
|
+
// @aws-blocks/blocks injects LambdaCompute — so tests don't repeat it 15 times.
|
|
68
|
+
const makeBackend = (scope, id, backendCDKPath) => BlocksBackend.create(scope, id, { backendHandlerPath: handlerPath, backendCDKPath, defaults: BlocksPresets.production, defaultComputeFactory: stubComputeFactory });
|
|
24
69
|
describe('ESM cache-busting (multi-stage)', () => {
|
|
25
70
|
test('BlocksBackend.create() with same backendCDKPath but different IDs produces constructs in each', async () => {
|
|
26
71
|
const app = new cdk.App();
|
|
27
72
|
const stack = new cdk.Stack(app, 'TestStack');
|
|
28
|
-
const backend1 = await
|
|
29
|
-
|
|
30
|
-
backendCDKPath: sideEffectBackendPath,
|
|
31
|
-
defaults: BlocksPresets.production,
|
|
32
|
-
});
|
|
33
|
-
const backend2 = await BlocksBackend.create(stack, 'Stage2', {
|
|
34
|
-
backendHandlerPath: handlerPath,
|
|
35
|
-
backendCDKPath: sideEffectBackendPath,
|
|
36
|
-
defaults: BlocksPresets.production,
|
|
37
|
-
});
|
|
73
|
+
const backend1 = await makeBackend(stack, 'Stage1', sideEffectBackendPath);
|
|
74
|
+
const backend2 = await makeBackend(stack, 'Stage2', sideEffectBackendPath);
|
|
38
75
|
const findMarker = (scope) => scope.node.tryFindChild('SideEffectMarker');
|
|
39
76
|
assert.ok(findMarker(backend1), 'Stage1 backend should have SideEffectMarker construct from module side effect');
|
|
40
77
|
assert.ok(findMarker(backend2), 'Stage2 backend should have SideEffectMarker construct from re-executed module');
|
|
@@ -44,11 +81,7 @@ describe('synth shape (drop into existing stack)', () => {
|
|
|
44
81
|
test('BlocksBackend lives inside the parent stack and synthesizes Lambda + API Gateway', async () => {
|
|
45
82
|
const app = new cdk.App();
|
|
46
83
|
const parent = new cdk.Stack(app, 'MyExistingStack');
|
|
47
|
-
const backend = await
|
|
48
|
-
backendHandlerPath: handlerPath,
|
|
49
|
-
backendCDKPath: sideEffectBackendPath,
|
|
50
|
-
defaults: BlocksPresets.production,
|
|
51
|
-
});
|
|
84
|
+
const backend = await makeBackend(parent, 'Blocks', sideEffectBackendPath);
|
|
52
85
|
// Public surface mirrors BlocksStack.
|
|
53
86
|
assert.ok(backend.handler, 'BlocksBackend should expose .handler');
|
|
54
87
|
assert.ok(backend.gateway, 'BlocksBackend should expose .gateway');
|
|
@@ -62,16 +95,8 @@ describe('synth shape (drop into existing stack)', () => {
|
|
|
62
95
|
test('multiple BlocksBackends in the same parent stack do not collide', async () => {
|
|
63
96
|
const app = new cdk.App();
|
|
64
97
|
const parent = new cdk.Stack(app, 'MultiBackendStack');
|
|
65
|
-
await
|
|
66
|
-
|
|
67
|
-
backendCDKPath: sideEffectBackendPath,
|
|
68
|
-
defaults: BlocksPresets.production,
|
|
69
|
-
});
|
|
70
|
-
await BlocksBackend.create(parent, 'BackendB', {
|
|
71
|
-
backendHandlerPath: handlerPath,
|
|
72
|
-
backendCDKPath: sideEffectBackendPath,
|
|
73
|
-
defaults: BlocksPresets.production,
|
|
74
|
-
});
|
|
98
|
+
await makeBackend(parent, 'BackendA', sideEffectBackendPath);
|
|
99
|
+
await makeBackend(parent, 'BackendB', sideEffectBackendPath);
|
|
75
100
|
const template = Template.fromStack(parent);
|
|
76
101
|
template.resourceCountIs('AWS::ApiGateway::RestApi', 2);
|
|
77
102
|
});
|
|
@@ -80,21 +105,13 @@ describe('shared execution role', () => {
|
|
|
80
105
|
test('exposes executionRole on the backend', async () => {
|
|
81
106
|
const app = new cdk.App();
|
|
82
107
|
const parent = new cdk.Stack(app, 'RoleSurfaceStack');
|
|
83
|
-
const backend = await
|
|
84
|
-
backendHandlerPath: handlerPath,
|
|
85
|
-
backendCDKPath: sideEffectBackendPath,
|
|
86
|
-
defaults: BlocksPresets.production,
|
|
87
|
-
});
|
|
108
|
+
const backend = await makeBackend(parent, 'Blocks', sideEffectBackendPath);
|
|
88
109
|
assert.ok(backend.executionRole, 'BlocksBackend should expose .executionRole');
|
|
89
110
|
});
|
|
90
111
|
test('synth produces a Lambda-assumable role with basic execution, and the handler uses it', async () => {
|
|
91
112
|
const app = new cdk.App();
|
|
92
113
|
const parent = new cdk.Stack(app, 'RoleSynthStack');
|
|
93
|
-
await
|
|
94
|
-
backendHandlerPath: handlerPath,
|
|
95
|
-
backendCDKPath: sideEffectBackendPath,
|
|
96
|
-
defaults: BlocksPresets.production,
|
|
97
|
-
});
|
|
114
|
+
await makeBackend(parent, 'Blocks', sideEffectBackendPath);
|
|
98
115
|
const template = Template.fromStack(parent);
|
|
99
116
|
// The shared role (logical id derived from the 'BlocksRole' construct id)
|
|
100
117
|
// is assumable by Lambda and carries AWSLambdaBasicExecutionRole (so
|
|
@@ -102,7 +119,7 @@ describe('shared execution role', () => {
|
|
|
102
119
|
// roles exist (API Gateway CloudWatch role, config BucketDeployment role),
|
|
103
120
|
// so we target ours by logical id.
|
|
104
121
|
const roles = template.findResources('AWS::IAM::Role');
|
|
105
|
-
const blocksRoleId = Object.keys(roles).find(k => k.includes('BlocksRole'));
|
|
122
|
+
const blocksRoleId = Object.keys(roles).find((k) => k.includes('BlocksRole'));
|
|
106
123
|
assert.ok(blocksRoleId, 'expected a role from the BlocksRole construct');
|
|
107
124
|
const blocksRole = roles[blocksRoleId];
|
|
108
125
|
assert.deepStrictEqual(blocksRole.Properties.AssumeRolePolicyDocument.Statement[0], {
|
|
@@ -111,19 +128,27 @@ describe('shared execution role', () => {
|
|
|
111
128
|
Principal: { Service: 'lambda.amazonaws.com' },
|
|
112
129
|
});
|
|
113
130
|
assert.ok(JSON.stringify(blocksRole.Properties.ManagedPolicyArns ?? []).includes('AWSLambdaBasicExecutionRole'), 'BlocksRole should attach AWSLambdaBasicExecutionRole');
|
|
131
|
+
// Core is BB-agnostic: with no Building Block that runs AS the shared role, the trust policy
|
|
132
|
+
// must NOT allow any compute principal beyond Lambda (e.g. bedrock-agentcore is added by the
|
|
133
|
+
// Agent BB's own construct, not here — see packages/bb-agent).
|
|
134
|
+
assert.ok(!JSON.stringify(blocksRole.Properties.AssumeRolePolicyDocument).includes('bedrock-agentcore'), 'core must not trust bedrock-agentcore by default (no agent → no AgentCore trust)');
|
|
114
135
|
// The Blocks handler references the shared role, not an auto-generated one.
|
|
115
136
|
template.hasResourceProperties('AWS::Lambda::Function', {
|
|
116
137
|
Role: { 'Fn::GetAtt': [blocksRoleId, 'Arn'] },
|
|
117
138
|
});
|
|
118
139
|
});
|
|
140
|
+
test('exposes backendModulePath (props.backendCDKPath) for co-bundling BBs', async () => {
|
|
141
|
+
const app = new cdk.App();
|
|
142
|
+
const parent = new cdk.Stack(app, 'BackendPathStack');
|
|
143
|
+
const backend = await makeBackend(parent, 'Blocks', sideEffectBackendPath);
|
|
144
|
+
// Building Blocks that co-bundle the app backend at synth time (e.g. the Agent BB's AgentCore
|
|
145
|
+
// Runtime) discover it via globalThis.CURRENT_BLOCKS_STACK.backendModulePath.
|
|
146
|
+
assert.strictEqual(backend.backendModulePath, sideEffectBackendPath);
|
|
147
|
+
});
|
|
119
148
|
test('a nested block resolves executionRole via the construct-tree walk', async () => {
|
|
120
149
|
const app = new cdk.App();
|
|
121
150
|
const parent = new cdk.Stack(app, 'RoleResolveStack');
|
|
122
|
-
const backend = await
|
|
123
|
-
backendHandlerPath: handlerPath,
|
|
124
|
-
backendCDKPath: sideEffectBackendPath,
|
|
125
|
-
defaults: BlocksPresets.production,
|
|
126
|
-
});
|
|
151
|
+
const backend = await makeBackend(parent, 'Blocks', sideEffectBackendPath);
|
|
127
152
|
// Build nested Scopes under the backend (outer → inner), the same shape a
|
|
128
153
|
// real Building Block tree has, and grant a uniquely-named marker action to
|
|
129
154
|
// `this.executionRole` from the innermost scope. If the getter's tree-walk
|
|
@@ -160,6 +185,7 @@ describe('CJS bundle: import.meta.url in the handler is shimmed (no Lambda-load
|
|
|
160
185
|
backendHandlerPath: importMetaHandlerPath,
|
|
161
186
|
backendCDKPath: sideEffectBackendPath,
|
|
162
187
|
defaults: BlocksPresets.production,
|
|
188
|
+
defaultComputeFactory: stubComputeFactory,
|
|
163
189
|
}));
|
|
164
190
|
});
|
|
165
191
|
});
|
|
@@ -167,11 +193,7 @@ describe('factory function support', () => {
|
|
|
167
193
|
test('BlocksBackend.create() calls default export function with the backend instance', async () => {
|
|
168
194
|
const app = new cdk.App();
|
|
169
195
|
const stack = new cdk.Stack(app, 'FactoryTestStack');
|
|
170
|
-
const backend = await
|
|
171
|
-
backendHandlerPath: handlerPath,
|
|
172
|
-
backendCDKPath: factoryBackendPath,
|
|
173
|
-
defaults: BlocksPresets.production,
|
|
174
|
-
});
|
|
196
|
+
const backend = await makeBackend(stack, 'FactoryStage', factoryBackendPath);
|
|
175
197
|
const marker = backend.node.tryFindChild('FactoryMarker');
|
|
176
198
|
assert.ok(marker, 'Factory function should have created FactoryMarker on the backend');
|
|
177
199
|
});
|
|
@@ -180,11 +202,7 @@ describe('fullId is token-free (construct IDs / env-var keys)', () => {
|
|
|
180
202
|
test('top-level stack: fullId is {stackName}-{id} and resolvable', async () => {
|
|
181
203
|
const app = new cdk.App();
|
|
182
204
|
const stack = new cdk.Stack(app, 'TopLevelStack');
|
|
183
|
-
const backend = await
|
|
184
|
-
backendHandlerPath: handlerPath,
|
|
185
|
-
backendCDKPath: sideEffectBackendPath,
|
|
186
|
-
defaults: BlocksPresets.production,
|
|
187
|
-
});
|
|
205
|
+
const backend = await makeBackend(stack, 'blocks', sideEffectBackendPath);
|
|
188
206
|
assert.strictEqual(backend.fullId, 'TopLevelStack-blocks');
|
|
189
207
|
assert.ok(!cdk.Token.isUnresolved(backend.fullId), 'fullId must not contain a token');
|
|
190
208
|
});
|
|
@@ -198,11 +216,7 @@ describe('fullId is token-free (construct IDs / env-var keys)', () => {
|
|
|
198
216
|
const nested = new cdk.NestedStack(parent, 'blocks');
|
|
199
217
|
// Sanity: the nested stack's own name really is a token.
|
|
200
218
|
assert.ok(cdk.Token.isUnresolved(nested.stackName), 'precondition: NestedStack.stackName should be an unresolved token');
|
|
201
|
-
const backend = await
|
|
202
|
-
backendHandlerPath: handlerPath,
|
|
203
|
-
backendCDKPath: sideEffectBackendPath,
|
|
204
|
-
defaults: BlocksPresets.production,
|
|
205
|
-
});
|
|
219
|
+
const backend = await makeBackend(nested, 'blocks', sideEffectBackendPath);
|
|
206
220
|
assert.ok(!cdk.Token.isUnresolved(backend.fullId), `fullId must be token-free inside a nested stack, got: ${backend.fullId}`);
|
|
207
221
|
// Falls back to the top-level (concrete) stack name, keeping uniqueness.
|
|
208
222
|
assert.strictEqual(backend.fullId, 'ParentStack-blocks');
|
|
@@ -213,11 +227,7 @@ describe('fullId is token-free (construct IDs / env-var keys)', () => {
|
|
|
213
227
|
const app = new cdk.App();
|
|
214
228
|
const parent = new cdk.Stack(app, 'Gen2ParentStack');
|
|
215
229
|
const nested = new cdk.NestedStack(parent, 'blocks');
|
|
216
|
-
const backend = await
|
|
217
|
-
backendHandlerPath: handlerPath,
|
|
218
|
-
backendCDKPath: fullIdConstructBackendPath,
|
|
219
|
-
defaults: BlocksPresets.production,
|
|
220
|
-
});
|
|
230
|
+
const backend = await makeBackend(nested, 'blocks', fullIdConstructBackendPath);
|
|
221
231
|
// The construct ID is `${scope.fullId}Marker` → `ParentStack-blocks-blocks-dbMarker`.
|
|
222
232
|
const expectedId = `${backend.fullId}-dbMarker`;
|
|
223
233
|
assert.ok(nested.node.tryFindChild(expectedId), `expected a child construct with id "${expectedId}"`);
|
|
@@ -234,11 +244,7 @@ describe('fullId is token-free (construct IDs / env-var keys)', () => {
|
|
|
234
244
|
const app = new cdk.App();
|
|
235
245
|
const parent = new cdk.Stack(app, 'InvariantParent');
|
|
236
246
|
const nested = new cdk.NestedStack(parent, 'blocks');
|
|
237
|
-
const backend = await
|
|
238
|
-
backendHandlerPath: handlerPath,
|
|
239
|
-
backendCDKPath: sideEffectBackendPath,
|
|
240
|
-
defaults: BlocksPresets.production,
|
|
241
|
-
});
|
|
247
|
+
const backend = await makeBackend(nested, 'blocks', sideEffectBackendPath);
|
|
242
248
|
const template = Template.fromStack(nested);
|
|
243
249
|
const fns = template.findResources('AWS::Lambda::Function');
|
|
244
250
|
const envValues = Object.values(fns)
|
|
@@ -259,11 +265,13 @@ describe('infrastructure defaults (backend-anchored)', () => {
|
|
|
259
265
|
backendHandlerPath: handlerPath,
|
|
260
266
|
backendCDKPath: sideEffectBackendPath,
|
|
261
267
|
defaults: BlocksPresets.production,
|
|
268
|
+
defaultComputeFactory: stubComputeFactory,
|
|
262
269
|
});
|
|
263
270
|
const b = await BlocksBackend.create(stack, 'B', {
|
|
264
271
|
backendHandlerPath: handlerPath,
|
|
265
272
|
backendCDKPath: sideEffectBackendPath,
|
|
266
273
|
defaults: BlocksPresets.sandbox,
|
|
274
|
+
defaultComputeFactory: stubComputeFactory,
|
|
267
275
|
});
|
|
268
276
|
// Two backends in one stack must NOT clobber each other — defaults are
|
|
269
277
|
// anchored on the backend, not the shared stack.
|
|
@@ -277,6 +285,7 @@ describe('infrastructure defaults (backend-anchored)', () => {
|
|
|
277
285
|
backendHandlerPath: handlerPath,
|
|
278
286
|
backendCDKPath: sideEffectBackendPath,
|
|
279
287
|
defaults: BlocksPresets.sandbox,
|
|
288
|
+
defaultComputeFactory: stubComputeFactory,
|
|
280
289
|
});
|
|
281
290
|
// A Scope under the backend resolves scope.defaults by walking up to it.
|
|
282
291
|
const outer = new Scope('outer');
|
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import { RemovalPolicy } from 'aws-cdk-lib';
|
|
2
|
+
import { RetentionDays } from 'aws-cdk-lib/aws-logs';
|
|
3
|
+
/**
|
|
4
|
+
* Request-rate limits applied to an API Gateway stage. On a REST API these are
|
|
5
|
+
* requests/second; on a WebSocket API the unit is messages/second across the
|
|
6
|
+
* connection. `rateLimit` is the steady-state ceiling and `burstLimit` the
|
|
7
|
+
* token-bucket size for short spikes.
|
|
8
|
+
*/
|
|
9
|
+
export interface BlocksThrottling {
|
|
10
|
+
/** Steady-state request (or WebSocket message) rate ceiling, per second. */
|
|
11
|
+
rateLimit: number;
|
|
12
|
+
/** Token-bucket burst size for short spikes above `rateLimit`. */
|
|
13
|
+
burstLimit: number;
|
|
14
|
+
}
|
|
2
15
|
/**
|
|
3
16
|
* Default values for every Amazon-authored Building Block created within a
|
|
4
17
|
* `BlocksStack` or `BlocksBackend`. A per-block option always overrides the
|
|
@@ -20,6 +33,62 @@ export interface BlocksDefaults {
|
|
|
20
33
|
* well as the CLI, API, and AWS console — until it is turned off.
|
|
21
34
|
*/
|
|
22
35
|
deletionProtection: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* CORS origins the compute's API accepts, as regular-expression patterns
|
|
38
|
+
* matched against the request `Origin` header. Empty means no cross-origin
|
|
39
|
+
* requests are allowed. The `sandbox` preset allows localhost so a local dev
|
|
40
|
+
* frontend can reach a deployed API; `production` allows none by default.
|
|
41
|
+
*/
|
|
42
|
+
allowedOrigins: string[];
|
|
43
|
+
/**
|
|
44
|
+
* Whether stateful resources that support continuous backups keep them on by
|
|
45
|
+
* default — e.g. DynamoDB Point-in-Time Recovery, letting you restore to any
|
|
46
|
+
* second in the retention window. On in `production`, off in `sandbox` (where
|
|
47
|
+
* throwaway data isn't worth the backup-storage cost). Blocks whose service
|
|
48
|
+
* has no equivalent simply ignore it.
|
|
49
|
+
*
|
|
50
|
+
* `true` enables backups with the service's default window; `false` disables
|
|
51
|
+
* them; `{ retentionDays: n }` enables them and pins the window (a block
|
|
52
|
+
* clamps/validates `n` to its service's supported range — DynamoDB PITR is
|
|
53
|
+
* 1–35 days). Backups only have a window when on, so the two are one field.
|
|
54
|
+
*/
|
|
55
|
+
pointInTimeRecovery: boolean | {
|
|
56
|
+
retentionDays: number;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* How long CloudWatch Logs keeps log events written by Blocks-managed log
|
|
60
|
+
* groups (the core handler Lambda, migration/GSI Lambdas, hosting compute,
|
|
61
|
+
* `bb-logger`, and API Gateway access logs) before expiring them. Without a
|
|
62
|
+
* retention set, AWS keeps log events forever, which grows cost unbounded —
|
|
63
|
+
* every Blocks-managed log group reads this default so retention is applied
|
|
64
|
+
* consistently.
|
|
65
|
+
*/
|
|
66
|
+
logRetention: RetentionDays;
|
|
67
|
+
/**
|
|
68
|
+
* Request-rate limits applied to every Blocks-managed API Gateway stage: the
|
|
69
|
+
* core REST API, the SSR/hosting REST API, and the `bb-realtime` WebSocket
|
|
70
|
+
* stage. Protects the backend from runaway clients and caps blast radius.
|
|
71
|
+
* The sandbox preset caps tighter (200/400) than production (1000/2000) so a
|
|
72
|
+
* disposable stack is well-protected without throttling real production
|
|
73
|
+
* traffic. See {@link BlocksThrottling}.
|
|
74
|
+
*/
|
|
75
|
+
throttling: BlocksThrottling;
|
|
76
|
+
/**
|
|
77
|
+
* Whether to emit structured JSON access logs from every Blocks-managed API
|
|
78
|
+
* Gateway stage to a dedicated CloudWatch log group (retention follows
|
|
79
|
+
* {@link logRetention}).
|
|
80
|
+
*
|
|
81
|
+
* **Off by default in both presets** — opt in with a per-stack override
|
|
82
|
+
* (`defaults: { ...BlocksPresets.production, accessLogging: true }`). It is
|
|
83
|
+
* off by default (rather than on for production) because enabling it
|
|
84
|
+
* provisions the account-level API Gateway CloudWatch Logs role, which is an
|
|
85
|
+
* account/region-level singleton: a second Blocks stack in the same
|
|
86
|
+
* account+region that also enables access logging can repoint that role on
|
|
87
|
+
* deploy or, on teardown, leave the survivor's access logging broken.
|
|
88
|
+
* Enabling it is therefore safe for **one Blocks stack per region** — see
|
|
89
|
+
* `ensureApiGatewayAccount` for the full multi-stack teardown caveat.
|
|
90
|
+
*/
|
|
91
|
+
accessLogging: boolean;
|
|
23
92
|
}
|
|
24
93
|
/**
|
|
25
94
|
* Prepared starting points for {@link BlocksDefaults}. Pick one and override
|
|
@@ -30,15 +99,36 @@ export interface BlocksDefaults {
|
|
|
30
99
|
* ```
|
|
31
100
|
*/
|
|
32
101
|
export declare const BlocksPresets: {
|
|
33
|
-
/** Disposable development stacks: tear down cleanly, no delete guard. */
|
|
102
|
+
/** Disposable development stacks: tear down cleanly, no delete guard, no backups. */
|
|
34
103
|
sandbox: {
|
|
35
104
|
removalPolicy: RemovalPolicy.DESTROY;
|
|
36
105
|
deletionProtection: false;
|
|
106
|
+
allowedOrigins: string[];
|
|
107
|
+
pointInTimeRecovery: false;
|
|
108
|
+
logRetention: RetentionDays.ONE_WEEK;
|
|
109
|
+
throttling: {
|
|
110
|
+
rateLimit: number;
|
|
111
|
+
burstLimit: number;
|
|
112
|
+
};
|
|
113
|
+
accessLogging: false;
|
|
37
114
|
};
|
|
38
|
-
/**
|
|
115
|
+
/**
|
|
116
|
+
* Durable, protected posture for permanent deployments. A higher throttle
|
|
117
|
+
* ceiling (1000/2000) than sandbox so the default doesn't 429 real
|
|
118
|
+
* production traffic — raise it via a per-stack `throttling` override for
|
|
119
|
+
* higher-volume APIs.
|
|
120
|
+
*/
|
|
39
121
|
production: {
|
|
40
122
|
removalPolicy: RemovalPolicy.RETAIN;
|
|
41
123
|
deletionProtection: true;
|
|
124
|
+
allowedOrigins: never[];
|
|
125
|
+
pointInTimeRecovery: true;
|
|
126
|
+
logRetention: RetentionDays.ONE_YEAR;
|
|
127
|
+
throttling: {
|
|
128
|
+
rateLimit: number;
|
|
129
|
+
burstLimit: number;
|
|
130
|
+
};
|
|
131
|
+
accessLogging: false;
|
|
42
132
|
};
|
|
43
133
|
};
|
|
44
134
|
//# sourceMappingURL=blocks-defaults.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blocks-defaults.d.ts","sourceRoot":"","sources":["../../src/cdk/blocks-defaults.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"blocks-defaults.d.ts","sourceRoot":"","sources":["../../src/cdk/blocks-defaults.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAChC,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,UAAU,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;;OAKG;IACH,aAAa,EAAE,aAAa,CAAC;IAE7B;;;;OAIG;IACH,kBAAkB,EAAE,OAAO,CAAC;IAE5B;;;;;OAKG;IACH,cAAc,EAAE,MAAM,EAAE,CAAC;IAEzB;;;;;;;;;;;OAWG;IACH,mBAAmB,EAAE,OAAO,GAAG;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IAEzD;;;;;;;OAOG;IACH,YAAY,EAAE,aAAa,CAAC;IAE5B;;;;;;;OAOG;IACH,UAAU,EAAE,gBAAgB,CAAC;IAE7B;;;;;;;;;;;;;;OAcG;IACH,aAAa,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;IACzB,qFAAqF;;;;;;;;;;;;;IAUrF;;;;;OAKG;;;;;;;;;;;;;CAcsC,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import { RemovalPolicy } from 'aws-cdk-lib';
|
|
4
|
+
import { RetentionDays } from 'aws-cdk-lib/aws-logs';
|
|
4
5
|
/**
|
|
5
6
|
* Prepared starting points for {@link BlocksDefaults}. Pick one and override
|
|
6
7
|
* individual fields with a spread:
|
|
@@ -10,14 +11,33 @@ import { RemovalPolicy } from 'aws-cdk-lib';
|
|
|
10
11
|
* ```
|
|
11
12
|
*/
|
|
12
13
|
export const BlocksPresets = {
|
|
13
|
-
/** Disposable development stacks: tear down cleanly, no delete guard. */
|
|
14
|
+
/** Disposable development stacks: tear down cleanly, no delete guard, no backups. */
|
|
14
15
|
sandbox: {
|
|
15
16
|
removalPolicy: RemovalPolicy.DESTROY,
|
|
16
17
|
deletionProtection: false,
|
|
18
|
+
allowedOrigins: ['^https?://(localhost|127\\.0\\.0\\.1)(:\\d+)?$'],
|
|
19
|
+
pointInTimeRecovery: false,
|
|
20
|
+
logRetention: RetentionDays.ONE_WEEK,
|
|
21
|
+
throttling: { rateLimit: 200, burstLimit: 400 },
|
|
22
|
+
accessLogging: false,
|
|
17
23
|
},
|
|
18
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* Durable, protected posture for permanent deployments. A higher throttle
|
|
26
|
+
* ceiling (1000/2000) than sandbox so the default doesn't 429 real
|
|
27
|
+
* production traffic — raise it via a per-stack `throttling` override for
|
|
28
|
+
* higher-volume APIs.
|
|
29
|
+
*/
|
|
19
30
|
production: {
|
|
20
31
|
removalPolicy: RemovalPolicy.RETAIN,
|
|
21
32
|
deletionProtection: true,
|
|
33
|
+
allowedOrigins: [],
|
|
34
|
+
pointInTimeRecovery: true,
|
|
35
|
+
logRetention: RetentionDays.ONE_YEAR,
|
|
36
|
+
throttling: { rateLimit: 1000, burstLimit: 2000 },
|
|
37
|
+
// Off by default even in production: enabling access logging mutates the
|
|
38
|
+
// account/region-level API Gateway CloudWatch role (a singleton). Opt in
|
|
39
|
+
// per stack once you've confirmed a single Blocks stack owns it in the
|
|
40
|
+
// region — see the `accessLogging` field doc.
|
|
41
|
+
accessLogging: false,
|
|
22
42
|
},
|
|
23
43
|
};
|
|
@@ -9,14 +9,33 @@
|
|
|
9
9
|
import { test, describe } from 'node:test';
|
|
10
10
|
import assert from 'node:assert';
|
|
11
11
|
import { RemovalPolicy } from 'aws-cdk-lib';
|
|
12
|
+
import { RetentionDays } from 'aws-cdk-lib/aws-logs';
|
|
12
13
|
import { BlocksPresets } from './blocks-defaults.js';
|
|
13
14
|
describe('BlocksPresets', () => {
|
|
14
|
-
test('sandbox is disposable: DESTROY + deletion protection off', () => {
|
|
15
|
+
test('sandbox is disposable: DESTROY + deletion protection off + no PITR', () => {
|
|
15
16
|
assert.strictEqual(BlocksPresets.sandbox.removalPolicy, RemovalPolicy.DESTROY);
|
|
16
17
|
assert.strictEqual(BlocksPresets.sandbox.deletionProtection, false);
|
|
18
|
+
assert.strictEqual(BlocksPresets.sandbox.pointInTimeRecovery, false);
|
|
17
19
|
});
|
|
18
|
-
test('production is durable: RETAIN + deletion protection on', () => {
|
|
20
|
+
test('production is durable: RETAIN + deletion protection on + PITR on', () => {
|
|
19
21
|
assert.strictEqual(BlocksPresets.production.removalPolicy, RemovalPolicy.RETAIN);
|
|
20
22
|
assert.strictEqual(BlocksPresets.production.deletionProtection, true);
|
|
23
|
+
assert.strictEqual(BlocksPresets.production.pointInTimeRecovery, true);
|
|
24
|
+
});
|
|
25
|
+
test('sandbox keeps logs briefly', () => {
|
|
26
|
+
assert.strictEqual(BlocksPresets.sandbox.logRetention, RetentionDays.ONE_WEEK);
|
|
27
|
+
});
|
|
28
|
+
test('production keeps logs a year', () => {
|
|
29
|
+
assert.strictEqual(BlocksPresets.production.logRetention, RetentionDays.ONE_YEAR);
|
|
30
|
+
});
|
|
31
|
+
test('access logging is off by default in BOTH presets (opt-in; mutates an account singleton)', () => {
|
|
32
|
+
assert.strictEqual(BlocksPresets.sandbox.accessLogging, false);
|
|
33
|
+
assert.strictEqual(BlocksPresets.production.accessLogging, false);
|
|
34
|
+
});
|
|
35
|
+
test('sandbox caps tighter (200/400) than production (1000/2000)', () => {
|
|
36
|
+
assert.strictEqual(BlocksPresets.sandbox.throttling.rateLimit, 200);
|
|
37
|
+
assert.strictEqual(BlocksPresets.sandbox.throttling.burstLimit, 400);
|
|
38
|
+
assert.strictEqual(BlocksPresets.production.throttling.rateLimit, 1000);
|
|
39
|
+
assert.strictEqual(BlocksPresets.production.throttling.burstLimit, 2000);
|
|
21
40
|
});
|
|
22
41
|
});
|