Skip to content

Commit 650ef1f

Browse files
Support dictionary values in API responses (#697)
Co-authored-by: ykim-1 <[email protected]>
1 parent 56d5801 commit 650ef1f

File tree

3 files changed

+107
-5
lines changed

3 files changed

+107
-5
lines changed

linodecli/baked/response.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,13 +169,25 @@ def _parse_response_model(schema, prefix=None, nested_list_depth=0):
169169
)
170170

171171
attrs = []
172+
172173
if schema.properties is None:
173174
return attrs
174175

175176
for k, v in schema.properties.items():
176177
pref = prefix + "." + k if prefix else k
177178

178-
if v.type == "object":
179+
if (
180+
v.type == "object"
181+
and v.properties is None
182+
and v.additionalProperties is not None
183+
):
184+
# This is a dictionary with arbitrary keys
185+
attrs.append(
186+
OpenAPIResponseAttr(
187+
k, v, prefix=prefix, nested_list_depth=nested_list_depth
188+
)
189+
)
190+
elif v.type == "object":
179191
attrs += _parse_response_model(v, prefix=pref)
180192
elif v.type == "array" and v.items.type == "object":
181193
attrs += _parse_response_model(
@@ -208,6 +220,7 @@ def __init__(self, response: MediaType):
208220
self.is_paginated = _is_paginated(response)
209221

210222
schema_override = response.extensions.get("linode-cli-use-schema")
223+
211224
if schema_override:
212225
override = type(response)(
213226
response.path, {"schema": schema_override}, response._root
@@ -222,6 +235,7 @@ def __init__(self, response: MediaType):
222235
)
223236
else:
224237
self.attrs = _parse_response_model(response.schema)
238+
225239
self.rows = response.extensions.get("linode-cli-rows")
226240
self.nested_list = response.extensions.get("linode-cli-nested-list")
227241
self.subtables = response.extensions.get("linode-cli-subtables")

tests/fixtures/response_test_get.yaml

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ paths:
2929
$ref: '#/components/schemas/PaginationEnvelope/properties/pages'
3030
results:
3131
$ref: '#/components/schemas/PaginationEnvelope/properties/results'
32+
dictLike:
33+
$ref: '#/components/schemas/DictionaryLikeObject'
34+
standard:
35+
$ref: '#/components/schemas/StandardObject'
36+
objectArray:
37+
$ref: '#/components/schemas/ArrayOfObjects'
3238

3339
components:
3440
schemas:
@@ -61,4 +67,27 @@ components:
6167
type: integer
6268
readOnly: true
6369
description: The total number of results.
64-
example: 1
70+
example: 1
71+
DictionaryLikeObject:
72+
type: object
73+
additionalProperties:
74+
type: string # Arbitrary keys with string values
75+
description: Dictionary with arbitrary keys
76+
StandardObject:
77+
type: object
78+
properties:
79+
key1:
80+
type: string
81+
key2:
82+
type: integer
83+
description: Standard object with defined properties
84+
ArrayOfObjects:
85+
type: array
86+
items:
87+
type: object
88+
properties:
89+
subkey1:
90+
type: string
91+
subkey2:
92+
type: boolean
93+
description: Array of objects

tests/unit/test_response.py

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,26 +22,85 @@ def test_model_fix_json_nested(self, list_operation_for_response_test):
2222
]
2323

2424
def test_attr_get_value(self, list_operation_for_response_test):
25-
model = {"foo": {"bar": "cool"}}
25+
model = {"data": {"foo": {"bar": "cool"}}}
2626
attr = list_operation_for_response_test.response_model.attrs[0]
2727

2828
result = attr._get_value(model)
2929

3030
assert result == "cool"
3131

3232
def test_attr_get_string(self, list_operation_for_response_test):
33-
model = {"foo": {"bar": ["cool1", "cool2"]}}
33+
model = {"data": {"foo": {"bar": ["cool1", "cool2"]}}}
3434
attr = list_operation_for_response_test.response_model.attrs[0]
3535

3636
result = attr.get_string(model)
3737

3838
assert result == "cool1 cool2"
3939

4040
def test_attr_render_value(self, list_operation_for_response_test):
41-
model = {"foo": {"bar": ["cool1", "cool2"]}}
41+
model = {"data": {"foo": {"bar": ["cool1", "cool2"]}}}
4242
attr = list_operation_for_response_test.response_model.attrs[0]
4343
attr.color_map = {"default_": "yellow"}
4444

4545
result = attr.render_value(model)
4646

4747
assert result == "[yellow]cool1, cool2[/]"
48+
49+
def test_fix_json_string_type(self, list_operation_for_response_test):
50+
model = list_operation_for_response_test.response_model
51+
model.rows = ["foo.bar", "type"]
52+
53+
input_json = {"foo": {"bar": "string_value"}, "type": "example_type"}
54+
result = model.fix_json(input_json)
55+
56+
assert result == ["string_value", "example_type"]
57+
58+
def test_fix_json_integer_type(self, list_operation_for_response_test):
59+
model = list_operation_for_response_test.response_model
60+
model.rows = ["size", "id"]
61+
62+
input_json = {"size": 42, "id": 123}
63+
result = model.fix_json(input_json)
64+
65+
assert result == [42, 123]
66+
67+
def test_dictionary_like_property(self, list_operation_for_response_test):
68+
model = list_operation_for_response_test.response_model
69+
70+
model.rows = ["dictLike"]
71+
72+
input_data = {"dictLike": {"keyA": "valueA", "keyB": "valueB"}}
73+
74+
result = model.fix_json(input_data)
75+
assert result == [{"keyA": "valueA", "keyB": "valueB"}]
76+
77+
def test_standard_object_property(self, list_operation_for_response_test):
78+
model = list_operation_for_response_test.response_model
79+
80+
# Set rows to include the standard object
81+
model.rows = ["standard"]
82+
83+
# Simulate input data
84+
input_data = {"standard": {"key1": "test", "key2": 42}}
85+
86+
result = model.fix_json(input_data)
87+
assert result == [{"key1": "test", "key2": 42}]
88+
89+
def test_array_of_objects_property(self, list_operation_for_response_test):
90+
model = list_operation_for_response_test.response_model
91+
92+
model.rows = ["objectArray"]
93+
94+
# Simulate input data
95+
input_data = {
96+
"objectArray": [
97+
{"subkey1": "item1", "subkey2": True},
98+
{"subkey1": "item2", "subkey2": False},
99+
]
100+
}
101+
102+
result = model.fix_json(input_data)
103+
assert result == [
104+
{"subkey1": "item1", "subkey2": True},
105+
{"subkey1": "item2", "subkey2": False},
106+
]

0 commit comments

Comments
 (0)