diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.disable-execute-api-endpoint.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.disable-execute-api-endpoint.ts new file mode 100644 index 0000000000000..c06df23b3ef3f --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2-integrations/test/websocket/integ.disable-execute-api-endpoint.ts @@ -0,0 +1,39 @@ +import { WebSocketApi, WebSocketStage } from 'aws-cdk-lib/aws-apigatewayv2'; +import * as cdk from 'aws-cdk-lib'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import { WebSocketMockIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations'; + +/* + * Stack verification steps: + * 1. Verify that the new property 'disableExecuteApiEndpoint' is set to 'true', and that it is not callable. + */ + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'integ-apigwv2-disable-execute-api-endpoint'); + +// API Gateway WebSocket API +const webSocketApi = new WebSocketApi(stack, 'webSocketApi', { + description: 'Test stack for the disableExecuteApiEndpoint property.', + disableExecuteApiEndpoint: true, + defaultRouteOptions: { integration: new WebSocketMockIntegration('DefaultIntegration') }, +}); + +// Optionally, create a WebSocket stage +new WebSocketStage(stack, 'DevStage', { + webSocketApi: webSocketApi, + stageName: 'dev', + autoDeploy: true, +}); + +new IntegTest(app, 'DisableExecuteApiEndpointPropIntegrationTest', { + testCases: [stack], + cdkCommandOptions: { + deploy: { + args: { + rollback: true, + }, + }, + }, +}); + +app.synth(); diff --git a/packages/aws-cdk-lib/aws-apigatewayv2/lib/websocket/api.ts b/packages/aws-cdk-lib/aws-apigatewayv2/lib/websocket/api.ts index 472744fa85266..f2a06adb10d63 100644 --- a/packages/aws-cdk-lib/aws-apigatewayv2/lib/websocket/api.ts +++ b/packages/aws-cdk-lib/aws-apigatewayv2/lib/websocket/api.ts @@ -101,6 +101,16 @@ export interface WebSocketApiProps { * @default false */ readonly disableSchemaValidation?: boolean; + + /** + * Specifies whether clients can invoke your API using the default endpoint. + * By default, clients can invoke your API with the default + * `https://{api_id}.execute-api.{region}.amazonaws.com` endpoint. Set this to + * true if you would like clients to use your custom domain name. + * + * @default false execute-api endpoint enabled. + */ + readonly disableExecuteApiEndpoint?: boolean; } /** @@ -148,19 +158,25 @@ export class WebSocketApi extends ApiBase implements IWebSocketApi { } public readonly apiId: string; - public readonly apiEndpoint: string; + private readonly _apiEndpoint: string; /** * A human friendly name for this WebSocket API. Note that this is different from `webSocketApiId`. */ public readonly webSocketApiName?: string; + /** + * Specifies whether clients can invoke this HTTP API by using the default execute-api endpoint. + */ + public readonly disableExecuteApiEndpoint?: boolean; + constructor(scope: Construct, id: string, props?: WebSocketApiProps) { super(scope, id); // Enhanced CDK Analytics Telemetry addConstructMetadata(this, props); this.webSocketApiName = props?.apiName ?? id; + this.disableExecuteApiEndpoint = props?.disableExecuteApiEndpoint; const resource = new CfnApi(this, 'Resource', { name: this.webSocketApiName, @@ -170,9 +186,10 @@ export class WebSocketApi extends ApiBase implements IWebSocketApi { routeSelectionExpression: props?.routeSelectionExpression ?? '$request.body.action', ipAddressType: props?.ipAddressType, disableSchemaValidation: props?.disableSchemaValidation, + disableExecuteApiEndpoint: this.disableExecuteApiEndpoint, }); this.apiId = resource.ref; - this.apiEndpoint = resource.attrApiEndpoint; + this._apiEndpoint = resource.attrApiEndpoint; if (props?.connectRouteOptions) { this.addRoute('$connect', props.connectRouteOptions); @@ -185,6 +202,16 @@ export class WebSocketApi extends ApiBase implements IWebSocketApi { } } + /** + * Get the default endpoint for this API. + */ + public get apiEndpoint(): string { + if (this.disableExecuteApiEndpoint) { + throw new ValidationError('apiEndpoint is not accessible when disableExecuteApiEndpoint is set to true.', this); + } + return this._apiEndpoint; + } + /** * Add a new route */ diff --git a/packages/aws-cdk-lib/aws-apigatewayv2/test/websocket/api.test.ts b/packages/aws-cdk-lib/aws-apigatewayv2/test/websocket/api.test.ts index 998df76b8aa32..877853ee88448 100644 --- a/packages/aws-cdk-lib/aws-apigatewayv2/test/websocket/api.test.ts +++ b/packages/aws-cdk-lib/aws-apigatewayv2/test/websocket/api.test.ts @@ -130,6 +130,30 @@ describe('WebSocketApi', () => { }); }); + test('disableExecuteApiEndpoint is enabled', () => { + const stack = new Stack(); + new WebSocketApi(stack, 'api', { + disableExecuteApiEndpoint: true, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Api', { + Name: 'api', + ProtocolType: 'WEBSOCKET', + DisableExecuteApiEndpoint: true, + }); + }); + + test('throws when accessing apiEndpoint and disableExecuteApiEndpoint is true', () => { + const stack = new Stack(); + const api = new WebSocketApi(stack, 'api', { + disableExecuteApiEndpoint: true, + }); + + expect(() => api.apiEndpoint).toThrow( + /apiEndpoint is not accessible when disableExecuteApiEndpoint is set to true./, + ); + }); + test('import', () => { // GIVEN const stack = new Stack();