Skip to content

Commit e4543eb

Browse files
author
Manasvi Jain
committed
ack-connect-route
1 parent dba43ae commit e4543eb

File tree

6 files changed

+293
-0
lines changed

6 files changed

+293
-0
lines changed

ack-connect-route/README.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
## Websocket API Gateway acknowledgement for $connect route.
2+
3+
The Serverless Application Model (SAM) template deploys an Amazon WebSocket API Gateway and two AWS Lambda functions. When a client connects, the API Gateway creates a $connect route with Lambda proxy integration. The first Lambda function processes the initial connection, capturing both the Connection ID and API Gateway stage URL, then asynchronously triggers the second Lambda function. This second function validates the Connection ID and, if valid, uses SDK API calls to send a greeting message back to the client.
4+
5+
Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns
6+
7+
Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the AWS Pricing page for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.
8+
9+
10+
## Requirements
11+
12+
* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
13+
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
14+
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
15+
* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed
16+
17+
## Deployment Instructions
18+
19+
1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository:
20+
```
21+
git clone https://github.com/aws-samples/serverless-patterns
22+
```
23+
2. Change directory to the pattern directory:
24+
```
25+
cd ack-connect-route
26+
```
27+
3. From the command line, use AWS SAM to build and deploy the AWS resources for the pattern as specified in the template.yml file:
28+
29+
```
30+
sam deploy --guided
31+
```
32+
4. During the prompts:
33+
* Enter a stack name
34+
* Enter the desired AWS Region
35+
* Allow SAM CLI to create IAM roles with the required permissions.
36+
37+
Once you have run guided mode once, you can use `sam deploy` in future to use these defaults.
38+
39+
1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing.
40+
41+
## Testing
42+
43+
Once the application is deployed, retrieve the WebSocketURL value from CloudFormation Outputs. To test the WebSocket API, you can use [wscat](https://github.com/websockets/wscat) which is an open-source command line tool.
44+
45+
1. [Install NPM](https://www.npmjs.com/get-npm).
46+
47+
2. Install wscat:
48+
```
49+
$ npm install -g wscat
50+
```
51+
52+
3. Connect to your WebSocketURL by executing the following command:
53+
$ wscat -c <YOUR WEBSOCKET URL>
54+
```
55+
56+
4. To test the custom route and its associated function, send a JSON-formatted request. The Lambda function sends back the value of the "data" key using the callback URL:
57+
```
58+
$ wscat -c <YOUR WEBSOCKET URL>
59+
connected (press CTRL+C to quit)
60+
```
61+
## Cleanup
62+
63+
1. Delete the stack
64+
```
65+
aws cloudformation delete-stack --stack-name <YOUR STACK NAME>
66+
```
67+
68+
2. Confirm the stack has been deleted
69+
```
70+
aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'<YOUR STACK NAME>')].StackStatus"
71+
```
72+
73+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"title": "Websocket API Gateway acknowledgement for $connect route.",
3+
"description": "The SAM template deploys a WebSocket API Gateway that creates a $connect route, triggering two Lambda functions in sequence - the first captures connection details and triggers the second asynchronously, which then validates the connection and sends a greeting via SDK API calls.",
4+
"language": "Python",
5+
"level": "200",
6+
"framework": "SAM",
7+
"gitHub": {
8+
"template": {
9+
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/ack-connect-route",
10+
"templateURL": "serverless-patterns/ack-connect-route",
11+
"projectFolder": "ack-connect-route",
12+
"templateFile": "template.yaml"
13+
}
14+
},
15+
"deploy": {
16+
"text": [
17+
"sam deploy"
18+
]
19+
},
20+
"testing": {
21+
"text": [
22+
"See the GitHub repo for detailed testing instructions."
23+
]
24+
},
25+
"cleanup": {
26+
"text": [
27+
"Delete the stack: <code>sam delete</code>."
28+
]
29+
},
30+
"authors": [
31+
{
32+
"name": "Manasvi Jain",
33+
"image": "https://avatars.githubusercontent.com/u/56217984?v=4",
34+
"bio": "Associate Partner Solutions Architect at AWS",
35+
"linkedin": "https://www.linkedin.com/in/manasvi-jain-36b9941a3/"
36+
},
37+
{
38+
"name": "Umang Aggarwal",
39+
"image": "https://avatars.githubusercontent.com/Umang071",
40+
"bio": "Technical Account Manager @ AWS",
41+
"linkedin": "https://www.linkedin.com/in/umangaggarwal"
42+
}
43+
]
44+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import json
2+
import boto3
3+
4+
lambdaClient = boto3.client('lambda')
5+
def lambda_handler(event, context):
6+
print("Got an event from some route ", event)
7+
if event['requestContext']['routeKey'] == "$connect":
8+
url = "https://" + event["requestContext"]["domainName"] + "/" + event["requestContext"]["stage"]
9+
10+
response = lambdaClient.invoke(FunctionName="WebsocketPostToConnectionId",InvocationType="Event",Payload=json.dumps({"url": url, "connectionId":event['requestContext']['connectionId']}))
11+
payload = "This won't be received by the client"
12+
return {'statusCode': 200,'body': payload}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import json
2+
import boto3
3+
4+
def lambda_handler(event, context):
5+
6+
print("Got an event from parent lambda ", event)
7+
gatewayapi = boto3.client("apigatewaymanagementapi",endpoint_url = event["url"])
8+
response = gatewayapi.get_connection(ConnectionId=event['connectionId'])
9+
print("Response is ", response)
10+
11+
if response["ResponseMetadata"]["HTTPStatusCode"] == 200:
12+
gatewayapi.post_to_connection(ConnectionId=event['connectionId'], Data="Hello XXX, we are now connected!! ")
13+
return {}

ack-connect-route/src/ondisconnect.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import json
2+
3+
def lambda_handler(event, context):
4+
# TODO implement
5+
print("Connection has been disconnected")
6+
return {
7+
'statusCode': 200,
8+
'body': json.dumps('Hello from Lambda!')
9+
}

ack-connect-route/template.yml

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
AWSTemplateFormatVersion: 2010-09-09
2+
Transform: 'AWS::Serverless-2016-10-31'
3+
Description: An Amazon API Gateway WebSocket API and an AWS Lambda function.
4+
5+
# Global values that are applied to all applicable resources in this template
6+
Globals:
7+
Function:
8+
CodeUri: ./src
9+
Runtime: python3.12
10+
MemorySize: 128
11+
Timeout: 15
12+
13+
Resources:
14+
# API Gateway WebSocket API
15+
WebSocketApi:
16+
Type: 'AWS::ApiGatewayV2::Api'
17+
Properties:
18+
Name: !Ref AWS::StackName
19+
Description: An Amazon API Gateway WebSocket API and an AWS Lambda function.
20+
ProtocolType: WEBSOCKET
21+
RouteSelectionExpression: "$request.body.action"
22+
# Lambda Function - uses Globals to define additional configuration values
23+
OnConnectLambdaFunction:
24+
Type: 'AWS::Serverless::Function'
25+
Properties:
26+
FunctionName: !Sub '${AWS::StackName}-onconnect-function'
27+
Handler: lambda_function.lambda_handler
28+
MemorySize: 256
29+
Policies:
30+
- Statement:
31+
- Effect: Allow
32+
Action:
33+
- 'lambda:InvokeFunction'
34+
Resource:
35+
- !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:WebsocketPostToConnectionId'
36+
# Function permissions grant an AWS service or another account permission to use a function
37+
OnConnectFunctionResourcePermission:
38+
Type: 'AWS::Lambda::Permission'
39+
Properties:
40+
Action: 'lambda:InvokeFunction'
41+
Principal: apigateway.amazonaws.com
42+
FunctionName: !Ref OnConnectLambdaFunction
43+
SourceArn: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${WebSocketApi}/*'
44+
OnConnectIntegration:
45+
Type: AWS::ApiGatewayV2::Integration
46+
Properties:
47+
ApiId: !Ref WebSocketApi
48+
Description: OnConnect Integration
49+
IntegrationType: AWS_PROXY
50+
IntegrationUri:
51+
Fn::Sub:
52+
arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${OnConnectLambdaFunction.Arn}/invocations
53+
OnConnectRoute:
54+
Type: AWS::ApiGatewayV2::Route
55+
Properties:
56+
ApiId: !Ref WebSocketApi
57+
RouteKey: $connect
58+
AuthorizationType: NONE
59+
OperationName: OnConnectRoute
60+
Target: !Join
61+
- '/'
62+
- - 'integrations'
63+
- !Ref OnConnectIntegration
64+
# Lambda Function - uses Globals to define additional configuration values
65+
PostLambdaFunction:
66+
Type: 'AWS::Serverless::Function'
67+
Properties:
68+
FunctionName: WebsocketPostToConnectionId
69+
Handler: lambda_function_postToConnection.lambda_handler
70+
MemorySize: 256
71+
Policies:
72+
- Statement:
73+
- Effect: Allow
74+
Action:
75+
- 'execute-api:ManageConnections'
76+
Resource:
77+
- !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${WebSocketApi}/*'
78+
# Function permissions grant an AWS service or another account permission to use a function
79+
80+
OnDisconnectLambdaFunction:
81+
Type: 'AWS::Serverless::Function'
82+
Properties:
83+
FunctionName: !Sub '${AWS::StackName}-ondisconnect-function'
84+
Handler: ondisconnect.lambda_handler
85+
MemorySize: 256
86+
# Function permissions grant an AWS service or another account permission to use a function
87+
OnDisconnectFunctionResourcePermission:
88+
Type: 'AWS::Lambda::Permission'
89+
Properties:
90+
Action: 'lambda:InvokeFunction'
91+
Principal: apigateway.amazonaws.com
92+
FunctionName: !Ref OnDisconnectLambdaFunction
93+
SourceArn: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${WebSocketApi}/*'
94+
OnDisconnectIntegration:
95+
Type: AWS::ApiGatewayV2::Integration
96+
Properties:
97+
ApiId: !Ref WebSocketApi
98+
Description: OnDisconnect Integration
99+
IntegrationType: AWS_PROXY
100+
IntegrationUri:
101+
Fn::Sub:
102+
arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${OnDisconnectLambdaFunction.Arn}/invocations
103+
OnDisconnectRoute:
104+
Type: AWS::ApiGatewayV2::Route
105+
Properties:
106+
ApiId: !Ref WebSocketApi
107+
RouteKey: $disconnect
108+
AuthorizationType: NONE
109+
OperationName: OnDisconnectRoute
110+
Target: !Join
111+
- '/'
112+
- - 'integrations'
113+
- !Ref OnDisconnectIntegration
114+
Deployment:
115+
Type: AWS::ApiGatewayV2::Deployment
116+
DependsOn:
117+
- OnConnectRoute
118+
- OnDisconnectRoute
119+
Properties:
120+
ApiId: !Ref WebSocketApi
121+
Stage:
122+
Type: AWS::ApiGatewayV2::Stage
123+
Properties:
124+
StageName: prod
125+
Description: Prod Stage
126+
DeploymentId: !Ref Deployment
127+
ApiId: !Ref WebSocketApi
128+
129+
Outputs:
130+
OnConnectLambdaFunctionArn:
131+
Description: "OnConnect function ARN"
132+
Value: !GetAtt OnConnectLambdaFunction.Arn
133+
OnDisconnectLambdaFunctionArn:
134+
Description: "OnDisconnect function ARN"
135+
Value: !GetAtt OnDisconnectLambdaFunction.Arn
136+
PostLambdaFunctionArn:
137+
Description: "Post function ARN"
138+
Value: !GetAtt PostLambdaFunction.Arn
139+
WebSocketURL:
140+
Description: "The WSS Protocol URL to connect to"
141+
Value: !Join [ '', [ 'wss://', !Ref WebSocketApi, '.execute-api.',!Ref 'AWS::Region','.amazonaws.com/',!Ref 'Stage'] ]
142+

0 commit comments

Comments
 (0)