Skip to content

Commit 965e055

Browse files
authored
feat(apigatewayv2): add stage variables support for HTTP and WebSocket API (#34548)
### Issue # Closes #11818 . ### Reason for this change To support stage variables to HttpApi & WebSocketApi. ### Description of changes Add `stageVariables` property to `WebSocketStage` & `HttpStage`. ### Describe any new or updated permissions being added N/A ### Description of how you validated changes * Add a unit test * Tested it manually by creating stack to use the new props and check the CF template & UI * ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent c3559b5 commit 965e055

File tree

6 files changed

+93
-2
lines changed

6 files changed

+93
-2
lines changed

packages/aws-cdk-lib/aws-apigatewayv2/lib/common/base.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export abstract class ApiBase extends Resource implements IApi {
3030
* @internal
3131
*/
3232
export abstract class StageBase extends Resource implements IStage {
33+
private stageVariables: { [key: string]: string } = {};
3334
public abstract readonly stageName: string;
3435
protected abstract readonly baseApi: IApi;
3536

@@ -87,4 +88,16 @@ export abstract class StageBase extends Resource implements IStage {
8788
dimensionsMap: { ApiId: this.baseApi.apiId, Stage: this.stageName },
8889
}).attachTo(this);
8990
}
91+
92+
public addStageVariable(name: string, value: string) {
93+
this.stageVariables[name] = value;
94+
}
95+
96+
/**
97+
* Returns the stage variables for this stage.
98+
* @internal
99+
*/
100+
protected get _stageVariables(): { [key: string]: string } | undefined {
101+
return Object.keys(this.stageVariables).length > 0 ? { ...this.stageVariables } : undefined;
102+
}
90103
}

packages/aws-cdk-lib/aws-apigatewayv2/lib/common/stage.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ export interface IStage extends IResource {
2525
* @default - average over 5 minutes
2626
*/
2727
metric(metricName: string, props?: MetricOptions): Metric;
28+
29+
/**
30+
* Adds a stage variable to this stage.
31+
*
32+
* @param name The name of the stage variable.
33+
* @param value The value of the stage variable.
34+
*
35+
* The allowed characters for variable names and the required pattern for variable values are specified here: https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-apigateway-stage.html#cfn-apigateway-stage-variables
36+
*/
37+
addStageVariable(name: string, value: string): void;
2838
}
2939

3040
/**
@@ -89,6 +99,16 @@ export interface StageOptions {
8999
* @default - No access logging
90100
*/
91101
readonly accessLogSettings?: IAccessLogSettings;
102+
103+
/**
104+
* Stage variables for the stage.
105+
* These are key-value pairs that you can define and use in your API routes.
106+
*
107+
* The allowed characters for variable names and the required pattern for variable values are specified here: https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-apigateway-stage.html#cfn-apigateway-stage-variables
108+
*
109+
* @default - No stage variables
110+
*/
111+
readonly stageVariables?: { [key: string]: string };
92112
}
93113

94114
/**

packages/aws-cdk-lib/aws-apigatewayv2/lib/http/stage.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Construct } from 'constructs';
22
import { IHttpApi } from './api';
33
import { CfnStage } from '.././index';
44
import { Metric, MetricOptions } from '../../../aws-cloudwatch';
5-
import { Stack } from '../../../core';
5+
import { Lazy, Stack } from '../../../core';
66
import { ValidationError } from '../../../core/lib/errors';
77
import { addConstructMetadata } from '../../../core/lib/metadata-resource';
88
import { propertyInjectable } from '../../../core/lib/prop-injectable';
@@ -172,6 +172,12 @@ export class HttpStage extends HttpStageBase {
172172
// Enhanced CDK Analytics Telemetry
173173
addConstructMetadata(this, props);
174174

175+
if (props.stageVariables) {
176+
Object.entries(props.stageVariables).forEach(([key, value]) => {
177+
this.addStageVariable(key, value);
178+
});
179+
}
180+
175181
new CfnStage(this, 'Resource', {
176182
apiId: props.httpApi.apiId,
177183
stageName: this.physicalName,
@@ -183,6 +189,7 @@ export class HttpStage extends HttpStageBase {
183189
detailedMetricsEnabled: props.detailedMetricsEnabled,
184190
} : undefined,
185191
description: props.description,
192+
stageVariables: Lazy.any({ produce: () => this._stageVariables }),
186193
});
187194

188195
this.stageName = this.physicalName;

packages/aws-cdk-lib/aws-apigatewayv2/lib/websocket/stage.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Construct } from 'constructs';
22
import { IWebSocketApi } from './api';
33
import { CfnStage } from '.././index';
44
import { Grant, IGrantable } from '../../../aws-iam';
5-
import { Stack } from '../../../core';
5+
import { Lazy, Stack } from '../../../core';
66
import { ValidationError } from '../../../core/lib/errors';
77
import { addConstructMetadata, MethodMetadata } from '../../../core/lib/metadata-resource';
88
import { propertyInjectable } from '../../../core/lib/prop-injectable';
@@ -92,6 +92,12 @@ export class WebSocketStage extends StageBase implements IWebSocketStage {
9292
// Enhanced CDK Analytics Telemetry
9393
addConstructMetadata(this, props);
9494

95+
if (props.stageVariables) {
96+
Object.entries(props.stageVariables).forEach(([key, value]) => {
97+
this.addStageVariable(key, value);
98+
});
99+
}
100+
95101
this.baseApi = props.webSocketApi;
96102
this.api = props.webSocketApi;
97103
this.stageName = this.physicalName;
@@ -106,6 +112,7 @@ export class WebSocketStage extends StageBase implements IWebSocketStage {
106112
detailedMetricsEnabled: props.detailedMetricsEnabled,
107113
} : undefined,
108114
description: props.description,
115+
stageVariables: Lazy.any({ produce: () => this._stageVariables }),
109116
});
110117

111118
if (props.domainMapping) {

packages/aws-cdk-lib/aws-apigatewayv2/test/http/stage.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,27 @@ describe('HttpStage', () => {
256256
})).not.toThrow();
257257
});
258258
});
259+
test('can add stage variables after creation', () => {
260+
// WHEN
261+
const stage = new HttpStage(stack, 'DefaultStage', {
262+
httpApi: api,
263+
stageVariables: {
264+
env: 'prod',
265+
},
266+
});
267+
268+
stage.addStageVariable('timeout', '300');
269+
270+
// THEN
271+
Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Stage', {
272+
ApiId: stack.resolve(api.apiId),
273+
StageName: '$default',
274+
StageVariables: {
275+
env: 'prod',
276+
timeout: '300',
277+
},
278+
});
279+
});
259280
});
260281

261282
describe('HttpStage with domain mapping', () => {

packages/aws-cdk-lib/aws-apigatewayv2/test/websocket/stage.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,4 +179,27 @@ describe('WebSocketStage', () => {
179179
Description: 'My Stage',
180180
});
181181
});
182+
183+
test('can add stage variables after creation', () => {
184+
// WHEN
185+
const stage = new WebSocketStage(stack, 'DefaultStage', {
186+
webSocketApi: api,
187+
stageName: 'dev',
188+
stageVariables: {
189+
env: 'dev',
190+
},
191+
});
192+
193+
stage.addStageVariable('timeout', '600');
194+
195+
// THEN
196+
Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Stage', {
197+
ApiId: stack.resolve(api.apiId),
198+
StageName: 'dev',
199+
StageVariables: {
200+
env: 'dev',
201+
timeout: '600',
202+
},
203+
});
204+
});
182205
});

0 commit comments

Comments
 (0)