Skip to content

Commit 50b1f12

Browse files
fix: use the [] after key names for array variables
1. If a value is of type ArrayAttribute then append '[]' to the name of the value. This is step 3 in a series of steps of our goal to add full support for the GitLab API data types[1]: * array * hash * array of hashes Step one was: commit 5127b15 Step two was: commit a57334f Fixes: #1698 [1] https://docs.gitlab.com/ee/api/#encoding-api-parameters-of-array-and-hash-types
1 parent 1ecbc7c commit 50b1f12

File tree

5 files changed

+65
-23
lines changed

5 files changed

+65
-23
lines changed

gitlab/types.py

+17-7
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ def get(self) -> Any:
6363
def set_from_cli(self, cli_value: Any) -> None:
6464
self._value = cli_value
6565

66-
def get_for_api(self) -> Any:
67-
return self._value
66+
def get_for_api(self, *, key: str) -> Tuple[str, Any]:
67+
return (key, self._value)
6868

6969

7070
class _ListArrayAttribute(GitlabAttribute):
@@ -76,30 +76,40 @@ def set_from_cli(self, cli_value: str) -> None:
7676
else:
7777
self._value = [item.strip() for item in cli_value.split(",")]
7878

79-
def get_for_api(self) -> str:
79+
def get_for_api(self, *, key: str) -> Tuple[str, str]:
8080
# Do not comma-split single value passed as string
8181
if isinstance(self._value, str):
82-
return self._value
82+
return (key, self._value)
8383

8484
if TYPE_CHECKING:
8585
assert isinstance(self._value, list)
86-
return ",".join([str(x) for x in self._value])
86+
return (key, ",".join([str(x) for x in self._value]))
8787

8888

8989
class ArrayAttribute(_ListArrayAttribute):
9090
"""To support `array` types as documented in
9191
https://docs.gitlab.com/ee/api/#array"""
9292

93+
def get_for_api(self, *, key: str) -> Tuple[str, Any]:
94+
if isinstance(self._value, str):
95+
return (f"{key}[]", self._value)
96+
97+
if TYPE_CHECKING:
98+
assert isinstance(self._value, list)
99+
return (f"{key}[]", self._value)
100+
93101

94102
class CommaSeparatedListAttribute(_ListArrayAttribute):
95103
"""For values which are sent to the server as a Comma Separated Values
96104
(CSV) string. We allow them to be specified as a list and we convert it
97105
into a CSV"""
98106

107+
pass
108+
99109

100110
class LowercaseStringAttribute(GitlabAttribute):
101-
def get_for_api(self) -> str:
102-
return str(self._value).lower()
111+
def get_for_api(self, *, key: str) -> Tuple[str, str]:
112+
return (key, str(self._value).lower())
103113

104114

105115
class FileAttribute(GitlabAttribute):

gitlab/utils.py

