From 8dc51900e09d82685fb34e199289796504bec248 Mon Sep 17 00:00:00 2001 From: Luca Pizzini Date: Thu, 17 Aug 2023 13:49:04 +0200 Subject: [PATCH] fix(apigateway): duplicate methodResponses if the same array is reused between addMethod calls (#26636) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding a new method to an API with `addMethod` and passing `methodOptions.methodResponses` was generating duplicate entries using `StepFunctionsIntegration.startExecution` as integration: For example: ``` const integ = apigw.StepFunctionsIntegration.startExecution(stateMachine, integrationOptions); api.root.addMethod('GET', integ, methodOptions); api.root.addMethod('POST', integ, methodOptions); ``` Would generate (fails on deployment): ``` "MethodResponses": [ { "ResponseParameters": { "method.response.header.Access-Control-Allow-Origin": true }, "StatusCode": "200" }, { "ResponseModels": { "application/json": "Empty" }, "StatusCode": "200" }, { "ResponseModels": { "application/json": "Error" }, "StatusCode": "400" }, { "ResponseModels": { "application/json": "Error" }, "StatusCode": "500" }, { "ResponseModels": { "application/json": "Empty" }, "StatusCode": "200" }, { "ResponseModels": { "application/json": "Error" }, "StatusCode": "400" }, { "ResponseModels": { "application/json": "Error" }, "StatusCode": "500" } ], ``` With this fix, it will keep only the specified `methodResponses`. Also, the `integrationResponses` option in `StepFunctionsIntegration.startExecution` was not used by the corresponding `Integration`. This fix will allow to use the specified option value. Closes #26586. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- ...y-stepfunctions-startexecution.assets.json | 19 + ...stepfunctions-startexecution.template.json | 394 +++++++++++ ...efaultTestDeployAssert3A9ACD49.assets.json | 19 + ...aultTestDeployAssert3A9ACD49.template.json | 36 + .../cdk.out | 1 + .../integ.json | 12 + .../manifest.json | 198 ++++++ .../tree.json | 668 ++++++++++++++++++ .../integ.stepfunctions-startexecution.ts | 51 ++ .../lib/integrations/stepfunctions.ts | 2 +- .../aws-cdk-lib/aws-apigateway/lib/method.ts | 26 +- .../test/integrations/stepfunctions.test.ts | 116 +++ 12 files changed, 1531 insertions(+), 11 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/aws-cdk-aws-apigateway-stepfunctions-startexecution.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/aws-cdk-aws-apigateway-stepfunctions-startexecution.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/awsapigatewaystepfunctionsstartexecutionDefaultTestDeployAssert3A9ACD49.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/awsapigatewaystepfunctionsstartexecutionDefaultTestDeployAssert3A9ACD49.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/aws-cdk-aws-apigateway-stepfunctions-startexecution.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/aws-cdk-aws-apigateway-stepfunctions-startexecution.assets.json new file mode 100644 index 0000000000000..b4c64ab60cc31 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/aws-cdk-aws-apigateway-stepfunctions-startexecution.assets.json @@ -0,0 +1,19 @@ +{ + "version": "33.0.0", + "files": { + "11071ca702d34295486ea4a64c45b64857f4bd23c672fe0895a0b7e753065d77": { + "source": { + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "11071ca702d34295486ea4a64c45b64857f4bd23c672fe0895a0b7e753065d77.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/aws-cdk-aws-apigateway-stepfunctions-startexecution.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/aws-cdk-aws-apigateway-stepfunctions-startexecution.template.json new file mode 100644 index 0000000000000..d117c9978ce39 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/aws-cdk-aws-apigateway-stepfunctions-startexecution.template.json @@ -0,0 +1,394 @@ +{ + "Resources": { + "myrestapiBAC2BF45": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Name": "my-rest-api" + } + }, + "myrestapiDeployment010A9D4F8d8391dd5e41be487121f43257df00d7": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "Automatically created by the RestApi construct", + "RestApiId": { + "Ref": "myrestapiBAC2BF45" + } + }, + "DependsOn": [ + "myrestapiGET3A49A218", + "myrestapiPOST155A9625" + ] + }, + "myrestapiDeploymentStageprod3140E1BE": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "myrestapiDeployment010A9D4F8d8391dd5e41be487121f43257df00d7" + }, + "RestApiId": { + "Ref": "myrestapiBAC2BF45" + }, + "StageName": "prod" + } + }, + "myrestapiGETStartSyncExecutionRoleC284C05B": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "myrestapiGETStartSyncExecutionRoleDefaultPolicy8B2F6ADF": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "states:StartSyncExecution", + "Effect": "Allow", + "Resource": { + "Ref": "StateMachine2E01A3A5" + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "myrestapiGETStartSyncExecutionRoleDefaultPolicy8B2F6ADF", + "Roles": [ + { + "Ref": "myrestapiGETStartSyncExecutionRoleC284C05B" + } + ] + } + }, + "myrestapiGET3A49A218": { + "Type": "AWS::ApiGateway::Method", + "Properties": { + "AuthorizationType": "NONE", + "HttpMethod": "GET", + "Integration": { + "Credentials": { + "Fn::GetAtt": [ + "myrestapiGETStartSyncExecutionRoleC284C05B", + "Arn" + ] + }, + "IntegrationHttpMethod": "POST", + "IntegrationResponses": [ + { + "ResponseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'" + }, + "StatusCode": "200" + } + ], + "PassthroughBehavior": "NEVER", + "RequestTemplates": { + "application/json": { + "Fn::Join": [ + "", + [ + "## Velocity Template used for API Gateway request mapping template\n##\n## This template forwards the request body, header, path, and querystring\n## to the execution input of the state machine.\n##\n## \"@@\" is used here as a placeholder for '\"' to avoid using escape characters.\n\n#set($inputString = '')\n#set($includeHeaders = false)\n#set($includeQueryString = true)\n#set($includePath = true)\n#set($includeAuthorizer = false)\n#set($allParams = $input.params())\n{\n \"stateMachineArn\": \"", + { + "Ref": "StateMachine2E01A3A5" + }, + "\",\n\n #set($inputString = \"$inputString,@@body@@: $input.body\")\n\n #if ($includeHeaders)\n #set($inputString = \"$inputString, @@header@@:{\")\n #foreach($paramName in $allParams.header.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.header.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n \n #end\n\n #if ($includeQueryString)\n #set($inputString = \"$inputString, @@querystring@@:{\")\n #foreach($paramName in $allParams.querystring.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.querystring.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #if ($includePath)\n #set($inputString = \"$inputString, @@path@@:{\")\n #foreach($paramName in $allParams.path.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.path.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n \n #if ($includeAuthorizer)\n #set($inputString = \"$inputString, @@authorizer@@:{\")\n #foreach($paramName in $context.authorizer.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($context.authorizer.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #set($requestContext = \"\")\n ## Check if the request context should be included as part of the execution input\n #if($requestContext && !$requestContext.empty)\n #set($inputString = \"$inputString,\")\n #set($inputString = \"$inputString @@requestContext@@: $requestContext\")\n #end\n\n #set($inputString = \"$inputString}\")\n #set($inputString = $inputString.replaceAll(\"@@\",'\"'))\n #set($len = $inputString.length() - 1)\n \"input\": \"{$util.escapeJavaScript($inputString.substring(1,$len)).replaceAll(\"\\\\'\",\"'\")}\"\n}\n" + ] + ] + } + }, + "Type": "AWS", + "Uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + ":states:action/StartSyncExecution" + ] + ] + } + }, + "MethodResponses": [ + { + "ResponseParameters": { + "method.response.header.Access-Control-Allow-Origin": true + }, + "StatusCode": "200" + }, + { + "ResponseModels": { + "application/json": "Empty" + }, + "StatusCode": "200" + }, + { + "ResponseModels": { + "application/json": "Error" + }, + "StatusCode": "400" + }, + { + "ResponseModels": { + "application/json": "Error" + }, + "StatusCode": "500" + } + ], + "ResourceId": { + "Fn::GetAtt": [ + "myrestapiBAC2BF45", + "RootResourceId" + ] + }, + "RestApiId": { + "Ref": "myrestapiBAC2BF45" + } + } + }, + "myrestapiPOSTStartSyncExecutionRole7AFBE835": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "myrestapiPOSTStartSyncExecutionRoleDefaultPolicy7D411AE9": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "states:StartSyncExecution", + "Effect": "Allow", + "Resource": { + "Ref": "StateMachine2E01A3A5" + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "myrestapiPOSTStartSyncExecutionRoleDefaultPolicy7D411AE9", + "Roles": [ + { + "Ref": "myrestapiPOSTStartSyncExecutionRole7AFBE835" + } + ] + } + }, + "myrestapiPOST155A9625": { + "Type": "AWS::ApiGateway::Method", + "Properties": { + "AuthorizationType": "NONE", + "HttpMethod": "POST", + "Integration": { + "Credentials": { + "Fn::GetAtt": [ + "myrestapiPOSTStartSyncExecutionRole7AFBE835", + "Arn" + ] + }, + "IntegrationHttpMethod": "POST", + "IntegrationResponses": [ + { + "ResponseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'" + }, + "StatusCode": "200" + } + ], + "PassthroughBehavior": "NEVER", + "RequestTemplates": { + "application/json": { + "Fn::Join": [ + "", + [ + "## Velocity Template used for API Gateway request mapping template\n##\n## This template forwards the request body, header, path, and querystring\n## to the execution input of the state machine.\n##\n## \"@@\" is used here as a placeholder for '\"' to avoid using escape characters.\n\n#set($inputString = '')\n#set($includeHeaders = false)\n#set($includeQueryString = true)\n#set($includePath = true)\n#set($includeAuthorizer = false)\n#set($allParams = $input.params())\n{\n \"stateMachineArn\": \"", + { + "Ref": "StateMachine2E01A3A5" + }, + "\",\n\n #set($inputString = \"$inputString,@@body@@: $input.body\")\n\n #if ($includeHeaders)\n #set($inputString = \"$inputString, @@header@@:{\")\n #foreach($paramName in $allParams.header.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.header.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n \n #end\n\n #if ($includeQueryString)\n #set($inputString = \"$inputString, @@querystring@@:{\")\n #foreach($paramName in $allParams.querystring.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.querystring.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #if ($includePath)\n #set($inputString = \"$inputString, @@path@@:{\")\n #foreach($paramName in $allParams.path.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.path.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n \n #if ($includeAuthorizer)\n #set($inputString = \"$inputString, @@authorizer@@:{\")\n #foreach($paramName in $context.authorizer.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($context.authorizer.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #set($requestContext = \"\")\n ## Check if the request context should be included as part of the execution input\n #if($requestContext && !$requestContext.empty)\n #set($inputString = \"$inputString,\")\n #set($inputString = \"$inputString @@requestContext@@: $requestContext\")\n #end\n\n #set($inputString = \"$inputString}\")\n #set($inputString = $inputString.replaceAll(\"@@\",'\"'))\n #set($len = $inputString.length() - 1)\n \"input\": \"{$util.escapeJavaScript($inputString.substring(1,$len)).replaceAll(\"\\\\'\",\"'\")}\"\n}\n" + ] + ] + } + }, + "Type": "AWS", + "Uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + ":states:action/StartSyncExecution" + ] + ] + } + }, + "MethodResponses": [ + { + "ResponseParameters": { + "method.response.header.Access-Control-Allow-Origin": true + }, + "StatusCode": "200" + }, + { + "ResponseModels": { + "application/json": "Empty" + }, + "StatusCode": "200" + }, + { + "ResponseModels": { + "application/json": "Error" + }, + "StatusCode": "400" + }, + { + "ResponseModels": { + "application/json": "Error" + }, + "StatusCode": "500" + } + ], + "ResourceId": { + "Fn::GetAtt": [ + "myrestapiBAC2BF45", + "RootResourceId" + ] + }, + "RestApiId": { + "Ref": "myrestapiBAC2BF45" + } + } + }, + "StateMachineRoleB840431D": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "states.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "StateMachine2E01A3A5": { + "Type": "AWS::StepFunctions::StateMachine", + "Properties": { + "DefinitionString": "{\"StartAt\":\"passTask\",\"States\":{\"passTask\":{\"Type\":\"Pass\",\"InputPath\":\"$.somekey\",\"End\":true}}}", + "RoleArn": { + "Fn::GetAtt": [ + "StateMachineRoleB840431D", + "Arn" + ] + }, + "StateMachineType": "EXPRESS" + }, + "DependsOn": [ + "StateMachineRoleB840431D" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + }, + "Outputs": { + "myrestapiEndpoint0DE8A5DE": { + "Value": { + "Fn::Join": [ + "", + [ + "https://", + { + "Ref": "myrestapiBAC2BF45" + }, + ".execute-api.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "myrestapiDeploymentStageprod3140E1BE" + }, + "/" + ] + ] + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/awsapigatewaystepfunctionsstartexecutionDefaultTestDeployAssert3A9ACD49.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/awsapigatewaystepfunctionsstartexecutionDefaultTestDeployAssert3A9ACD49.assets.json new file mode 100644 index 0000000000000..914ca646ec74f --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/awsapigatewaystepfunctionsstartexecutionDefaultTestDeployAssert3A9ACD49.assets.json @@ -0,0 +1,19 @@ +{ + "version": "33.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "awsapigatewaystepfunctionsstartexecutionDefaultTestDeployAssert3A9ACD49.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/awsapigatewaystepfunctionsstartexecutionDefaultTestDeployAssert3A9ACD49.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/awsapigatewaystepfunctionsstartexecutionDefaultTestDeployAssert3A9ACD49.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/awsapigatewaystepfunctionsstartexecutionDefaultTestDeployAssert3A9ACD49.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/cdk.out new file mode 100644 index 0000000000000..560dae10d018f --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"33.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/integ.json new file mode 100644 index 0000000000000..d0083afa83de3 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "33.0.0", + "testCases": { + "aws-apigateway-stepfunctions-startexecution/DefaultTest": { + "stacks": [ + "aws-cdk-aws-apigateway-stepfunctions-startexecution" + ], + "assertionStack": "aws-apigateway-stepfunctions-startexecution/DefaultTest/DeployAssert", + "assertionStackName": "awsapigatewaystepfunctionsstartexecutionDefaultTestDeployAssert3A9ACD49" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/manifest.json new file mode 100644 index 0000000000000..7f56cd4cd730a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/manifest.json @@ -0,0 +1,198 @@ +{ + "version": "33.0.0", + "artifacts": { + "aws-cdk-aws-apigateway-stepfunctions-startexecution.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-cdk-aws-apigateway-stepfunctions-startexecution.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-cdk-aws-apigateway-stepfunctions-startexecution": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-cdk-aws-apigateway-stepfunctions-startexecution.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/11071ca702d34295486ea4a64c45b64857f4bd23c672fe0895a0b7e753065d77.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-cdk-aws-apigateway-stepfunctions-startexecution.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-cdk-aws-apigateway-stepfunctions-startexecution.assets" + ], + "metadata": { + "/aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "myrestapiBAC2BF45" + } + ], + "/aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Deployment/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "myrestapiDeployment010A9D4F8d8391dd5e41be487121f43257df00d7" + } + ], + "/aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/DeploymentStage.prod/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "myrestapiDeploymentStageprod3140E1BE" + } + ], + "/aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Endpoint": [ + { + "type": "aws:cdk:logicalId", + "data": "myrestapiEndpoint0DE8A5DE" + } + ], + "/aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/GET": [ + { + "type": "aws:cdk:warning", + "data": "addMethodResponse called multiple times with statusCode=200, deployment will be nondeterministic. Use a single addMethodResponse call to configure the entire response." + } + ], + "/aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/GET/StartSyncExecutionRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "myrestapiGETStartSyncExecutionRoleC284C05B" + } + ], + "/aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/GET/StartSyncExecutionRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "myrestapiGETStartSyncExecutionRoleDefaultPolicy8B2F6ADF" + } + ], + "/aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/GET/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "myrestapiGET3A49A218" + } + ], + "/aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/POST": [ + { + "type": "aws:cdk:warning", + "data": "addMethodResponse called multiple times with statusCode=200, deployment will be nondeterministic. Use a single addMethodResponse call to configure the entire response." + } + ], + "/aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/POST/StartSyncExecutionRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "myrestapiPOSTStartSyncExecutionRole7AFBE835" + } + ], + "/aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/POST/StartSyncExecutionRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "myrestapiPOSTStartSyncExecutionRoleDefaultPolicy7D411AE9" + } + ], + "/aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/POST/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "myrestapiPOST155A9625" + } + ], + "/aws-cdk-aws-apigateway-stepfunctions-startexecution/StateMachine/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "StateMachineRoleB840431D" + } + ], + "/aws-cdk-aws-apigateway-stepfunctions-startexecution/StateMachine/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "StateMachine2E01A3A5" + } + ], + "/aws-cdk-aws-apigateway-stepfunctions-startexecution/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-cdk-aws-apigateway-stepfunctions-startexecution/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ], + "myrestapiDeployment010A9D4F59b46296e944eb988c600755d169a00d": [ + { + "type": "aws:cdk:logicalId", + "data": "myrestapiDeployment010A9D4F59b46296e944eb988c600755d169a00d", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } + ] + }, + "displayName": "aws-cdk-aws-apigateway-stepfunctions-startexecution" + }, + "awsapigatewaystepfunctionsstartexecutionDefaultTestDeployAssert3A9ACD49.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "awsapigatewaystepfunctionsstartexecutionDefaultTestDeployAssert3A9ACD49.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "awsapigatewaystepfunctionsstartexecutionDefaultTestDeployAssert3A9ACD49": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "awsapigatewaystepfunctionsstartexecutionDefaultTestDeployAssert3A9ACD49.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "awsapigatewaystepfunctionsstartexecutionDefaultTestDeployAssert3A9ACD49.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "awsapigatewaystepfunctionsstartexecutionDefaultTestDeployAssert3A9ACD49.assets" + ], + "metadata": { + "/aws-apigateway-stepfunctions-startexecution/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-apigateway-stepfunctions-startexecution/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-apigateway-stepfunctions-startexecution/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/tree.json new file mode 100644 index 0000000000000..73b02800aefcc --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.js.snapshot/tree.json @@ -0,0 +1,668 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-cdk-aws-apigateway-stepfunctions-startexecution": { + "id": "aws-cdk-aws-apigateway-stepfunctions-startexecution", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution", + "children": { + "my-rest-api": { + "id": "my-rest-api", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ApiGateway::RestApi", + "aws:cdk:cloudformation:props": { + "name": "my-rest-api" + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "Deployment": { + "id": "Deployment", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Deployment", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Deployment/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ApiGateway::Deployment", + "aws:cdk:cloudformation:props": { + "description": "Automatically created by the RestApi construct", + "restApiId": { + "Ref": "myrestapiBAC2BF45" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "DeploymentStage.prod": { + "id": "DeploymentStage.prod", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/DeploymentStage.prod", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/DeploymentStage.prod/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ApiGateway::Stage", + "aws:cdk:cloudformation:props": { + "deploymentId": { + "Ref": "myrestapiDeployment010A9D4F8d8391dd5e41be487121f43257df00d7" + }, + "restApiId": { + "Ref": "myrestapiBAC2BF45" + }, + "stageName": "prod" + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "Endpoint": { + "id": "Endpoint", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Endpoint", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "Default": { + "id": "Default", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default", + "children": { + "GET": { + "id": "GET", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/GET", + "children": { + "StartSyncExecutionRole": { + "id": "StartSyncExecutionRole", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/GET/StartSyncExecutionRole", + "children": { + "ImportStartSyncExecutionRole": { + "id": "ImportStartSyncExecutionRole", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/GET/StartSyncExecutionRole/ImportStartSyncExecutionRole", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/GET/StartSyncExecutionRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/GET/StartSyncExecutionRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/GET/StartSyncExecutionRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "states:StartSyncExecution", + "Effect": "Allow", + "Resource": { + "Ref": "StateMachine2E01A3A5" + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "myrestapiGETStartSyncExecutionRoleDefaultPolicy8B2F6ADF", + "roles": [ + { + "Ref": "myrestapiGETStartSyncExecutionRoleC284C05B" + } + ] + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/GET/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ApiGateway::Method", + "aws:cdk:cloudformation:props": { + "authorizationType": "NONE", + "httpMethod": "GET", + "integration": { + "type": "AWS", + "uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + ":states:action/StartSyncExecution" + ] + ] + }, + "integrationHttpMethod": "POST", + "requestTemplates": { + "application/json": { + "Fn::Join": [ + "", + [ + "## Velocity Template used for API Gateway request mapping template\n##\n## This template forwards the request body, header, path, and querystring\n## to the execution input of the state machine.\n##\n## \"@@\" is used here as a placeholder for '\"' to avoid using escape characters.\n\n#set($inputString = '')\n#set($includeHeaders = false)\n#set($includeQueryString = true)\n#set($includePath = true)\n#set($includeAuthorizer = false)\n#set($allParams = $input.params())\n{\n \"stateMachineArn\": \"", + { + "Ref": "StateMachine2E01A3A5" + }, + "\",\n\n #set($inputString = \"$inputString,@@body@@: $input.body\")\n\n #if ($includeHeaders)\n #set($inputString = \"$inputString, @@header@@:{\")\n #foreach($paramName in $allParams.header.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.header.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n \n #end\n\n #if ($includeQueryString)\n #set($inputString = \"$inputString, @@querystring@@:{\")\n #foreach($paramName in $allParams.querystring.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.querystring.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #if ($includePath)\n #set($inputString = \"$inputString, @@path@@:{\")\n #foreach($paramName in $allParams.path.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.path.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n \n #if ($includeAuthorizer)\n #set($inputString = \"$inputString, @@authorizer@@:{\")\n #foreach($paramName in $context.authorizer.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($context.authorizer.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #set($requestContext = \"\")\n ## Check if the request context should be included as part of the execution input\n #if($requestContext && !$requestContext.empty)\n #set($inputString = \"$inputString,\")\n #set($inputString = \"$inputString @@requestContext@@: $requestContext\")\n #end\n\n #set($inputString = \"$inputString}\")\n #set($inputString = $inputString.replaceAll(\"@@\",'\"'))\n #set($len = $inputString.length() - 1)\n \"input\": \"{$util.escapeJavaScript($inputString.substring(1,$len)).replaceAll(\"\\\\'\",\"'\")}\"\n}\n" + ] + ] + } + }, + "passthroughBehavior": "NEVER", + "integrationResponses": [ + { + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'" + }, + "statusCode": "200" + } + ], + "credentials": { + "Fn::GetAtt": [ + "myrestapiGETStartSyncExecutionRoleC284C05B", + "Arn" + ] + } + }, + "methodResponses": [ + { + "statusCode": "200", + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": true + } + }, + { + "statusCode": "200", + "responseModels": { + "application/json": "Empty" + } + }, + { + "statusCode": "400", + "responseModels": { + "application/json": "Error" + } + }, + { + "statusCode": "500", + "responseModels": { + "application/json": "Error" + } + } + ], + "resourceId": { + "Fn::GetAtt": [ + "myrestapiBAC2BF45", + "RootResourceId" + ] + }, + "restApiId": { + "Ref": "myrestapiBAC2BF45" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "POST": { + "id": "POST", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/POST", + "children": { + "StartSyncExecutionRole": { + "id": "StartSyncExecutionRole", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/POST/StartSyncExecutionRole", + "children": { + "ImportStartSyncExecutionRole": { + "id": "ImportStartSyncExecutionRole", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/POST/StartSyncExecutionRole/ImportStartSyncExecutionRole", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/POST/StartSyncExecutionRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/POST/StartSyncExecutionRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/POST/StartSyncExecutionRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "states:StartSyncExecution", + "Effect": "Allow", + "Resource": { + "Ref": "StateMachine2E01A3A5" + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "myrestapiPOSTStartSyncExecutionRoleDefaultPolicy7D411AE9", + "roles": [ + { + "Ref": "myrestapiPOSTStartSyncExecutionRole7AFBE835" + } + ] + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/my-rest-api/Default/POST/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ApiGateway::Method", + "aws:cdk:cloudformation:props": { + "authorizationType": "NONE", + "httpMethod": "POST", + "integration": { + "type": "AWS", + "uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + ":states:action/StartSyncExecution" + ] + ] + }, + "integrationHttpMethod": "POST", + "requestTemplates": { + "application/json": { + "Fn::Join": [ + "", + [ + "## Velocity Template used for API Gateway request mapping template\n##\n## This template forwards the request body, header, path, and querystring\n## to the execution input of the state machine.\n##\n## \"@@\" is used here as a placeholder for '\"' to avoid using escape characters.\n\n#set($inputString = '')\n#set($includeHeaders = false)\n#set($includeQueryString = true)\n#set($includePath = true)\n#set($includeAuthorizer = false)\n#set($allParams = $input.params())\n{\n \"stateMachineArn\": \"", + { + "Ref": "StateMachine2E01A3A5" + }, + "\",\n\n #set($inputString = \"$inputString,@@body@@: $input.body\")\n\n #if ($includeHeaders)\n #set($inputString = \"$inputString, @@header@@:{\")\n #foreach($paramName in $allParams.header.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.header.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n \n #end\n\n #if ($includeQueryString)\n #set($inputString = \"$inputString, @@querystring@@:{\")\n #foreach($paramName in $allParams.querystring.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.querystring.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #if ($includePath)\n #set($inputString = \"$inputString, @@path@@:{\")\n #foreach($paramName in $allParams.path.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.path.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n \n #if ($includeAuthorizer)\n #set($inputString = \"$inputString, @@authorizer@@:{\")\n #foreach($paramName in $context.authorizer.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($context.authorizer.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #set($requestContext = \"\")\n ## Check if the request context should be included as part of the execution input\n #if($requestContext && !$requestContext.empty)\n #set($inputString = \"$inputString,\")\n #set($inputString = \"$inputString @@requestContext@@: $requestContext\")\n #end\n\n #set($inputString = \"$inputString}\")\n #set($inputString = $inputString.replaceAll(\"@@\",'\"'))\n #set($len = $inputString.length() - 1)\n \"input\": \"{$util.escapeJavaScript($inputString.substring(1,$len)).replaceAll(\"\\\\'\",\"'\")}\"\n}\n" + ] + ] + } + }, + "passthroughBehavior": "NEVER", + "integrationResponses": [ + { + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'*'" + }, + "statusCode": "200" + } + ], + "credentials": { + "Fn::GetAtt": [ + "myrestapiPOSTStartSyncExecutionRole7AFBE835", + "Arn" + ] + } + }, + "methodResponses": [ + { + "statusCode": "200", + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": true + } + }, + { + "statusCode": "200", + "responseModels": { + "application/json": "Empty" + } + }, + { + "statusCode": "400", + "responseModels": { + "application/json": "Error" + } + }, + { + "statusCode": "500", + "responseModels": { + "application/json": "Error" + } + } + ], + "resourceId": { + "Fn::GetAtt": [ + "myrestapiBAC2BF45", + "RootResourceId" + ] + }, + "restApiId": { + "Ref": "myrestapiBAC2BF45" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "passTask": { + "id": "passTask", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/passTask", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "StateMachine": { + "id": "StateMachine", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/StateMachine", + "children": { + "Role": { + "id": "Role", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/StateMachine/Role", + "children": { + "ImportRole": { + "id": "ImportRole", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/StateMachine/Role/ImportRole", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/StateMachine/Role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "states.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/StateMachine/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::StepFunctions::StateMachine", + "aws:cdk:cloudformation:props": { + "definitionString": "{\"StartAt\":\"passTask\",\"States\":{\"passTask\":{\"Type\":\"Pass\",\"InputPath\":\"$.somekey\",\"End\":true}}}", + "roleArn": { + "Fn::GetAtt": [ + "StateMachineRoleB840431D", + "Arn" + ] + }, + "stateMachineType": "EXPRESS" + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/BootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-cdk-aws-apigateway-stepfunctions-startexecution/CheckBootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "aws-apigateway-stepfunctions-startexecution": { + "id": "aws-apigateway-stepfunctions-startexecution", + "path": "aws-apigateway-stepfunctions-startexecution", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "aws-apigateway-stepfunctions-startexecution/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "aws-apigateway-stepfunctions-startexecution/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "aws-apigateway-stepfunctions-startexecution/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-apigateway-stepfunctions-startexecution/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-apigateway-stepfunctions-startexecution/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.ts new file mode 100644 index 0000000000000..86dbe8f9c0974 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigateway/test/integ.stepfunctions-startexecution.ts @@ -0,0 +1,51 @@ +import * as sfn from 'aws-cdk-lib/aws-stepfunctions'; +import * as cdk from 'aws-cdk-lib'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import * as apigw from 'aws-cdk-lib/aws-apigateway'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-cdk-aws-apigateway-stepfunctions-startexecution'); + +const api = new apigw.RestApi(stack, 'my-rest-api'); +const passTask = new sfn.Pass(stack, 'passTask', { + inputPath: '$.somekey', +}); + +const stateMachine: sfn.IStateMachine = new sfn.StateMachine(stack, 'StateMachine', { + definitionBody: sfn.DefinitionBody.fromChainable(passTask), + stateMachineType: sfn.StateMachineType.EXPRESS, +}); + +const methodOptions = { + methodResponses: [ + { + statusCode: '200', + responseParameters: { + 'method.response.header.Access-Control-Allow-Origin': true, + }, + }, + ], +}; + +const integrationOptions = { + integrationResponses: [ + { + responseParameters: { + 'method.response.header.Access-Control-Allow-Origin': "'*'", + }, + statusCode: '200', + }, + ], +}; + +const integ = apigw.StepFunctionsIntegration.startExecution(stateMachine, integrationOptions); +api.root.addMethod('GET', integ, methodOptions); +api.root.addMethod('POST', integ, methodOptions); + +new IntegTest(app, 'aws-apigateway-stepfunctions-startexecution', { + testCases: [ + stack, + ], +}); + +app.synth(); \ No newline at end of file diff --git a/packages/aws-cdk-lib/aws-apigateway/lib/integrations/stepfunctions.ts b/packages/aws-cdk-lib/aws-apigateway/lib/integrations/stepfunctions.ts index 4e75eb967afab..252322158d2b3 100644 --- a/packages/aws-cdk-lib/aws-apigateway/lib/integrations/stepfunctions.ts +++ b/packages/aws-cdk-lib/aws-apigateway/lib/integrations/stepfunctions.ts @@ -117,7 +117,7 @@ class StepFunctionsExecutionIntegration extends AwsIntegration { action: 'StartSyncExecution', options: { credentialsRole: options.credentialsRole, - integrationResponses: integrationResponse(), + integrationResponses: options.integrationResponses ?? integrationResponse(), passthroughBehavior: PassthroughBehavior.NEVER, requestTemplates: requestTemplates(stateMachine, options), ...options, diff --git a/packages/aws-cdk-lib/aws-apigateway/lib/method.ts b/packages/aws-cdk-lib/aws-apigateway/lib/method.ts index db93c7ffef611..5b4ca0428f86f 100644 --- a/packages/aws-cdk-lib/aws-apigateway/lib/method.ts +++ b/packages/aws-cdk-lib/aws-apigateway/lib/method.ts @@ -13,7 +13,7 @@ import { IStage } from './stage'; import { validateHttpMethod } from './util'; import * as cloudwatch from '../../aws-cloudwatch'; import * as iam from '../../aws-iam'; -import { ArnFormat, FeatureFlags, Lazy, Names, Resource, Stack } from '../../core'; +import { Annotations, ArnFormat, FeatureFlags, Lazy, Names, Resource, Stack } from '../../core'; import { APIGATEWAY_REQUEST_VALIDATOR_UNIQUE_ID } from '../../cx-api'; export interface MethodOptions { @@ -173,7 +173,7 @@ export class Method extends Resource { */ public readonly api: IRestApi; - private methodResponses: MethodResponse[]; + private readonly methodResponses: MethodResponse[] = []; constructor(scope: Construct, id: string, props: MethodProps) { super(scope, id); @@ -203,7 +203,9 @@ export class Method extends Resource { authorizer._attachToApi(this.api); } - this.methodResponses = options.methodResponses ?? defaultMethodOptions.methodResponses ?? []; + for (const mr of options.methodResponses ?? defaultMethodOptions.methodResponses ?? []) { + this.addMethodResponse(mr); + } const integration = props.integration ?? this.resource.defaultIntegration ?? new MockIntegration(); const bindResult = integration.bind(this); @@ -278,8 +280,16 @@ export class Method extends Resource { /** * Add a method response to this method + * + * You should only add one method reponse for every status code. The API allows it + * for historical reasons, but will add a warning if this happens. If you do, your Method + * will nondeterministically use one of the responses, and ignore the rest. */ public addMethodResponse(methodResponse: MethodResponse): void { + const mr = this.methodResponses.find((x) => x.statusCode === methodResponse.statusCode); + if (mr) { + Annotations.of(this).addWarning(`addMethodResponse called multiple times with statusCode=${methodResponse.statusCode}, deployment will be nondeterministic. Use a single addMethodResponse call to configure the entire response.`); + } this.methodResponses.push(methodResponse); } @@ -322,12 +332,8 @@ export class Method extends Resource { let responseModels: {[contentType: string]: string} | undefined; if (mr.responseModels) { - responseModels = {}; - for (const contentType in mr.responseModels) { - if (mr.responseModels.hasOwnProperty(contentType)) { - responseModels[contentType] = mr.responseModels[contentType].modelId; - } - } + responseModels = Object.fromEntries(Object.entries(mr.responseModels) + .map(([contentType, rm]) => [contentType, rm.modelId])); } const methodResponseProp = { @@ -506,4 +512,4 @@ export enum AuthorizationType { function pathForArn(path: string): string { return path.replace(/\{[^\}]*\}/g, '*'); // replace path parameters (like '{bookId}') with asterisk -} +} \ No newline at end of file diff --git a/packages/aws-cdk-lib/aws-apigateway/test/integrations/stepfunctions.test.ts b/packages/aws-cdk-lib/aws-apigateway/test/integrations/stepfunctions.test.ts index 0f2a5c65bcf9a..26b2a34662b09 100644 --- a/packages/aws-cdk-lib/aws-apigateway/test/integrations/stepfunctions.test.ts +++ b/packages/aws-cdk-lib/aws-apigateway/test/integrations/stepfunctions.test.ts @@ -372,6 +372,122 @@ describe('StepFunctionsIntegration', () => { .toThrow(/State Machine must be of type "EXPRESS". Please use StateMachineType.EXPRESS as the stateMachineType/); }); }); + + test('addMethod is not susceptible to false sharing of arrays', () => { + //GIVEN + const { stack, api, stateMachine } = givenSetup(); + + //WHEN + const methodOptions = { + methodResponses: [ + { + statusCode: '200', + responseParameters: { + 'method.response.header.Access-Control-Allow-Origin': true, + }, + }, + ], + }; + + const integrationOptions = { + integrationResponses: [ + { + responseParameters: { + 'method.response.header.Access-Control-Allow-Origin': "'*'", + }, + statusCode: '200', + }, + ], + }; + + const integ = apigw.StepFunctionsIntegration.startExecution(stateMachine, integrationOptions); + api.root.addMethod('GET', integ, methodOptions); + api.root.addMethod('POST', integ, methodOptions); + + // THEN - the MethodResponses arrays have 4 elements instead of 8 + // (This is still incorrect because 200 occurs multiple times, but that's a separate + // issue with a non-straightforward solution) + Template.fromStack(stack).resourceCountIs('AWS::ApiGateway::Method', 2); + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { + HttpMethod: 'GET', + MethodResponses: [ + { + ResponseParameters: { + 'method.response.header.Access-Control-Allow-Origin': true, + }, + StatusCode: '200', + }, + { + ResponseModels: { + 'application/json': 'Empty', + }, + StatusCode: '200', + }, + { + ResponseModels: { + 'application/json': 'Error', + }, + StatusCode: '400', + }, + { + ResponseModels: { + 'application/json': 'Error', + }, + StatusCode: '500', + }, + ], + Integration: { + IntegrationResponses: [ + { + ResponseParameters: { + 'method.response.header.Access-Control-Allow-Origin': "'*'", + }, + StatusCode: '200', + }, + ], + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { + HttpMethod: 'POST', + MethodResponses: [ + { + ResponseParameters: { + 'method.response.header.Access-Control-Allow-Origin': true, + }, + StatusCode: '200', + }, + { + ResponseModels: { + 'application/json': 'Empty', + }, + StatusCode: '200', + }, + { + ResponseModels: { + 'application/json': 'Error', + }, + StatusCode: '400', + }, + { + ResponseModels: { + 'application/json': 'Error', + }, + StatusCode: '500', + }, + ], + Integration: { + IntegrationResponses: [ + { + ResponseParameters: { + 'method.response.header.Access-Control-Allow-Origin': "'*'", + }, + StatusCode: '200', + }, + ], + }, + }); + }); }); function givenSetup() {