konokenj.cdk-api-mcp-server 0.41.0__py3-none-any.whl → 0.42.0__py3-none-any.whl
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.
Potentially problematic release.
This version of konokenj.cdk-api-mcp-server might be problematic. Click here for more details.
- cdk_api_mcp_server/__about__.py +1 -1
- cdk_api_mcp_server/resources/aws-cdk/constructs/@aws-cdk/aws-bedrock-alpha/README.md +540 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/@aws-cdk/aws-eks-v2-alpha/README.md +44 -46
- cdk_api_mcp_server/resources/aws-cdk/constructs/@aws-cdk/aws-lambda-python-alpha/README.md +6 -6
- cdk_api_mcp_server/resources/aws-cdk/constructs/@aws-cdk/aws-s3tables-alpha/README.md +28 -1
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2/integ.api-dualstack.ts +3 -4
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2/integ.api.ts +2 -4
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2/integ.stage.ts +7 -20
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2-authorizers/integ.iam.ts +34 -38
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-apigatewayv2-integrations/integ.sqs.ts +58 -71
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-codepipeline-actions/integ.pipeline-elastic-beanstalk-deploy.ts +1 -1
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-cognito/README.md +11 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-cognito/integ.user-pool-client-explicit-props.ts +1 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-dynamodb/README.md +38 -13
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-dynamodb/integ.dynamodb-v2.cci.ts +49 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-dynamodb/integ.dynamodb.cci.ts +27 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-dynamodb/integ.dynamodb.contirubtor-insights-for-gsi.ts +6 -2
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-dynamodb/integ.table-v2-global.ts +9 -3
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-ecs/README.md +3 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-ecs/integ.ebs-volume-initialization-rate.ts +80 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-ecs-patterns/README.md +2 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-ecs-patterns/integ.alb-fargate-service-smart-defaults.ts +143 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-events/README.md +25 -3
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-events/integ.archive-customer-managed-key.ts +23 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-s3-deployment/README.md +18 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-sns/README.md +2 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-sns-subscriptions/integ.sns-sqs-subscription-filter.ts +75 -0
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-sns-subscriptions/integ.sns-sqs.ts +21 -40
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-stepfunctions-tasks/integ.invoke-jsonata.ts +87 -80
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-stepfunctions-tasks/integ.invoke.ts +87 -69
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-stepfunctions-tasks/integ.start-job-run.ts +102 -104
- cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/cx-api/FEATURE_FLAGS.md +28 -1
- {konokenj_cdk_api_mcp_server-0.41.0.dist-info → konokenj_cdk_api_mcp_server-0.42.0.dist-info}/METADATA +2 -2
- {konokenj_cdk_api_mcp_server-0.41.0.dist-info → konokenj_cdk_api_mcp_server-0.42.0.dist-info}/RECORD +37 -31
- {konokenj_cdk_api_mcp_server-0.41.0.dist-info → konokenj_cdk_api_mcp_server-0.42.0.dist-info}/WHEEL +0 -0
- {konokenj_cdk_api_mcp_server-0.41.0.dist-info → konokenj_cdk_api_mcp_server-0.42.0.dist-info}/entry_points.txt +0 -0
- {konokenj_cdk_api_mcp_server-0.41.0.dist-info → konokenj_cdk_api_mcp_server-0.42.0.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration test for the conditional openListener behavior in ApplicationLoadBalancedFargateService.
|
|
3
|
+
*
|
|
4
|
+
* This test validates the security feature that automatically sets openListener to false when custom
|
|
5
|
+
* security groups are detected on the load balancer, preventing unintended internet exposure.
|
|
6
|
+
*
|
|
7
|
+
* Test scenarios:
|
|
8
|
+
* 1. DefaultService: No custom security groups provided
|
|
9
|
+
* - Expected: openListener defaults to true, creates 0.0.0.0/0 ingress rules
|
|
10
|
+
* - Validates: Default behavior when CDK manages all security groups
|
|
11
|
+
*
|
|
12
|
+
* 2. ExplicitOpenService: Explicit openListener: true
|
|
13
|
+
* - Expected: Creates 0.0.0.0/0 ingress rules regardless of other settings
|
|
14
|
+
* - Validates: Explicit override functionality works correctly
|
|
15
|
+
*
|
|
16
|
+
* 3. ExplicitClosedService: Explicit openListener: false
|
|
17
|
+
* - Expected: Does NOT create 0.0.0.0/0 ingress rules
|
|
18
|
+
* - Validates: Explicit closed listener prevents internet access
|
|
19
|
+
*
|
|
20
|
+
* 4. ConditionalWithCustomSG: Custom security groups + no explicit openListener
|
|
21
|
+
* - Expected: Conditional behavior kicks in, openListener defaults to false
|
|
22
|
+
* - Validates: Core feature - prevents 0.0.0.0/0 rules when custom SGs detected
|
|
23
|
+
*
|
|
24
|
+
* The test uses AWS SDK calls to verify actual security group configurations in deployed resources,
|
|
25
|
+
* ensuring the feature works correctly in real AWS environments.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { Vpc, SecurityGroup, Port } from 'aws-cdk-lib/aws-ec2';
|
|
29
|
+
import { Cluster, ContainerImage } from 'aws-cdk-lib/aws-ecs';
|
|
30
|
+
import { ApplicationLoadBalancer } from 'aws-cdk-lib/aws-elasticloadbalancingv2';
|
|
31
|
+
import { App, Stack, Duration } from 'aws-cdk-lib';
|
|
32
|
+
import * as integ from '@aws-cdk/integ-tests-alpha';
|
|
33
|
+
import { ApplicationLoadBalancedFargateService } from 'aws-cdk-lib/aws-ecs-patterns';
|
|
34
|
+
|
|
35
|
+
const app = new App({
|
|
36
|
+
postCliContext: {
|
|
37
|
+
// Enable the feature flag for this test
|
|
38
|
+
'@aws-cdk/aws-ecs-patterns:secGroupsDisablesImplicitOpenListener': true,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const stack = new Stack(app, 'aws-ecs-integ-alb-fg-smart-defaults');
|
|
43
|
+
const vpc = new Vpc(stack, 'Vpc', { maxAzs: 3, natGateways: 1, restrictDefaultSecurityGroup: false });
|
|
44
|
+
const cluster = new Cluster(stack, 'Cluster', { vpc });
|
|
45
|
+
|
|
46
|
+
// Test case 1: Service with conditional default (no openListener specified)
|
|
47
|
+
// CDK creates load balancer, should default to openListener: true (no custom security groups)
|
|
48
|
+
new ApplicationLoadBalancedFargateService(stack, 'SmartDefaultService', {
|
|
49
|
+
cluster,
|
|
50
|
+
memoryLimitMiB: 512,
|
|
51
|
+
taskImageOptions: {
|
|
52
|
+
image: ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
|
|
53
|
+
},
|
|
54
|
+
// No openListener specified - should default to true since no custom security groups
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Test case 2: Service with explicit openListener: true
|
|
58
|
+
new ApplicationLoadBalancedFargateService(stack, 'ExplicitOpenService', {
|
|
59
|
+
cluster,
|
|
60
|
+
memoryLimitMiB: 512,
|
|
61
|
+
taskImageOptions: {
|
|
62
|
+
image: ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
|
|
63
|
+
},
|
|
64
|
+
openListener: true, // Should create 0.0.0.0/0 rules
|
|
65
|
+
listenerPort: 8080,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Test case 3: Service with explicit openListener: false
|
|
69
|
+
new ApplicationLoadBalancedFargateService(stack, 'ExplicitClosedService', {
|
|
70
|
+
cluster,
|
|
71
|
+
memoryLimitMiB: 512,
|
|
72
|
+
taskImageOptions: {
|
|
73
|
+
image: ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
|
|
74
|
+
},
|
|
75
|
+
openListener: false, // Should NOT create 0.0.0.0/0 rules
|
|
76
|
+
listenerPort: 9090,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Test case 4: Service with custom security groups (conditional default should apply)
|
|
80
|
+
const customSecurityGroup = new SecurityGroup(stack, 'CustomSecurityGroup', {
|
|
81
|
+
vpc,
|
|
82
|
+
description: 'Custom security group for load balancer',
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Add a custom rule to the security group
|
|
86
|
+
customSecurityGroup.addIngressRule(
|
|
87
|
+
customSecurityGroup,
|
|
88
|
+
Port.tcp(80),
|
|
89
|
+
'Allow HTTP from custom security group',
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const customLoadBalancer = new ApplicationLoadBalancer(stack, 'CustomLoadBalancer', {
|
|
93
|
+
vpc,
|
|
94
|
+
internetFacing: true,
|
|
95
|
+
securityGroup: customSecurityGroup,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// This should use conditional default (openListener: false) because custom security groups are detected
|
|
99
|
+
new ApplicationLoadBalancedFargateService(stack, 'SmartDefaultWithCustomSG', {
|
|
100
|
+
cluster,
|
|
101
|
+
memoryLimitMiB: 512,
|
|
102
|
+
taskImageOptions: {
|
|
103
|
+
image: ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
|
|
104
|
+
},
|
|
105
|
+
loadBalancer: customLoadBalancer,
|
|
106
|
+
// No openListener specified - should default to false due to custom security groups
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const integTest = new integ.IntegTest(app, 'albFargateServiceSmartDefaultsTest', {
|
|
110
|
+
testCases: [stack],
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Validate the core conditional behavior by checking the custom security group
|
|
114
|
+
// This confirms that when custom security groups are provided, the conditional default prevents
|
|
115
|
+
// creating overly permissive 0.0.0.0/0 ingress rules
|
|
116
|
+
// Assert that the custom security group only contains self-referencing rules (no 0.0.0.0/0)
|
|
117
|
+
// This validates the feature prevents unintended internet exposure
|
|
118
|
+
integTest.assertions.awsApiCall('EC2', 'describeSecurityGroups', {
|
|
119
|
+
GroupIds: [customSecurityGroup.securityGroupId],
|
|
120
|
+
}).expect(integ.ExpectedResult.objectLike({
|
|
121
|
+
SecurityGroups: [
|
|
122
|
+
{
|
|
123
|
+
IpPermissions: integ.Match.arrayWith([
|
|
124
|
+
integ.Match.objectLike({
|
|
125
|
+
FromPort: 80,
|
|
126
|
+
ToPort: 80,
|
|
127
|
+
// Verify only security group references exist, no public internet access (0.0.0.0/0)
|
|
128
|
+
UserIdGroupPairs: integ.Match.arrayWith([
|
|
129
|
+
integ.Match.objectLike({
|
|
130
|
+
GroupId: customSecurityGroup.securityGroupId,
|
|
131
|
+
}),
|
|
132
|
+
]),
|
|
133
|
+
// Ensure no IpRanges with 0.0.0.0/0 are present
|
|
134
|
+
IpRanges: [],
|
|
135
|
+
}),
|
|
136
|
+
]),
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
})).waitForAssertions({
|
|
140
|
+
totalTimeout: Duration.minutes(5),
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
app.synth();
|
|
@@ -332,6 +332,28 @@ new events.EventBus(this, 'Bus', {
|
|
|
332
332
|
});
|
|
333
333
|
```
|
|
334
334
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
335
|
+
To use a customer managed key for an archive, use the `kmsKey` attribute.
|
|
336
|
+
|
|
337
|
+
Note: When you attach a customer managed key to either an EventBus or an Archive, a policy that allows EventBridge to interact with your resource will be added.
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
import * as kms from 'aws-cdk-lib/aws-kms';
|
|
341
|
+
import { Archive, EventBus } from 'aws-cdk-lib/aws-events';
|
|
342
|
+
|
|
343
|
+
const stack = new Stack();
|
|
344
|
+
|
|
345
|
+
declare const kmsKey: kms.IKey;
|
|
346
|
+
|
|
347
|
+
const eventBus = new EventBus(stack, 'Bus');
|
|
348
|
+
|
|
349
|
+
const archive = new Archive(stack, 'Archive', {
|
|
350
|
+
kmsKey: kmsKey,
|
|
351
|
+
sourceEventBus: eventBus,
|
|
352
|
+
eventPattern: {
|
|
353
|
+
source: ['aws.ec2']
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
To enable archives or schema discovery on an event bus, customers has the choice of using either an AWS owned key or a customer managed key.
|
|
359
|
+
For more information, see [KMS key options for event bus encryption](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-encryption-at-rest-key-options.html).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as kms from 'aws-cdk-lib/aws-kms';
|
|
2
|
+
import { App, RemovalPolicy, Stack } from 'aws-cdk-lib';
|
|
3
|
+
import { IntegTest } from '@aws-cdk/integ-tests-alpha';
|
|
4
|
+
import { Archive, EventBus } from 'aws-cdk-lib/aws-events';
|
|
5
|
+
|
|
6
|
+
const app = new App();
|
|
7
|
+
const stack = new Stack(app, 'archive-customer-managed-key');
|
|
8
|
+
|
|
9
|
+
const kmsKey = new kms.Key(stack, 'KmsKey', {
|
|
10
|
+
removalPolicy: RemovalPolicy.DESTROY,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
new Archive(stack, 'Archive', {
|
|
14
|
+
kmsKey: kmsKey,
|
|
15
|
+
sourceEventBus: EventBus.fromEventBusName(stack, 'DefaultEventBus', 'default'),
|
|
16
|
+
eventPattern: {
|
|
17
|
+
source: ['test'],
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
new IntegTest(app, 'archive-customer-managed-key-test', {
|
|
22
|
+
testCases: [stack],
|
|
23
|
+
});
|
|
@@ -325,6 +325,24 @@ new s3deploy.BucketDeployment(this, 'DeployWithInvalidation', {
|
|
|
325
325
|
});
|
|
326
326
|
```
|
|
327
327
|
|
|
328
|
+
By default, the deployment will wait for invalidation to succeed to complete. This will poll Cloudfront for a maximum of 13 minutes to check for a successful invalidation. The drawback to this is that the deployment will fail if invalidation fails or if it takes longer than 13 minutes. As a workaround, there is the option `waitForDistributionInvalidation`, which can be set to false to skip waiting for the invalidation, but this can be risky as invalidation errors will not be reported.
|
|
329
|
+
|
|
330
|
+
```ts
|
|
331
|
+
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
|
|
332
|
+
|
|
333
|
+
declare const bucket: s3.IBucket;
|
|
334
|
+
declare const distribution: cloudfront.IDistribution;
|
|
335
|
+
|
|
336
|
+
new s3deploy.BucketDeployment(this, 'DeployWithInvalidation', {
|
|
337
|
+
sources: [s3deploy.Source.asset('./website-dist')],
|
|
338
|
+
destinationBucket: bucket,
|
|
339
|
+
distribution,
|
|
340
|
+
distributionPaths: ['/images/*.png'],
|
|
341
|
+
// Invalidate cache but don't wait or verify that invalidation has completed successfully.
|
|
342
|
+
waitForDistributionInvalidation: false
|
|
343
|
+
});
|
|
344
|
+
```
|
|
345
|
+
|
|
328
346
|
## Signed Content Payloads
|
|
329
347
|
|
|
330
348
|
By default, deployment uses streaming uploads which set the `x-amz-content-sha256`
|
|
@@ -121,6 +121,7 @@ declare const fn: lambda.Function;
|
|
|
121
121
|
|
|
122
122
|
// Lambda should receive only message matching the following conditions on message body:
|
|
123
123
|
// color: 'red' or 'orange'
|
|
124
|
+
// store: property must not be present
|
|
124
125
|
myTopic.addSubscription(new subscriptions.LambdaSubscription(fn, {
|
|
125
126
|
filterPolicyWithMessageBody: {
|
|
126
127
|
background: sns.FilterOrPolicy.policy({
|
|
@@ -128,6 +129,7 @@ myTopic.addSubscription(new subscriptions.LambdaSubscription(fn, {
|
|
|
128
129
|
allowlist: ['red', 'orange'],
|
|
129
130
|
})),
|
|
130
131
|
}),
|
|
132
|
+
store: sns.FilterOrPolicy.filter(sns.SubscriptionFilter.notExistsFilter()),
|
|
131
133
|
},
|
|
132
134
|
}));
|
|
133
135
|
```
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as sns from 'aws-cdk-lib/aws-sns';
|
|
2
|
+
import * as sqs from 'aws-cdk-lib/aws-sqs';
|
|
3
|
+
import * as cdk from 'aws-cdk-lib';
|
|
4
|
+
import { IntegTest, ExpectedResult } from '@aws-cdk/integ-tests-alpha';
|
|
5
|
+
import * as subs from 'aws-cdk-lib/aws-sns-subscriptions';
|
|
6
|
+
|
|
7
|
+
const app = new cdk.App();
|
|
8
|
+
|
|
9
|
+
const stack = new cdk.Stack(app, 'sns-sqs-subscription-filter');
|
|
10
|
+
|
|
11
|
+
const topic = new sns.Topic(stack, 'MyTopic');
|
|
12
|
+
|
|
13
|
+
const queue1 = new sqs.Queue(stack, 'MyQueue1');
|
|
14
|
+
const queue2 = new sqs.Queue(stack, 'MyQueue2');
|
|
15
|
+
|
|
16
|
+
topic.addSubscription(new subs.SqsSubscription(queue1, {
|
|
17
|
+
filterPolicyWithMessageBody: {
|
|
18
|
+
background: sns.Policy.policy({
|
|
19
|
+
color: sns.Filter.filter(sns.SubscriptionFilter.stringFilter({
|
|
20
|
+
allowlist: ['red', 'green'],
|
|
21
|
+
})),
|
|
22
|
+
}),
|
|
23
|
+
price: sns.Filter.filter(sns.SubscriptionFilter.numericFilter({
|
|
24
|
+
allowlist: [100, 200],
|
|
25
|
+
})),
|
|
26
|
+
store: sns.Filter.filter(sns.SubscriptionFilter.existsFilter()),
|
|
27
|
+
},
|
|
28
|
+
rawMessageDelivery: true,
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
topic.addSubscription(new subs.SqsSubscription(queue2, {
|
|
32
|
+
filterPolicyWithMessageBody: {
|
|
33
|
+
background: sns.Policy.policy({
|
|
34
|
+
color: sns.Filter.filter(sns.SubscriptionFilter.stringFilter({
|
|
35
|
+
denylist: ['red', 'green'],
|
|
36
|
+
})),
|
|
37
|
+
}),
|
|
38
|
+
price: sns.Filter.filter(sns.SubscriptionFilter.numericFilter({
|
|
39
|
+
betweenStrict: { start: 100, stop: 200 },
|
|
40
|
+
})),
|
|
41
|
+
store: sns.Filter.filter(sns.SubscriptionFilter.notExistsFilter()),
|
|
42
|
+
},
|
|
43
|
+
rawMessageDelivery: true,
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
const integTest = new IntegTest(app, 'SNS Subscription filters', {
|
|
47
|
+
testCases: [stack],
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const message1 = JSON.stringify({ background: { color: 'green' }, price: 200, store: 'fans' }); // matches queue1 subscription filter
|
|
51
|
+
const message2 = JSON.stringify({ background: { color: 'blue' }, price: 150 }); // matches queue2 subscription filter
|
|
52
|
+
|
|
53
|
+
// publish messages to SNS topic
|
|
54
|
+
integTest.assertions.awsApiCall('SNS', 'publish', {
|
|
55
|
+
Message: message1,
|
|
56
|
+
TopicArn: topic.topicArn,
|
|
57
|
+
});
|
|
58
|
+
integTest.assertions.awsApiCall('SNS', 'publish', {
|
|
59
|
+
Message: message2,
|
|
60
|
+
TopicArn: topic.topicArn,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// check messages arrived at expected destination queue
|
|
64
|
+
integTest.assertions.awsApiCall('SQS', 'receiveMessage', {
|
|
65
|
+
QueueUrl: queue1.queueUrl,
|
|
66
|
+
WaitTimeSeconds: 20,
|
|
67
|
+
}).expect(ExpectedResult.objectLike({
|
|
68
|
+
Messages: [{ Body: message1 }],
|
|
69
|
+
}));
|
|
70
|
+
integTest.assertions.awsApiCall('SQS', 'receiveMessage', {
|
|
71
|
+
QueueUrl: queue2.queueUrl,
|
|
72
|
+
WaitTimeSeconds: 20,
|
|
73
|
+
}).expect(ExpectedResult.objectLike({
|
|
74
|
+
Messages: [{ Body: message2 }],
|
|
75
|
+
}));
|
cdk_api_mcp_server/resources/aws-cdk/constructs/aws-cdk-lib/aws-sns-subscriptions/integ.sns-sqs.ts
CHANGED
|
@@ -3,50 +3,31 @@ import * as sqs from 'aws-cdk-lib/aws-sqs';
|
|
|
3
3
|
import * as cdk from 'aws-cdk-lib';
|
|
4
4
|
import { IntegTest, ExpectedResult } from '@aws-cdk/integ-tests-alpha';
|
|
5
5
|
import * as subs from 'aws-cdk-lib/aws-sns-subscriptions';
|
|
6
|
-
|
|
7
|
-
topic: sns.Topic;
|
|
8
|
-
queue: sqs.Queue;
|
|
9
|
-
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
|
|
10
|
-
super(scope, id, props);
|
|
11
|
-
this.topic = new sns.Topic(this, 'MyTopic');
|
|
12
|
-
const queueStack = new cdk.Stack(app, 'QueueStack');
|
|
13
|
-
this.queue = new sqs.Queue(queueStack, 'MyQueue');
|
|
14
|
-
this.topic.addSubscription(new subs.SqsSubscription(this.queue, {
|
|
15
|
-
filterPolicyWithMessageBody: {
|
|
16
|
-
background: sns.Policy.policy({
|
|
17
|
-
color: sns.Filter.filter(sns.SubscriptionFilter.stringFilter({
|
|
18
|
-
allowlist: ['red', 'green'],
|
|
19
|
-
denylist: ['white', 'orange'],
|
|
20
|
-
})),
|
|
21
|
-
}),
|
|
22
|
-
price: sns.Filter.filter(sns.SubscriptionFilter.numericFilter({
|
|
23
|
-
allowlist: [100, 200],
|
|
24
|
-
between: { start: 300, stop: 350 },
|
|
25
|
-
greaterThan: 500,
|
|
26
|
-
lessThan: 1000,
|
|
27
|
-
betweenStrict: { start: 2000, stop: 3000 },
|
|
28
|
-
})),
|
|
29
|
-
},
|
|
30
|
-
}));
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
// Beginning of the test suite
|
|
6
|
+
|
|
34
7
|
const app = new cdk.App();
|
|
35
|
-
|
|
8
|
+
|
|
9
|
+
const stack = new cdk.Stack(app, 'SnsToSqsStack');
|
|
10
|
+
|
|
11
|
+
const topic = new sns.Topic(stack, 'MyTopic');
|
|
12
|
+
|
|
13
|
+
const queue = new sqs.Queue(stack, 'MyQueue');
|
|
14
|
+
|
|
15
|
+
topic.addSubscription(new subs.SqsSubscription(queue, { rawMessageDelivery: true }));
|
|
16
|
+
|
|
36
17
|
const integTest = new IntegTest(app, 'SNS Subscriptions', {
|
|
37
|
-
testCases: [
|
|
38
|
-
stack,
|
|
39
|
-
],
|
|
18
|
+
testCases: [stack],
|
|
40
19
|
});
|
|
20
|
+
|
|
21
|
+
const message = JSON.stringify({ color: 'green', price: 200 });
|
|
22
|
+
|
|
41
23
|
integTest.assertions.awsApiCall('SNS', 'publish', {
|
|
42
|
-
Message:
|
|
43
|
-
TopicArn:
|
|
24
|
+
Message: message,
|
|
25
|
+
TopicArn: topic.topicArn,
|
|
44
26
|
});
|
|
45
|
-
|
|
46
|
-
|
|
27
|
+
|
|
28
|
+
integTest.assertions.awsApiCall('SQS', 'receiveMessage', {
|
|
29
|
+
QueueUrl: queue.queueUrl,
|
|
47
30
|
WaitTimeSeconds: 20,
|
|
48
|
-
})
|
|
49
|
-
message
|
|
50
|
-
Messages: [{ Body: '{color: "green", price: 200}' }],
|
|
31
|
+
}).expect(ExpectedResult.objectLike({
|
|
32
|
+
Messages: [{ Body: message }],
|
|
51
33
|
}));
|
|
52
|
-
app.synth();
|
|
@@ -1,112 +1,119 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { IntegTest, ExpectedResult } from '@aws-cdk/integ-tests-alpha';
|
|
3
|
-
import * as cdk from 'aws-cdk-lib';
|
|
4
|
-
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
|
|
5
|
-
import * as events from 'aws-cdk-lib/aws-events';
|
|
1
|
+
import { Code, Function } from 'aws-cdk-lib/aws-lambda';
|
|
6
2
|
import * as sfn from 'aws-cdk-lib/aws-stepfunctions';
|
|
7
|
-
import * as
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
3
|
+
import * as cdk from 'aws-cdk-lib';
|
|
4
|
+
import { LambdaInvoke } from 'aws-cdk-lib/aws-stepfunctions-tasks';
|
|
5
|
+
import { IntegTest, ExpectedResult } from '@aws-cdk/integ-tests-alpha';
|
|
6
|
+
import { STANDARD_NODEJS_RUNTIME } from '../../../config';
|
|
10
7
|
|
|
11
8
|
/*
|
|
12
|
-
* Creates
|
|
13
|
-
*
|
|
14
|
-
*
|
|
9
|
+
* Creates a state machine with a task state to invoke a Lambda function
|
|
10
|
+
* The state machine creates a couple of Lambdas that pass results forward
|
|
11
|
+
* and into a Choice state that validates the output.
|
|
12
|
+
*
|
|
13
|
+
* Stack verification steps:
|
|
14
|
+
* The generated State Machine can be executed from the CLI (or Step Functions console)
|
|
15
|
+
* and runs with an execution status of `Succeeded`.
|
|
15
16
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* * aws stepfunctions describe-execution --execution-arn <execution-arn generated before> : should return status as SUCCEEDED
|
|
17
|
+
* -- aws stepfunctions start-execution --state-machine-arn <state-machine-arn-from-output> provides execution arn
|
|
18
|
+
* -- aws stepfunctions describe-execution --execution-arn <state-machine-arn-from-output> returns a status of `Succeeded`
|
|
19
19
|
*/
|
|
20
20
|
const app = new cdk.App({
|
|
21
21
|
postCliContext: {
|
|
22
22
|
'@aws-cdk/aws-lambda:useCdkManagedLogGroup': false,
|
|
23
23
|
},
|
|
24
24
|
});
|
|
25
|
-
const stack = new cdk.Stack(app, 'aws-stepfunctions-tasks-
|
|
25
|
+
const stack = new cdk.Stack(app, 'aws-stepfunctions-tasks-lambda-invoke-jsonata-integ');
|
|
26
26
|
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
const submitJobLambda = new Function(stack, 'submitJobLambda', {
|
|
28
|
+
code: Code.fromInline(`exports.handler = async (event, context) => {
|
|
29
|
+
return {
|
|
30
|
+
statusCode: '200',
|
|
31
|
+
body: 'hello, world!',
|
|
32
|
+
...event,
|
|
33
|
+
};
|
|
34
|
+
};`),
|
|
35
|
+
runtime: STANDARD_NODEJS_RUNTIME,
|
|
36
|
+
handler: 'index.handler',
|
|
30
37
|
});
|
|
31
38
|
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
statusCode: '200',
|
|
46
|
-
responseTemplates: {
|
|
47
|
-
'application/json': JSON.stringify({ message: 'Hello, tester!' }),
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
],
|
|
51
|
-
passthroughBehavior: apigateway.PassthroughBehavior.NEVER,
|
|
52
|
-
requestTemplates: {
|
|
53
|
-
'application/json': '{ "statusCode": 200 }',
|
|
54
|
-
},
|
|
39
|
+
const submitJob = LambdaInvoke.jsonata(stack, 'Invoke Handler', {
|
|
40
|
+
lambdaFunction: submitJobLambda,
|
|
41
|
+
payload: sfn.TaskInput.fromObject({
|
|
42
|
+
execId: '{% $states.context.Execution.Id %}',
|
|
43
|
+
execInput: '{% $states.context.Execution.Input %}',
|
|
44
|
+
execName: '{% $states.context.Execution.Name %}',
|
|
45
|
+
execRoleArn: '{% $states.context.Execution.RoleArn %}',
|
|
46
|
+
execStartTime: '{% $states.context.Execution.StartTime %}',
|
|
47
|
+
stateEnteredTime: '{% $states.context.State.EnteredTime %}',
|
|
48
|
+
stateName: '{% $states.context.State.Name %}',
|
|
49
|
+
stateRetryCount: '{% $states.context.State.RetryCount %}',
|
|
50
|
+
stateMachineId: '{% $states.context.StateMachine.Id %}',
|
|
51
|
+
stateMachineName: '{% $states.context.StateMachine.Name %}',
|
|
55
52
|
}),
|
|
56
|
-
{
|
|
57
|
-
|
|
58
|
-
methodResponses: [
|
|
59
|
-
{
|
|
60
|
-
statusCode: '200',
|
|
61
|
-
},
|
|
62
|
-
],
|
|
63
|
-
},
|
|
64
|
-
);
|
|
53
|
+
outputs: '{% $states.result.Payload %}',
|
|
54
|
+
});
|
|
65
55
|
|
|
66
|
-
const
|
|
67
|
-
|
|
56
|
+
const checkJobStateLambda = new Function(stack, 'checkJobStateLambda', {
|
|
57
|
+
code: Code.fromInline(`exports.handler = async function(event, context) {
|
|
58
|
+
const expectedFields = [
|
|
59
|
+
'execId', 'execInput', 'execName', 'execRoleArn',
|
|
60
|
+
'execStartTime', 'stateEnteredTime', 'stateName',
|
|
61
|
+
'stateRetryCount', 'stateMachineId', 'stateMachineName',
|
|
62
|
+
];
|
|
63
|
+
const fieldsAreSet = expectedFields.every(field => event[field] !== undefined);
|
|
64
|
+
return {
|
|
65
|
+
status: event.statusCode === '200' && fieldsAreSet ? 'SUCCEEDED' : 'FAILED'
|
|
66
|
+
};
|
|
67
|
+
};`),
|
|
68
|
+
runtime: STANDARD_NODEJS_RUNTIME,
|
|
69
|
+
handler: 'index.handler',
|
|
68
70
|
});
|
|
69
71
|
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
apiEndpoint: sfn.TaskInput.fromText('{% $states.input.apiEndpoint %}'),
|
|
73
|
-
connection,
|
|
74
|
-
method: sfn.TaskInput.fromText('GET'),
|
|
72
|
+
const checkJobState = LambdaInvoke.jsonata(stack, 'Check the job state', {
|
|
73
|
+
lambdaFunction: checkJobStateLambda,
|
|
75
74
|
outputs: {
|
|
76
|
-
|
|
75
|
+
status: '{% $states.result.Payload.status %}',
|
|
77
76
|
},
|
|
78
77
|
});
|
|
79
78
|
|
|
79
|
+
const isComplete = sfn.Choice.jsonata(stack, 'Job Complete?');
|
|
80
|
+
const jobFailed = sfn.Fail.jsonata(stack, 'Job Failed', {
|
|
81
|
+
cause: 'Job Failed',
|
|
82
|
+
error: 'Received a status that was not 200',
|
|
83
|
+
});
|
|
84
|
+
const finalStatus = sfn.Pass.jsonata(stack, 'Final step');
|
|
85
|
+
|
|
86
|
+
const chain = sfn.Chain.start(submitJob)
|
|
87
|
+
.next(checkJobState)
|
|
88
|
+
.next(
|
|
89
|
+
isComplete
|
|
90
|
+
.when(sfn.Condition.jsonata("{% $states.input.status = 'FAILED' %}"), jobFailed)
|
|
91
|
+
.when(sfn.Condition.jsonata("{% $states.input.status = 'SUCCEEDED' %}"), finalStatus),
|
|
92
|
+
);
|
|
93
|
+
|
|
80
94
|
const sm = new sfn.StateMachine(stack, 'StateMachine', {
|
|
81
|
-
definition:
|
|
95
|
+
definition: chain,
|
|
82
96
|
timeout: cdk.Duration.seconds(30),
|
|
83
97
|
});
|
|
84
98
|
|
|
85
|
-
|
|
86
|
-
|
|
99
|
+
new cdk.CfnOutput(stack, 'stateMachineArn', {
|
|
100
|
+
value: sm.stateMachineArn,
|
|
87
101
|
});
|
|
88
102
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
stateMachineArn: sm.stateMachineArn,
|
|
92
|
-
input: JSON.stringify({
|
|
93
|
-
apiEndpoint: '/test',
|
|
94
|
-
}),
|
|
103
|
+
const integ = new IntegTest(app, 'IntegTest', {
|
|
104
|
+
testCases: [stack],
|
|
95
105
|
});
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const describe = testCase.assertions.awsApiCall('@aws-sdk/client-sfn', 'DescribeExecution', {
|
|
99
|
-
executionArn: start.getAttString('executionArn'),
|
|
106
|
+
const res = integ.assertions.awsApiCall('@aws-sdk/client-sfn', 'StartExecution', {
|
|
107
|
+
stateMachineArn: sm.stateMachineArn,
|
|
100
108
|
});
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
109
|
+
const executionArn = res.getAttString('executionArn');
|
|
110
|
+
integ.assertions.awsApiCall('@aws-sdk/client-sfn', 'DescribeExecution', {
|
|
111
|
+
executionArn,
|
|
112
|
+
}).expect(ExpectedResult.objectLike({
|
|
104
113
|
status: 'SUCCEEDED',
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}),
|
|
110
|
-
}));
|
|
114
|
+
})).waitForAssertions({
|
|
115
|
+
totalTimeout: cdk.Duration.seconds(10),
|
|
116
|
+
interval: cdk.Duration.seconds(3),
|
|
117
|
+
});
|
|
111
118
|
|
|
112
119
|
app.synth();
|