Skip to content

Commit 78e9fa9

Browse files
authored
Allow Transform section to have the full transform def (#3470)
* Allow Transform section to have the full transform def * Switch transform validation to rule E1005
1 parent d598742 commit 78e9fa9

File tree

12 files changed

+177
-25
lines changed

12 files changed

+177
-25
lines changed

src/cfnlint/context/context.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def __post_init__(self, transforms) -> None:
3838

3939
for transform in transforms:
4040
if not isinstance(transform, str):
41-
raise ValueError("Transform must be a string")
41+
continue
4242
self._transforms.append(transform)
4343

4444
def has_language_extensions_transform(self):

src/cfnlint/data/schemas/other/template/configuration.json

+1-9
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,7 @@
2222
"Rules": {
2323
"type": "object"
2424
},
25-
"Transform": {
26-
"items": {
27-
"type": "string"
28-
},
29-
"type": [
30-
"string",
31-
"array"
32-
]
33-
}
25+
"Transform": {}
3426
},
3527
"required": [
3628
"Resources"

src/cfnlint/data/schemas/other/transforms/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"additionalProperties": false,
3+
"items": {
4+
"$ref": "#/",
5+
"type": [
6+
"string",
7+
"object"
8+
]
9+
},
10+
"properties": {
11+
"Name": {
12+
"type": "string"
13+
},
14+
"Parameters": {
15+
"patternProperties": {
16+
".*": {
17+
"type": [
18+
"string",
19+
"array",
20+
"boolean",
21+
"object",
22+
"number",
23+
"integer"
24+
]
25+
}
26+
},
27+
"type": "object"
28+
}
29+
},
30+
"required": [
31+
"Name"
32+
],
33+
"type": [
34+
"string",
35+
"array",
36+
"object"
37+
]
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""
2+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
SPDX-License-Identifier: MIT-0
4+
"""
5+
6+
from __future__ import annotations
7+
8+
import cfnlint.data.schemas.other.transforms
9+
from cfnlint.rules.jsonschema.CfnLintJsonSchema import CfnLintJsonSchema, SchemaDetails
10+
11+
12+
class Configuration(CfnLintJsonSchema):
13+
"""Check if Parameters are configured correctly"""
14+
15+
id = "E1005"
16+
shortdesc = "Validate Transform configuration"
17+
description = (
18+
"Validate that the transforms section of a template is properly configured"
19+
)
20+
source_url = "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/transform-section-structure.html"
21+
tags = ["transform"]
22+
23+
def __init__(self):
24+
"""Init"""
25+
super().__init__(
26+
keywords=["Transform"],
27+
schema_details=SchemaDetails(
28+
cfnlint.data.schemas.other.transforms, "configuration.json"
29+
),
30+
all_matches=True,
31+
)

src/cfnlint/rules/transforms/__init__.py

Whitespace-only changes.

test/integration/test_schema_files.py

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class TestSchemaFiles(TestCase):
5151
"Resources/*/Type",
5252
"Resources/*/UpdatePolicy",
5353
"Resources/*/UpdateReplacePolicy",
54+
"Transform",
5455
]
5556

5657
def setUp(self) -> None:

test/unit/module/context/test_transforms.py

+5-12
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@
1212
"name,instance,expected",
1313
[
1414
("Valid transforms", "AWS::LanguageExtensions", True),
15-
("Valid transforms lists", ["AWS::LanguageExtensions"], True),
15+
(
16+
"Valid transforms lists",
17+
["AWS::LanguageExtensions", {"Name": "Include"}],
18+
True,
19+
),
1620
("Valid transforms lists", None, False),
1721
("Valid transforms lists", "", False),
1822
],
@@ -23,14 +27,3 @@ def test_transforms(name, instance, expected):
2327
assert (
2428
expected == transforms.has_language_extensions_transform()
2529
), f"{name!r} test got {transforms.has_language_extensions_transform()}"
26-
27-
28-
@pytest.mark.parametrize(
29-
"name,instance",
30-
[
31-
("Invalid Type", {}),
32-
],
33-
)
34-
def test_errors(name, instance):
35-
with pytest.raises(ValueError):
36-
Transforms(instance)

test/unit/rules/__init__.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,7 @@ def helper_file_negative(self, filename, err_count, config=None):
5656
self.assertEqual(
5757
err_count,
5858
len(failures),
59-
"Expected {} failures but got {} on {}".format(0, failures, filename),
59+
"Expected {} failures but got {} on {}".format(
60+
err_count, failures, filename
61+
),
6062
)

test/unit/rules/templates/test_base_template.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def test_file_negative(self):
4040

4141
def test_file_base(self):
4242
"""Test failure"""
43-
self.helper_file_negative("test/fixtures/templates/bad/templates/base.yaml", 2)
43+
self.helper_file_negative("test/fixtures/templates/bad/templates/base.yaml", 1)
4444

4545
def test_file_base_date(self):
4646
"""Test failure"""
@@ -51,5 +51,5 @@ def test_file_base_date(self):
5151
def test_file_base_null(self):
5252
"""Test failure"""
5353
self.helper_file_negative(
54-
"test/fixtures/templates/bad/templates/base_null.yaml", 3
54+
"test/fixtures/templates/bad/templates/base_null.yaml", 2
5555
)

test/unit/rules/transforms/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
"""
2+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
SPDX-License-Identifier: MIT-0
4+
"""
5+
6+
from collections import deque
7+
8+
import pytest
9+
10+
from cfnlint.jsonschema import ValidationError
11+
from cfnlint.rules.transforms.Configuration import Configuration
12+
13+
14+
@pytest.fixture(scope="module")
15+
def rule():
16+
rule = Configuration()
17+
yield rule
18+
19+
20+
@pytest.mark.parametrize(
21+
"name,instance,expected",
22+
[
23+
(
24+
"Empty list is ok",
25+
[],
26+
[],
27+
),
28+
(
29+
"String is ok",
30+
"Foo",
31+
[],
32+
),
33+
(
34+
"List is ok",
35+
["Foo", "Bar"],
36+
[],
37+
),
38+
(
39+
"Object is ok",
40+
{"Name": "Foo", "Parameters": {"Bar": "Test"}},
41+
[],
42+
),
43+
(
44+
"Array of objects is ok",
45+
[
46+
{"Name": "Foo", "Parameters": {"Bar": "Test"}},
47+
"Foo",
48+
],
49+
[],
50+
),
51+
(
52+
"Missing required Name",
53+
{"Parameters": {"Bar": "Test"}},
54+
[
55+
ValidationError(
56+
"'Name' is a required property",
57+
validator="required",
58+
rule=Configuration(),
59+
path=deque([]),
60+
schema_path=deque(["required"]),
61+
)
62+
],
63+
),
64+
(
65+
"No additional property names are allowed",
66+
{"Name": "Foo", "Foo": "Bar", "Parameters": {"Bar": "Test"}},
67+
[
68+
ValidationError(
69+
"Additional properties are not allowed ('Foo' was unexpected)",
70+
validator="additionalProperties",
71+
rule=Configuration(),
72+
path=deque(["Foo"]),
73+
schema_path=deque(["additionalProperties"]),
74+
)
75+
],
76+
),
77+
(
78+
"Null is not ok",
79+
None,
80+
[
81+
ValidationError(
82+
"None is not of type 'string', 'array', 'object'",
83+
validator="type",
84+
rule=Configuration(),
85+
path=deque([]),
86+
schema_path=deque(["type"]),
87+
)
88+
],
89+
),
90+
],
91+
)
92+
def test_validate(name, instance, expected, rule, validator):
93+
errs = list(rule.validate(validator, {}, instance, {}))
94+
95+
assert errs == expected, f"Test {name!r} got {errs!r}"

0 commit comments

Comments
 (0)