Skip to content
This repository was archived by the owner on Aug 31, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions serverlessrepo/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import six
import yaml
from yaml.resolver import ScalarNode, SequenceNode
from collections import OrderedDict

from .application_metadata import ApplicationMetadata
from .exceptions import ApplicationMetadataNotFoundError
Expand Down Expand Up @@ -51,6 +52,10 @@ def intrinsics_multi_constructor(loader, tag_prefix, node):
return {cfntag: value}


def _dict_representer(dumper, data):
return dumper.represent_dict(data.items())


def yaml_dump(dict_to_dump):
"""
This function dumps the dictionary as a YAML document
Expand All @@ -59,9 +64,14 @@ def yaml_dump(dict_to_dump):
:return: YAML document
:rtype: str
"""
yaml.SafeDumper.add_representer(OrderedDict, _dict_representer)
return yaml.safe_dump(dict_to_dump, default_flow_style=False)


def _dict_constructor(loader, node):
return OrderedDict(loader.construct_pairs(node))


def parse_template(template_str):
"""
This function parses the SAM template
Expand All @@ -75,10 +85,11 @@ def parse_template(template_str):
# PyYAML doesn't support json as well as it should, so if the input
# is actually just json it is better to parse it with the standard
# json parser.
return json.loads(template)
return json.loads(template_str)
except ValueError:
yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, _dict_constructor)
yaml.SafeLoader.add_multi_constructor('!', intrinsics_multi_constructor)
return yaml.safe_load(template)
return yaml.safe_load(template_str)


def get_app_metadata(template_dict):
Expand All @@ -89,6 +100,7 @@ def get_app_metadata(template_dict):
:type template_dict: dict
:return: Application metadata as defined in the template
:rtype: ApplicationMetadata
:raises ApplicationMetadataNotFoundError
"""
if METADATA in template_dict and SERVERLESS_REPO_APPLICATION in template_dict[METADATA]:
app_metadata_dict = template_dict[METADATA][SERVERLESS_REPO_APPLICATION]
Expand Down
44 changes: 37 additions & 7 deletions tests/unit/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from serverlessrepo.exceptions import ApplicationMetadataNotFoundError
from serverlessrepo.application_metadata import ApplicationMetadata
from serverlessrepo.parser import parse_sam_template, yaml_dump, get_app_metadata
from serverlessrepo.parser import parse_template, yaml_dump, get_app_metadata


class TestParser(TestCase):
Expand Down Expand Up @@ -46,12 +46,12 @@ class TestParser(TestCase):
}

def test_parse_yaml_with_tags(self):
output = parse_sam_template(self.yaml_with_tags)
output = parse_template(self.yaml_with_tags)
self.assertEquals(self.parsed_yaml_dict, output)

# Make sure formatter and parser work well with each other
formatted_str = yaml_dump(output)
output_again = parse_sam_template(formatted_str)
output_again = parse_template(formatted_str)
self.assertEquals(output, output_again)

def test_yaml_getatt(self):
Expand All @@ -70,14 +70,44 @@ def test_yaml_getatt(self):
}
}

actual_output = parse_sam_template(input)
actual_output = parse_template(input)
self.assertEquals(actual_output, output)

def test_parse_json_with_tabs(self):
template = '{\n\t"foo": "bar"\n}'
output = parse_sam_template(template)
output = parse_template(template)
self.assertEqual(output, {'foo': 'bar'})

def test_parse_yaml_preserve_elements_order(self):
input_template = """
B_Resource:
Key2:
Name: name2
Key1:
Name: name1
A_Resource:
Key2:
Name: name2
Key1:
Name: name1
"""
output_dict = parse_template(input_template)
expected_dict = {
'B_Resource': {
'Key2': {'Name': 'name2'},
'Key1': {'Name': 'name1'}
},
'A_Resource': {
'Key2': {'Name': 'name2'},
'Key1': {'Name': 'name1'}
}
}
self.assertEqual(expected_dict, output_dict)
output_template = yaml_dump(output_dict)
# yaml dump changes indentation, remove spaces and new line characters to just compare the text
self.assertEqual(input_template.translate(None, '\n '),
output_template.translate(None, '\n '))

def test_get_app_metadata_missing_metadata(self):
template_dict_without_metadata = {
'RandomKey': {
Expand All @@ -88,7 +118,7 @@ def test_get_app_metadata_missing_metadata(self):
get_app_metadata(template_dict_without_metadata)

message = str(context.exception)
expected = 'missing Metadata section'
expected = 'missing AWS::ServerlessRepo::Application section in template Metadata'
self.assertTrue(expected in message)

def test_get_app_metadata_missing_app_metadata(self):
Expand All @@ -101,7 +131,7 @@ def test_get_app_metadata_missing_app_metadata(self):
get_app_metadata(template_dict_without_app_metadata)

message = str(context.exception)
expected = 'missing AWS::ServerlessRepo::Application section'
expected = 'missing AWS::ServerlessRepo::Application section in template Metadata'
self.assertTrue(expected in message)

def test_get_app_metadata_return_metadata(self):
Expand Down