Skip to content

Commit f0e3769

Browse files
authored
Fix for docs generation when no items key for a property is present. (#1052)
1 parent c29b56c commit f0e3769

File tree

4 files changed

+209
-0
lines changed

4 files changed

+209
-0
lines changed

src/rpdk/core/project.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,22 @@ def __set_property_type(prop_type, single_type=True):
959959
lambda markdown_value: f"List of {markdown_value}"
960960
) # noqa: E731
961961

962+
if "items" not in prop.keys():
963+
LOG.warning(
964+
'Warning: found schema property of array type with no "items" key; \n'
965+
'defaulting data types for array items to "Map" in generated docs.\n'
966+
'If "Map" is not what you need instead, specify the expected data type \n'
967+
"for array items such as (example with items of string type):\n"
968+
' "ExampleProperty" : {\n'
969+
' "description" : "Example description.",\n'
970+
' "type": "array",\n'
971+
' "items": {\n'
972+
' "type": "string"\n'
973+
" }\n"
974+
" }\n"
975+
)
976+
prop["items"] = {}
977+
962978
# potential circular ref
963979
# setting up markdown before going deep in the heap to reuse markdown
964980
if "$ref" in prop["items"]:
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"typeName": "TestOnly::Sample::Hook",
3+
"description": "Example hook schema for unit tests.",
4+
"sourceUrl": "https://github.com/aws-cloudformation/SAMPLE-URL-SUFFIX",
5+
"documentationUrl": "https://github.com/aws-cloudformation/SAMPLE-URL-SUFFIX/blob/main/README.md",
6+
"typeConfiguration": {
7+
"properties": {
8+
"exampleArrayProperty": {
9+
"type": "array",
10+
"description": "Example property of array type with items of string type.",
11+
"items": {
12+
"type": "string"
13+
}
14+
}
15+
},
16+
"additionalProperties": false
17+
},
18+
"required": [],
19+
"handlers": {
20+
"preCreate": {
21+
"targetNames": [
22+
"AWS::S3::Bucket"
23+
],
24+
"permissions": []
25+
},
26+
"preUpdate": {
27+
"targetNames": [
28+
"AWS::S3::Bucket"
29+
],
30+
"permissions": []
31+
},
32+
"preDelete": {
33+
"targetNames": [
34+
"AWS::S3::Bucket"
35+
],
36+
"permissions": []
37+
}
38+
},
39+
"additionalProperties": false
40+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"typeName": "TestOnly::Sample::Hook",
3+
"description": "Example hook schema for unit tests.",
4+
"sourceUrl": "https://github.com/aws-cloudformation/SAMPLE-URL-SUFFIX",
5+
"documentationUrl": "https://github.com/aws-cloudformation/SAMPLE-URL-SUFFIX/blob/main/README.md",
6+
"typeConfiguration": {
7+
"properties": {
8+
"exampleArrayProperty": {
9+
"type": "array",
10+
"description": "Example property of array type without items (that is, an 'items` key at this same level)."
11+
}
12+
},
13+
"additionalProperties": false
14+
},
15+
"required": [],
16+
"handlers": {
17+
"preCreate": {
18+
"targetNames": [
19+
"AWS::S3::Bucket"
20+
],
21+
"permissions": []
22+
},
23+
"preUpdate": {
24+
"targetNames": [
25+
"AWS::S3::Bucket"
26+
],
27+
"permissions": []
28+
},
29+
"preDelete": {
30+
"targetNames": [
31+
"AWS::S3::Bucket"
32+
],
33+
"permissions": []
34+
}
35+
},
36+
"additionalProperties": false
37+
}

tests/test_project.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,122 @@ def test_generate_docs_with_multiref_property(project, tmp_path_factory):
601601
assert read_me_stripped == read_me_target_stripped
602602

603603

