From cef95dcb39300a6a143e68638bee50eb1f425847 Mon Sep 17 00:00:00 2001 From: Vikranth Srivatsa Date: Thu, 15 Aug 2019 16:49:13 -0700 Subject: [PATCH 1/8] Fix and Update Template Resolution Tests --- .../commands/local/lib/sam_base_provider.py | 3 +- .../intrinsic_property_resolver.py | 21 +- .../intrinsics_symbol_table.py | 41 +- .../local/lib/test_sam_api_provider.py | 1 + .../test_intrinsic_template_resolution.json | 53 +-- .../inputs/test_layers_resolution.json | 174 +++++++ .../test_methods_resource_resolution.json | 449 ++++++++++++++++++ ...ut_test_intrinsic_template_resolution.json | 101 ++++ .../outputs_methods_resource_resolution.json | 223 +++++++++ .../outputs_test_layers_resolution.json | 169 +++++++ .../test_intrinsic_resolver.py | 212 ++++++++- 11 files changed, 1383 insertions(+), 64 deletions(-) rename tests/unit/lib/intrinsic_resolver/test_data/{ => inputs}/test_intrinsic_template_resolution.json (74%) create mode 100644 tests/unit/lib/intrinsic_resolver/test_data/inputs/test_layers_resolution.json create mode 100644 tests/unit/lib/intrinsic_resolver/test_data/inputs/test_methods_resource_resolution.json create mode 100644 tests/unit/lib/intrinsic_resolver/test_data/outputs/output_test_intrinsic_template_resolution.json create mode 100644 tests/unit/lib/intrinsic_resolver/test_data/outputs/outputs_methods_resource_resolution.json create mode 100644 tests/unit/lib/intrinsic_resolver/test_data/outputs/outputs_test_layers_resolution.json diff --git a/samcli/commands/local/lib/sam_base_provider.py b/samcli/commands/local/lib/sam_base_provider.py index d9f279acc6..897fa06c56 100644 --- a/samcli/commands/local/lib/sam_base_provider.py +++ b/samcli/commands/local/lib/sam_base_provider.py @@ -36,7 +36,6 @@ def get_template(template_dict, parameter_overrides=None): dict Processed SAM template """ - template_dict = template_dict or {} if template_dict: template_dict = SamTranslatorWrapper(template_dict).run_plugins() @@ -44,6 +43,7 @@ def get_template(template_dict, parameter_overrides=None): logical_id_translator = SamBaseProvider._get_parameter_values( template_dict, parameter_overrides ) + resolver = IntrinsicResolver( template=template_dict, symbol_resolver=IntrinsicsSymbolTable( @@ -51,7 +51,6 @@ def get_template(template_dict, parameter_overrides=None): ), ) template_dict = resolver.resolve_template(ignore_errors=True) - return template_dict @staticmethod diff --git a/samcli/lib/intrinsic_resolver/intrinsic_property_resolver.py b/samcli/lib/intrinsic_resolver/intrinsic_property_resolver.py index db30bdd3d5..ce438dfe25 100644 --- a/samcli/lib/intrinsic_resolver/intrinsic_property_resolver.py +++ b/samcli/lib/intrinsic_resolver/intrinsic_property_resolver.py @@ -201,14 +201,16 @@ def intrinsic_property_resolver(self, intrinsic, parent_function="template"): if ( any( isinstance(intrinsic, object_type) - for object_type in [string_types, list, bool, int] + for object_type in [string_types, bool, int] ) or intrinsic == {} ): return intrinsic - + if isinstance(intrinsic, list): + return [self.intrinsic_property_resolver(item) for item in intrinsic] + keys = list(intrinsic.keys()) key = keys[0] - + if key in self.intrinsic_key_function_map: intrinsic_value = intrinsic.get(key) return self.intrinsic_key_function_map.get(key)(intrinsic_value) @@ -238,12 +240,13 @@ def resolve_template(self, ignore_errors=False): ------- Return a processed template """ - processed_template = OrderedDict() - processed_template["Resources"] = self.resolve_attribute(self._resources, ignore_errors) - processed_template["Outputs"] = self.resolve_attribute(self._outputs, ignore_errors) - processed_template["Mappings"] = self.resolve_attribute(self._resources, ignore_errors) - processed_template["Parameters"] = self.resolve_attribute(self._resources, ignore_errors) - processed_template["Conditions"] = self.resolve_attribute(self._resources, ignore_errors) + processed_template = self._template + + if self._resources: + processed_template["Resources"] = self.resolve_attribute(self._resources, ignore_errors) + if self._outputs: + processed_template["Outputs"] = self.resolve_attribute(self._outputs, ignore_errors) + return processed_template def resolve_attribute(self, cloud_formation_property, ignore_errors=False): diff --git a/samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py b/samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py index 081cf3bb17..d385a91812 100644 --- a/samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py +++ b/samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py @@ -40,7 +40,7 @@ class IntrinsicsSymbolTable(object): "AWS::Region": "us-east-1", "AWS::StackName": "local", "AWS::StackId": "arn:aws:cloudformation:us-east-1:123456789012:stack/" - "local/51af3dc0-da77-11e4-872e-1234567db123", + "local/51af3dc0-da77-11e4-872e-1234567db123", "AWS::URLSuffix": "localhost", } @@ -88,11 +88,11 @@ class IntrinsicsSymbolTable(object): CFN_RESOURCE_TYPE = "Type" def __init__( - self, - template=None, - logical_id_translator=None, - default_type_resolver=None, - common_attribute_resolver=None, + self, + template=None, + logical_id_translator=None, + default_type_resolver=None, + common_attribute_resolver=None, ): """ Initializes the Intrinsic Symbol Table so that runtime attributes can be resolved. @@ -148,10 +148,10 @@ def __init__( self._resources = self._template.get("Resources", {}) self.default_type_resolver = ( - default_type_resolver or self.get_default_type_resolver() + default_type_resolver or self.get_default_type_resolver() ) self.common_attribute_resolver = ( - common_attribute_resolver or self.get_default_attribute_resolver() + common_attribute_resolver or self.get_default_attribute_resolver() ) self.default_pseudo_resolver = self.get_default_pseudo_resolver() @@ -239,6 +239,10 @@ def resolve_symbols(self, logical_id, resource_attribute, ignore_errors=False): if callable(attribute_resolver): return attribute_resolver(logical_id) return attribute_resolver + # + # # Handle the Case that the code is a ref of top level resource + # if resource_attribute == IntrinsicResolver.REF and logical_id in self._resources: + # return logical_id if ignore_errors: return "${}".format(logical_id + "." + resource_attribute) @@ -267,7 +271,8 @@ def arn_resolver(self, logical_id, service_name="lambda"): """ aws_region = self.handle_pseudo_region() account_id = ( - self.logical_id_translator.get(IntrinsicsSymbolTable.AWS_ACCOUNT_ID) or self.handle_pseudo_account_id() + self.logical_id_translator.get(IntrinsicsSymbolTable.AWS_ACCOUNT_ID) + or self.handle_pseudo_account_id() ) partition_name = self.handle_pseudo_partition() resource_name = logical_id @@ -301,12 +306,17 @@ def get_translation(self, logical_id, resource_attributes=IntrinsicResolver.REF) """ logical_id_item = self.logical_id_translator.get(logical_id, {}) - if any(isinstance(logical_id_item, object_type) for object_type in [string_types, list, bool, int]): + if any( + isinstance(logical_id_item, object_type) + for object_type in [string_types, list, bool, int] + ): if ( - resource_attributes != IntrinsicResolver.REF and resource_attributes != "" + resource_attributes != IntrinsicResolver.REF + and resource_attributes != "" ): return None return logical_id_item + return logical_id_item.get(resource_attributes) @staticmethod @@ -348,10 +358,11 @@ def handle_pseudo_region(self): The region from the environment or a default one """ return ( - self.logical_id_translator.get(IntrinsicsSymbolTable.AWS_REGION) or os.getenv("AWS_REGION") or - IntrinsicsSymbolTable.DEFAULT_PSEUDO_PARAM_VALUES.get( - IntrinsicsSymbolTable.AWS_REGION - ) + self.logical_id_translator.get(IntrinsicsSymbolTable.AWS_REGION) + or os.getenv("AWS_REGION") + or IntrinsicsSymbolTable.DEFAULT_PSEUDO_PARAM_VALUES.get( + IntrinsicsSymbolTable.AWS_REGION + ) ) def handle_pseudo_url_prefix(self): diff --git a/tests/unit/commands/local/lib/test_sam_api_provider.py b/tests/unit/commands/local/lib/test_sam_api_provider.py index 3e582ebac4..76bc06b7d3 100644 --- a/tests/unit/commands/local/lib/test_sam_api_provider.py +++ b/tests/unit/commands/local/lib/test_sam_api_provider.py @@ -288,6 +288,7 @@ def test_provider_must_support_binary_media_types(self): list(provider.routes)[0], Route(path="/path", methods=["GET"], function_name="SamFunc1"), ) + assertCountEqual( self, provider.api.binary_media_types, ["image/gif", "image/png"] ) diff --git a/tests/unit/lib/intrinsic_resolver/test_data/test_intrinsic_template_resolution.json b/tests/unit/lib/intrinsic_resolver/test_data/inputs/test_intrinsic_template_resolution.json similarity index 74% rename from tests/unit/lib/intrinsic_resolver/test_data/test_intrinsic_template_resolution.json rename to tests/unit/lib/intrinsic_resolver/test_data/inputs/test_intrinsic_template_resolution.json index 654484874b..0ec54a824a 100644 --- a/tests/unit/lib/intrinsic_resolver/test_data/test_intrinsic_template_resolution.json +++ b/tests/unit/lib/intrinsic_resolver/test_data/inputs/test_intrinsic_template_resolution.json @@ -26,11 +26,7 @@ }, true, { - "Fn::If": [ - "TestCondition", - true, - false - ] + "Fn::If": ["TestCondition", true, false] } ] }, @@ -49,11 +45,24 @@ } ] }, - "InvalidCondition": [ - "random items" - ] + "InvalidCondition": ["random items"] }, "Resources": { + "ReferenceLambdaLayerVersionLambdaFunction": { + "Properties": { + "Handler": "layer-main.custom_layer_handler", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [{ "Ref": "MyCustomLambdaLayer" }] + }, + "Type": "AWS::Serverless::Function" + }, + "MyCustomLambdaLayer": { + "Type": "AWS::Lambda::LayerVersion", + "Properties": { + "Content": "custom_layer/" + } + }, "RestApi.Deployment": { "Type": "AWS::ApiGateway::RestApi", "Properties": { @@ -65,15 +74,7 @@ "Fn::Split": [ ",", { - "Fn::Join": [ - ",", - [ - "a", - "e", - "f", - "d" - ] - ] + "Fn::Join": [",", ["a", "e", "f", "d"]] } ] } @@ -81,21 +82,14 @@ } }, "BodyS3Location": { - "Fn::FindInMap": [ - "TopLevel", - "SecondLevelKey", - "key" - ] + "Fn::FindInMap": ["TopLevel", "SecondLevelKey", "key"] } } }, "RestApiResource": { "Properties": { "parentId": { - "Fn::GetAtt": [ - "RestApi.Deployment", - "RootResourceId" - ] + "Fn::GetAtt": ["RestApi.Deployment", "RootResourceId"] }, "PathPart": "{proxy+}", "RestApiId": { @@ -133,10 +127,7 @@ }, ":lambda:path/2015-03-31/functions/", { - "Fn::GetAtt": [ - "HelloHandler2E4FBA4D", - "Arn" - ] + "Fn::GetAtt": ["HelloHandler2E4FBA4D", "Arn"] }, "/invocations" ] @@ -145,4 +136,4 @@ } } } -} \ No newline at end of file +} diff --git a/tests/unit/lib/intrinsic_resolver/test_data/inputs/test_layers_resolution.json b/tests/unit/lib/intrinsic_resolver/test_data/inputs/test_layers_resolution.json new file mode 100644 index 0000000000..f23194f779 --- /dev/null +++ b/tests/unit/lib/intrinsic_resolver/test_data/inputs/test_layers_resolution.json @@ -0,0 +1,174 @@ +{ + "Transform": "AWS::Serverless-2016-10-31", + "Parameters": + { + "LayerOneArn": + { + "Default": "arn:aws:lambda:us-west-2:111111111111:layer:layer:1", + "Type": "String" + }, + "LayerTwoArn": + { + "Default": "arn:aws:lambda:us-west-2:111111111111:layer:layer2:1", + "Type": "String" + }, + "ChangedLayerArn": + { + "Default": "arn:aws:lambda:us-west-2:111111111111:layer:changed_layer:1", + "Type": "String" + }, + "NonExistentLayerArn": + { + "Default": "arn:aws:lambda:us-west-2:111111111111:layer:non_existent_layer:1", + "Type": "String" + } + }, + "Resources": + { + "OneLayerVersionServerlessFunction": + { + "Type": "AWS::Serverless::Function", + "Properties": + { + "Handler": "layer-main.one_layer_hanlder", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [{ "Ref": "LayerOneArn" }] + } + }, + "ChangedLayerVersionServerlessFunction": + { + "Type": "AWS::Serverless::Function", + "Properties": + { + "Handler": "layer-main.one_layer_hanlder", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [{ "Ref": "ChangedLayerArn" }] + } + }, + "ReferenceServerlessLayerVersionServerlessFunction": + { + "Type": "AWS::Serverless::Function", + "Properties": + { + "Handler": "layer-main.custom_layer_handler", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [{ "Ref": "MyCustomServerlessLayer" }] + } + }, + "ReferenceLambdaLayerVersionServerlessFunction": + { + "Type": "AWS::Serverless::Function", + "Properties": + { + "Handler": "layer-main.custom_layer_handler", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [{ "Ref": "MyCustomLambdaLayer" }] + } + }, + "TwoLayerVersionServerlessFunction": + { + "Type": "AWS::Serverless::Function", + "Properties": + { + "Handler": "layer-main.one_layer_hanlder", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [{ "Ref": "LayerOneArn" }, { "Ref": "LayerTwoArn" }] + } + }, + "OneLayerVersionLambdaFunction": + { + "Type": "AWS::Serverless::Function", + "Properties": + { + "Handler": "layer-main.one_layer_hanlder", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [{ "Ref": "LayerOneArn" }] + } + }, + "ChangedLayerVersionLambdaFunction": + { + "Type": "AWS::Serverless::Function", + "Properties": + { + "Handler": "layer-main.one_layer_hanlder", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [{ "Ref": "ChangedLayerArn" }] + } + }, + "ReferenceServerlessLayerVersionLambdaFunction": + { + "Type": "AWS::Lambda::Function", + "Properties": + { + "Handler": "layer-main.custom_layer_handler", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [{ "Ref": "MyCustomServerlessLayer" }] + } + }, + "ReferenceLambdaLayerVersionLambdaFunction": + { + "Type": "AWS::Serverless::Function", + "Properties": + { + "Handler": "layer-main.custom_layer_handler", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [{ "Ref": "MyCustomLambdaLayer" }] + } + }, + "TwoLayerVersionLambdaFunction": + { + "Type": "AWS::Serverless::Function", + "Properties": + { + "Handler": "layer-main.one_layer_hanlder", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [{ "Ref": "LayerOneArn" }, { "Ref": "LayerTwoArn" }] + } + }, + "LayerVersionDoesNotExistFunction": + { + "Type": "AWS::Serverless::Function", + "Properties": + { + "Handler": "layer-main.handler", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [{ "Ref": "NonExistentLayerArn" }] + } + }, + "LayerVersionAccountDoesNotExistFunction": + { + "Type": "AWS::Serverless::Function", + "Properties": + { + "Handler": "layer-main.handler", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": + [ + "arn:aws:lambda:us-west-2:111111111101:layer:layerDoesNotExist:1" + ] + } + }, + "MyCustomLambdaLayer": + { + "Type": "AWS::Lambda::LayerVersion", + "Properties": { "Content": "custom_layer/" } + }, + "MyCustomServerlessLayer": + { + "Type": "AWS::Serverless::LayerVersion", + "Properties": { "ContentUri": "custom_layer/" } + } + } +} diff --git a/tests/unit/lib/intrinsic_resolver/test_data/inputs/test_methods_resource_resolution.json b/tests/unit/lib/intrinsic_resolver/test_data/inputs/test_methods_resource_resolution.json new file mode 100644 index 0000000000..ea6d0e84cf --- /dev/null +++ b/tests/unit/lib/intrinsic_resolver/test_data/inputs/test_methods_resource_resolution.json @@ -0,0 +1,449 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Resources": { + "HelloHandlerServiceRole11EF7C63": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + "iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/HelloHandler/ServiceRole/Resource" + } + }, + "HelloHandler2E4FBA4D": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": ".", + "Handler": "main.handler", + "Runtime": "python3.6" + } + }, + "HelloHandlerApiPermissionANYAC4E141E": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "HelloHandler2E4FBA4D" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "EndpointEEF1FD8F" + }, + "/", + { + "Ref": "EndpointDeploymentStageprodB78BEEA0" + }, + "/*/" + ] + ] + } + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/HelloHandler/ApiPermission.ANY.." + } + }, + "HelloHandlerApiPermissionTestANYDDD56D72": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "HelloHandler2E4FBA4D" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "EndpointEEF1FD8F" + }, + "/test-invoke-stage/*/" + ] + ] + } + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/HelloHandler/ApiPermission.Test.ANY.." + } + }, + "HelloHandlerApiPermissionANYproxy90E90CD6": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "HelloHandler2E4FBA4D" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "EndpointEEF1FD8F" + }, + "/", + { + "Ref": "EndpointDeploymentStageprodB78BEEA0" + }, + "/*/{proxy+}" + ] + ] + } + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/HelloHandler/ApiPermission.ANY..{proxy+}" + } + }, + "HelloHandlerApiPermissionTestANYproxy9803526C": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "HelloHandler2E4FBA4D" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "EndpointEEF1FD8F" + }, + "/test-invoke-stage/*/{proxy+}" + ] + ] + } + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/HelloHandler/ApiPermission.Test.ANY..{proxy+}" + } + }, + "EndpointEEF1FD8F": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Name": "Endpoint" + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/Endpoint/Resource" + } + }, + "EndpointDeployment318525DA37c0e38727e25b4317827bf43e918fbf": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "EndpointEEF1FD8F" + }, + "Description": "Automatically created by the RestApi construct" + }, + "DependsOn": [ + "Endpointproxy39E2174E", + "EndpointANY485C938B", + "EndpointproxyANYC09721C5" + ], + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/Endpoint/Deployment/Resource" + } + }, + "EndpointDeploymentStageprodB78BEEA0": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "RestApiId": { + "Ref": "EndpointEEF1FD8F" + }, + "DeploymentId": { + "Ref": "EndpointDeployment318525DA37c0e38727e25b4317827bf43e918fbf" + }, + "StageName": "prod" + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/Endpoint/DeploymentStage.prod/Resource" + } + }, + "EndpointCloudWatchRoleC3C64E0F": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + "iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs" + ] + ] + } + ] + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/Endpoint/CloudWatchRole/Resource" + } + }, + "EndpointAccountB8304247": { + "Type": "AWS::ApiGateway::Account", + "Properties": { + "CloudWatchRoleArn": { + "Fn::GetAtt": [ + "EndpointCloudWatchRoleC3C64E0F", + "Arn" + ] + } + }, + "DependsOn": [ + "EndpointEEF1FD8F" + ], + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/Endpoint/Account" + } + }, + "Endpointproxy39E2174E": { + "Type": "AWS::ApiGateway::Resource", + "Properties": { + "ParentId": { + "Fn::GetAtt": [ + "EndpointEEF1FD8F", + "RootResourceId" + ] + }, + "PathPart": "{proxy+}", + "RestApiId": { + "Ref": "EndpointEEF1FD8F" + } + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/Endpoint/{proxy+}/Resource" + } + }, + "EndpointproxyANYC09721C5": { + "Type": "AWS::ApiGateway::Method", + "Properties": { + "HttpMethod": "ANY", + "ResourceId": { + "Ref": "Endpointproxy39E2174E" + }, + "RestApiId": { + "Ref": "EndpointEEF1FD8F" + }, + "AuthorizationType": "NONE", + "Integration": { + "IntegrationHttpMethod": "POST", + "Type": "AWS_PROXY", + "Uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + "lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "HelloHandler2E4FBA4D", + "Arn" + ] + }, + "/invocations" + ] + ] + } + } + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/Endpoint/{proxy+}/ANY/Resource" + } + }, + "EndpointANY485C938B": { + "Type": "AWS::ApiGateway::Method", + "Properties": { + "HttpMethod": "ANY", + "ResourceId": { + "Fn::GetAtt": [ + "EndpointEEF1FD8F", + "RootResourceId" + ] + }, + "RestApiId": { + "Ref": "EndpointEEF1FD8F" + }, + "AuthorizationType": "NONE", + "Integration": { + "IntegrationHttpMethod": "POST", + "Type": "AWS_PROXY", + "Uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + "lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "HelloHandler2E4FBA4D", + "Arn" + ] + }, + "/invocations" + ] + ] + } + } + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/Endpoint/ANY/Resource" + } + }, + "CDKMetadata": { + "Type": "AWS::CDK::Metadata", + "Properties": { + "Modules": "aws-cdk=0.22.0,jsii-runtime=node.js/v12.4.0" + } + } + }, + "Parameters": { + "HelloHandlerCodeS3Bucket4359A483": { + "Type": "String", + "Description": "S3 bucket for asset \"CdkWorkshopStack/HelloHandler/Code\"" + }, + "HelloHandlerCodeS3VersionKey07D12610": { + "Type": "String", + "Description": "S3 key for asset version \"CdkWorkshopStack/HelloHandler/Code\"" + } + }, + "Outputs": { + "Endpoint8024A810": { + "Value": { + "Fn::Join": [ + "", + [ + "https://", + { + "Ref": "EndpointEEF1FD8F" + }, + ".execute-api.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "EndpointDeploymentStageprodB78BEEA0" + }, + "/" + ] + ] + }, + "Export": { + "Name": "CdkWorkshopStack:Endpoint8024A810" + } + } + } +} \ No newline at end of file diff --git a/tests/unit/lib/intrinsic_resolver/test_data/outputs/output_test_intrinsic_template_resolution.json b/tests/unit/lib/intrinsic_resolver/test_data/outputs/output_test_intrinsic_template_resolution.json new file mode 100644 index 0000000000..a7f25fb20b --- /dev/null +++ b/tests/unit/lib/intrinsic_resolver/test_data/outputs/output_test_intrinsic_template_resolution.json @@ -0,0 +1,101 @@ +{ + "Mappings": { + "TopLevel": { + "SecondLevelKey": { + "key": "https://s3location/" + } + } + }, + "Conditions": { + "ComplexCondition": { + "Fn::And": [ + { + "Fn::Equals": [ + { + "Fn::Or": [ + { + "Condition": "NotTestCondition" + }, + { + "Condition": "TestCondition" + } + ] + }, + false + ] + }, + true, + { + "Fn::If": [ + "TestCondition", + true, + false + ] + } + ] + }, + "TestCondition": { + "Fn::Equals": [ + { + "Ref": "EnvironmentType" + }, + "prod" + ] + }, + "NotTestCondition": { + "Fn::Not": [ + { + "Condition": "TestCondition" + } + ] + }, + "InvalidCondition": [ + "random items" + ] + }, + "Resources": { + "ReferenceLambdaLayerVersionLambdaFunction": { + "Properties": { + "Handler": "layer-main.custom_layer_handler", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [ + "MyCustomLambdaLayer" + ] + }, + "Type": "AWS::Serverless::Function" + }, + "MyCustomLambdaLayer": { + "Type": "AWS::Lambda::LayerVersion", + "Properties": { + "Content": "custom_layer/" + } + }, + "RestApi.Deployment": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": "YTtlO2Y7ZA==", + "BodyS3Location": "https://s3location/" + } + }, + "RestApiResource": { + "Properties": { + "parentId": "/", + "PathPart": "{proxy+}", + "RestApiId": "RestApi.Deployment" + } + }, + "HelloHandler2E4FBA4D": { + "Type": "AWS::Lambda::Function", + "Properties": { + "handler": "main.handle" + } + }, + "LambdaFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Uri": "arn:aws:apigateway:us-east-1a:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:HelloHandler2E4FBA4D/invocations" + } + } + } +} \ No newline at end of file diff --git a/tests/unit/lib/intrinsic_resolver/test_data/outputs/outputs_methods_resource_resolution.json b/tests/unit/lib/intrinsic_resolver/test_data/outputs/outputs_methods_resource_resolution.json new file mode 100644 index 0000000000..1b0cccd711 --- /dev/null +++ b/tests/unit/lib/intrinsic_resolver/test_data/outputs/outputs_methods_resource_resolution.json @@ -0,0 +1,223 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Resources": { + "HelloHandlerServiceRole11EF7C63": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:awsiam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/HelloHandler/ServiceRole/Resource" + } + }, + "HelloHandler2E4FBA4D": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": ".", + "Handler": "main.handler", + "Runtime": "python3.6" + } + }, + "HelloHandlerApiPermissionANYAC4E141E": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": "HelloHandler2E4FBA4D", + "Principal": "apigateway.amazonaws.com", + "SourceArn": "arn:aws:execute-api:us-east-1:123456789012:EndpointEEF1FD8F/EndpointDeploymentStageprodB78BEEA0/*/" + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/HelloHandler/ApiPermission.ANY.." + } + }, + "HelloHandlerApiPermissionTestANYDDD56D72": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": "HelloHandler2E4FBA4D", + "Principal": "apigateway.amazonaws.com", + "SourceArn": "arn:aws:execute-api:us-east-1:123456789012:EndpointEEF1FD8F/test-invoke-stage/*/" + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/HelloHandler/ApiPermission.Test.ANY.." + } + }, + "HelloHandlerApiPermissionANYproxy90E90CD6": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": "HelloHandler2E4FBA4D", + "Principal": "apigateway.amazonaws.com", + "SourceArn": "arn:aws:execute-api:us-east-1:123456789012:EndpointEEF1FD8F/EndpointDeploymentStageprodB78BEEA0/*/{proxy+}" + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/HelloHandler/ApiPermission.ANY..{proxy+}" + } + }, + "HelloHandlerApiPermissionTestANYproxy9803526C": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": "HelloHandler2E4FBA4D", + "Principal": "apigateway.amazonaws.com", + "SourceArn": "arn:aws:execute-api:us-east-1:123456789012:EndpointEEF1FD8F/test-invoke-stage/*/{proxy+}" + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/HelloHandler/ApiPermission.Test.ANY..{proxy+}" + } + }, + "EndpointEEF1FD8F": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Name": "Endpoint" + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/Endpoint/Resource" + } + }, + "EndpointDeployment318525DA37c0e38727e25b4317827bf43e918fbf": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": "EndpointEEF1FD8F", + "Description": "Automatically created by the RestApi construct" + }, + "DependsOn": [ + "Endpointproxy39E2174E", + "EndpointANY485C938B", + "EndpointproxyANYC09721C5" + ], + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/Endpoint/Deployment/Resource" + } + }, + "EndpointDeploymentStageprodB78BEEA0": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "RestApiId": "EndpointEEF1FD8F", + "DeploymentId": "EndpointDeployment318525DA37c0e38727e25b4317827bf43e918fbf", + "StageName": "prod" + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/Endpoint/DeploymentStage.prod/Resource" + } + }, + "EndpointCloudWatchRoleC3C64E0F": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:awsiam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs" + ] + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/Endpoint/CloudWatchRole/Resource" + } + }, + "EndpointAccountB8304247": { + "Type": "AWS::ApiGateway::Account", + "Properties": { + "CloudWatchRoleArn": "arn:aws:lambda:us-east-1:123456789012:function:EndpointCloudWatchRoleC3C64E0F" + }, + "DependsOn": [ + "EndpointEEF1FD8F" + ], + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/Endpoint/Account" + } + }, + "Endpointproxy39E2174E": { + "Type": "AWS::ApiGateway::Resource", + "Properties": { + "ParentId": "/", + "PathPart": "{proxy+}", + "RestApiId": "EndpointEEF1FD8F" + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/Endpoint/{proxy+}/Resource" + } + }, + "EndpointproxyANYC09721C5": { + "Type": "AWS::ApiGateway::Method", + "Properties": { + "HttpMethod": "ANY", + "ResourceId": "Endpointproxy39E2174E", + "RestApiId": "EndpointEEF1FD8F", + "AuthorizationType": "NONE", + "Integration": { + "IntegrationHttpMethod": "POST", + "Type": "AWS_PROXY", + "Uri": "arn:aws:apigateway:us-east-1lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:HelloHandler2E4FBA4D/invocations" + } + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/Endpoint/{proxy+}/ANY/Resource" + } + }, + "EndpointANY485C938B": { + "Type": "AWS::ApiGateway::Method", + "Properties": { + "HttpMethod": "ANY", + "ResourceId": "/", + "RestApiId": "EndpointEEF1FD8F", + "AuthorizationType": "NONE", + "Integration": { + "IntegrationHttpMethod": "POST", + "Type": "AWS_PROXY", + "Uri": "arn:aws:apigateway:us-east-1lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:HelloHandler2E4FBA4D/invocations" + } + }, + "Metadata": { + "aws:cdk:path": "CdkWorkshopStack/Endpoint/ANY/Resource" + } + }, + "CDKMetadata": { + "Type": "AWS::CDK::Metadata", + "Properties": { + "Modules": "aws-cdk=0.22.0,jsii-runtime=node.js/v12.4.0" + } + } + }, + "Parameters": { + "HelloHandlerCodeS3Bucket4359A483": { + "Type": "String", + "Description": "S3 bucket for asset \"CdkWorkshopStack/HelloHandler/Code\"" + }, + "HelloHandlerCodeS3VersionKey07D12610": { + "Type": "String", + "Description": "S3 key for asset version \"CdkWorkshopStack/HelloHandler/Code\"" + } + }, + "Outputs": { + "Endpoint8024A810": { + "Value": "https://EndpointEEF1FD8F.execute-api.us-east-1.amazonaws.com/EndpointDeploymentStageprodB78BEEA0/", + "Export": { + "Name": "CdkWorkshopStack:Endpoint8024A810" + } + } + } +} \ No newline at end of file diff --git a/tests/unit/lib/intrinsic_resolver/test_data/outputs/outputs_test_layers_resolution.json b/tests/unit/lib/intrinsic_resolver/test_data/outputs/outputs_test_layers_resolution.json new file mode 100644 index 0000000000..4f72492e93 --- /dev/null +++ b/tests/unit/lib/intrinsic_resolver/test_data/outputs/outputs_test_layers_resolution.json @@ -0,0 +1,169 @@ +{ + "Transform": "AWS::Serverless-2016-10-31", + "Parameters": { + "LayerOneArn": { + "Default": "arn:aws:lambda:us-west-2:111111111111:layer:layer:1", + "Type": "String" + }, + "LayerTwoArn": { + "Default": "arn:aws:lambda:us-west-2:111111111111:layer:layer2:1", + "Type": "String" + }, + "ChangedLayerArn": { + "Default": "arn:aws:lambda:us-west-2:111111111111:layer:changed_layer:1", + "Type": "String" + }, + "NonExistentLayerArn": { + "Default": "arn:aws:lambda:us-west-2:111111111111:layer:non_existent_layer:1", + "Type": "String" + } + }, + "Resources": { + "OneLayerVersionServerlessFunction": { + "Type": "AWS::Serverless::Function", + "Properties": { + "Handler": "layer-main.one_layer_hanlder", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [ + "arn:aws:lambda:us-west-2:111111111111:layer:layer:1" + ] + } + }, + "ChangedLayerVersionServerlessFunction": { + "Type": "AWS::Serverless::Function", + "Properties": { + "Handler": "layer-main.one_layer_hanlder", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [ + "arn:aws:lambda:us-west-2:111111111111:layer:changed_layer:1" + ] + } + }, + "ReferenceServerlessLayerVersionServerlessFunction": { + "Type": "AWS::Serverless::Function", + "Properties": { + "Handler": "layer-main.custom_layer_handler", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [ + "MyCustomServerlessLayer" + ] + } + }, + "ReferenceLambdaLayerVersionServerlessFunction": { + "Type": "AWS::Serverless::Function", + "Properties": { + "Handler": "layer-main.custom_layer_handler", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [ + "MyCustomLambdaLayer" + ] + } + }, + "TwoLayerVersionServerlessFunction": { + "Type": "AWS::Serverless::Function", + "Properties": { + "Handler": "layer-main.one_layer_hanlder", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [ + "arn:aws:lambda:us-west-2:111111111111:layer:layer:1", + "arn:aws:lambda:us-west-2:111111111111:layer:layer2:1" + ] + } + }, + "OneLayerVersionLambdaFunction": { + "Type": "AWS::Serverless::Function", + "Properties": { + "Handler": "layer-main.one_layer_hanlder", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [ + "arn:aws:lambda:us-west-2:111111111111:layer:layer:1" + ] + } + }, + "ChangedLayerVersionLambdaFunction": { + "Type": "AWS::Serverless::Function", + "Properties": { + "Handler": "layer-main.one_layer_hanlder", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [ + "arn:aws:lambda:us-west-2:111111111111:layer:changed_layer:1" + ] + } + }, + "ReferenceServerlessLayerVersionLambdaFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "layer-main.custom_layer_handler", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [ + "MyCustomServerlessLayer" + ] + } + }, + "ReferenceLambdaLayerVersionLambdaFunction": { + "Type": "AWS::Serverless::Function", + "Properties": { + "Handler": "layer-main.custom_layer_handler", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [ + "MyCustomLambdaLayer" + ] + } + }, + "TwoLayerVersionLambdaFunction": { + "Type": "AWS::Serverless::Function", + "Properties": { + "Handler": "layer-main.one_layer_hanlder", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [ + "arn:aws:lambda:us-west-2:111111111111:layer:layer:1", + "arn:aws:lambda:us-west-2:111111111111:layer:layer2:1" + ] + } + }, + "LayerVersionDoesNotExistFunction": { + "Type": "AWS::Serverless::Function", + "Properties": { + "Handler": "layer-main.handler", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [ + "arn:aws:lambda:us-west-2:111111111111:layer:non_existent_layer:1" + ] + } + }, + "LayerVersionAccountDoesNotExistFunction": { + "Type": "AWS::Serverless::Function", + "Properties": { + "Handler": "layer-main.handler", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": [ + "arn:aws:lambda:us-west-2:111111111101:layer:layerDoesNotExist:1" + ] + } + }, + "MyCustomLambdaLayer": { + "Type": "AWS::Lambda::LayerVersion", + "Properties": { + "Content": "custom_layer/" + } + }, + "MyCustomServerlessLayer": { + "Type": "AWS::Serverless::LayerVersion", + "Properties": { + "ContentUri": "custom_layer/" + } + } + } +} \ No newline at end of file diff --git a/tests/unit/lib/intrinsic_resolver/test_intrinsic_resolver.py b/tests/unit/lib/intrinsic_resolver/test_intrinsic_resolver.py index 5b73a304dc..df48fd4974 100644 --- a/tests/unit/lib/intrinsic_resolver/test_intrinsic_resolver.py +++ b/tests/unit/lib/intrinsic_resolver/test_intrinsic_resolver.py @@ -1,5 +1,7 @@ import json +from collections import OrderedDict from copy import deepcopy + try: from pathlib import Path except ImportError: @@ -342,10 +344,10 @@ def test_fn_find_in_map_invalid_number_arguments(self, name, intrinsic): item, ) for item in [ - ["", "Test", "key"], - ["Basic", "", "key"], - ["Basic", "Test", ""], - ] + ["", "Test", "key"], + ["Basic", "", "key"], + ["Basic", "Test", ""], + ] ] ) def test_fn_find_in_map_invalid_key_entries(self, name, intrinsic): @@ -1192,7 +1194,7 @@ def test_fn_if_condition_not_bool_fail(self): ) -class TestIntrinsicTemplateResolution(TestCase): +class TestIntrinsicAttribteResolution(TestCase): def setUp(self): logical_id_translator = { "RestApi": "NewRestApi", @@ -1208,7 +1210,7 @@ def setUp(self): self.logical_id_translator = logical_id_translator integration_path = str( - Path(__file__).resolve().parents[0].joinpath('test_data', 'test_intrinsic_template_resolution.json')) + Path(__file__).resolve().parents[0].joinpath('test_data', 'inputs/test_intrinsic_template_resolution.json')) with open(integration_path) as f: template = json.load(f) @@ -1224,7 +1226,7 @@ def setUp(self): template=self.template, symbol_resolver=symbol_resolver ) - def test_basic_template_resolution(self): + def test_basic_attribte_resolution(self): resolved_template = self.resolver.resolve_attribute(self.resources, ignore_errors=False) expected_resources = { "HelloHandler2E4FBA4D": { @@ -1238,6 +1240,21 @@ def test_basic_template_resolution(self): }, "Type": "AWS::Lambda::Function", }, + "ReferenceLambdaLayerVersionLambdaFunction": { + "Properties": { + "Handler": "layer-main.custom_layer_handler", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": ["MyCustomLambdaLayer"] + }, + "Type": "AWS::Serverless::Function", + }, + "MyCustomLambdaLayer": { + "Type": "AWS::Lambda::LayerVersion", + "Properties": { + "Content": "custom_layer/" + } + }, "RestApi": { "Properties": { "Body": "YTtlO2Y7ZA==", @@ -1292,6 +1309,21 @@ def test_template_ignore_errors(self): "Properties": {"handler": "main.handle"}, "Type": "AWS::Lambda::Function", }, + "ReferenceLambdaLayerVersionLambdaFunction": { + "Properties": { + "Handler": "layer-main.custom_layer_handler", + "Runtime": "python3.6", + "CodeUri": ".", + "Layers": ["MyCustomLambdaLayer"] + }, + "Type": "AWS::Serverless::Function", + }, + "MyCustomLambdaLayer": { + "Type": "AWS::Lambda::LayerVersion", + "Properties": { + "Content": "custom_layer/" + } + }, "LambdaFunction": { "Properties": { "Uri": "arn:aws:apigateway:us-east-1a:lambda:path/2015-03-31" @@ -1330,6 +1362,172 @@ def test_template_ignore_errors(self): self.assertEqual(expected_template, result) +class TestResolveTemplate(TestCase): + def test_parameter_not_resolved(self): + template = { + "Parameters": { + "TestStageName": { + "Default": "test", + "Type": "string" + } + }, + "Resources": { + "Test": { + "Type": "AWS::ApiGateway::RestApi", + "Parameters": { + "StageName": { + "Ref": "TestStageName" + } + } + } + } + } + + expected_template = { + "Parameters": { + "TestStageName": { + "Default": "test", + "Type": "string" + }, + }, + "Resources": OrderedDict({ + "Test": { + "Type": "AWS::ApiGateway::RestApi", + "Parameters": { + "StageName": "test" + } + } + }) + } + + symbol_resolver = IntrinsicsSymbolTable( + template=template, logical_id_translator={} + ) + resolver = IntrinsicResolver(template=template, symbol_resolver=symbol_resolver) + self.assertEqual(resolver.resolve_template(), expected_template) + + def test_mappings_directory_resolved(self): + template = { + "Mappings": { + "TestStageName": { + "TestKey": { + "key": "StageName" + } + } + }, + "Resources": { + "Test": { + "Type": "AWS::ApiGateway::RestApi", + "Parameters": { + "StageName": { + "Fn::FindInMap": ["TestStageName", "TestKey", "key"] + } + } + } + } + } + + expected_template = { + "Mappings": { + "TestStageName": { + "TestKey": { + "key": "StageName" + } + } + }, + "Resources": OrderedDict({ + "Test": { + "Type": "AWS::ApiGateway::RestApi", + "Parameters": { + "StageName": "StageName" + } + } + }) + } + + symbol_resolver = IntrinsicsSymbolTable( + template=template, logical_id_translator={} + ) + resolver = IntrinsicResolver(template=template, symbol_resolver=symbol_resolver) + self.assertEqual(resolver.resolve_template(), expected_template) + + def test_output_resolved(self): + template = { + "Parameters": { + "StageRef": { + "Default": "StageName" + } + }, + "Outputs": { + "TestStageName": { + "Ref": "Test" + }, + "ParameterRef": { + "Ref": "StageRef" + } + }, + "Resources": { + "Test": { + "Type": "AWS::ApiGateway::RestApi", + "Parameters": { + "StageName": { + "Ref": "StageRef" + } + } + } + } + } + + expected_template = { + "Parameters": { + "StageRef": { + "Default": "StageName" + } + }, + "Resources": OrderedDict({ + "Test": { + "Type": "AWS::ApiGateway::RestApi", + "Parameters": { + "StageName": "StageName" + } + } + }), + "Outputs": OrderedDict({ + "TestStageName": "Test", + "ParameterRef": "StageName" + }) + } + + symbol_resolver = IntrinsicsSymbolTable( + template=template, logical_id_translator={} + ) + resolver = IntrinsicResolver(template=template, symbol_resolver=symbol_resolver) + self.assertEqual(resolver.resolve_template(), expected_template) + + def load_test_data(self, template_path): + integration_path = str( + Path(__file__).resolve().parents[0].joinpath('test_data', template_path)) + with open(integration_path) as f: + template = json.load(f) + return template + + @parameterized.expand([ + ('inputs/test_intrinsic_template_resolution.json', 'outputs/output_test_intrinsic_template_resolution.json'), + ('inputs/test_layers_resolution.json', 'outputs/outputs_test_layers_resolution.json'), + ('inputs/test_methods_resource_resolution.json', 'outputs/outputs_methods_resource_resolution.json'), + ]) + def test_intrinsic_sample_inputs_outputs(self, input, output): + input_template = self.load_test_data(input) + + symbol_resolver = IntrinsicsSymbolTable( + template=input_template, logical_id_translator={} + ) + resolver = IntrinsicResolver(template=input_template, symbol_resolver=symbol_resolver) + processed_template = resolver.resolve_template() + expected_template = self.load_test_data(output) + self.assertEqual(processed_template, expected_template) + + class TestIntrinsicResolverInitialization(TestCase): def test_conditional_key_function_map(self): resolver = IntrinsicResolver(None, None) From 5a4b4735908e42a43894e39284ef78f6faeb3685 Mon Sep 17 00:00:00 2001 From: Vikranth Srivatsa Date: Thu, 15 Aug 2019 16:51:44 -0700 Subject: [PATCH 2/8] Remove Test with ignoring Refs with globals --- tests/unit/commands/local/lib/test_sam_api_provider.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/commands/local/lib/test_sam_api_provider.py b/tests/unit/commands/local/lib/test_sam_api_provider.py index 76bc06b7d3..6215196fa8 100644 --- a/tests/unit/commands/local/lib/test_sam_api_provider.py +++ b/tests/unit/commands/local/lib/test_sam_api_provider.py @@ -259,7 +259,6 @@ def test_provider_must_support_binary_media_types(self): "image~1gif", "image~1png", "image~1png", # Duplicates must be ignored - {"Ref": "SomeParameter"}, # Refs are ignored as well ] } }, From 9e2c0e7f72062548b381eae949726b6e60e03899 Mon Sep 17 00:00:00 2001 From: Vikranth Srivatsa Date: Thu, 15 Aug 2019 16:52:43 -0700 Subject: [PATCH 3/8] Handle Top level resource case --- samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py b/samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py index d385a91812..4f1dda8fff 100644 --- a/samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py +++ b/samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py @@ -239,10 +239,10 @@ def resolve_symbols(self, logical_id, resource_attribute, ignore_errors=False): if callable(attribute_resolver): return attribute_resolver(logical_id) return attribute_resolver - # - # # Handle the Case that the code is a ref of top level resource - # if resource_attribute == IntrinsicResolver.REF and logical_id in self._resources: - # return logical_id + + # Handle the Case that the code is a ref of top level resource + if resource_attribute == IntrinsicResolver.REF and logical_id in self._resources: + return logical_id if ignore_errors: return "${}".format(logical_id + "." + resource_attribute) From e5bd554345d5b77ce1319ba76895b14a9c43cfd5 Mon Sep 17 00:00:00 2001 From: Vikranth Srivatsa Date: Thu, 15 Aug 2019 16:55:51 -0700 Subject: [PATCH 4/8] Update Style --- .../intrinsic_property_resolver.py | 2 -- .../intrinsics_symbol_table.py | 35 +++++++++---------- .../test_intrinsic_resolver.py | 8 ++--- 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/samcli/lib/intrinsic_resolver/intrinsic_property_resolver.py b/samcli/lib/intrinsic_resolver/intrinsic_property_resolver.py index ce438dfe25..8c03787d29 100644 --- a/samcli/lib/intrinsic_resolver/intrinsic_property_resolver.py +++ b/samcli/lib/intrinsic_resolver/intrinsic_property_resolver.py @@ -207,10 +207,8 @@ def intrinsic_property_resolver(self, intrinsic, parent_function="template"): return intrinsic if isinstance(intrinsic, list): return [self.intrinsic_property_resolver(item) for item in intrinsic] - keys = list(intrinsic.keys()) key = keys[0] - if key in self.intrinsic_key_function_map: intrinsic_value = intrinsic.get(key) return self.intrinsic_key_function_map.get(key)(intrinsic_value) diff --git a/samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py b/samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py index 4f1dda8fff..624163c85c 100644 --- a/samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py +++ b/samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py @@ -40,7 +40,7 @@ class IntrinsicsSymbolTable(object): "AWS::Region": "us-east-1", "AWS::StackName": "local", "AWS::StackId": "arn:aws:cloudformation:us-east-1:123456789012:stack/" - "local/51af3dc0-da77-11e4-872e-1234567db123", + "local/51af3dc0-da77-11e4-872e-1234567db123", "AWS::URLSuffix": "localhost", } @@ -88,11 +88,11 @@ class IntrinsicsSymbolTable(object): CFN_RESOURCE_TYPE = "Type" def __init__( - self, - template=None, - logical_id_translator=None, - default_type_resolver=None, - common_attribute_resolver=None, + self, + template=None, + logical_id_translator=None, + default_type_resolver=None, + common_attribute_resolver=None, ): """ Initializes the Intrinsic Symbol Table so that runtime attributes can be resolved. @@ -148,10 +148,10 @@ def __init__( self._resources = self._template.get("Resources", {}) self.default_type_resolver = ( - default_type_resolver or self.get_default_type_resolver() + default_type_resolver or self.get_default_type_resolver() ) self.common_attribute_resolver = ( - common_attribute_resolver or self.get_default_attribute_resolver() + common_attribute_resolver or self.get_default_attribute_resolver() ) self.default_pseudo_resolver = self.get_default_pseudo_resolver() @@ -271,8 +271,7 @@ def arn_resolver(self, logical_id, service_name="lambda"): """ aws_region = self.handle_pseudo_region() account_id = ( - self.logical_id_translator.get(IntrinsicsSymbolTable.AWS_ACCOUNT_ID) - or self.handle_pseudo_account_id() + self.logical_id_translator.get(IntrinsicsSymbolTable.AWS_ACCOUNT_ID) or self.handle_pseudo_account_id() ) partition_name = self.handle_pseudo_partition() resource_name = logical_id @@ -307,12 +306,11 @@ def get_translation(self, logical_id, resource_attributes=IntrinsicResolver.REF) """ logical_id_item = self.logical_id_translator.get(logical_id, {}) if any( - isinstance(logical_id_item, object_type) - for object_type in [string_types, list, bool, int] + isinstance(logical_id_item, object_type) + for object_type in [string_types, list, bool, int] ): if ( - resource_attributes != IntrinsicResolver.REF - and resource_attributes != "" + resource_attributes != IntrinsicResolver.REF and resource_attributes != "" ): return None return logical_id_item @@ -358,11 +356,10 @@ def handle_pseudo_region(self): The region from the environment or a default one """ return ( - self.logical_id_translator.get(IntrinsicsSymbolTable.AWS_REGION) - or os.getenv("AWS_REGION") - or IntrinsicsSymbolTable.DEFAULT_PSEUDO_PARAM_VALUES.get( - IntrinsicsSymbolTable.AWS_REGION - ) + self.logical_id_translator.get(IntrinsicsSymbolTable.AWS_REGION) or os.getenv("AWS_REGION") or + IntrinsicsSymbolTable.DEFAULT_PSEUDO_PARAM_VALUES.get( + IntrinsicsSymbolTable.AWS_REGION + ) ) def handle_pseudo_url_prefix(self): diff --git a/tests/unit/lib/intrinsic_resolver/test_intrinsic_resolver.py b/tests/unit/lib/intrinsic_resolver/test_intrinsic_resolver.py index df48fd4974..bee5ea852a 100644 --- a/tests/unit/lib/intrinsic_resolver/test_intrinsic_resolver.py +++ b/tests/unit/lib/intrinsic_resolver/test_intrinsic_resolver.py @@ -344,10 +344,10 @@ def test_fn_find_in_map_invalid_number_arguments(self, name, intrinsic): item, ) for item in [ - ["", "Test", "key"], - ["Basic", "", "key"], - ["Basic", "Test", ""], - ] + ["", "Test", "key"], + ["Basic", "", "key"], + ["Basic", "Test", ""], + ] ] ) def test_fn_find_in_map_invalid_key_entries(self, name, intrinsic): From bb0b4771b3547e7ff0b2cfba28e2adc33b3d7ee7 Mon Sep 17 00:00:00 2001 From: Vikranth Srivatsa Date: Fri, 16 Aug 2019 11:16:30 -0700 Subject: [PATCH 5/8] Handle Layer Integ Test Failure on KeyboardExit When running local layer integration tests, there was a cleanup error that occured if there was a KeyboardExit occurred on the previous run. This causes problems with cached values on consecutive tests. --- tests/integration/local/invoke/test_integrations_cli.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/integration/local/invoke/test_integrations_cli.py b/tests/integration/local/invoke/test_integrations_cli.py index ee842143bb..dcd7586b8c 100644 --- a/tests/integration/local/invoke/test_integrations_cli.py +++ b/tests/integration/local/invoke/test_integrations_cli.py @@ -466,6 +466,15 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): cls.layer_utils.delete_layers() + # Added to handle the case where ^C failed the test due to invalid cleanup of layers + docker_client = docker.from_env() + samcli_images = docker_client.images.list(name='samcli/lambda') + for image in samcli_images: + docker_client.images.remove(image.id) + integ_layer_cache_dir = Path().home().joinpath("integ_layer_cache") + if integ_layer_cache_dir.exists(): + shutil.rmtree(str(integ_layer_cache_dir)) + super(TestLayerVersion, cls).tearDownClass() @parameterized.expand([ From 11bdaad36253e23f4fdf939bd8df5b253ae79384 Mon Sep 17 00:00:00 2001 From: Vikranth Srivatsa Date: Fri, 16 Aug 2019 11:16:30 -0700 Subject: [PATCH 6/8] Handle Layer Integ Test Failure on KeyboardExit When running local layer integration tests, there was a cleanup error that occured if there was a KeyboardExit occurred on the previous run. This causes problems with cached values on consecutive tests. --- tests/integration/local/invoke/test_integrations_cli.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/integration/local/invoke/test_integrations_cli.py b/tests/integration/local/invoke/test_integrations_cli.py index ee842143bb..dcd7586b8c 100644 --- a/tests/integration/local/invoke/test_integrations_cli.py +++ b/tests/integration/local/invoke/test_integrations_cli.py @@ -466,6 +466,15 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): cls.layer_utils.delete_layers() + # Added to handle the case where ^C failed the test due to invalid cleanup of layers + docker_client = docker.from_env() + samcli_images = docker_client.images.list(name='samcli/lambda') + for image in samcli_images: + docker_client.images.remove(image.id) + integ_layer_cache_dir = Path().home().joinpath("integ_layer_cache") + if integ_layer_cache_dir.exists(): + shutil.rmtree(str(integ_layer_cache_dir)) + super(TestLayerVersion, cls).tearDownClass() @parameterized.expand([ From 5785cc4c89b0ca6fc5e4cfddc7f62902986ba286 Mon Sep 17 00:00:00 2001 From: Vikranth Srivatsa Date: Fri, 16 Aug 2019 15:08:13 -0700 Subject: [PATCH 7/8] Update Intrinsic Resolver to handle Layers Lambda Layers were added as a special case where Ref's to unresolved resources remained as {"Ref": logical_id}. This was chosen to be the default since SamTranslator handles the translation of some resource and converts some types such as {"Ref":"AWS::Region"} -> {"Ref": "us-east-1"} and replacing Globals. --- samcli/commands/local/lib/cfn_api_provider.py | 3 --- .../intrinsic_property_resolver.py | 16 +++++----------- .../intrinsics_symbol_table.py | 16 ++++++++++++---- .../methods-resources-api-template.yaml | 6 +++--- ...utput_test_intrinsic_template_resolution.json | 2 +- .../outputs/outputs_test_layers_resolution.json | 8 ++++---- .../test_intrinsic_resolver.py | 14 ++++++++------ .../test_intrinsics_symbol_table.py | 2 +- 8 files changed, 34 insertions(+), 33 deletions(-) diff --git a/samcli/commands/local/lib/cfn_api_provider.py b/samcli/commands/local/lib/cfn_api_provider.py index e6076d359a..fb13450290 100644 --- a/samcli/commands/local/lib/cfn_api_provider.py +++ b/samcli/commands/local/lib/cfn_api_provider.py @@ -107,12 +107,9 @@ def _extract_cloud_formation_stage(resources, stage_resource, collector): stage_name = properties.get("StageName") stage_variables = properties.get("Variables") - # Currently, we aren't resolving any Refs or other intrinsic properties that come with it - # A separate pr will need to fully resolve intrinsics logical_id = properties.get("RestApiId") if not logical_id: raise InvalidSamTemplateException("The AWS::ApiGateway::Stage must have a RestApiId property") - rest_api_resource_type = resources.get(logical_id, {}).get("Type") if rest_api_resource_type != CfnApiProvider.APIGATEWAY_RESTAPI: raise InvalidSamTemplateException( diff --git a/samcli/lib/intrinsic_resolver/intrinsic_property_resolver.py b/samcli/lib/intrinsic_resolver/intrinsic_property_resolver.py index 8c03787d29..3b87f5f23f 100644 --- a/samcli/lib/intrinsic_resolver/intrinsic_property_resolver.py +++ b/samcli/lib/intrinsic_resolver/intrinsic_property_resolver.py @@ -194,21 +194,15 @@ def intrinsic_property_resolver(self, intrinsic, parent_function="template"): The simplified version of the intrinsic function. This could be a list,str,dict depending on the format required """ if intrinsic is None: - raise InvalidIntrinsicException( - "Missing Intrinsic property in {}".format(parent_function) - ) - - if ( - any( - isinstance(intrinsic, object_type) - for object_type in [string_types, bool, int] - ) or intrinsic == {} - ): + raise InvalidIntrinsicException("Missing Intrinsic property in {}".format(parent_function)) + if any(isinstance(intrinsic, object_type) for object_type in [string_types, bool, int]) or intrinsic == {}: return intrinsic if isinstance(intrinsic, list): return [self.intrinsic_property_resolver(item) for item in intrinsic] + keys = list(intrinsic.keys()) key = keys[0] + if key in self.intrinsic_key_function_map: intrinsic_value = intrinsic.get(key) return self.intrinsic_key_function_map.get(key)(intrinsic_value) @@ -266,7 +260,7 @@ def resolve_attribute(self, cloud_formation_property, ignore_errors=False): for key, val in cloud_formation_property.items(): processed_key = self._symbol_resolver.get_translation(key) or key try: - processed_resource = self.intrinsic_property_resolver(val) + processed_resource = self.intrinsic_property_resolver(val, parent_function=processed_key) processed_dict[processed_key] = processed_resource except (InvalidIntrinsicException, InvalidSymbolException) as e: resource_type = val.get("Type", "") diff --git a/samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py b/samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py index 624163c85c..c8171d7d79 100644 --- a/samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py +++ b/samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py @@ -1,6 +1,7 @@ """ The symbol table that is used in IntrinsicResolver in order to resolve runtime attributes """ +import logging import os from six import string_types @@ -10,6 +11,8 @@ InvalidSymbolException, ) +LOG = logging.getLogger(__name__) + class IntrinsicsSymbolTable(object): AWS_ACCOUNT_ID = "AWS::AccountId" @@ -174,6 +177,12 @@ def get_default_type_resolver(): return { "AWS::ApiGateway::RestApi": { "RootResourceId": "/" # It usually used as a reference to the parent id of the RestApi, + }, + "AWS::Lambda::LayerVersion": { + IntrinsicResolver.REF: lambda logical_id: {IntrinsicResolver.REF: logical_id} + }, + "AWS::Serverless::LayerVersion": { + IntrinsicResolver.REF: lambda logical_id: {IntrinsicResolver.REF: logical_id} } } @@ -230,7 +239,7 @@ def resolve_symbols(self, logical_id, resource_attribute, ignore_errors=False): ) if resolver: if callable(resolver): - return resolver(logical_id, resource_attribute) + return resolver(logical_id) return resolver # Handle Attribute Type Resolution @@ -240,9 +249,8 @@ def resolve_symbols(self, logical_id, resource_attribute, ignore_errors=False): return attribute_resolver(logical_id) return attribute_resolver - # Handle the Case that the code is a ref of top level resource - if resource_attribute == IntrinsicResolver.REF and logical_id in self._resources: - return logical_id + if resource_attribute == IntrinsicResolver.REF: + return {IntrinsicResolver.REF: logical_id} if ignore_errors: return "${}".format(logical_id + "." + resource_attribute) diff --git a/tests/integration/testdata/start_api/methods-resources-api-template.yaml b/tests/integration/testdata/start_api/methods-resources-api-template.yaml index abaa69501c..9c9fc662a4 100644 --- a/tests/integration/testdata/start_api/methods-resources-api-template.yaml +++ b/tests/integration/testdata/start_api/methods-resources-api-template.yaml @@ -18,9 +18,9 @@ Resources: Type: AWS::ApiGateway::Stage Properties: StageName: Dev - RestApiId: TestApi + RestApiId: !Ref TestApi Variables: - Stack: Dev + Stack: Dev MyNonServerlessLambdaFunction: Properties: @@ -144,4 +144,4 @@ Resources: Uri: Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyNonServerlessLambdaFunction.Arn}/invocations ResourceId: "NoServerlessFunctionResource" - RestApiId: "TestApi" \ No newline at end of file + RestApiId: "TestApi" diff --git a/tests/unit/lib/intrinsic_resolver/test_data/outputs/output_test_intrinsic_template_resolution.json b/tests/unit/lib/intrinsic_resolver/test_data/outputs/output_test_intrinsic_template_resolution.json index a7f25fb20b..ce04aaea73 100644 --- a/tests/unit/lib/intrinsic_resolver/test_data/outputs/output_test_intrinsic_template_resolution.json +++ b/tests/unit/lib/intrinsic_resolver/test_data/outputs/output_test_intrinsic_template_resolution.json @@ -60,7 +60,7 @@ "Runtime": "python3.6", "CodeUri": ".", "Layers": [ - "MyCustomLambdaLayer" + {"Ref": "MyCustomLambdaLayer"} ] }, "Type": "AWS::Serverless::Function" diff --git a/tests/unit/lib/intrinsic_resolver/test_data/outputs/outputs_test_layers_resolution.json b/tests/unit/lib/intrinsic_resolver/test_data/outputs/outputs_test_layers_resolution.json index 4f72492e93..0db313fd7e 100644 --- a/tests/unit/lib/intrinsic_resolver/test_data/outputs/outputs_test_layers_resolution.json +++ b/tests/unit/lib/intrinsic_resolver/test_data/outputs/outputs_test_layers_resolution.json @@ -48,7 +48,7 @@ "Runtime": "python3.6", "CodeUri": ".", "Layers": [ - "MyCustomServerlessLayer" + {"Ref": "MyCustomServerlessLayer"} ] } }, @@ -59,7 +59,7 @@ "Runtime": "python3.6", "CodeUri": ".", "Layers": [ - "MyCustomLambdaLayer" + {"Ref": "MyCustomLambdaLayer"} ] } }, @@ -104,7 +104,7 @@ "Runtime": "python3.6", "CodeUri": ".", "Layers": [ - "MyCustomServerlessLayer" + {"Ref": "MyCustomServerlessLayer"} ] } }, @@ -115,7 +115,7 @@ "Runtime": "python3.6", "CodeUri": ".", "Layers": [ - "MyCustomLambdaLayer" + {"Ref": "MyCustomLambdaLayer"} ] } }, diff --git a/tests/unit/lib/intrinsic_resolver/test_intrinsic_resolver.py b/tests/unit/lib/intrinsic_resolver/test_intrinsic_resolver.py index bee5ea852a..b257604ab1 100644 --- a/tests/unit/lib/intrinsic_resolver/test_intrinsic_resolver.py +++ b/tests/unit/lib/intrinsic_resolver/test_intrinsic_resolver.py @@ -1196,6 +1196,7 @@ def test_fn_if_condition_not_bool_fail(self): class TestIntrinsicAttribteResolution(TestCase): def setUp(self): + self.maxDiff = None logical_id_translator = { "RestApi": "NewRestApi", "LambdaFunction": { @@ -1228,6 +1229,7 @@ def setUp(self): def test_basic_attribte_resolution(self): resolved_template = self.resolver.resolve_attribute(self.resources, ignore_errors=False) + expected_resources = { "HelloHandler2E4FBA4D": { "Properties": {"handler": "main.handle"}, @@ -1245,7 +1247,7 @@ def test_basic_attribte_resolution(self): "Handler": "layer-main.custom_layer_handler", "Runtime": "python3.6", "CodeUri": ".", - "Layers": ["MyCustomLambdaLayer"] + "Layers": [{"Ref": "MyCustomLambdaLayer"}] }, "Type": "AWS::Serverless::Function", }, @@ -1270,7 +1272,7 @@ def test_basic_attribte_resolution(self): } }, } - self.assertEqual(resolved_template, expected_resources) + self.assertEqual(dict(resolved_template), expected_resources) def test_template_fail_errors(self): resources = deepcopy(self.resources) @@ -1314,7 +1316,7 @@ def test_template_ignore_errors(self): "Handler": "layer-main.custom_layer_handler", "Runtime": "python3.6", "CodeUri": ".", - "Layers": ["MyCustomLambdaLayer"] + "Layers": [{"Ref": "MyCustomLambdaLayer"}] }, "Type": "AWS::Serverless::Function", }, @@ -1357,9 +1359,9 @@ def test_template_ignore_errors(self): "RestApiId": "RestApi", "parentId": "/", } - }, + } } - self.assertEqual(expected_template, result) + self.assertEqual(expected_template, dict(result)) class TestResolveTemplate(TestCase): @@ -1518,12 +1520,12 @@ def load_test_data(self, template_path): ]) def test_intrinsic_sample_inputs_outputs(self, input, output): input_template = self.load_test_data(input) - symbol_resolver = IntrinsicsSymbolTable( template=input_template, logical_id_translator={} ) resolver = IntrinsicResolver(template=input_template, symbol_resolver=symbol_resolver) processed_template = resolver.resolve_template() + processed_template = json.loads(json.dumps(processed_template)) # Removes formatting of ordered dicts expected_template = self.load_test_data(output) self.assertEqual(processed_template, expected_template) diff --git a/tests/unit/lib/intrinsic_resolver/test_intrinsics_symbol_table.py b/tests/unit/lib/intrinsic_resolver/test_intrinsics_symbol_table.py index 5254b8dd9a..6887b54e74 100644 --- a/tests/unit/lib/intrinsic_resolver/test_intrinsics_symbol_table.py +++ b/tests/unit/lib/intrinsic_resolver/test_intrinsics_symbol_table.py @@ -92,7 +92,7 @@ def test_default_type_resolver_function(self): } default_type_resolver = { "AWS::ApiGateway::RestApi": { - "RootResourceId": lambda logical_id, resource_attribute: logical_id + "RootResourceId": lambda logical_id: logical_id } } From 383e445d0effef2f598753c9f827dfb24b78a112 Mon Sep 17 00:00:00 2001 From: Vikranth Srivatsa Date: Fri, 16 Aug 2019 15:11:55 -0700 Subject: [PATCH 8/8] Remove old layer ref code --- samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py b/samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py index c8171d7d79..922ed92577 100644 --- a/samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py +++ b/samcli/lib/intrinsic_resolver/intrinsics_symbol_table.py @@ -249,9 +249,6 @@ def resolve_symbols(self, logical_id, resource_attribute, ignore_errors=False): return attribute_resolver(logical_id) return attribute_resolver - if resource_attribute == IntrinsicResolver.REF: - return {IntrinsicResolver.REF: logical_id} - if ignore_errors: return "${}".format(logical_id + "." + resource_attribute) raise InvalidSymbolException(