Skip to content

Commit 142a9d8

Browse files
Support external ApiGateway websocket API
fixes #6270
1 parent f596448 commit 142a9d8

File tree

12 files changed

+85
-20
lines changed

12 files changed

+85
-20
lines changed

docs/providers/aws/events/apigateway.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,7 @@ provider:
11031103
apiGateway:
11041104
restApiId: xxxxxxxxxx # REST API resource ID. Default is generated by the framework
11051105
restApiRootResourceId: xxxxxxxxxx # Root resource, represent as / path
1106+
websocketApiId: xxxxxxxxxx # Websocket API resource ID. Default is generated by the framewok
11061107
description: Some Description # optional - description of deployment history
11071108
11081109
functions:
@@ -1119,6 +1120,7 @@ provider:
11191120
apiGateway:
11201121
restApiId: xxxxxxxxxx
11211122
restApiRootResourceId: xxxxxxxxxx
1123+
websocketApiId: xxxxxxxxxx
11221124
description: Some Description
11231125
11241126
functions:
@@ -1136,6 +1138,7 @@ provider:
11361138
apiGateway:
11371139
restApiId: xxxxxxxxxx
11381140
restApiRootResourceId: xxxxxxxxxx
1141+
websocketApiId: xxxxxxxxxx
11391142
description: Some Description
11401143
11411144
functions:
@@ -1155,6 +1158,7 @@ provider:
11551158
apiGateway:
11561159
restApiId: xxxxxxxxxx
11571160
restApiRootResourceId: xxxxxxxxxx
1161+
websocketApiId: xxxxxxxxxx
11581162
description: Some Description
11591163
restApiResources:
11601164
/posts: xxxxxxxxxx
@@ -1170,6 +1174,7 @@ provider:
11701174
apiGateway:
11711175
restApiId: xxxxxxxxxx
11721176
restApiRootResourceId: xxxxxxxxxx
1177+
websocketApiId: xxxxxxxxxx
11731178
description: Some Description
11741179
restApiResources:
11751180
/posts: xxxxxxxxxx
@@ -1188,6 +1193,7 @@ provider:
11881193
apiGateway:
11891194
restApiId: xxxxxxxxxx
11901195
# restApiRootResourceId: xxxxxxxxxx # Optional
1196+
websocketApiId: xxxxxxxxxx
11911197
description: Some Description
11921198
restApiResources:
11931199
/posts: xxxxxxxxxx
@@ -1213,7 +1219,7 @@ functions:
12131219

12141220
### Easiest and CI/CD friendly example of using shared API Gateway and API Resources.
12151221

1216-
You can define your API Gateway resource in its own service and export the `restApiId` and `restApiRootResourceId` using cloudformation cross-stack references.
1222+
You can define your API Gateway resource in its own service and export the `restApiId`, `restApiRootResourceId` and `websocketApiId` using cloudformation cross-stack references.
12171223

12181224
```yml
12191225
service: my-api
@@ -1231,6 +1237,13 @@ resources:
12311237
Properties:
12321238
Name: MyApiGW
12331239
1240+
MyWebsocketApi:
1241+
Type: AWS::ApiGatewayV2::Api
1242+
Properties:
1243+
Name: MyWebsocketApi
1244+
ProtocolType: WEBSOCKET
1245+
RouteSelectionExpression: '$request.body.action'
1246+
12341247
Outputs:
12351248
apiGatewayRestApiId:
12361249
Value:
@@ -1245,9 +1258,16 @@ resources:
12451258
- RootResourceId
12461259
Export:
12471260
Name: MyApiGateway-rootResourceId
1261+
1262+
websocketApiId:
1263+
Value:
1264+
Ref: MyWebsocketApi
1265+
Export:
1266+
Name: MyApiGateway-websocketApiId
1267+
12481268
```
12491269

1250-
This creates API gateway and then exports the `restApiId` and `rootResourceId` values using cloudformation cross stack output.
1270+
This creates API gateway and then exports the `restApiId`, `rootResourceId` and `websocketApiId` values using cloudformation cross stack output.
12511271
We will import this and reference in future services.
12521272