604+
def test_when_array_property_has_items_then_generated_docs_should_use_specified_items_type(
605+
project, tmp_path_factory, session
606+
):
607+
project.artifact_type = ARTIFACT_TYPE_HOOK
608+
project.schema = resource_json(
609+
__name__,
610+
"data/schema/hook/valid/valid_hook_configuration_with_array_items.json",
611+
)
612+
project.type_name = "TestOnly::Sample::Hook"
613+
# tmpdir conflicts with other tests, make a unique one
614+
project.root = tmp_path_factory.mktemp(
615+
"generate_docs_when_array_property_has_items"
616+
)
617+
618+
project.load_configuration_schema()
619+
620+
mock_plugin = MagicMock(spec=["generate"])
621+
patch_session = patch("rpdk.core.boto_helpers.Boto3Session")
622+
623+
def get_test_schema():
624+
return {
625+
"typeName": "AWS::S3::Bucket",
626+
"description": "test schema",
627+
"properties": {"foo": {"type": "string"}},
628+
"primaryIdentifier": ["/properties/foo"],
629+
"additionalProperties": False,
630+
}
631+
632+
mock_cfn_client = MagicMock(spec=["describe_type"])
633+
with patch.object(project, "_plugin", mock_plugin), patch_session as mock_session:
634+
mock_cfn_client.describe_type.return_value = {
635+
"Schema": json.dumps(get_test_schema()),
636+
"Type": "",
637+
"ProvisioningType": "",
638+
}
639+
session.client.side_effect = [mock_cfn_client, MagicMock()]
640+
mock_session.return_value = session
641+
project.generate()
642+
project.generate_docs()
643+
mock_plugin.generate.assert_called_once_with(project)
644+
645+
docs_dir = project.root / "docs"
646+
readme_file = project.root / "docs" / "README.md"
647+
648+
assert docs_dir.is_dir()
649+
assert readme_file.is_file()
650+
with patch.object(project, "_plugin", mock_plugin), patch_session as mock_session:
651+
session.client.side_effect = [mock_cfn_client, MagicMock()]
652+
mock_session.return_value = session
653+
project.generate()
654+
readme_contents = readme_file.read_text(encoding="utf-8").strip().replace("\n", " ")
655+
assert project.type_name in readme_contents
656+
assert (
657+
"exampleArrayProperty Example property of array type with items of string type. _Required_: No _Type_: List of String"
658+
in readme_contents
659+
)
660+
661+
662+
def test_when_array_property_has_no_items_then_generated_docs_should_default_to_map_items_type(
663+
project, tmp_path_factory, session
664+
):
665+
project.artifact_type = ARTIFACT_TYPE_HOOK
666+
project.schema = resource_json(
667+
__name__,
668+
"data/schema/hook/valid/valid_hook_configuration_without_array_items.json",
669+
)
670+
project.type_name = "TestOnly::Sample::Hook"
671+
# tmpdir conflicts with other tests, make a unique one
672+
project.root = tmp_path_factory.mktemp(
673+
"generate_docs_when_array_property_has_no_items"
674+
)
675+
676+
project.load_configuration_schema()
677+
678+
mock_plugin = MagicMock(spec=["generate"])
679+
patch_session = patch("rpdk.core.boto_helpers.Boto3Session")
680+
681+
def get_test_schema():
682+
return {
683+
"typeName": "AWS::S3::Bucket",
684+
"description": "test schema",
685+
"properties": {"foo": {"type": "string"}},
686+
"primaryIdentifier": ["/properties/foo"],
687+
"additionalProperties": False,
688+
}
689+
690+
mock_cfn_client = MagicMock(spec=["describe_type"])
691+
with patch.object(project, "_plugin", mock_plugin), patch_session as mock_session:
692+
mock_cfn_client.describe_type.return_value = {
693+
"Schema": json.dumps(get_test_schema()),
694+
"Type": "",
695+
"ProvisioningType": "",
696+
}
697+
session.client.side_effect = [mock_cfn_client, MagicMock()]
698+
mock_session.return_value = session
699+
project.generate()
700+
project.generate_docs()
701+
mock_plugin.generate.assert_called_once_with(project)
702+
703+
docs_dir = project.root / "docs"
704+
readme_file = project.root / "docs" / "README.md"
705+
706+
assert docs_dir.is_dir()
707+
assert readme_file.is_file()
708+
with patch.object(project, "_plugin", mock_plugin), patch_session as mock_session:
709+
session.client.side_effect = [mock_cfn_client, MagicMock()]
710+
mock_session.return_value = session
711+
project.generate()
712+
readme_contents = readme_file.read_text(encoding="utf-8").strip().replace("\n", " ")
713+
assert project.type_name in readme_contents
714+
assert (
715+
"exampleArrayProperty Example property of array type without items (that is, an 'items` key at this same level). _Required_: No _Type_: List of Map"
716+
in readme_contents
717+
)
718+
719+
604720
def test_generate_with_docs_invalid_property_type(project, tmp_path_factory):
605721
project.schema = resource_json(
606722
__name__, "data/schema/invalid/invalid_property_type_invalid.json"

0 commit comments

Comments
 (0)