Skip to content

Commit

Permalink
Render patternProperties (#419)
Browse files Browse the repository at this point in the history
* Render patterProperties

* Use exception class rather than generic Exception

* fixed tests

* Addressing some PR comments

* Consider that objects can have no properties

* Adding missed schema for no prop tests
  • Loading branch information
arnulfojr authored Apr 22, 2020
1 parent c2f4ff2 commit 1a6fcdd
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 3 deletions.
24 changes: 22 additions & 2 deletions src/rpdk/core/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,18 @@
}


MARKDOWN_RESERVED_CHARACTERS = frozenset({"^", "*", "+", ".", "(", "[", "{", "#"})


def escape_markdown(string):
"""Escapes the reserved Markdown characters."""
if not string:
return string
if string[0] in MARKDOWN_RESERVED_CHARACTERS:
return "\\{}".format(string)
return string


class Project: # pylint: disable=too-many-instance-attributes
def __init__(self, overwrite_enabled=False, root=None):
self.overwrite_enabled = overwrite_enabled
Expand All @@ -98,6 +110,8 @@ def __init__(self, overwrite_enabled=False, root=None):
autoescape=select_autoescape(["html", "htm", "xml", "md"]),
)

self.env.filters["escape_markdown"] = escape_markdown

LOG.debug("Root directory: %s", self.root)

@property
Expand Down Expand Up @@ -399,7 +413,9 @@ def _get_property_description(prop):
for prop in docs_schema.get("readOnlyProperties", [])
]

def _set_docs_properties(self, propname, prop, proppath):
def _set_docs_properties(
self, propname, prop, proppath
): # pylint: disable=too-many-locals
if "$ref" in prop:
prop = RefResolver.from_schema(self.schema).resolve(prop["$ref"])[1]

Expand Down Expand Up @@ -432,9 +448,13 @@ def _set_docs_properties(self, propname, prop, proppath):
template = self.env.get_template("docs-subproperty.md")
docs_path = self.root / "docs"

object_properties = (
prop.get("properties") or prop.get("patternProperties") or {}
)

prop["properties"] = {
name: self._set_docs_properties(name, value, proppath + (name,))
for name, value in prop["properties"].items()
for name, value in object_properties.items()
}

subproperty_name = " ".join(proppath)
Expand Down
2 changes: 1 addition & 1 deletion src/rpdk/core/templates/docs-subproperty.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ To declare this entity in your AWS CloudFormation template, use the following sy

{% for propname, prop in schema.properties.items() %}
{% if not prop.readonly %}
#### {{ propname }}
#### {{ propname | escape_markdown }}
{% if prop.description %}

{{ prop.description }}
Expand Down
17 changes: 17 additions & 0 deletions tests/data/schema/valid/valid_no_properties.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"typeName": "AWS::Valid::TypeName",
"description": "a test schema",
"properties": {
"obj2": {
"type": "object",
"description": "obj with no props"
}
},
"primaryIdentifier": [
"/properties/enum1"
],
"readOnlyProperties": [
"/properties/str3"
],
"additionalProperties": false
}
81 changes: 81 additions & 0 deletions tests/data/schema/valid/valid_pattern_properties.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"typeName": "AWS::Valid::TypeName",
"description": "a test schema",
"definitions": {
"obj1def": {
"type": "object",
"additionalProperties": false,
"properties": {
"str1": {
"type": "string",
"minLength": 2
}
}
},
"obj2def": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
".*": {
"type": "string"
}
}
}
},
"properties": {
"enum1": {
"type": "string",
"enum": [
"test",
"test2"
]
},
"str2": {
"type": "string",
"description": "some description"
},
"obj1": {
"type": "object",
"description": "",
"$ref": "#/definitions/obj1def"
},
"obj2": {
"type": "object",
"description": "",
"$ref": "#/definitions/obj2def"
},
"str3": {
"type": "string",
"description": ""
},
"arr1": {
"type": "array",
"description": "",
"items": {
"$ref": "#/definitions/obj2def"
}
}
},
"primaryIdentifier": [
"/properties/enum1"
],
"readOnlyProperties": [
"/properties/str3"
],
"additionalIdentifiers": [
[
"/properties/enum1",
"/properties/str2"
],
[
"/properties/obj1/obj2/str1"
],
[
"/properties/str2"
]
],
"createOnlyProperties": [
"/properties/str2"
],
"additionalProperties": false
}
28 changes: 28 additions & 0 deletions tests/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
SCHEMA_UPLOAD_FILENAME,
SETTINGS_FILENAME,
Project,
escape_markdown,
)
from rpdk.core.test import empty_override
from rpdk.core.upload import Uploader
Expand Down Expand Up @@ -58,6 +59,25 @@
}


@pytest.mark.parametrize(
"string", ["^[a-z]$", "([a-z])", ".*", "*."],
)
def test_escape_markdown_with_regex_names(string):
assert escape_markdown(string).startswith("\\")


def test_escape_markdown_with_empty_string():
assert escape_markdown("") == ""
assert escape_markdown(None) is None


@pytest.mark.parametrize(
"string", ["Hello", "SomeProperty"],
)
def test_escape_markdown(string):
assert escape_markdown(string) == string


@pytest.fixture
def project(tmpdir):
unique_dir = "".join(random.choices(string.ascii_uppercase, k=12))
Expand Down Expand Up @@ -185,6 +205,14 @@ def test_generate_no_handlers(project):
"data/schema/valid/valid_type_complex.json",
"generate_with_docs_type_complex",
),
(
"data/schema/valid/valid_pattern_properties.json",
"generate_with_docs_pattern_properties",
),
(
"data/schema/valid/valid_no_properties.json",
"generate_with_docs_no_properties",
),
(
"data/schema/valid/valid_nested_property_object.json",
"generate_with_docs_nested_object",
Expand Down

0 comments on commit 1a6fcdd

Please sign in to comment.