From f8fe1d292feb3fc39a99687bf454a829302c4ff5 Mon Sep 17 00:00:00 2001 From: Emil Rowland Date: Sat, 18 Feb 2023 10:16:26 +0100 Subject: [PATCH] feat(apigatewayv2): allow websockets routes to return response to client (#22984) This PR allows WebSocket routes to return a response to the client. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../package.json | 1 + ...efaultTestDeployAssert4E6713E1.assets.json | 19 ++ ...aultTestDeployAssert4E6713E1.template.json | 36 +++ ...dk-aws-apigatewayv2-websockets.assets.json | 19 ++ ...-aws-apigatewayv2-websockets.template.json | 90 ++++++++ .../integ.route-response.js.snapshot/cdk.out | 1 + .../integ.json | 12 + .../manifest.json | 129 +++++++++++ .../tree.json | 215 ++++++++++++++++++ .../test/websocket/integ.route-response.ts | 18 ++ packages/@aws-cdk/aws-apigatewayv2/README.md | 13 ++ .../aws-apigatewayv2/lib/websocket/route.ts | 17 +- .../test/websocket/api.test.ts | 22 ++ .../test/websocket/route.test.ts | 44 ++++ 14 files changed, 635 insertions(+), 1 deletion(-) create mode 100644 packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/IntegDefaultTestDeployAssert4E6713E1.assets.json create mode 100644 packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/IntegDefaultTestDeployAssert4E6713E1.template.json create mode 100644 packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/aws-cdk-aws-apigatewayv2-websockets.assets.json create mode 100644 packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/aws-cdk-aws-apigatewayv2-websockets.template.json create mode 100644 packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.ts diff --git a/packages/@aws-cdk/aws-apigatewayv2-integrations/package.json b/packages/@aws-cdk/aws-apigatewayv2-integrations/package.json index 3eae9be949a44..3b9a0665e8acb 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-integrations/package.json +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/package.json @@ -81,6 +81,7 @@ "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/integ-runner": "0.0.0", + "@aws-cdk/integ-tests": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.5.2" }, diff --git a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/IntegDefaultTestDeployAssert4E6713E1.assets.json b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/IntegDefaultTestDeployAssert4E6713E1.assets.json new file mode 100644 index 0000000000000..1c86a68a43509 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/IntegDefaultTestDeployAssert4E6713E1.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "IntegDefaultTestDeployAssert4E6713E1.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/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/IntegDefaultTestDeployAssert4E6713E1.template.json b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/IntegDefaultTestDeployAssert4E6713E1.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/IntegDefaultTestDeployAssert4E6713E1.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/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/aws-cdk-aws-apigatewayv2-websockets.assets.json b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/aws-cdk-aws-apigatewayv2-websockets.assets.json new file mode 100644 index 0000000000000..1b84d4a69430f --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/aws-cdk-aws-apigatewayv2-websockets.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "536558280cda4b74d67fdcdc23a22d0fb7b7e175f5bb56b260d43680056a3718": { + "source": { + "path": "aws-cdk-aws-apigatewayv2-websockets.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "536558280cda4b74d67fdcdc23a22d0fb7b7e175f5bb56b260d43680056a3718.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/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/aws-cdk-aws-apigatewayv2-websockets.template.json b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/aws-cdk-aws-apigatewayv2-websockets.template.json new file mode 100644 index 0000000000000..fc78860fb477a --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/aws-cdk-aws-apigatewayv2-websockets.template.json @@ -0,0 +1,90 @@ +{ + "Resources": { + "MyWebsocketApiEBAC53DF": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Name": "MyWebsocketApi", + "ProtocolType": "WEBSOCKET", + "RouteSelectionExpression": "$request.body.action" + } + }, + "MyWebsocketApitestRouteSendMessageIntegration526C7CB6": { + "Type": "AWS::ApiGatewayV2::Integration", + "Properties": { + "ApiId": { + "Ref": "MyWebsocketApiEBAC53DF" + }, + "IntegrationType": "MOCK", + "IntegrationUri": "" + } + }, + "MyWebsocketApitestRoute893E635A": { + "Type": "AWS::ApiGatewayV2::Route", + "Properties": { + "ApiId": { + "Ref": "MyWebsocketApiEBAC53DF" + }, + "RouteKey": "test", + "AuthorizationType": "NONE", + "RouteResponseSelectionExpression": "$default", + "Target": { + "Fn::Join": [ + "", + [ + "integrations/", + { + "Ref": "MyWebsocketApitestRouteSendMessageIntegration526C7CB6" + } + ] + ] + } + } + }, + "MyWebsocketApitestRouteResponse5A88D743": { + "Type": "AWS::ApiGatewayV2::RouteResponse", + "Properties": { + "ApiId": { + "Ref": "MyWebsocketApiEBAC53DF" + }, + "RouteId": { + "Ref": "MyWebsocketApitestRoute893E635A" + }, + "RouteResponseKey": "$default" + } + } + }, + "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/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/cdk.out b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/cdk.out new file mode 100644 index 0000000000000..8ecc185e9dbee --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/integ.json b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/integ.json new file mode 100644 index 0000000000000..96c7a9ea56951 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "21.0.0", + "testCases": { + "Integ/DefaultTest": { + "stacks": [ + "aws-cdk-aws-apigatewayv2-websockets" + ], + "assertionStack": "Integ/DefaultTest/DeployAssert", + "assertionStackName": "IntegDefaultTestDeployAssert4E6713E1" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/manifest.json b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/manifest.json new file mode 100644 index 0000000000000..394ee072dfc5a --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/manifest.json @@ -0,0 +1,129 @@ +{ + "version": "21.0.0", + "artifacts": { + "aws-cdk-aws-apigatewayv2-websockets.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-cdk-aws-apigatewayv2-websockets.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-cdk-aws-apigatewayv2-websockets": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-cdk-aws-apigatewayv2-websockets.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}/536558280cda4b74d67fdcdc23a22d0fb7b7e175f5bb56b260d43680056a3718.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-cdk-aws-apigatewayv2-websockets.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-apigatewayv2-websockets.assets" + ], + "metadata": { + "/aws-cdk-aws-apigatewayv2-websockets/MyWebsocketApi/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyWebsocketApiEBAC53DF" + } + ], + "/aws-cdk-aws-apigatewayv2-websockets/MyWebsocketApi/test-Route/SendMessageIntegration/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyWebsocketApitestRouteSendMessageIntegration526C7CB6" + } + ], + "/aws-cdk-aws-apigatewayv2-websockets/MyWebsocketApi/test-Route/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyWebsocketApitestRoute893E635A" + } + ], + "/aws-cdk-aws-apigatewayv2-websockets/MyWebsocketApi/test-Route/Response": [ + { + "type": "aws:cdk:logicalId", + "data": "MyWebsocketApitestRouteResponse5A88D743" + } + ], + "/aws-cdk-aws-apigatewayv2-websockets/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-cdk-aws-apigatewayv2-websockets/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-cdk-aws-apigatewayv2-websockets" + }, + "IntegDefaultTestDeployAssert4E6713E1.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "IntegDefaultTestDeployAssert4E6713E1.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "IntegDefaultTestDeployAssert4E6713E1": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "IntegDefaultTestDeployAssert4E6713E1.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": [ + "IntegDefaultTestDeployAssert4E6713E1.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": [ + "IntegDefaultTestDeployAssert4E6713E1.assets" + ], + "metadata": { + "/Integ/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/Integ/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "Integ/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/tree.json b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/tree.json new file mode 100644 index 0000000000000..62f1b75e222d9 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.js.snapshot/tree.json @@ -0,0 +1,215 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-cdk-aws-apigatewayv2-websockets": { + "id": "aws-cdk-aws-apigatewayv2-websockets", + "path": "aws-cdk-aws-apigatewayv2-websockets", + "children": { + "MyWebsocketApi": { + "id": "MyWebsocketApi", + "path": "aws-cdk-aws-apigatewayv2-websockets/MyWebsocketApi", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-apigatewayv2-websockets/MyWebsocketApi/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ApiGatewayV2::Api", + "aws:cdk:cloudformation:props": { + "name": "MyWebsocketApi", + "protocolType": "WEBSOCKET", + "routeSelectionExpression": "$request.body.action" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-apigatewayv2.CfnApi", + "version": "0.0.0" + } + }, + "test-Route": { + "id": "test-Route", + "path": "aws-cdk-aws-apigatewayv2-websockets/MyWebsocketApi/test-Route", + "children": { + "SendMessageIntegration": { + "id": "SendMessageIntegration", + "path": "aws-cdk-aws-apigatewayv2-websockets/MyWebsocketApi/test-Route/SendMessageIntegration", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-apigatewayv2-websockets/MyWebsocketApi/test-Route/SendMessageIntegration/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ApiGatewayV2::Integration", + "aws:cdk:cloudformation:props": { + "apiId": { + "Ref": "MyWebsocketApiEBAC53DF" + }, + "integrationType": "MOCK", + "integrationUri": "" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-apigatewayv2.CfnIntegration", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-apigatewayv2.WebSocketIntegration", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-apigatewayv2-websockets/MyWebsocketApi/test-Route/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ApiGatewayV2::Route", + "aws:cdk:cloudformation:props": { + "apiId": { + "Ref": "MyWebsocketApiEBAC53DF" + }, + "routeKey": "test", + "authorizationType": "NONE", + "routeResponseSelectionExpression": "$default", + "target": { + "Fn::Join": [ + "", + [ + "integrations/", + { + "Ref": "MyWebsocketApitestRouteSendMessageIntegration526C7CB6" + } + ] + ] + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-apigatewayv2.CfnRoute", + "version": "0.0.0" + } + }, + "Response": { + "id": "Response", + "path": "aws-cdk-aws-apigatewayv2-websockets/MyWebsocketApi/test-Route/Response", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ApiGatewayV2::RouteResponse", + "aws:cdk:cloudformation:props": { + "apiId": { + "Ref": "MyWebsocketApiEBAC53DF" + }, + "routeId": { + "Ref": "MyWebsocketApitestRoute893E635A" + }, + "routeResponseKey": "$default" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-apigatewayv2.CfnRouteResponse", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-apigatewayv2.WebSocketRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-apigatewayv2.WebSocketApi", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-cdk-aws-apigatewayv2-websockets/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-cdk-aws-apigatewayv2-websockets/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "Integ": { + "id": "Integ", + "path": "Integ", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "Integ/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "Integ/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.161" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "Integ/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "Integ/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "Integ/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.161" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.ts b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.ts new file mode 100644 index 0000000000000..063b1d20d962e --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/websocket/integ.route-response.ts @@ -0,0 +1,18 @@ +import { WebSocketApi } from '@aws-cdk/aws-apigatewayv2'; +import * as cdk from '@aws-cdk/core'; +import { IntegTest } from '@aws-cdk/integ-tests'; +import { WebSocketMockIntegration } from '../../lib'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'aws-cdk-aws-apigatewayv2-websockets'); + +const api = new WebSocketApi(stack, 'MyWebsocketApi'); +api.addRoute('test', { + integration: new WebSocketMockIntegration('SendMessageIntegration'), + returnResponse: true, +}); + +new IntegTest(app, 'Integ', { testCases: [stack] }); + +app.synth(); diff --git a/packages/@aws-cdk/aws-apigatewayv2/README.md b/packages/@aws-cdk/aws-apigatewayv2/README.md index 2d35001373921..ceca6f35c3854 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2/README.md @@ -404,6 +404,19 @@ webSocketApi.addRoute('sendmessage', { }); ``` +To add a route that can return a result: + +```ts +import { WebSocketLambdaIntegration } from '@aws-cdk/aws-apigatewayv2-integrations'; + +declare const messageHandler: lambda.Function; +const webSocketApi = new apigwv2.WebSocketApi(this, 'mywsapi'); +webSocketApi.addRoute('sendmessage', { + integration: new WebSocketLambdaIntegration('SendMessageIntegration', messageHandler), + returnResponse: true, +}); +``` + To import an existing WebSocketApi: ```ts diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/websocket/route.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/websocket/route.ts index 923311a2b524e..84328d18e795d 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/websocket/route.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/websocket/route.ts @@ -1,6 +1,6 @@ import { Resource } from '@aws-cdk/core'; import { Construct } from 'constructs'; -import { CfnRoute } from '../apigatewayv2.generated'; +import { CfnRoute, CfnRouteResponse } from '../apigatewayv2.generated'; import { IRoute } from '../common'; import { IWebSocketApi } from './api'; import { IWebSocketRouteAuthorizer, WebSocketNoneAuthorizer } from './authorizer'; @@ -37,6 +37,13 @@ export interface WebSocketRouteOptions { * @default - No Authorizer */ readonly authorizer?: IWebSocketRouteAuthorizer; + + /** + * Should the route send a response to the client + * @default false + */ + readonly returnResponse?: boolean; + } /** @@ -102,7 +109,15 @@ export class WebSocketRoute extends Resource implements IWebSocketRoute { target: `integrations/${config.integrationId}`, authorizerId: authBindResult.authorizerId, authorizationType: authBindResult.authorizationType, + routeResponseSelectionExpression: props.returnResponse ? '$default' : undefined, }); this.routeId = route.ref; + if (props.returnResponse) { + new CfnRouteResponse(this, 'Response', { + apiId: props.webSocketApi.apiId, + routeId: route.ref, + routeResponseKey: '$default', + }); + } } } diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/websocket/api.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/websocket/api.test.ts index f9fb740c0e190..fd7839b4b7657 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/websocket/api.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/websocket/api.test.ts @@ -65,6 +65,28 @@ describe('WebSocketApi', () => { }); }); + test('addRoute: adds a route with passed key and allows it to return a response', () => { + // GIVEN + const stack = new Stack(); + const api = new WebSocketApi(stack, 'api'); + + // WHEN + const route = api.addRoute('myroute', { integration: new DummyIntegration(), returnResponse: true }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Route', { + ApiId: stack.resolve(api.apiId), + RouteKey: 'myroute', + RouteResponseSelectionExpression: '$default', + }); + + Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::RouteResponse', { + ApiId: stack.resolve(api.apiId), + RouteId: stack.resolve(route.routeId), + RouteResponseKey: '$default', + }); + }); + test('connectRouteOptions: adds a $connect route', () => { // GIVEN const stack = new Stack(); diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/websocket/route.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/websocket/route.test.ts index 94c4e969a08b6..63bc3e6e969c7 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/websocket/route.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/websocket/route.test.ts @@ -120,6 +120,50 @@ describe('WebSocketRoute', () => { Template.fromStack(stack).hasResource('AWS::ApiGatewayV2::Integration', 2); }); + + test('default RouteResponseSelectionExpression is set if route will return a response to the client', () => { + // GIVEN + const stack = new Stack(); + const webSocketApi = new WebSocketApi(stack, 'Api'); + + // WHEN + const route = new WebSocketRoute(stack, 'Route', { + webSocketApi, + integration: new DummyIntegration(), + routeKey: 'message', + returnResponse: true, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Route', { + ApiId: stack.resolve(webSocketApi.apiId), + RouteKey: 'message', + RouteResponseSelectionExpression: '$default', + Target: { + 'Fn::Join': [ + '', + [ + 'integrations/', + { + Ref: 'RouteDummyIntegrationE40E82B4', + }, + ], + ], + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::RouteResponse', { + ApiId: stack.resolve(webSocketApi.apiId), + RouteId: stack.resolve(route.routeId), + RouteResponseKey: '$default', + }); + + Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Integration', { + ApiId: stack.resolve(webSocketApi.apiId), + IntegrationType: 'AWS_PROXY', + IntegrationUri: 'some-uri', + }); + }); });