diff --git a/packages/@aws-cdk/aws-sam/test/api.test.ts b/packages/@aws-cdk/aws-sam/test/api.test.ts new file mode 100644 index 0000000000000..bed8a90ae4e57 --- /dev/null +++ b/packages/@aws-cdk/aws-sam/test/api.test.ts @@ -0,0 +1,44 @@ +import '@aws-cdk/assert-internal/jest'; +import * as cdk from '@aws-cdk/core'; +import * as sam from '../lib'; + +describe('AWS::Serverless::Api', () => { + let stack: cdk.Stack; + beforeEach(() => { + stack = new cdk.Stack(); + }); + + test('can be created by passing a complex type to EndpointConfiguration', () => { + new sam.CfnApi(stack, 'Api', { + stageName: 'prod', + definitionBody: { + body: 'definitionBody', + }, + endpointConfiguration: { + type: 'GLOBAL', + }, + }); + + expect(stack).toHaveResourceLike('AWS::Serverless::Api', { + StageName: 'prod', + EndpointConfiguration: { + Type: 'GLOBAL', + }, + }); + }); + + test('can be created by passing a string to EndpointConfiguration', () => { + new sam.CfnApi(stack, 'Api', { + stageName: 'prod', + definitionBody: { + body: 'definitionBody', + }, + endpointConfiguration: 'GLOBAL', + }); + + expect(stack).toHaveResourceLike('AWS::Serverless::Api', { + StageName: 'prod', + EndpointConfiguration: 'GLOBAL', + }); + }); +}); diff --git a/packages/@aws-cdk/cfnspec/spec-source/901_SAM_Api_EndpointConfiguration_patch.json b/packages/@aws-cdk/cfnspec/spec-source/901_SAM_Api_EndpointConfiguration_patch.json new file mode 100644 index 0000000000000..906dbbb88466c --- /dev/null +++ b/packages/@aws-cdk/cfnspec/spec-source/901_SAM_Api_EndpointConfiguration_patch.json @@ -0,0 +1,29 @@ +{ + "ResourceTypes": { + "AWS::Serverless::Api": { + "Properties": { + "EndpointConfiguration": { + "patch": { + "description": "Make the EndpointConfiguration property of AWS::Serverless::Api have a union type", + "operations": [ + { + "op": "add", + "path": "/PrimitiveTypes", + "value": ["String"] + }, + { + "op": "add", + "path": "/Types", + "value": ["EndpointConfiguration"] + }, + { + "op": "remove", + "path": "/Type" + } + ] + } + } + } + } + } +} diff --git a/packages/@aws-cdk/cloudformation-include/test/serverless-transform.test.ts b/packages/@aws-cdk/cloudformation-include/test/serverless-transform.test.ts index 853869ec1dd24..ce0d044bb4ed3 100644 --- a/packages/@aws-cdk/cloudformation-include/test/serverless-transform.test.ts +++ b/packages/@aws-cdk/cloudformation-include/test/serverless-transform.test.ts @@ -54,6 +54,30 @@ describe('CDK Include for templates with SAM transform', () => { loadTestFileToJsObject('only-sam-function-policies-array-ddb-crud-if.yaml'), ); }); + + test('can ingest a template with a a union-type property provided as an object, and output it unchanged', () => { + includeTestTemplate(stack, 'api-endpoint-config-object.yaml'); + + expect(stack).toMatchTemplate( + loadTestFileToJsObject('api-endpoint-config-object.yaml'), + ); + }); + + test('can ingest a template with a a union-type property provided as a string, and output it unchanged', () => { + includeTestTemplate(stack, 'api-endpoint-config-string.yaml'); + + expect(stack).toMatchTemplate( + loadTestFileToJsObject('api-endpoint-config-string.yaml'), + ); + }); + + test('can ingest a template with a a union-type property provided as an empty string, and output it unchanged', () => { + includeTestTemplate(stack, 'api-endpoint-config-string-empty.yaml'); + + expect(stack).toMatchTemplate( + loadTestFileToJsObject('api-endpoint-config-string-empty.yaml'), + ); + }); }); function includeTestTemplate(scope: constructs.Construct, testTemplate: string): inc.CfnInclude { diff --git a/packages/@aws-cdk/cloudformation-include/test/test-templates/sam/api-endpoint-config-object.yaml b/packages/@aws-cdk/cloudformation-include/test/test-templates/sam/api-endpoint-config-object.yaml new file mode 100644 index 0000000000000..77be57c23823c --- /dev/null +++ b/packages/@aws-cdk/cloudformation-include/test/test-templates/sam/api-endpoint-config-object.yaml @@ -0,0 +1,11 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Resources: + Api: + Type: AWS::Serverless::Api + Properties: + StageName: prod + DefinitionBody: + Body: DefinitionBody + EndpointConfiguration: + Type: GLOBAL diff --git a/packages/@aws-cdk/cloudformation-include/test/test-templates/sam/api-endpoint-config-string-empty.yaml b/packages/@aws-cdk/cloudformation-include/test/test-templates/sam/api-endpoint-config-string-empty.yaml new file mode 100644 index 0000000000000..1898e5105a864 --- /dev/null +++ b/packages/@aws-cdk/cloudformation-include/test/test-templates/sam/api-endpoint-config-string-empty.yaml @@ -0,0 +1,10 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Resources: + Api: + Type: AWS::Serverless::Api + Properties: + StageName: prod + DefinitionBody: + Body: DefinitionBody + EndpointConfiguration: '' # empty string diff --git a/packages/@aws-cdk/cloudformation-include/test/test-templates/sam/api-endpoint-config-string.yaml b/packages/@aws-cdk/cloudformation-include/test/test-templates/sam/api-endpoint-config-string.yaml new file mode 100644 index 0000000000000..a12949dabd226 --- /dev/null +++ b/packages/@aws-cdk/cloudformation-include/test/test-templates/sam/api-endpoint-config-string.yaml @@ -0,0 +1,10 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Resources: + Api: + Type: AWS::Serverless::Api + Properties: + StageName: prod + DefinitionBody: + Body: DefinitionBody + EndpointConfiguration: GLOBAL diff --git a/tools/cfn2ts/lib/codegen.ts b/tools/cfn2ts/lib/codegen.ts index f952bb34a832e..6a02f08b1e253 100644 --- a/tools/cfn2ts/lib/codegen.ts +++ b/tools/cfn2ts/lib/codegen.ts @@ -585,9 +585,17 @@ export default class CodeGenerator { this.code.closeBlock(); } - this.code.line('properties = properties || {};'); - this.code.line(`const ret = new ${CFN_PARSE}.FromCloudFormationPropertyObject<${typeName.fqn}>();`); + this.code.line('properties = properties == null ? {} : properties;'); + // if the passed value is not an object, immediately return it, + // and let a validator report an error - + // otherwise, we'll just return an empty object for this case, + // which a validator might not catch + // (if the interface we're emitting this function for has no required properties, for example) + this.code.openBlock("if (typeof properties !== 'object')"); + this.code.line(`return new ${CFN_PARSE}.FromCloudFormationResult(properties);`); + this.code.closeBlock(); + this.code.line(`const ret = new ${CFN_PARSE}.FromCloudFormationPropertyObject<${typeName.fqn}>();`); const self = this; // class used for the visitor class FromCloudFormationFactoryVisitor implements genspec.PropertyVisitor {