Skip to content

Commit

Permalink
feat(aws-apigatewayv2): add support for api-key in websocket apis
Browse files Browse the repository at this point in the history
  • Loading branch information
alpacamybags118 committed Sep 28, 2021
1 parent 6390cb5 commit a0723b6
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 1 deletion.
24 changes: 23 additions & 1 deletion packages/@aws-cdk/aws-apigatewayv2/lib/websocket/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,37 @@ export interface IWebSocketApi extends IApi {
_addIntegration(scope: Construct, config: WebSocketRouteIntegrationConfig): WebSocketIntegration
}

/**
* Represents the currently available API Key Selection Expressions
*/
export enum ApiKeySelectionExpression {
/**
* x-api-key type
*/
X_API_KEY = '$request.header.x-api-key',

/**
* usageIdentifierKey type
*/
USAGE_IDENTIFIER_KEY = '$context.authorizer.usageIdentifierKey'
}

/**
* Props for WebSocket API
*/
export interface WebSocketApiProps {
/**
* Name for the WebSocket API resoruce
* Name for the WebSocket API resource
* @default - id of the WebSocketApi construct.
*/
readonly apiName?: string;

/**
* An API key selection expression. Currently only supports '$request.header.x-api-key' and '$context.authorizer.usageIdentifierKey'
* @default - none
*/
readonly apiKeySelectionExpression?: ApiKeySelectionExpression

/**
* The description of the API.
* @default - none
Expand Down Expand Up @@ -80,6 +101,7 @@ export class WebSocketApi extends ApiBase implements IWebSocketApi {

const resource = new CfnApi(this, 'Resource', {
name: this.webSocketApiName,
apiKeySelectionExpression: props?.apiKeySelectionExpression,
protocolType: 'WEBSOCKET',
description: props?.description,
routeSelectionExpression: props?.routeSelectionExpression ?? '$request.body.action',
Expand Down
7 changes: 7 additions & 0 deletions packages/@aws-cdk/aws-apigatewayv2/lib/websocket/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ export interface WebSocketRouteProps extends WebSocketRouteOptions {
* The key to this route.
*/
readonly routeKey: string;

/**
* Whether the route requires an API Key to be provided
* @default - false
*/
readonly apiKeyRequired?: boolean;
}

/**
Expand Down Expand Up @@ -76,6 +82,7 @@ export class WebSocketRoute extends Resource implements IWebSocketRoute {

const route = new CfnRoute(this, 'Resource', {
apiId: props.webSocketApi.apiId,
apiKeyRequired: props.apiKeyRequired,
routeKey: props.routeKey,
target: `integrations/${integration.integrationId}`,
});
Expand Down
22 changes: 22 additions & 0 deletions packages/@aws-cdk/aws-apigatewayv2/test/websocket/api.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Template } from '@aws-cdk/assertions';
import { Stack } from '@aws-cdk/core';
import {
ApiKeySelectionExpression,
IWebSocketRouteIntegration, WebSocketApi, WebSocketIntegrationType,
WebSocketRouteIntegrationBindOptions, WebSocketRouteIntegrationConfig,
} from '../../lib';
Expand All @@ -24,6 +25,27 @@ describe('WebSocketApi', () => {
Template.fromStack(stack).resourceCountIs('AWS::ApiGatewayV2::Integration', 0);
});

test('apiKeySelectionExpression: given a value', () => {
// GIVEN
const stack = new Stack();

// WHEN
new WebSocketApi(stack, 'api', {
apiKeySelectionExpression: ApiKeySelectionExpression.USAGE_IDENTIFIER_KEY,
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Api', {
ApiKeySelectionExpression: '$context.authorizer.usageIdentifierKey',
Name: 'api',
ProtocolType: 'WEBSOCKET',
});

Template.fromStack(stack).resourceCountIs('AWS::ApiGatewayV2::Stage', 0);
Template.fromStack(stack).resourceCountIs('AWS::ApiGatewayV2::Route', 0);
Template.fromStack(stack).resourceCountIs('AWS::ApiGatewayV2::Integration', 0);
});

test('addRoute: adds a route with passed key', () => {
// GIVEN
const stack = new Stack();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"Resources": {
"MyWebsocketApiEBAC53DF": {
"Type": "AWS::ApiGatewayV2::Api",
"Properties": {
"ApiKeySelectionExpression": "$request.header.x-api-key",
"Name": "MyWebsocketApi",
"ProtocolType": "WEBSOCKET",
"RouteSelectionExpression": "$request.body.action"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env node
import * as cdk from '@aws-cdk/core';
import * as apigw from '../../lib';
import { ApiKeySelectionExpression } from '../../lib';

const app = new cdk.App();

const stack = new cdk.Stack(app, 'aws-cdk-aws-apigatewayv2-websockets');

new apigw.WebSocketApi(stack, 'MyWebsocketApi', {
apiKeySelectionExpression: ApiKeySelectionExpression.X_API_KEY,
});

app.synth();
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"Resources": {
"MyWebsocketApiEBAC53DF": {
"Type": "AWS::ApiGatewayV2::Api",
"Properties": {
"Name": "MyWebsocketApi",
"ProtocolType": "WEBSOCKET",
"RouteSelectionExpression": "$request.body.action"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env node
import * as cdk from '@aws-cdk/core';
import * as apigw from '../../lib';

const app = new cdk.App();

const stack = new cdk.Stack(app, 'aws-cdk-aws-apigatewayv2-websockets');

new apigw.WebSocketApi(stack, 'MyWebsocketApi');

app.synth();
38 changes: 38 additions & 0 deletions packages/@aws-cdk/aws-apigatewayv2/test/websocket/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,44 @@ describe('WebSocketRoute', () => {
IntegrationUri: 'some-uri',
});
});

test('Api Key is required for route when apiKeyIsRequired is true', () => {
// GIVEN
const stack = new Stack();
const webSocketApi = new WebSocketApi(stack, 'Api');

// WHEN
new WebSocketRoute(stack, 'Route', {
webSocketApi,
integration: new DummyIntegration(),
routeKey: 'message',
apiKeyRequired: true,
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Route', {
ApiId: stack.resolve(webSocketApi.apiId),
ApiKeyRequired: true,
RouteKey: 'message',
Target: {
'Fn::Join': [
'',
[
'integrations/',
{
Ref: 'RouteWebSocketIntegrationb7742333c7ab20d7b2b178df59bb17f20338431E',
},
],
],
},
});

Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Integration', {
ApiId: stack.resolve(webSocketApi.apiId),
IntegrationType: 'AWS_PROXY',
IntegrationUri: 'some-uri',
});
});
});


Expand Down

0 comments on commit a0723b6

Please sign in to comment.