Skip to content

Commit a8cee69

Browse files
Fixed bug causing deeply nested fields to be improperly displayed in --help output
1 parent d9a1804 commit a8cee69

3 files changed

Lines changed: 71 additions & 1 deletion

File tree

‎linodecli/baked/request.py‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,12 +182,20 @@ def _parse_request_model(
182182
depth=depth,
183183
)
184184

185-
# Handle arrays of objects that not marked as JSON
185+
# Handle arrays of objects that not marked as JSON.
186+
# NOTE: We only expand an array of objects into individual child
187+
# arguments when it is not already nested under another list
188+
# (i.e. parent is None). The CLI can only associate one level of
189+
# nested list objects, so a list of objects nested within another
190+
# list must be treated as JSON instead.
191+
# Otherwise, we would generate child arguments that can never
192+
# be used because they always conflict with their implicit JSON parent.
186193
elif (
187194
v.type == "array"
188195
and v.items
189196
and v.items.type == "object"
190197
and v.extensions.get("linode-cli-format") != "json"
198+
and parent is None
191199
):
192200
# handle lists of objects as a special case, where each property
193201
# of the object in the list is its own argument

‎tests/fixtures/api_request_test_foobar_post.yaml‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,19 @@ components:
119119
description: An arbitrary deeply nested array.
120120
items:
121121
type: string
122+
field_object_array:
123+
type: array
124+
description: An arbitrary array of objects nested within a list.
125+
items:
126+
type: object
127+
description: An arbitrary deeply nested object.
128+
properties:
129+
nested_object_string:
130+
type: string
131+
description: A string on a deeply nested list object.
132+
nested_object_int:
133+
type: number
134+
description: An int on a deeply nested list object.
122135
field_string:
123136
type: string
124137
description: An arbitrary field.

‎tests/unit/test_operation.py‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ def test_parse_args_object_list(self, create_operation):
196196
"field_int": 123,
197197
"field_dict": {"nested_string": "test2", "nested_int": 789},
198198
"field_array": ExplicitJsonValue(json_value=["foo", "bar"]),
199+
"field_object_array": None, # We expect this to be filtered out later
199200
"nullable_string": None, # We expect this to be filtered out later
200201
},
201202
{"field_int": 456, "field_dict": {"nested_string": "test3"}},
@@ -218,6 +219,54 @@ def test_parse_args_object_list_json(self, create_operation):
218219

219220
assert result.object_list.json_value == expected
220221

222+
def test_nested_object_list_treated_as_json(self, create_operation):
223+
"""
224+
An array of objects nested within another array of objects (e.g.
225+
--object_list.field_object_array) can only be specified as JSON.
226+
It should not be expanded into unusable child arguments (e.g.
227+
--object_list.field_object_array.nested_object_string).
228+
"""
229+
args_by_path = {arg.path: arg for arg in create_operation.args}
230+
231+
nested = args_by_path.get("object_list.field_object_array")
232+
assert nested is not None
233+
assert nested.format == "json"
234+
assert nested.datatype == "object"
235+
assert nested.is_child
236+
assert nested.parent == "object_list"
237+
assert not nested.is_parent
238+
239+
# No child arguments should have been generated for its properties.
240+
assert "object_list.field_object_array.nested_object_string" not in (
241+
args_by_path
242+
)
243+
assert "object_list.field_object_array.nested_object_int" not in (
244+
args_by_path
245+
)
246+
247+
def test_parse_args_nested_object_list_json(self, create_operation):
248+
"""
249+
A nested array of objects should be accepted as a JSON string value
250+
associated with each entry of its parent list.
251+
"""
252+
result = create_operation.parse_args(
253+
[
254+
"--object_list.field_string",
255+
"test1",
256+
"--object_list.field_object_array",
257+
json.dumps([{"nested_object_string": "foo"}]),
258+
]
259+
)
260+
261+
assert result.object_list == [
262+
{
263+
"field_string": "test1",
264+
"field_object_array": ExplicitJsonValue(
265+
json_value=[{"nested_object_string": "foo"}]
266+
),
267+
},
268+
]
269+
221270
def test_parse_args_conflicting_parent_child(self, create_operation):
222271
stderr_buf = io.StringIO()
223272

0 commit comments

Comments
 (0)