12531273
```yml
@@ -1259,6 +1279,8 @@ provider:
12591279
'Fn::ImportValue': MyApiGateway-restApiId
12601280
restApiRootResourceId:
12611281
'Fn::ImportValue': MyApiGateway-rootResourceId
1282+
websocketApiId:
1283+
'Fn::ImportValue': MyApiGateway-websocketApiId
12621284
12631285
functions:
12641286
service-a-functions
@@ -1272,6 +1294,8 @@ provider:
12721294
'Fn::ImportValue': MyApiGateway-restApiId
12731295
restApiRootResourceId:
12741296
'Fn::ImportValue': MyApiGateway-rootResourceId
1297+
websocketApiId:
1298+
'Fn::ImportValue': MyApiGateway-websocketApiId
12751299
12761300
functions:
12771301
service-b-functions

docs/providers/aws/guide/serverless.yml.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ provider:
6060
restApiResources: # List of existing resources that were created in the REST API. This is required or the stack will be conflicted
6161
'/users': xxxxxxxxxx
6262
'/users/create': xxxxxxxxxx
63+
websocketApiId: # Websocket API resource ID. Default is generated by the framewok
6364
apiKeySourceType: HEADER # Source of API key for usage plan. HEADER or AUTHORIZER.
6465
minimumCompressionSize: 1024 # Compress response when larger than specified size in bytes (must be between 0 and 10485760)
6566
description: Some Description # Optional description for the API Gateway stage deployment

lib/plugins/aws/package/compile/events/websockets/lib/api.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ const BbPromise = require('bluebird');
55

66
module.exports = {
77
compileApi() {
8+
const apiGateway = this.serverless.service.provider.apiGateway || {};
9+
10+
// immediately return if we're using an external websocket API id
11+
if (apiGateway.websocketApiId) {
12+
return BbPromise.resolve();
13+
}
14+
815
this.websocketsApiLogicalId = this.provider.naming.getWebsocketsApiLogicalId();
916

1017
const RouteSelectionExpression = this.serverless.service.provider

lib/plugins/aws/package/compile/events/websockets/lib/api.test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,18 @@ describe('#compileApi()', () => {
5050
});
5151
}));
5252

53+
it('should ignore API resource creation if there is predefined websocketApi config', () => {
54+
awsCompileWebsocketsEvents.serverless.service.provider.apiGateway = {
55+
websocketApiId: '5ezys3sght',
56+
};
57+
return awsCompileWebsocketsEvents.compileApi().then(() => {
58+
const resources = awsCompileWebsocketsEvents.serverless.service.provider
59+
.compiledCloudFormationTemplate.Resources;
60+
61+
expect(resources).to.not.have.property('WebsocketsApi');
62+
});
63+
});
64+
5365
it('should add the websockets policy', () => awsCompileWebsocketsEvents
5466
.compileApi().then(() => {
5567
const resources = awsCompileWebsocketsEvents.serverless.service.provider

lib/plugins/aws/package/compile/events/websockets/lib/authorizers.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ module.exports = {
1616
[websocketsAuthorizerLogicalId]: {
1717
Type: 'AWS::ApiGatewayV2::Authorizer',
1818
Properties: {
19-
ApiId: {
20-
Ref: this.websocketsApiLogicalId,
21-
},
19+
ApiId: this.provider.getApiGatewayWebsocketApiId(),
2220
Name: event.authorizer.name,
2321
AuthorizerType: 'REQUEST',
2422
AuthorizerUri: event.authorizer.uri,

lib/plugins/aws/package/compile/events/websockets/lib/authorizers.test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,20 @@ describe('#compileAuthorizers()', () => {
9090
});
9191
});
9292

93+
it('should use existing Api if there is predefined websocketApi config', () => {
94+
awsCompileWebsocketsEvents.serverless.service.provider.apiGateway = {
95+
websocketApiId: '5ezys3sght',
96+
};
97+
98+
return awsCompileWebsocketsEvents.compileAuthorizers().then(() => {
99+
const resources = awsCompileWebsocketsEvents.serverless.service.provider
100+
.compiledCloudFormationTemplate.Resources;
101+
102+
expect(resources.AuthWebsocketsAuthorizer.Properties).to.contain({
103+
ApiId: '5ezys3sght',
104+
});
105+
});
106+
});
93107
});
94108

95109
describe('for routes without authorizer definition', () => {

lib/plugins/aws/package/compile/events/websockets/lib/deployment.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ module.exports = {
1818
Type: 'AWS::ApiGatewayV2::Deployment',
1919
DependsOn: routeLogicalIds,
2020
Properties: {
21-
ApiId: {
22-
Ref: this.websocketsApiLogicalId,
23-
},
21+
ApiId: this.provider.getApiGatewayWebsocketApiId(),
2422
Description: this.serverless.service.provider
2523
.websocketsDescription || 'Serverless Websockets',
2624
},
@@ -34,7 +32,7 @@ module.exports = {
3432
'Fn::Join': ['',
3533
[
3634
'wss://',
37-
{ Ref: this.provider.naming.getWebsocketsApiLogicalId() },
35+
this.provider.getApiGatewayWebsocketApiId(),
3836
'.execute-api.',
3937
{ Ref: 'AWS::Region' },
4038
'.',

lib/plugins/aws/package/compile/events/websockets/lib/integrations.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@ module.exports = {
1414
[websocketsIntegrationLogicalId]: {
1515
Type: 'AWS::ApiGatewayV2::Integration',
1616
Properties: {
17-
ApiId: {
18-
Ref: this.websocketsApiLogicalId,
19-
},
17+
ApiId: this.provider.getApiGatewayWebsocketApiId(),
2018
IntegrationType: 'AWS_PROXY',
2119
IntegrationUri: {
2220
'Fn::Join': ['',

lib/plugins/aws/package/compile/events/websockets/lib/permissions.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const BbPromise = require('bluebird');
66
module.exports = {
77
compilePermissions() {
88
this.validated.events.forEach(event => {
9+
const websocketApiId = this.provider.getApiGatewayWebsocketApiId();
910
const lambdaLogicalId = this.provider.naming.getLambdaLogicalId(event.functionName);
1011

1112
const websocketsPermissionLogicalId = this.provider.naming
@@ -14,7 +15,9 @@ module.exports = {
1415
_.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, {
1516
[websocketsPermissionLogicalId]: {
1617
Type: 'AWS::Lambda::Permission',
17-
DependsOn: [this.websocketsApiLogicalId, lambdaLogicalId],
18+
DependsOn: (websocketApiId.Ref !== undefined)
19+
? [websocketApiId.Ref, lambdaLogicalId]
20+
: [lambdaLogicalId],
1821
Properties: {
1922
FunctionName: {
2023
'Fn::GetAtt': [lambdaLogicalId, 'Arn'],
@@ -32,7 +35,9 @@ module.exports = {
3235
const authorizerPermissionTemplate = {
3336
[websocketsAuthorizerPermissionLogicalId]: {
3437
Type: 'AWS::Lambda::Permission',
35-
DependsOn: [this.websocketsApiLogicalId],
38+
DependsOn: (websocketApiId.Ref !== undefined)
39+
? [websocketApiId.Ref]
40+
: [],
3641
Properties: {
3742
Action: 'lambda:InvokeFunction',
3843
Principal: 'apigateway.amazonaws.com',

lib/plugins/aws/package/compile/events/websockets/lib/routes.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ module.exports = {
1616
[websocketsRouteLogicalId]: {
1717
Type: 'AWS::ApiGatewayV2::Route',
1818
Properties: {
19-
ApiId: {
20-
Ref: this.websocketsApiLogicalId,
21-
},
19+
ApiId: this.provider.getApiGatewayWebsocketApiId(),
2220
RouteKey: event.route,
2321
AuthorizationType: 'NONE',
2422
Target: {

0 commit comments

Comments
 (0)