@aws-solutions-constructs/core
Version:
Core CDK Construct for patterns library
819 lines • 96.3 kB
JavaScript
"use strict";
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
* with the License. A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
* OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
const aws_cdk_lib_1 = require("aws-cdk-lib");
const lambda = require("aws-cdk-lib/aws-lambda");
const api = require("aws-cdk-lib/aws-apigateway");
const iam = require("aws-cdk-lib/aws-iam");
const defaults = require("../index");
const assertions_1 = require("aws-cdk-lib/assertions");
function deployRegionalApiGateway(stack) {
const lambdaFunctionProps = {
runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME,
handler: 'index.handler',
code: lambda.Code.fromAsset(`${__dirname}/lambda`)
};
const fn = defaults.deployLambdaFunction(stack, lambdaFunctionProps);
return defaults.RegionalLambdaRestApi(stack, fn, undefined);
}
function setupRestApi(stack, apiProps, createUsagePlan = true) {
const globalRestApiResponse = defaults.GlobalRestApi(stack, apiProps, undefined, createUsagePlan);
// Setup the API Gateway resource
const apiGatewayResource = globalRestApiResponse.api.root.addResource('api-gateway-resource');
// Setup the API Gateway Integration
const apiGatewayIntegration = new api.AwsIntegration({
service: "sqs",
integrationHttpMethod: "POST",
options: {
passthroughBehavior: api.PassthroughBehavior.NEVER,
requestParameters: {
"integration.request.header.Content-Type": "'application/x-www-form-urlencoded'"
},
requestTemplates: {
"application/x-www-form-urlencoded": "Action=SendMessage&MessageBody=$util.urlEncode(\"$input.body\")&MessageAttribute.1.Name=queryParam1&MessageAttribute.1.Value.StringValue=$input.params(\"query_param_1\")&MessageAttribute.1.Value.DataType=String"
},
integrationResponses: [
{
statusCode: "200",
responseTemplates: {
"text/html": "Success"
}
},
{
statusCode: "500",
responseTemplates: {
"text/html": "Error"
},
selectionPattern: "500"
}
]
},
path: '11112222/thisqueuequeueName'
});
// Setup the API Gateway method(s)
apiGatewayResource.addMethod('POST', apiGatewayIntegration, {
requestParameters: {
"method.request.querystring.query_param_1": true
},
methodResponses: [
{
statusCode: "200",
responseParameters: {
"method.response.header.Content-Type": true
}
},
{
statusCode: "500",
responseParameters: {
"method.response.header.Content-Type": true
},
}
]
});
}
test('Test override for RegionalApiGateway', () => {
const stack = new aws_cdk_lib_1.Stack();
const lambdaFunctionProps = {
runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME,
handler: 'index.handler',
code: lambda.Code.fromAsset(`${__dirname}/lambda`)
};
const fn = defaults.deployLambdaFunction(stack, lambdaFunctionProps);
defaults.RegionalLambdaRestApi(stack, fn, {
handler: fn,
description: 'Hello World'
});
const template = assertions_1.Template.fromStack(stack);
template.hasResource('AWS::ApiGateway::RestApi', {
Type: "AWS::ApiGateway::RestApi",
Properties: {
Description: "Hello World",
EndpointConfiguration: {
Types: [
"REGIONAL"
]
},
Name: "LambdaRestApi"
}
});
template.hasResource("AWS::ApiGateway::UsagePlan", {});
});
test('Test override for GlobalApiGateway', () => {
const stack = new aws_cdk_lib_1.Stack();
const lambdaFunctionProps = {
runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME,
handler: 'index.handler',
code: lambda.Code.fromAsset(`${__dirname}/lambda`)
};
const fn = defaults.deployLambdaFunction(stack, lambdaFunctionProps);
defaults.GlobalLambdaRestApi(stack, fn, {
handler: fn,
restApiName: "HelloWorld"
});
const template = assertions_1.Template.fromStack(stack);
template.hasResource('AWS::ApiGateway::RestApi', {
Type: "AWS::ApiGateway::RestApi",
Properties: {
EndpointConfiguration: {
Types: [
"EDGE"
]
},
Name: "HelloWorld"
}
});
template.hasResource("AWS::ApiGateway::UsagePlan", {});
});
test('Test ApiGateway::Account resource for RegionalApiGateway', () => {
const stack = new aws_cdk_lib_1.Stack();
const lambdaFunctionProps = {
runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME,
handler: 'index.handler',
code: lambda.Code.fromAsset(`${__dirname}/lambda`)
};
const fn = defaults.deployLambdaFunction(stack, lambdaFunctionProps);
defaults.RegionalLambdaRestApi(stack, fn);
const template = assertions_1.Template.fromStack(stack);
template.hasResourceProperties('AWS::ApiGateway::Account', {
CloudWatchRoleArn: {
"Fn::GetAtt": [
"LambdaRestApiCloudWatchRoleF339D4E6",
"Arn"
]
}
});
});
test('Test ApiGateway::Account resource for GlobalApiGateway', () => {
const stack = new aws_cdk_lib_1.Stack();
const lambdaFunctionProps = {
runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME,
handler: 'index.handler',
code: lambda.Code.fromAsset(`${__dirname}/lambda`)
};
const fn = defaults.deployLambdaFunction(stack, lambdaFunctionProps);
defaults.GlobalLambdaRestApi(stack, fn);
const template = assertions_1.Template.fromStack(stack);
template.hasResourceProperties('AWS::ApiGateway::Account', {
CloudWatchRoleArn: {
"Fn::GetAtt": [
"LambdaRestApiCloudWatchRoleF339D4E6",
"Arn"
]
}
});
});
test('Test default RestApi deployment w/ ApiGatewayProps', () => {
const stack = new aws_cdk_lib_1.Stack();
setupRestApi(stack, {
restApiName: "customRestApi"
});
const template = assertions_1.Template.fromStack(stack);
template.hasResourceProperties('AWS::ApiGateway::RestApi', {
Name: "customRestApi"
});
});
test('Test suppress UsagePlan', () => {
const stack = new aws_cdk_lib_1.Stack();
setupRestApi(stack, {
restApiName: "customRestApi"
}, false);
const template = assertions_1.Template.fromStack(stack);
template.hasResourceProperties('AWS::ApiGateway::RestApi', {
Name: "customRestApi"
});
template.resourceCountIs("AWS::ApiGateway::UsagePlan", 0);
});
test('Test default RestApi deployment w/ cloudWatchRole set to false', () => {
const stack = new aws_cdk_lib_1.Stack();
setupRestApi(stack, {
cloudWatchRole: false
});
const template = assertions_1.Template.fromStack(stack);
template.resourceCountIs("AWS::ApiGateway::Account", 0);
template.hasResource("AWS::ApiGateway::UsagePlan", {});
});
test('Test default RestApi deployment for Cloudwatch loggroup', () => {
const stack = new aws_cdk_lib_1.Stack();
deployRegionalApiGateway(stack);
const template = assertions_1.Template.fromStack(stack);
template.hasResource('AWS::Logs::LogGroup', {
UpdateReplacePolicy: "Retain",
DeletionPolicy: "Retain"
});
template.hasResourceProperties('AWS::ApiGateway::Stage', {
AccessLogSetting: {
DestinationArn: {
"Fn::GetAtt": [
"ApiAccessLogGroupCEA70788",
"Arn"
]
},
Format: "{\"requestId\":\"$context.requestId\",\"ip\":\"$context.identity.sourceIp\",\"user\":\"$context.identity.user\",\"caller\":\"$context.identity.caller\",\"requestTime\":\"$context.requestTime\",\"httpMethod\":\"$context.httpMethod\",\"resourcePath\":\"$context.resourcePath\",\"status\":\"$context.status\",\"protocol\":\"$context.protocol\",\"responseLength\":\"$context.responseLength\"}",
},
});
});
test('Test addMethodToApiResource with action', () => {
const stack = new aws_cdk_lib_1.Stack();
const globalRestApiResponse = defaults.GlobalRestApi(stack);
// Setup the API Gateway role
const apiGatewayRole = new iam.Role(stack, 'api-gateway-role', {
assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com')
});
// Setup the API Gateway resource
const apiGatewayResource = globalRestApiResponse.api.root.addResource('api-gateway-resource');
const getRequestTemplate = "{}";
// Add Method
defaults.addProxyMethodToApiResource({
action: "Query",
service: "dynamodb",
apiResource: apiGatewayResource,
apiGatewayRole,
apiMethod: "GET",
requestTemplate: getRequestTemplate
});
// Add Method
defaults.addProxyMethodToApiResource({
path: '11112222/thisqueuequeueName',
service: "sqs",
apiResource: apiGatewayResource,
apiGatewayRole,
apiMethod: "PUT",
requestTemplate: getRequestTemplate
});
// Error scenario: missing action and path
try {
defaults.addProxyMethodToApiResource({
service: "sqs",
apiResource: apiGatewayResource,
apiGatewayRole,
apiMethod: "DELETE",
requestTemplate: getRequestTemplate
});
}
catch (e) {
expect(e).toBeInstanceOf(Error);
}
});
test('Test default RestApi w/ request model and validator', () => {
const stack = new aws_cdk_lib_1.Stack();
const globalRestApiResponse = defaults.GlobalRestApi(stack);
// Setup the API Gateway role
const apiGatewayRole = new iam.Role(stack, 'api-gateway-role', {
assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com')
});
// Setup the API Gateway resource
const apiGatewayResource = globalRestApiResponse.api.root.addResource('api-gateway-resource');
const validator = globalRestApiResponse.api.addRequestValidator('default-validator', {
requestValidatorName: 'default-validator',
validateRequestBody: true
});
defaults.addProxyMethodToApiResource({
service: "kinesis",
action: "PutRecord",
apiGatewayRole,
apiMethod: 'POST',
apiResource: apiGatewayResource,
requestTemplate: "{}",
contentType: "'x-amz-json-1.1'",
methodOptions: {
requestValidator: validator,
requestModels: { "application/json": api.Model.EMPTY_MODEL }
}
});
const template = assertions_1.Template.fromStack(stack);
template.hasResourceProperties('AWS::ApiGateway::RequestValidator', {
Name: "default-validator",
ValidateRequestBody: true
});
template.hasResourceProperties('AWS::ApiGateway::Method', {
RequestModels: { "application/json": "Empty" }
});
});
// -----------------------------------------------------------------------
// Test for Regional ApiGateway Creation
// -----------------------------------------------------------------------
test('Test for RegionalRestApiGateway', () => {
const stack = new aws_cdk_lib_1.Stack();
const regionalRestApiResponse = defaults.RegionalRestApi(stack, {
restApiName: "HelloWorld-RegionalApi"
});
// Setup the API Gateway role
const apiGatewayRole = new iam.Role(stack, 'api-gateway-role', {
assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com')
});
// Setup the API Gateway resource
const apiGatewayResource = regionalRestApiResponse.api.root.addResource('hello');
defaults.addProxyMethodToApiResource({
service: 'iotdata',
path: 'hello',
apiGatewayRole,
apiMethod: 'POST',
apiResource: apiGatewayResource,
requestTemplate: "$input.json('$')"
});
const template = assertions_1.Template.fromStack(stack);
template.hasResource('AWS::ApiGateway::RestApi', {
Type: "AWS::ApiGateway::RestApi",
Properties: {
EndpointConfiguration: {
Types: [
"REGIONAL"
]
},
Name: "HelloWorld-RegionalApi"
}
});
});
test('Test supporess usage plan in RegionalRestApiGateway', () => {
const stack = new aws_cdk_lib_1.Stack();
const regionalRestApiResponse = defaults.RegionalRestApi(stack, {
restApiName: "HelloWorld-RegionalApi"
}, undefined, false);
// Setup the API Gateway role
const apiGatewayRole = new iam.Role(stack, 'api-gateway-role', {
assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com')
});
// Setup the API Gateway resource
const apiGatewayResource = regionalRestApiResponse.api.root.addResource('hello');
defaults.addProxyMethodToApiResource({
service: 'iotdata',
path: 'hello',
apiGatewayRole,
apiMethod: 'POST',
apiResource: apiGatewayResource,
requestTemplate: "$input.json('$')"
});
const template = assertions_1.Template.fromStack(stack);
template.resourceCountIs("AWS::ApiGateway::UsagePlan", 0);
});
// -----------------------------------------------------------------------
// Tests for exception while overriding restApiProps using endPointTypes
// -----------------------------------------------------------------------
test('Test for Exception while overriding restApiProps using endPointTypes', () => {
const stack = new aws_cdk_lib_1.Stack();
try {
defaults.RegionalRestApi(stack, {
endpointTypes: [api.EndpointType.REGIONAL]
});
}
catch (e) {
expect(e).toBeInstanceOf(Error);
}
});
// -----------------------------------------------------------------------
// Tests for exception while overriding LambdaRestApiProps using endPointTypes
// -----------------------------------------------------------------------
test('Test for Exception while overriding LambdaRestApiProps using endPointTypes', () => {
const stack = new aws_cdk_lib_1.Stack();
const lambdaFunctionProps = {
runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME,
handler: 'index.handler',
code: lambda.Code.fromAsset(`${__dirname}/lambda`)
};
const fn = defaults.deployLambdaFunction(stack, lambdaFunctionProps);
try {
defaults.GlobalLambdaRestApi(stack, fn, {
handler: fn,
endpointTypes: [api.EndpointType.REGIONAL]
});
}
catch (e) {
expect(e).toBeInstanceOf(Error);
}
});
// -----------------------------------------------------------------------
// Test for Integration Request Props Override
// -----------------------------------------------------------------------
test('Test for Integration Request Props Override', () => {
const stack = new aws_cdk_lib_1.Stack();
const regionalRestApiResponse = defaults.RegionalRestApi(stack);
// Setup the API Gateway role
const apiGatewayRole = new iam.Role(stack, 'api-gateway-role', {
assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com')
});
// Setup the API Gateway resource
const apiGatewayResource = regionalRestApiResponse.api.root.addResource('hello');
const integReqParams = { 'integration.request.path.topic-level-1': "'method.request.path.topic-level-1'" };
const integResp = [
{
statusCode: "200",
selectionPattern: "2\\d{2}",
responseTemplates: {
"application/json": "$input.json('$')"
}
}
];
// Override the default Integration Request Props
const integrationReqProps = {
subdomain: 'abcdefgh12345',
options: {
requestParameters: integReqParams,
integrationResponses: integResp,
passthroughBehavior: api.PassthroughBehavior.WHEN_NO_MATCH
}
};
defaults.addProxyMethodToApiResource({
service: 'iotdata',
path: 'hello',
apiGatewayRole,
apiMethod: 'POST',
apiResource: apiGatewayResource,
requestTemplate: "$input.json('$')",
awsIntegrationProps: integrationReqProps
});
const template = assertions_1.Template.fromStack(stack);
template.hasResourceProperties("AWS::ApiGateway::Method", {
HttpMethod: "POST",
AuthorizationType: "AWS_IAM",
Integration: {
IntegrationHttpMethod: "POST",
IntegrationResponses: [
{
ResponseTemplates: {
"application/json": "$input.json('$')"
},
SelectionPattern: "2\\d{2}",
StatusCode: "200"
}
],
PassthroughBehavior: "WHEN_NO_MATCH",
RequestParameters: {
"integration.request.header.Content-Type": "'application/json'",
"integration.request.path.topic-level-1": "'method.request.path.topic-level-1'",
},
RequestTemplates: {
"application/json": "$input.json('$')"
},
Type: "AWS",
Uri: {
"Fn::Join": [
"",
[
"arn:",
{
Ref: "AWS::Partition"
},
":apigateway:",
{
Ref: "AWS::Region"
},
`:abcdefgh12345.iotdata:path/hello`
]
]
}
},
MethodResponses: [
{
StatusCode: "200",
ResponseParameters: {
"method.response.header.Content-Type": true
}
},
{
StatusCode: "500",
ResponseParameters: {
"method.response.header.Content-Type": true
}
}
]
});
});
// -----------------------------------------------------------------------
// Test for Method Options Override
// -----------------------------------------------------------------------
test('Test for Method Request Props Override', () => {
const stack = new aws_cdk_lib_1.Stack();
const globalRestApiResponse = defaults.GlobalRestApi(stack);
// Setup the API Gateway role
const apiGatewayRole = new iam.Role(stack, 'api-gateway-role', {
assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com')
});
// Setup the API Gateway resource
const apiGatewayResource = globalRestApiResponse.api.root.addResource('hello');
const methodReqParams = { 'method.request.path.topic-level-1': true };
const methodResp = [
{
statusCode: "403"
}
];
const resourceMethodOptions = {
requestParameters: methodReqParams,
methodResponses: methodResp,
};
defaults.addProxyMethodToApiResource({
service: 'iotdata',
path: 'hello',
apiGatewayRole,
apiMethod: 'POST',
apiResource: apiGatewayResource,
requestTemplate: "$input.json('$')",
methodOptions: resourceMethodOptions
});
const template = assertions_1.Template.fromStack(stack);
template.hasResourceProperties("AWS::ApiGateway::Method", {
HttpMethod: "POST",
AuthorizationType: "AWS_IAM",
Integration: {
IntegrationHttpMethod: "POST",
IntegrationResponses: [
{
StatusCode: "200"
},
{
StatusCode: "500",
ResponseTemplates: {
"text/html": "Error"
},
SelectionPattern: "500"
}
],
PassthroughBehavior: "NEVER",
RequestParameters: {
"integration.request.header.Content-Type": "'application/json'",
},
RequestTemplates: {
"application/json": "$input.json('$')"
},
Type: "AWS",
Uri: {
"Fn::Join": [
"",
[
"arn:",
{
Ref: "AWS::Partition"
},
":apigateway:",
{
Ref: "AWS::Region"
},
`:iotdata:path/hello`
]
]
}
},
MethodResponses: [
{
StatusCode: "403"
}
],
RequestParameters: {
"method.request.path.topic-level-1": true
}
});
});
// -----------------------------------------------------------------------
// Test for ApiKey Creation of RestApi
// -----------------------------------------------------------------------
test('Test for ApiKey creation using restApiProps', () => {
const stack = new aws_cdk_lib_1.Stack();
const globalRestApiResponse = defaults.GlobalRestApi(stack, {
defaultMethodOptions: {
apiKeyRequired: true
}
});
// Setup the API Gateway role
const apiGatewayRole = new iam.Role(stack, 'api-gateway-role', {
assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com')
});
// Setup the API Gateway resource
const apiGatewayResource = globalRestApiResponse.api.root.addResource('hello');
defaults.addProxyMethodToApiResource({
service: 'iotdata',
path: 'hello',
apiGatewayRole,
apiMethod: 'POST',
apiResource: apiGatewayResource,
requestTemplate: "$input.json('$')"
});
const template = assertions_1.Template.fromStack(stack);
// Assertion to check for ApiKey
template.hasResourceProperties("AWS::ApiGateway::Method", {
ApiKeyRequired: true
});
template.hasResourceProperties("AWS::ApiGateway::ApiKey", {
Enabled: true
});
// Assertion to check for UsagePlan Api Key Mapping
template.hasResourceProperties("AWS::ApiGateway::UsagePlanKey", {
KeyType: "API_KEY"
});
});
// -----------------------------------------------------------------------
// Test for ApiKey Creation of LambdaRestApi
// -----------------------------------------------------------------------
test('Test for ApiKey creation using lambdaApiProps', () => {
const stack = new aws_cdk_lib_1.Stack();
const lambdaFunctionProps = {
runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME,
handler: 'index.handler',
code: lambda.Code.fromAsset(`${__dirname}/lambda`)
};
const fn = defaults.deployLambdaFunction(stack, lambdaFunctionProps);
defaults.RegionalLambdaRestApi(stack, fn, {
handler: fn,
defaultMethodOptions: {
apiKeyRequired: true
}
});
const template = assertions_1.Template.fromStack(stack);
// Assertion to check for ApiKey
template.hasResourceProperties("AWS::ApiGateway::Method", {
ApiKeyRequired: true
});
template.hasResourceProperties("AWS::ApiGateway::ApiKey", {
Enabled: true
});
// Assertion to check for UsagePlan Api Key Mapping
template.hasResourceProperties("AWS::ApiGateway::UsagePlanKey", {
KeyType: "API_KEY"
});
});
test('Additional request templates can be specified on addMethodToApiResource method', () => {
const stack = new aws_cdk_lib_1.Stack();
const globalRestApiResponse = defaults.GlobalRestApi(stack);
// Setup the API Gateway role
const apiGatewayRole = new iam.Role(stack, 'api-gateway-role', {
assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com')
});
// Setup the API Gateway resource
const apiGatewayResource = globalRestApiResponse.api.root.addResource('api-gateway-resource');
const requestTemplate = '{}';
const additionalRequestTemplates = {
'text/plain': 'additional-request-template'
};
// Add Method
defaults.addProxyMethodToApiResource({
action: "Query",
service: "dynamodb",
apiResource: apiGatewayResource,
apiGatewayRole,
apiMethod: "GET",
requestTemplate,
additionalRequestTemplates
});
const template = assertions_1.Template.fromStack(stack);
template.hasResourceProperties('AWS::ApiGateway::Method', {
HttpMethod: 'GET',
Integration: {
RequestTemplates: {
'application/json': `{}`,
'text/plain': 'additional-request-template'
}
}
});
});
test('Default integration responses are used on addMethodToApiResource method', () => {
const stack = new aws_cdk_lib_1.Stack();
const globalRestApiResponse = defaults.GlobalRestApi(stack);
// Setup the API Gateway role
const apiGatewayRole = new iam.Role(stack, 'api-gateway-role', {
assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com')
});
// Setup the API Gateway resource
const apiGatewayResource = globalRestApiResponse.api.root.addResource('api-gateway-resource');
// Add Method
defaults.addProxyMethodToApiResource({
action: 'Query',
service: 'dynamodb',
apiResource: apiGatewayResource,
apiGatewayRole,
apiMethod: 'GET',
requestTemplate: '{}',
});
const template = assertions_1.Template.fromStack(stack);
template.hasResourceProperties('AWS::ApiGateway::Method', {
HttpMethod: 'GET',
Integration: {
IntegrationResponses: [
{
StatusCode: '200'
},
{
ResponseTemplates: {
'text/html': 'Error'
},
SelectionPattern: '500',
StatusCode: '500'
}
]
}
});
});
test('Can override integration responses on addMethodToApiResource method', () => {
const stack = new aws_cdk_lib_1.Stack();
const globalRestApiResponse = defaults.GlobalRestApi(stack);
// Setup the API Gateway role
const apiGatewayRole = new iam.Role(stack, 'api-gateway-role', {
assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com')
});
// Setup the API Gateway resource
const apiGatewayResource = globalRestApiResponse.api.root.addResource('api-gateway-resource');
// Add Method
defaults.addProxyMethodToApiResource({
action: 'Query',
service: 'dynamodb',
apiResource: apiGatewayResource,
apiGatewayRole,
apiMethod: 'GET',
requestTemplate: '{}',
integrationResponses: [
{
statusCode: "200",
responseTemplates: {
"text/html": "OK"
}
}
]
});
const template = assertions_1.Template.fromStack(stack);
template.hasResourceProperties('AWS::ApiGateway::Method', {
HttpMethod: 'GET',
Integration: {
IntegrationResponses: [
{
ResponseTemplates: {
'text/html': 'OK'
},
StatusCode: '200'
}
],
}
});
});
test('Specifying application/json content-type in additionalRequestTemplates property throws an error', () => {
const stack = new aws_cdk_lib_1.Stack();
const globalRestApiResponse = defaults.GlobalRestApi(stack);
const apiGatewayRole = new iam.Role(stack, 'api-gateway-role', {
assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com')
});
const apiGatewayResource = globalRestApiResponse.api.root.addResource('api-gateway-resource');
const app = () => {
defaults.addProxyMethodToApiResource({
action: 'Query',
service: 'dynamodb',
apiResource: apiGatewayResource,
apiGatewayRole,
apiMethod: 'GET',
requestTemplate: '{}',
additionalRequestTemplates: {
'application/json': '{}'
}
});
};
expect(app).toThrowError('Request Template for the application/json content-type must be specified in the requestTemplate property and not in the additionalRequestTemplates property');
});
test('Test CheckApiProps', () => {
const apiWithKeyProps = {
handler: {},
defaultMethodOptions: {
apiKeyRequired: true
}
};
const apiWithNoKeyProps = {
handler: {},
};
const noErrApp = () => {
defaults.CheckApiProps({
apiGatewayProps: apiWithNoKeyProps,
createUsagePlan: false
});
};
expect(noErrApp).not.toThrowError();
const app = () => {
defaults.CheckApiProps({
apiGatewayProps: apiWithKeyProps,
createUsagePlan: false
});
};
expect(app).toThrowError('Error - if API key is required, then the Usage plan must be created\n');
});
test('Correctly generate a SpecRestApi', () => {
const stack = new aws_cdk_lib_1.Stack();
const myApiDefinition = api.ApiDefinition.fromAsset('./test/openapi/apiDefinition.json');
defaults.CreateSpecRestApi(stack, {
apiDefinition: myApiDefinition
});
const template = assertions_1.Template.fromStack(stack);
template.resourceCountIs("AWS::ApiGateway::RestApi", 1);
template.resourceCountIs("AWS::ApiGateway::Deployment", 1);
template.resourceCountIs("AWS::ApiGateway::Stage", 1);
template.resourceCountIs("AWS::ApiGateway::UsagePlan", 1);
template.hasResourceProperties("AWS::ApiGateway::RestApi", {
BodyS3Location: {
Bucket: assertions_1.Match.anyValue()
}
});
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBpZ2F0ZXdheS1oZWxwZXIudGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImFwaWdhdGV3YXktaGVscGVyLnRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7Ozs7OztHQVdHOztBQUVILDZDQUFvQztBQUNwQyxpREFBaUQ7QUFDakQsa0RBQWtEO0FBQ2xELDJDQUEyQztBQUMzQyxxQ0FBcUM7QUFDckMsdURBQXlEO0FBRXpELFNBQVMsd0JBQXdCLENBQUMsS0FBWTtJQUM1QyxNQUFNLG1CQUFtQixHQUF5QjtRQUNoRCxPQUFPLEVBQUUsUUFBUSxDQUFDLHFDQUFxQztRQUN2RCxPQUFPLEVBQUUsZUFBZTtRQUN4QixJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxTQUFTLFNBQVMsQ0FBQztLQUNuRCxDQUFDO0lBRUYsTUFBTSxFQUFFLEdBQUcsUUFBUSxDQUFDLG9CQUFvQixDQUFDLEtBQUssRUFBRSxtQkFBbUIsQ0FBQyxDQUFDO0lBRXJFLE9BQU8sUUFBUSxDQUFDLHFCQUFxQixDQUFDLEtBQUssRUFBRSxFQUFFLEVBQUUsU0FBUyxDQUFDLENBQUM7QUFDOUQsQ0FBQztBQUVELFNBQVMsWUFBWSxDQUFDLEtBQVksRUFBRSxRQUFjLEVBQUUsa0JBQTJCLElBQUk7SUFDakYsTUFBTSxxQkFBcUIsR0FBRyxRQUFRLENBQUMsYUFBYSxDQUFDLEtBQUssRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLGVBQWUsQ0FBQyxDQUFDO0lBQ2xHLGlDQUFpQztJQUNqQyxNQUFNLGtCQUFrQixHQUFHLHFCQUFxQixDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLHNCQUFzQixDQUFDLENBQUM7SUFDOUYsb0NBQW9DO0lBQ3BDLE1BQU0scUJBQXFCLEdBQUcsSUFBSSxHQUFHLENBQUMsY0FBYyxDQUFDO1FBQ25ELE9BQU8sRUFBRSxLQUFLO1FBQ2QscUJBQXFCLEVBQUUsTUFBTTtRQUM3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