diff --git a/CHANGELOG.md b/CHANGELOG.md index 13af6d0..01867f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Fixed +- Merge PR #208, fixing invalid parameterized type resolution for properties containing nested aggregate types ## [1.9.1] - 2018-10-30 ### Changed diff --git a/data/sam_20161031_cfn.json b/data/sam_20161031_cfn.json index 51018cb..dfa37e3 100644 --- a/data/sam_20161031_cfn.json +++ b/data/sam_20161031_cfn.json @@ -1 +1 @@ -{"PropertyTypes":{"AWS::Serverless::Api.S3Location":{"Documentation":"","Properties":{"Bucket":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["S3Location#Bucket","S3Location#Bucket"]},"Key":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["S3Location#Key","S3Location#Key"]},"Version":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["S3Location#Version","S3Location#Version","S3Location#Version"]}}},"AWS::Serverless::Function.AlexaSkillEvent":{"Documentation":"","Properties":{"Variables":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["AlexaSkillEvent#Variables>","AlexaSkillEvent#Variables>"]}}},"AWS::Serverless::Function.ApiEvent":{"Documentation":"","Properties":{"Method":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["ApiEvent#Method","ApiEvent#Method"]},"Path":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["ApiEvent#Path","ApiEvent#Path"]},"RestApiId":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["ApiEvent#RestApiId","ApiEvent#RestApiId","ApiEvent#RestApiId"]}}},"AWS::Serverless::Function.CloudWatchEventEvent":{"Documentation":"","Properties":{"Input":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["CloudWatchEventEvent#Input","CloudWatchEventEvent#Input"]},"InputPath":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["CloudWatchEventEvent#InputPath","CloudWatchEventEvent#InputPath"]},"Pattern":{"Documentation":"","Required":true,"UpdateType":"Mutable","PrimitiveType":"Json"}}},"AWS::Serverless::Function.LogEvent":{"Documentation":"","Properties":{"LogGroupName":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["LogEvent#LogGroupName","LogEvent#LogGroupName"]},"FilterPattern":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["LogEvent#FilterPattern","LogEvent#FilterPattern"]}}},"AWS::Serverless::Function.DeadLetterQueue":{"Documentation":"","Properties":{"TargetArn":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["DeadLetterQueue#TargetArn","DeadLetterQueue#TargetArn"]},"Type":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["DeadLetterQueue#Type","DeadLetterQueue#Type"]}}},"AWS::Serverless::Function.DynamoDBEvent":{"Documentation":"","Properties":{"BatchSize":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["DynamoDBEvent#BatchSize","DynamoDBEvent#BatchSize","DynamoDBEvent#BatchSize"]},"StartingPosition":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["DynamoDBEvent#StartingPosition","DynamoDBEvent#StartingPosition"]},"Stream":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["DynamoDBEvent#Stream","DynamoDBEvent#Stream"]}}},"AWS::Serverless::Function.EventSource":{"Documentation":"","Properties":{"Properties":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["EventSource#Properties","EventSource#Properties","EventSource#Properties","EventSource#Properties","EventSource#Properties","EventSource#Properties","EventSource#Properties","EventSource#Properties","EventSource#Properties","EventSource#Properties","EventSource#Properties"]},"Type":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["EventSource#Type","EventSource#Type"]}}},"AWS::Serverless::Function.FunctionEnvironment":{"Documentation":"","Properties":{"Variables":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["FunctionEnvironment#Variables>","FunctionEnvironment#Variables>"]}}},"AWS::Serverless::Function.IAMPolicyDocument":{"Documentation":"","Properties":{"Statement":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":"List","PrimitiveItemType":"Json"}}},"AWS::Serverless::Function.IoTRuleEvent":{"Documentation":"","Properties":{"AwsIotSqlVersion":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["IoTRuleEvent#AwsIotSqlVersion","IoTRuleEvent#AwsIotSqlVersion"]},"Sql":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["IoTRuleEvent#Sql","IoTRuleEvent#Sql"]}}},"AWS::Serverless::Function.KinesisEvent":{"Documentation":"","Properties":{"BatchSize":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["KinesisEvent#BatchSize","KinesisEvent#BatchSize","KinesisEvent#BatchSize"]},"StartingPosition":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["KinesisEvent#StartingPosition","KinesisEvent#StartingPosition"]},"Stream":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["KinesisEvent#Stream","KinesisEvent#Stream"]}}},"AWS::Serverless::Function.SQSEvent":{"Documentation":"","Properties":{"BatchSize":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["SQSEvent#BatchSize","SQSEvent#BatchSize","SQSEvent#BatchSize"]},"Queue":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["SQSEvent#Queue","SQSEvent#Queue","SQSEvent#Queue"]}}},"AWS::Serverless::Function.S3Event":{"Documentation":"","Properties":{"Bucket":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["S3Event#Bucket","S3Event#Bucket","S3Event#Bucket"]},"Events":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["S3Event#Events","S3Event#Events","S3Event#Events>","S3Event#Events>"]},"Filter":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":"S3NotificationFilter"}}},"AWS::Serverless::Function.S3Location":{"Documentation":"","Properties":{"Bucket":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["S3Location#Bucket","S3Location#Bucket"]},"Key":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["S3Location#Key","S3Location#Key"]},"Version":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["S3Location#Version","S3Location#Version","S3Location#Version"]}}},"AWS::Serverless::Function.S3NotificationFilter":{"Documentation":"","Properties":{"S3Key":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["S3NotificationFilter#S3Key","S3NotificationFilter#S3Key","S3NotificationFilter#S3Key"]}}},"AWS::Serverless::Function.SNSEvent":{"Documentation":"","Properties":{"Topic":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["SNSEvent#Topic","SNSEvent#Topic"]}}},"AWS::Serverless::Function.ScheduleEvent":{"Documentation":"","Properties":{"Input":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["ScheduleEvent#Input","ScheduleEvent#Input"]},"Schedule":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["ScheduleEvent#Schedule","ScheduleEvent#Schedule"]}}},"AWS::Serverless::Function.VpcConfig":{"Documentation":"","Properties":{"SecurityGroupIds":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["VpcConfig#SecurityGroupIds>","VpcConfig#SecurityGroupIds>"]},"SubnetIds":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["VpcConfig#SubnetIds>","VpcConfig#SubnetIds>"]},"SubnetIdsUsingRef":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":"List","PrimitiveItemType":"Json"}}},"AWS::Serverless::SimpleTable.PrimaryKey":{"Documentation":"","Properties":{"Name":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["PrimaryKey#Name","PrimaryKey#Name"]},"Type":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["PrimaryKey#Type","PrimaryKey#Type"]}}},"AWS::Serverless::SimpleTable.ProvisionedThroughput":{"Documentation":"","Properties":{"ReadCapacityUnits":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["ProvisionedThroughput#ReadCapacityUnits","ProvisionedThroughput#ReadCapacityUnits","ProvisionedThroughput#ReadCapacityUnits"]},"WriteCapacityUnits":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["ProvisionedThroughput#WriteCapacityUnits","ProvisionedThroughput#WriteCapacityUnits","ProvisionedThroughput#WriteCapacityUnits"]}}},"AWS::Serverless::SimpleTable.SSESpecification":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-table-ssespecification.html","Properties":{"SSEEnabled":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-table-ssespecification.html#cfn-dynamodb-table-ssespecification-sseenabled","Required":true,"UpdateType":"Mutable","PrimitiveType":"Boolean"}}},"AWS::Serverless::Function.Hooks":{"Documentation":"https://github.com/awslabs/serverless-application-model/blob/develop/docs/safe_lambda_deployments.rst#pretraffic--posttraffic-hooks","Properties":{"PreTraffic":{"Required":false,"PrimitiveType":"String","UpdateType":"Mutable"},"PostTraffic":{"Required":false,"PrimitiveType":"String","UpdateType":"Mutable"}}},"AWS::Serverless::Function.DeploymentPreference":{"Documentation":"https://github.com/awslabs/serverless-application-model/blob/develop/versions/2016-10-31.md#deploymentpreference-object","Properties":{"Enabled":{"Required":false,"PrimitiveType":"Boolean","UpdateType":"Mutable"},"Type":{"Required":false,"PrimitiveType":"String","UpdateType":"Mutable"},"Alarms":{"Required":false,"Type":"List","PrimitiveItemType":"String","UpdateType":"Mutable"},"Hooks":{"Required":false,"Type":"Hooks","UpdateType":"Mutable"}}},"AWS::Serverless::Api.CorsConfiguration":{"Documentation":"https://github.com/awslabs/serverless-application-model/blob/develop/versions/2016-10-31.md#cors-configuration","Properties":{"AllowedHeaders":{"Required":false,"PrimitiveType":"String","UpdateType":"Mutable"},"AllowedMethods":{"Required":false,"PrimitiveType":"String","UpdateType":"Mutable"},"AllowedOrigin":{"Required":false,"PrimitiveType":"String","UpdateType":"Mutable"},"MaxAge":{"PrimitiveType":"Integer","Required":false,"UpdateType":"Mutable"}}},"AWS::Serverless::Api.MethodSetting":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html","Properties":{"CacheDataEncrypted":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html#cfn-apigateway-stage-methodsetting-cachedataencrypted","PrimitiveType":"Boolean","Required":false,"UpdateType":"Mutable"},"CacheTtlInSeconds":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html#cfn-apigateway-stage-methodsetting-cachettlinseconds","PrimitiveType":"Integer","Required":false,"UpdateType":"Mutable"},"CachingEnabled":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html#cfn-apigateway-stage-methodsetting-cachingenabled","PrimitiveType":"Boolean","Required":false,"UpdateType":"Mutable"},"DataTraceEnabled":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html#cfn-apigateway-stage-methodsetting-datatraceenabled","PrimitiveType":"Boolean","Required":false,"UpdateType":"Mutable"},"HttpMethod":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html#cfn-apigateway-stage-methodsetting-httpmethod","PrimitiveType":"String","Required":false,"UpdateType":"Mutable"},"LoggingLevel":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html#cfn-apigateway-stage-methodsetting-logginglevel","PrimitiveType":"String","Required":false,"UpdateType":"Mutable"},"MetricsEnabled":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html#cfn-apigateway-stage-methodsetting-metricsenabled","PrimitiveType":"Boolean","Required":false,"UpdateType":"Mutable"},"ResourcePath":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html#cfn-apigateway-stage-methodsetting-resourcepath","PrimitiveType":"String","Required":false,"UpdateType":"Mutable"},"ThrottlingBurstLimit":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html#cfn-apigateway-stage-methodsetting-throttlingburstlimit","PrimitiveType":"Integer","Required":false,"UpdateType":"Mutable"},"ThrottlingRateLimit":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html#cfn-apigateway-stage-methodsetting-throttlingratelimit","PrimitiveType":"Double","Required":false,"UpdateType":"Mutable"}}}},"ResourceTypes":{"AWS::Serverless::Api":{"Documentation":"https://github.com/awslabs/serverless-application-model/blob/develop/versions/2016-10-31.md#awsserverlessapi","AdditionalProperties":false,"Properties":{"CacheClusterEnabled":{"Documentation":"","Required":false,"UpdateType":"Mutable","PrimitiveType":"Boolean"},"CacheClusterSize":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#CacheClusterSize","#CacheClusterSize"]},"DefinitionBody":{"Documentation":"","Required":false,"UpdateType":"Mutable","PrimitiveType":"Json"},"DefinitionUri":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#DefinitionUri","#DefinitionUri","#DefinitionUri"]},"Name":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#Name","#Name"]},"StageName":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["#StageName","#StageName","#StageName"]},"Variables":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#Variables>","#Variables>","#Variables>"]},"BinaryMediaTypes":{"Required":false,"Type":"List","PrimitiveItemType":"String","UpdateType":"Mutable"},"Cors":{"Required":false,"Type":["#Cors","#Cors"],"UpdateType":"Mutable"},"EndpointConfiguration":{"Required":false,"PrimitiveType":"String","UpdateType":"Mutable"},"MethodSettings":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigateway-deployment-stagedescription.html#cfn-apigateway-deployment-stagedescription-methodsettings","DuplicatesAllowed":false,"Required":false,"Type":"MethodSetting","UpdateType":"Mutable"}}},"AWS::Serverless::Function":{"Documentation":"https://github.com/awslabs/serverless-application-model/blob/develop/versions/2016-10-31.md#awsserverlessfunction","AdditionalProperties":false,"Properties":{"InlineCode":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#InlineCode","#InlineCode"]},"CodeUri":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#CodeUri","#CodeUri","#CodeUri"]},"DeadLetterQueue":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":"DeadLetterQueue"},"Description":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#Description","#Description"]},"Environment":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":"FunctionEnvironment"},"Events":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":"Map","ItemType":"EventSource"},"FunctionName":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#FunctionName","#FunctionName"]},"Handler":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#Handler","#Handler"]},"KmsKeyArn":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#KmsKeyArn","#KmsKeyArn"]},"MemorySize":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#MemorySize","#MemorySize","#MemorySize"]},"Policies":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#Policies","#Policies","#Policies","#Policies>"]},"Role":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#Role","#Role","#Role"]},"Runtime":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#Runtime","#Runtime"]},"Tags":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#Tags>","#Tags>"]},"Timeout":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#Timeout","#Timeout","#Timeout"]},"Tracing":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#Tracing","#Tracing"]},"VpcConfig":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":"VpcConfig"},"AutoPublishAlias":{"Required":false,"PrimitiveType":"String","UpdateType":"Mutable"},"DeploymentPreference":{"Required":false,"Type":"DeploymentPreference","UpdateType":"Mutable"},"ReservedConcurrentExecutions":{"Required":false,"PrimitiveType":"Integer","UpdateType":"Mutable"}}},"AWS::Serverless::SimpleTable":{"Documentation":"https://github.com/awslabs/serverless-application-model/blob/develop/versions/2016-10-31.md#awsserverlesssimpletable","AdditionalProperties":false,"Properties":{"PrimaryKey":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":"PrimaryKey"},"ProvisionedThroughput":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":"ProvisionedThroughput"},"SSESpecification":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":"SSESpecification"},"Tags":{"DuplicatesAllowed":false,"Required":false,"Type":"Map","ItemType":"String","UpdateType":"Mutable"},"TableName":{"Required":false,"PrimitiveType":"String","UpdateType":"Mutable"}}}}} \ No newline at end of file +{"PropertyTypes":{"AWS::Serverless::Api.S3Location":{"Documentation":"","Properties":{"Bucket":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["S3Location#Bucket","S3Location#Bucket"]},"Key":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["S3Location#Key","S3Location#Key"]},"Version":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["S3Location#Version","S3Location#Version","S3Location#Version"]}}},"AWS::Serverless::Function.AlexaSkillEvent":{"Documentation":"","Properties":{"Variables":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["AlexaSkillEvent#Variables>","AlexaSkillEvent#Variables>"]}}},"AWS::Serverless::Function.ApiEvent":{"Documentation":"","Properties":{"Method":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["ApiEvent#Method","ApiEvent#Method"]},"Path":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["ApiEvent#Path","ApiEvent#Path"]},"RestApiId":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["ApiEvent#RestApiId","ApiEvent#RestApiId","ApiEvent#RestApiId"]}}},"AWS::Serverless::Function.CloudWatchEventEvent":{"Documentation":"","Properties":{"Input":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["CloudWatchEventEvent#Input","CloudWatchEventEvent#Input"]},"InputPath":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["CloudWatchEventEvent#InputPath","CloudWatchEventEvent#InputPath"]},"Pattern":{"Documentation":"","Required":true,"UpdateType":"Mutable","PrimitiveType":"Json"}}},"AWS::Serverless::Function.LogEvent":{"Documentation":"","Properties":{"LogGroupName":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["LogEvent#LogGroupName","LogEvent#LogGroupName"]},"FilterPattern":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["LogEvent#FilterPattern","LogEvent#FilterPattern"]}}},"AWS::Serverless::Function.DeadLetterQueue":{"Documentation":"","Properties":{"TargetArn":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["DeadLetterQueue#TargetArn","DeadLetterQueue#TargetArn"]},"Type":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["DeadLetterQueue#Type","DeadLetterQueue#Type"]}}},"AWS::Serverless::Function.DynamoDBEvent":{"Documentation":"","Properties":{"BatchSize":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["DynamoDBEvent#BatchSize","DynamoDBEvent#BatchSize","DynamoDBEvent#BatchSize"]},"StartingPosition":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["DynamoDBEvent#StartingPosition","DynamoDBEvent#StartingPosition"]},"Stream":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["DynamoDBEvent#Stream","DynamoDBEvent#Stream"]}}},"AWS::Serverless::Function.EventSource":{"Documentation":"","Properties":{"Properties":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["EventSource#Properties","EventSource#Properties","EventSource#Properties","EventSource#Properties","EventSource#Properties","EventSource#Properties","EventSource#Properties","EventSource#Properties","EventSource#Properties","EventSource#Properties","EventSource#Properties"]},"Type":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["EventSource#Type","EventSource#Type"]}}},"AWS::Serverless::Function.FunctionEnvironment":{"Documentation":"","Properties":{"Variables":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["FunctionEnvironment#Variables>","FunctionEnvironment#Variables>"]}}},"AWS::Serverless::Function.IAMPolicyDocument":{"Documentation":"","Properties":{"Statement":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":"List","PrimitiveItemType":"Json"},"Version":{"Required":false,"PrimitiveType":"String","UpdateType":"Mutable"}}},"AWS::Serverless::Function.IoTRuleEvent":{"Documentation":"","Properties":{"AwsIotSqlVersion":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["IoTRuleEvent#AwsIotSqlVersion","IoTRuleEvent#AwsIotSqlVersion"]},"Sql":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["IoTRuleEvent#Sql","IoTRuleEvent#Sql"]}}},"AWS::Serverless::Function.KinesisEvent":{"Documentation":"","Properties":{"BatchSize":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["KinesisEvent#BatchSize","KinesisEvent#BatchSize","KinesisEvent#BatchSize"]},"StartingPosition":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["KinesisEvent#StartingPosition","KinesisEvent#StartingPosition"]},"Stream":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["KinesisEvent#Stream","KinesisEvent#Stream"]}}},"AWS::Serverless::Function.SQSEvent":{"Documentation":"","Properties":{"BatchSize":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["SQSEvent#BatchSize","SQSEvent#BatchSize","SQSEvent#BatchSize"]},"Queue":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["SQSEvent#Queue","SQSEvent#Queue","SQSEvent#Queue"]}}},"AWS::Serverless::Function.S3Event":{"Documentation":"","Properties":{"Bucket":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["S3Event#Bucket","S3Event#Bucket","S3Event#Bucket"]},"Events":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["S3Event#Events","S3Event#Events","S3Event#Events>","S3Event#Events>"]},"Filter":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":"S3NotificationFilter"}}},"AWS::Serverless::Function.S3Location":{"Documentation":"","Properties":{"Bucket":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["S3Location#Bucket","S3Location#Bucket"]},"Key":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["S3Location#Key","S3Location#Key"]},"Version":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["S3Location#Version","S3Location#Version","S3Location#Version"]}}},"AWS::Serverless::Function.S3NotificationFilter":{"Documentation":"","Properties":{"S3Key":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["S3NotificationFilter#S3Key","S3NotificationFilter#S3Key","S3NotificationFilter#S3Key"]}}},"AWS::Serverless::Function.SNSEvent":{"Documentation":"","Properties":{"Topic":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["SNSEvent#Topic","SNSEvent#Topic"]}}},"AWS::Serverless::Function.ScheduleEvent":{"Documentation":"","Properties":{"Input":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["ScheduleEvent#Input","ScheduleEvent#Input"]},"Schedule":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["ScheduleEvent#Schedule","ScheduleEvent#Schedule"]}}},"AWS::Serverless::Function.VpcConfig":{"Documentation":"","Properties":{"SecurityGroupIds":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["VpcConfig#SecurityGroupIds>","VpcConfig#SecurityGroupIds>"]},"SubnetIds":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["VpcConfig#SubnetIds>","VpcConfig#SubnetIds>"]},"SubnetIdsUsingRef":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":"List","PrimitiveItemType":"Json"}}},"AWS::Serverless::SimpleTable.PrimaryKey":{"Documentation":"","Properties":{"Name":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["PrimaryKey#Name","PrimaryKey#Name"]},"Type":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["PrimaryKey#Type","PrimaryKey#Type"]}}},"AWS::Serverless::SimpleTable.ProvisionedThroughput":{"Documentation":"","Properties":{"ReadCapacityUnits":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["ProvisionedThroughput#ReadCapacityUnits","ProvisionedThroughput#ReadCapacityUnits","ProvisionedThroughput#ReadCapacityUnits"]},"WriteCapacityUnits":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["ProvisionedThroughput#WriteCapacityUnits","ProvisionedThroughput#WriteCapacityUnits","ProvisionedThroughput#WriteCapacityUnits"]}}},"AWS::Serverless::SimpleTable.SSESpecification":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-table-ssespecification.html","Properties":{"SSEEnabled":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-table-ssespecification.html#cfn-dynamodb-table-ssespecification-sseenabled","Required":true,"UpdateType":"Mutable","PrimitiveType":"Boolean"}}},"AWS::Serverless::Function.Hooks":{"Documentation":"https://github.com/awslabs/serverless-application-model/blob/develop/docs/safe_lambda_deployments.rst#pretraffic--posttraffic-hooks","Properties":{"PreTraffic":{"Required":false,"PrimitiveType":"String","UpdateType":"Mutable"},"PostTraffic":{"Required":false,"PrimitiveType":"String","UpdateType":"Mutable"}}},"AWS::Serverless::Function.DeploymentPreference":{"Documentation":"https://github.com/awslabs/serverless-application-model/blob/develop/versions/2016-10-31.md#deploymentpreference-object","Properties":{"Enabled":{"Required":false,"PrimitiveType":"Boolean","UpdateType":"Mutable"},"Type":{"Required":false,"PrimitiveType":"String","UpdateType":"Mutable"},"Alarms":{"Required":false,"Type":"List","PrimitiveItemType":"String","UpdateType":"Mutable"},"Hooks":{"Required":false,"Type":"Hooks","UpdateType":"Mutable"}}},"AWS::Serverless::Api.CorsConfiguration":{"Documentation":"https://github.com/awslabs/serverless-application-model/blob/develop/versions/2016-10-31.md#cors-configuration","Properties":{"AllowedHeaders":{"Required":false,"PrimitiveType":"String","UpdateType":"Mutable"},"AllowedMethods":{"Required":false,"PrimitiveType":"String","UpdateType":"Mutable"},"AllowedOrigin":{"Required":false,"PrimitiveType":"String","UpdateType":"Mutable"},"MaxAge":{"PrimitiveType":"Integer","Required":false,"UpdateType":"Mutable"}}},"AWS::Serverless::Api.MethodSetting":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html","Properties":{"CacheDataEncrypted":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html#cfn-apigateway-stage-methodsetting-cachedataencrypted","PrimitiveType":"Boolean","Required":false,"UpdateType":"Mutable"},"CacheTtlInSeconds":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html#cfn-apigateway-stage-methodsetting-cachettlinseconds","PrimitiveType":"Integer","Required":false,"UpdateType":"Mutable"},"CachingEnabled":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html#cfn-apigateway-stage-methodsetting-cachingenabled","PrimitiveType":"Boolean","Required":false,"UpdateType":"Mutable"},"DataTraceEnabled":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html#cfn-apigateway-stage-methodsetting-datatraceenabled","PrimitiveType":"Boolean","Required":false,"UpdateType":"Mutable"},"HttpMethod":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html#cfn-apigateway-stage-methodsetting-httpmethod","PrimitiveType":"String","Required":false,"UpdateType":"Mutable"},"LoggingLevel":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html#cfn-apigateway-stage-methodsetting-logginglevel","PrimitiveType":"String","Required":false,"UpdateType":"Mutable"},"MetricsEnabled":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html#cfn-apigateway-stage-methodsetting-metricsenabled","PrimitiveType":"Boolean","Required":false,"UpdateType":"Mutable"},"ResourcePath":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html#cfn-apigateway-stage-methodsetting-resourcepath","PrimitiveType":"String","Required":false,"UpdateType":"Mutable"},"ThrottlingBurstLimit":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html#cfn-apigateway-stage-methodsetting-throttlingburstlimit","PrimitiveType":"Integer","Required":false,"UpdateType":"Mutable"},"ThrottlingRateLimit":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apitgateway-stage-methodsetting.html#cfn-apigateway-stage-methodsetting-throttlingratelimit","PrimitiveType":"Double","Required":false,"UpdateType":"Mutable"}}}},"ResourceTypes":{"AWS::Serverless::Api":{"Documentation":"https://github.com/awslabs/serverless-application-model/blob/develop/versions/2016-10-31.md#awsserverlessapi","AdditionalProperties":false,"Properties":{"CacheClusterEnabled":{"Documentation":"","Required":false,"UpdateType":"Mutable","PrimitiveType":"Boolean"},"CacheClusterSize":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#CacheClusterSize","#CacheClusterSize"]},"DefinitionBody":{"Documentation":"","Required":false,"UpdateType":"Mutable","PrimitiveType":"Json"},"DefinitionUri":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#DefinitionUri","#DefinitionUri","#DefinitionUri"]},"Name":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#Name","#Name"]},"StageName":{"Documentation":"","Required":true,"UpdateType":"Mutable","Type":["#StageName","#StageName","#StageName"]},"Variables":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#Variables>","#Variables>","#Variables>"]},"BinaryMediaTypes":{"Required":false,"Type":"List","PrimitiveItemType":"String","UpdateType":"Mutable"},"Cors":{"Required":false,"Type":["#Cors","#Cors"],"UpdateType":"Mutable"},"EndpointConfiguration":{"Required":false,"PrimitiveType":"String","UpdateType":"Mutable"},"MethodSettings":{"Documentation":"http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigateway-deployment-stagedescription.html#cfn-apigateway-deployment-stagedescription-methodsettings","DuplicatesAllowed":false,"Required":false,"Type":"MethodSetting","UpdateType":"Mutable"}}},"AWS::Serverless::Function":{"Documentation":"https://github.com/awslabs/serverless-application-model/blob/develop/versions/2016-10-31.md#awsserverlessfunction","AdditionalProperties":false,"Properties":{"InlineCode":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#InlineCode","#InlineCode"]},"CodeUri":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#CodeUri","#CodeUri","#CodeUri"]},"DeadLetterQueue":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":"DeadLetterQueue"},"Description":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#Description","#Description"]},"Environment":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":"FunctionEnvironment"},"Events":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":"Map","ItemType":"EventSource"},"FunctionName":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#FunctionName","#FunctionName"]},"Handler":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#Handler","#Handler"]},"KmsKeyArn":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#KmsKeyArn","#KmsKeyArn"]},"MemorySize":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#MemorySize","#MemorySize","#MemorySize"]},"Policies":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#Policies","#Policies","#Policies","#Policies>"]},"Role":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#Role","#Role","#Role"]},"Runtime":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#Runtime","#Runtime"]},"Tags":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#Tags>","#Tags>"]},"Timeout":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#Timeout","#Timeout","#Timeout"]},"Tracing":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":["#Tracing","#Tracing"]},"VpcConfig":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":"VpcConfig"},"AutoPublishAlias":{"Required":false,"PrimitiveType":"String","UpdateType":"Mutable"},"DeploymentPreference":{"Required":false,"Type":"DeploymentPreference","UpdateType":"Mutable"},"ReservedConcurrentExecutions":{"Required":false,"PrimitiveType":"Integer","UpdateType":"Mutable"}}},"AWS::Serverless::SimpleTable":{"Documentation":"https://github.com/awslabs/serverless-application-model/blob/develop/versions/2016-10-31.md#awsserverlesssimpletable","AdditionalProperties":false,"Properties":{"PrimaryKey":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":"PrimaryKey"},"ProvisionedThroughput":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":"ProvisionedThroughput"},"SSESpecification":{"Documentation":"","Required":false,"UpdateType":"Mutable","Type":"SSESpecification"},"Tags":{"DuplicatesAllowed":false,"Required":false,"Type":"Map","ItemType":"String","UpdateType":"Mutable"},"TableName":{"Required":false,"PrimitiveType":"String","UpdateType":"Mutable"}}}}} \ No newline at end of file diff --git a/data/sam_20161031_custom_specification.json b/data/sam_20161031_custom_specification.json index 3a9a2d9..71fd0c6 100644 --- a/data/sam_20161031_custom_specification.json +++ b/data/sam_20161031_custom_specification.json @@ -1,5 +1,15 @@ { "PropertyTypes": { + "AWS::Serverless::Function.IAMPolicyDocument": { + "Documentation": "", + "Properties": { + "Version": { + "Required": false, + "PrimitiveType": "String", + "UpdateType": "Mutable" + } + } + }, "AWS::Serverless::Function.EventSource": { "Properties": { "Properties": { diff --git a/src/awsData.ts b/src/awsData.ts index 7859c54..fc1e313 100644 --- a/src/awsData.ts +++ b/src/awsData.ts @@ -4,7 +4,7 @@ export const awsExtraDocs = require('../data/aws_extra_docs.json') as AWSExtraDo export const awsPrimitiveTypes = ['String', 'Integer', 'Boolean', 'Json', 'Double', 'Long', 'Timestamp']; -export const awsComplexTypes = ['Map', 'List']; +export const awsAggregateTypes = ['Map', 'List']; export type AWSPrimitiveType = 'String' | 'Integer' | 'Boolean' | 'Json' | 'Double' | 'Long' | 'Timestamp'; diff --git a/src/resourcesSpec.ts b/src/resourcesSpec.ts index d7074e9..9f72fd3 100644 --- a/src/resourcesSpec.ts +++ b/src/resourcesSpec.ts @@ -284,7 +284,7 @@ export function rebaseTypeFormat(baseType: string, type: string): string { return parameterizeTypeFormat(typeName, typeArgument); } - if (isPrimitiveType(type) || isComplexType(type)) { + if (isPrimitiveType(type) || isAggregateType(type)) { return type; } @@ -301,11 +301,11 @@ export function isPrimitiveType(type: string) { return false; } -export function isComplexType(type: string) { +export function isAggregateType(type: string) { if (isParameterizedTypeFormat(type)) { type = getParameterizedTypeName(type); } - if (!!~awsData.awsComplexTypes.indexOf(type)) { + if (!!~awsData.awsAggregateTypes.indexOf(type)) { return true; } return false; @@ -445,7 +445,7 @@ export function getItemType(baseType: string, propType: string, key: string) { const property = getProperty(propType, key); if (!property.ItemType) { return undefined; - } else if (isComplexType(property.ItemType)) { + } else if (isAggregateType(property.ItemType)) { return property.ItemType; } else { return baseType + '.' + property.ItemType; diff --git a/src/test/validatorTest.ts b/src/test/validatorTest.ts index 4ce2b10..fdd87fd 100644 --- a/src/test/validatorTest.ts +++ b/src/test/validatorTest.ts @@ -9,6 +9,8 @@ import {awsResources} from '../awsData'; import util = require('util'); import { isObject } from 'util'; +const clone = require('clone'); + describe('validator', () => { before(() => { @@ -915,6 +917,13 @@ describe('validator', () => { expect(result).to.have.deep.property('templateValid', true); expect(result['errors']['crit']).to.have.lengthOf(0); }); + + it('a template with properties containing nested aggregate types should be validated successfully', () => { + const input = 'testData/valid/yaml/sam_20161031_issue_207.yaml'; + let result = validator.validateFile(input); + expect(result).to.have.deep.property('templateValid', true); + expect(result['errors']['crit']).to.have.lengthOf(0); + }); }); describe('parameters-validation', () => { @@ -1472,6 +1481,30 @@ describe('validator', () => { expect(result).to.equal('Timestamp'); }); + it('should be nullipotent upon input data - typeof number', () => { + let input = 1; + let result = validator.__TESTING__.inferPrimitiveValueType(input); + expect(input).to.equal(1); + }); + + it('should be nullipotent upon input data - typeof string', () => { + let input = 'someString'; + let result = validator.__TESTING__.inferPrimitiveValueType(input); + expect(input).to.equal('someString'); + }); + + it('should be nullipotent upon input data - typeof boolean', () => { + let input = true; + let result = validator.__TESTING__.inferPrimitiveValueType(input); + expect(input).to.equal(true); + }); + + it('should be nullipotent upon input data - typeof object', () => { + let input = {someKey: 'someValue'}; + let result = validator.__TESTING__.inferPrimitiveValueType(input); + expect(input).to.deep.equal({someKey: 'someValue'}); + }); + }); describe('inferStructureValueType', () => { @@ -1514,6 +1547,26 @@ describe('validator', () => { expect(result).to.equal(null); }); + it('should be nullipotent upon input data', () => { + let input = { + 'Type': 'AWS::Serverless::Function', + 'Properties': { + 'Variables': { + 'someKey': 'someValue' + }, + } + }; + let result = validator.__TESTING__.inferPrimitiveValueType(input); + expect(input).to.deep.equal({ + 'Type': 'AWS::Serverless::Function', + 'Properties': { + 'Variables': { + 'someKey': 'someValue' + }, + } + }); + }); + }); describe('inferAggregateValueType', () => { @@ -1527,6 +1580,15 @@ describe('validator', () => { expect(result).to.equal('List'); }); + it('should be able to infer a List of Json aggregate value type', () => { + let input = [ + { 1: 'somethingBeautiful' }, { 2: 'somethingAwesome' }, { 3: 'somethingCool' } + ]; + let candidateTypes: string[] = []; + let result = validator.__TESTING__.inferAggregateValueType(input, candidateTypes); + expect(result).to.equal('List'); + }); + it('should be able to infer a Map of Strings aggregate value type', () => { let input = { 1: 'somethingBeautiful', @@ -1538,6 +1600,17 @@ describe('validator', () => { expect(result).to.equal('Map'); }); + it('should be able to infer a Map of Json aggregate value type', () => { + let input = { + 1: { 1: 'somethingBeautiful' }, + 2: { 2: 'somethingAwesome' }, + 3: { 3: 'somethingCool' } + }; + let candidateTypes: string[] = []; + let result = validator.__TESTING__.inferAggregateValueType(input, candidateTypes); + expect(result).to.equal('Map'); + }); + it('should not be able to infer a non-aggregate value type', () => { let input = {}; let candidateTypes: string[] = []; @@ -1545,6 +1618,58 @@ describe('validator', () => { expect(result).to.equal(null); }); + it('should be nullipotent upon input data - List of Strings aggregate value type', () => { + let input = [ + 'somethingBeautiful', 'somethingAwesome', 'somethingCool' + ]; + let candidateTypes: string[] = []; + let result = validator.__TESTING__.inferAggregateValueType(input, candidateTypes); + expect(input).to.deep.equal([ + 'somethingBeautiful', 'somethingAwesome', 'somethingCool' + ]); + }); + + it('should be nullipotent upon input data - List of Json aggregate value type', () => { + let input = [ + { 1: 'somethingBeautiful' }, { 2: 'somethingAwesome' }, { 3: 'somethingCool' } + ]; + let candidateTypes: string[] = []; + let result = validator.__TESTING__.inferAggregateValueType(input, candidateTypes); + expect(input).to.deep.equal([ + { 1: 'somethingBeautiful' }, { 2: 'somethingAwesome' }, { 3: 'somethingCool' } + ]); + }); + + it('should be nullipotent upon input data - Map of Strings aggregate value type', () => { + let input = { + 1: 'somethingBeautiful', + 2: 'somethingAwesome', + 3: 'somethingCool' + }; + let candidateTypes: string[] = []; + let result = validator.__TESTING__.inferAggregateValueType(input, candidateTypes); + expect(input).to.deep.equal({ + 1: 'somethingBeautiful', + 2: 'somethingAwesome', + 3: 'somethingCool' + }); + }); + + it('should be nullipotent upon input data - Map of Json aggregate value type', () => { + let input = { + 1: { 1: 'somethingBeautiful' }, + 2: { 2: 'somethingAwesome' }, + 3: { 3: 'somethingCool' } + }; + let candidateTypes: string[] = []; + let result = validator.__TESTING__.inferAggregateValueType(input, candidateTypes); + expect(input).to.deep.equal({ + 1: { 1: 'somethingBeautiful' }, + 2: { 2: 'somethingAwesome' }, + 3: { 3: 'somethingCool' } + }); + }); + }); describe('inferValueType', () => { @@ -1579,6 +1704,49 @@ describe('validator', () => { }); + describe('templateHasUnresolvedIntrinsics', () => { + + it('should be able to detect if a given template has top-level unresolved intrinsics', () => { + const input = { + 'Ref': 'AWS::Region' + }; + const result = validator.__TESTING__.templateHasUnresolvedIntrinsics(clone(input)); + expect(result).to.equal(true); + expect(input).to.deep.equal(input); + }); + + it('should be able to detect if a given template has nested unresolved intrinsics', () => { + const input = { + 'Resources': { + 'MyBucket': { + 'Type': 'AWS::S3::Bucket', + 'BucketName': { + 'Ref': 'AWS::Region' + } + } + } + }; + const result = validator.__TESTING__.templateHasUnresolvedIntrinsics(clone(input)); + expect(result).to.equal(true); + expect(input).to.deep.equal(input); + }); + + it('should be able to detect if a given template does not contain unresolved intrinsics', () => { + const input = { + 'Resources': { + 'MyBucket': { + 'Type': 'AWS::S3::Bucket', + 'BucketName': 'somethingCool' + } + } + }; + const result = validator.__TESTING__.templateHasUnresolvedIntrinsics(clone(input)); + expect(result).to.equal(false); + expect(input).to.deep.equal(input); + }); + + }); + describe('SAM-20161031', function() { this.timeout(5000); diff --git a/src/validator.ts b/src/validator.ts index 72493ff..2171685 100644 --- a/src/validator.ts +++ b/src/validator.ts @@ -69,6 +69,8 @@ let errorObject: ErrorObject = { export function resetValidator(){ errorObject = {"templateValid": true, "errors": {"info": [], "warn": [], "crit": []}, outputs: {}, exports: {}}; + workingInput = null; + workingInputTransform = []; stopValidation = false; parameterRuntimeOverride = {}; importRuntimeOverride = {}; @@ -382,18 +384,18 @@ function inferAggregateValueType(v: any, candidateItemTypes: string[]): string | if (isList(v)) { type = 'List'; - item = v.pop(); + item = v[0]; } else if (isObject(v)) { type = 'Map'; - item = v[Object.keys(v).pop()!]; + item = v[Object.keys(v)[0]!]; } if (!!item) { itemType = inferValueType(item, candidateItemTypes); // aggregate type nesting is not currently supported - if (!!itemType && resourcesSpec.isComplexType(itemType)) { - return 'Json'; + if (!!itemType && resourcesSpec.isAggregateType(itemType)) { + itemType = 'Json'; } } @@ -404,14 +406,14 @@ function inferValueType(v: any, candidateTypes: string[]) { candidateTypes = candidateTypes.filter((x) => !resourcesSpec.isPrimitiveType(x)); let candidateStructureTypes: string[] = candidateTypes.filter((x) => { if (resourcesSpec.isParameterizedTypeFormat(x) && - resourcesSpec.isComplexType(resourcesSpec.getParameterizedTypeName(x))) { + resourcesSpec.isAggregateType(resourcesSpec.getParameterizedTypeName(x))) { return false; } return true; }); let candidateAggregateTypes: string[] = candidateTypes.filter((x) => { if (resourcesSpec.isParameterizedTypeFormat(x) && - resourcesSpec.isComplexType(resourcesSpec.getParameterizedTypeName(x))) { + resourcesSpec.isAggregateType(resourcesSpec.getParameterizedTypeName(x))) { return true; } return false; @@ -438,21 +440,36 @@ function formatCandidateTypes(baseType: string, candidateTypes: string[]) { return candidateTypes; } +function templateHasUnresolvedIntrinsics(template: any): boolean { + if (typeof template == 'object') { + const templateKeys = Object.keys(template); + const awsIntrinsicFunctionNames = Object.keys(awsIntrinsicFunctions); + for (const templateKey of templateKeys) { + const nestedTemplate: any = template[templateKey]; + if (!!~awsIntrinsicFunctionNames.indexOf(templateKey) || templateHasUnresolvedIntrinsics(nestedTemplate)) { + return true; + } + } + } + return false; +} + function applySAMPropertyOverrides(baseType: string, baseName: string, type: string, name: string, spec: awsData.Property, template: any) { // early exit for unsupported template or base type if (!~workingInputTransform.indexOf('AWS::Serverless-2016-10-31') || !~baseType.indexOf('AWS::Serverless')) { return; } - let localizedType = baseType == type ? type : `${baseType}.${type}<${name}>`; + const localizedType = (baseType == type) ? `${baseType}<${baseName}>` : `${baseType}.${type}<${baseName}>`; // specialize property based on inferred Type - let specType: any = spec['Type']; + const specType: any = spec['Type']; if (Array.isArray(specType)) { // skip if template has unresolved intrinsic functions - if (arrayIntersection(Object.keys(awsIntrinsicFunctions), Object.keys(template)).length > 0) { + if (templateHasUnresolvedIntrinsics(template)) { return; } + let candidateTypes = formatCandidateTypes(baseType, specType); // infer property spec based on value in template @@ -654,14 +671,14 @@ function applyTemplateTypeOverrides(baseType: string, type: string, name: string doSAMTransform(baseType, type, name, template, parentTemplate); // early exit for invalid type - let localizedType = baseType == type ? type : `${baseType}.${type}<${name}>`; + const localizedType = (baseType == type) ? `${baseType}<${name}>` : `${baseType}.${type}<${name}>`; if (!resourcesSpec.hasType(localizedType)) { return; } // initialize specification override - let originalSpec = resourcesSpec.getType(localizedType); - let overrideSpec = clone(originalSpec); + const originalSpec = resourcesSpec.getType(localizedType); + const overrideSpec = clone(originalSpec); // determine properties section let templateProperties = template; @@ -671,28 +688,28 @@ function applyTemplateTypeOverrides(baseType: string, type: string, name: string // apply property overrides if (!!templateProperties && (typeof templateProperties == 'object')) { - for (let propertyName of Object.keys(templateProperties)) { + for (const propertyName of Object.keys(templateProperties)) { // acquire property template - let templateProperty = templateProperties[propertyName]; + const templateProperty = templateProperties[propertyName]; if (!templateProperty) { continue; } // process property - let specProperty = overrideSpec['Properties'][propertyName] as awsData.Property; + const specProperty = overrideSpec['Properties'][propertyName] as awsData.Property; if (!!specProperty) { applyTemplatePropertyOverrides(baseType, name, type, propertyName, specProperty, templateProperty, template); // descend into nested types if (specProperty.hasOwnProperty('ItemType')) { - let subType = specProperty['ItemType'] as string; - for (let subKey of Object.keys(templateProperty)) { - let subTemplate = templateProperty[subKey]; + const subType = specProperty['ItemType'] as string; + for (const subKey of Object.keys(templateProperty)) { + const subTemplate = templateProperty[subKey]; applyTemplateTypeOverrides(baseType, subType, `${name}#${propertyName}#${subKey}`, subTemplate, template); } } else if (specProperty.hasOwnProperty('Type')) { - let subType = specProperty['Type'] as string; + const subType = specProperty['Type'] as string; applyTemplateTypeOverrides(baseType, subType, `${name}#${propertyName}`, templateProperty, template); } } @@ -2285,7 +2302,7 @@ function localizeType(type: string) { // don't localize primitive, complex or already localized types let localType = type; if (!resourcesSpec.isPrimitiveType(type) && - !resourcesSpec.isComplexType(type) && + !resourcesSpec.isAggregateType(type) && !resourcesSpec.isParameterizedTypeFormat(type)) { localType = `${type}<${typePlaceInTemplate}>`; } @@ -2365,5 +2382,6 @@ export const __TESTING__: any = { 'inferPrimitiveValueType': inferPrimitiveValueType, 'inferStructureValueType': inferStructureValueType, 'inferAggregateValueType': inferAggregateValueType, - 'inferValueType': inferValueType + 'inferValueType': inferValueType, + 'templateHasUnresolvedIntrinsics': templateHasUnresolvedIntrinsics }; diff --git a/testData/valid/yaml/sam_20161031_issue_207.yaml b/testData/valid/yaml/sam_20161031_issue_207.yaml new file mode 100644 index 0000000..3bdb51c --- /dev/null +++ b/testData/valid/yaml/sam_20161031_issue_207.yaml @@ -0,0 +1,18 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 + +Resources: + somethingBeautiful: + Type: AWS::Serverless::Function + Properties: + CodeUri: somethingCool + Handler: somethingAwesome + Policies: + - + Statement: + - + Effect: "Allow" + Action: "*" + Resource: "*" + Runtime: someRuntime + Timeout: 300