Skip to content

Commit 18c73b0

Browse files
authored
add cfn transformation function with key skip (#12976)
1 parent 1a3a1ce commit 18c73b0

File tree

2 files changed

+96
-1
lines changed

2 files changed

+96
-1
lines changed

localstack-core/localstack/services/cloudformation/provider_utils.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,12 @@ def recurse_properties(properties: dict, fn: Callable) -> dict:
7373
return _recurse_properties(deepcopy(properties), fn)
7474

7575

76-
def keys_pascalcase_to_lower_camelcase(model: dict) -> dict:
76+
def keys_pascalcase_to_lower_camelcase(model: dict, skip_keys: set = None) -> dict:
7777
"""Recursively change any dicts keys to lower camelcase"""
7878

79+
if skip_keys:
80+
return _pascal_to_camel_keys_preserve_values(model, skip_keys)
81+
7982
def _keys_pascalcase_to_lower_camelcase(obj):
8083
if isinstance(obj, dict):
8184
return {convert_pascalcase_to_lower_camelcase(k): v for k, v in obj.items()}
@@ -85,6 +88,33 @@ def _keys_pascalcase_to_lower_camelcase(obj):
8588
return _recurse_properties(model, _keys_pascalcase_to_lower_camelcase)
8689

8790

91+
def _pascal_to_camel_keys_preserve_values(model: dict, skip_keys: set = None) -> dict:
92+
"""
93+
Variant of keys_pascalcase_to_lower_camelcase
94+
All VALUES of provided keys are skipped and not transformed to lower camelcase.
95+
The keys themselves will be transformed.
96+
The function simply stops recursion if a key matches, so make sure no lower level values are ignored.
97+
"""
98+
skip_keys = skip_keys or set()
99+
100+
def _transform(obj):
101+
if isinstance(obj, dict):
102+
new_dict = {}
103+
for k, v in obj.items():
104+
new_key = convert_pascalcase_to_lower_camelcase(k)
105+
if k in skip_keys:
106+
new_dict[new_key] = v
107+
else:
108+
new_dict[new_key] = _transform(v)
109+
return new_dict
110+
elif isinstance(obj, list):
111+
return [_transform(i) for i in obj]
112+
else:
113+
return obj
114+
115+
return _transform(model)
116+
117+
88118
def keys_lower_camelcase_to_pascalcase(model: dict) -> dict:
89119
"""Recursively change any dicts keys to PascalCase"""
90120

tests/unit/services/cloudformation/test_provider_utils.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,68 @@ def test_lower_camelcase_to_pascalcase(self):
135135
}
136136
],
137137
}
138+
139+
def test_lower_camelcase_to_pascalcase_skip_keys(self):
140+
original_dict = {
141+
"Stages": [
142+
{
143+
"Actions": [
144+
{
145+
"Actiontypeid": {
146+
"Category": "Source",
147+
"Owner": "AWS",
148+
"Provider": "S3",
149+
"Version": "1",
150+
},
151+
"Configuration": {
152+
"S3bucket": "localstack-codepipeline-source-86a13a88",
153+
"S3objectkey": "source-key",
154+
"Subconfig": {"Subconfig1": "Foo", "Subconfig2": "bar"},
155+
},
156+
"Inputartifacts": [],
157+
"Name": "S3Source",
158+
"Namespace": "S3SourceVariables",
159+
"Outputartifacts": [{"Name": "Artifact_Source_S3Source"}],
160+
"Rolearn": "arn:aws:iam::096845016391:role/EcrPipelineStack-MyPipelineSourceS3SourceCodePipeli-YOoRQUZQe6WU",
161+
"Runorder": 1,
162+
}
163+
],
164+
"Name": "Source",
165+
}
166+
]
167+
}
168+
target_dict = {
169+
"stages": [
170+
{
171+
"actions": [
172+
{
173+
"actiontypeid": {
174+
"category": "Source",
175+
"owner": "AWS",
176+
"provider": "S3",
177+
"version": "1",
178+
},
179+
# The excluded key itself is transformed
180+
# Its values are not
181+
# Recursion stops, items at lower levels are not transformed as well
182+
"configuration": {
183+
"S3bucket": "localstack-codepipeline-source-86a13a88",
184+
"S3objectkey": "source-key",
185+
"Subconfig": {"Subconfig1": "Foo", "Subconfig2": "bar"},
186+
},
187+
"inputartifacts": [],
188+
"name": "S3Source",
189+
"namespace": "S3SourceVariables",
190+
"outputartifacts": [{"name": "Artifact_Source_S3Source"}],
191+
"rolearn": "arn:aws:iam::096845016391:role/EcrPipelineStack-MyPipelineSourceS3SourceCodePipeli-YOoRQUZQe6WU",
192+
"runorder": 1,
193+
}
194+
],
195+
"name": "Source",
196+
}
197+
]
198+
}
199+
converted_dict = utils.keys_pascalcase_to_lower_camelcase(
200+
original_dict, skip_keys={"Configuration"}
201+
)
202+
assert converted_dict == target_dict

0 commit comments

Comments
 (0)