+15-7
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,19 @@ def _transform_types(
7676
continue
7777

7878
type_obj = type_cls(data[attr_name])
79-
80-
# if the type if FileAttribute we need to pass the data as file
81-
if transform_files and isinstance(type_obj, types.FileAttribute):
82-
key = type_obj.get_file_name(attr_name)
83-
files[attr_name] = (key, data.pop(attr_name))
84-
else:
85-
data[attr_name] = type_obj.get_for_api()
79+
assert isinstance(type_obj, types.GitlabAttribute)
80+
81+
if isinstance(type_obj, types.GitlabAttribute):
82+
# if the type is FileAttribute we need to pass the data as file
83+
if isinstance(type_obj, types.FileAttribute):
84+
if transform_files:
85+
key = type_obj.get_file_name(attr_name)
86+
files[attr_name] = (key, data.pop(attr_name))
87+
continue
88+
key, value = type_obj.get_for_api(key=attr_name)
89+
if key != attr_name:
90+
del data[attr_name]
91+
data[key] = value
8692

8793
return data, files
8894

@@ -94,6 +100,8 @@ def copy_dict(
94100
) -> None:
95101
for k, v in src.items():
96102
if isinstance(v, dict):
103+
# NOTE(jlvillal): This provides some support for the `hash` type
104+
# https://docs.gitlab.com/ee/api/#hash
97105
# Transform dict values to new attributes. For example:
98106
# custom_attributes: {'foo', 'bar'} =>
99107
# "custom_attributes['foo']": "bar"

tests/functional/api/test_groups.py

+5
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ def test_groups(gl):
9999
assert len(group1.members.list()) == 3
100100
assert len(group2.members.list()) == 2
101101

102+
# Test `user_ids` array
103+
result = group1.members.list(user_ids=[user.id, 99999])
104+
assert len(result) == 1
105+
assert result[0].id == user.id
106+
102107
group1.members.delete(user.id)
103108
assert user not in group1.members.list()
104109
assert group1.members_all.list()

tests/unit/mixins/test_mixin_methods.py

+19
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,25 @@ class M(ListMixin, FakeManager):
205205
assert responses.assert_call_count(url, 2) is True
206206

207207

208+
@responses.activate
209+
def test_list_mixin_with_attributes(gl):
210+
class M(ListMixin, FakeManager):
211+
_types = {"my_array": gl_types.ArrayAttribute}
212+
213+
url = "http://localhost/api/v4/tests?my_array%5B%5D=1&my_array%5B%5D=2&my_array%5B%5D=3"
214+
responses.add(
215+
method=responses.GET,
216+
headers={},
217+
url=url,
218+
json=[],
219+
status=200,
220+
match=[responses.matchers.query_param_matcher({"my_array[]": ["1", "2", "3"]})],
221+
)
222+
223+
mgr = M(gl)
224+
mgr.list(iterator=True, my_array=[1, 2, 3])
225+
226+
208227
@responses.activate
209228
def test_list_other_url(gl):
210229
class M(ListMixin, FakeManager):

tests/unit/test_types.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def test_gitlab_attribute_get():
7373

7474
o.set_from_cli("whatever2")
7575
assert o.get() == "whatever2"
76-
assert o.get_for_api() == "whatever2"
76+
assert o.get_for_api(key="spam") == ("spam", "whatever2")
7777

7878
o = types.GitlabAttribute()
7979
assert o._value is None
@@ -100,42 +100,42 @@ def test_array_attribute_empty_input():
100100
def test_array_attribute_get_for_api_from_cli():
101101
o = types.ArrayAttribute()
102102
o.set_from_cli("foo,bar,baz")
103-
assert o.get_for_api() == "foo,bar,baz"
103+
assert o.get_for_api(key="spam") == ("spam[]", ["foo", "bar", "baz"])
104104

105105

106106
def test_array_attribute_get_for_api_from_list():
107107
o = types.ArrayAttribute(["foo", "bar", "baz"])
108-
assert o.get_for_api() == "foo,bar,baz"
108+
assert o.get_for_api(key="spam") == ("spam[]", ["foo", "bar", "baz"])
109109

110110

111111
def test_array_attribute_get_for_api_from_int_list():
112112
o = types.ArrayAttribute([1, 9, 7])
113-
assert o.get_for_api() == "1,9,7"
113+
assert o.get_for_api(key="spam") == ("spam[]", [1, 9, 7])
114114

115115

116116
def test_array_attribute_does_not_split_string():
117117
o = types.ArrayAttribute("foo")
118-
assert o.get_for_api() == "foo"
118+
assert o.get_for_api(key="spam") == ("spam[]", "foo")
119119

120120

121121
# CommaSeparatedListAttribute tests
122122
def test_csv_string_attribute_get_for_api_from_cli():
123123
o = types.CommaSeparatedListAttribute()
124124
o.set_from_cli("foo,bar,baz")
125-
assert o.get_for_api() == "foo,bar,baz"
125+
assert o.get_for_api(key="spam") == ("spam", "foo,bar,baz")
126126

127127

128128
def test_csv_string_attribute_get_for_api_from_list():
129129
o = types.CommaSeparatedListAttribute(["foo", "bar", "baz"])
130-
assert o.get_for_api() == "foo,bar,baz"
130+
assert o.get_for_api(key="spam") == ("spam", "foo,bar,baz")
131131

132132

133133
def test_csv_string_attribute_get_for_api_from_int_list():
134134
o = types.CommaSeparatedListAttribute([1, 9, 7])
135-
assert o.get_for_api() == "1,9,7"
135+
assert o.get_for_api(key="spam") == ("spam", "1,9,7")
136136

137137

138138
# LowercaseStringAttribute tests
139139
def test_lowercase_string_attribute_get_for_api():
140140
o = types.LowercaseStringAttribute("FOO")
141-
assert o.get_for_api() == "foo"
141+
assert o.get_for_api(key="spam") == ("spam", "foo")

0 commit comments

Comments
 (0)