Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a939db1

Browse filesBrowse 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. 2. Move processing of most GitlabAttributes into the client.py:http_request() method. Now we convert our params into a list of tuples so that we can have multiple identical keys but with different values. 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 a939db1
Copy full SHA for a939db1

File tree

Expand file treeCollapse file tree

6 files changed

+111
-6
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

6 files changed

+111
-6
lines changed
Open diff view settings
Collapse file

‎gitlab/client.py‎

Copy file name to clipboardExpand all lines: gitlab/client.py
+29-1Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717
"""Wrapper for the GitLab API."""
1818

19+
import copy
1920
import os
2021
import time
2122
from typing import Any, cast, Dict, List, Optional, Tuple, TYPE_CHECKING, Union
@@ -28,6 +29,7 @@
2829
import gitlab.config
2930
import gitlab.const
3031
import gitlab.exceptions
32+
from gitlab import types as gl_types
3133
from gitlab import utils
3234

3335
REDIRECT_MSG = (
@@ -626,6 +628,28 @@ def _prepare_send_data(
626628

627629
return (post_data, None, "application/json")
628630

631+
@staticmethod
632+
def _prepare_dict_for_api(*, in_dict: Dict[str, Any]) -> Dict[str, Any]:
633+
result: Dict[str, Any] = {}
634+
for key, value in in_dict.items():
635+
if isinstance(value, gl_types.GitlabAttribute):
636+
result[key] = value.get_for_api()
637+
else:
638+
result[key] = copy.deepcopy(in_dict[key])
639+
return result
640+
641+
@staticmethod
642+
def _param_dict_to_param_tuples(*, params: Dict[str, Any]) -> List[Tuple[str, Any]]:
643+
"""Convert a dict to a list of key/values. This will be used to pass
644+
values to requests"""
645+
result: List[Tuple[str, Any]] = []
646+
for key, value in params.items():
647+
if isinstance(value, gl_types.GitlabAttribute):
648+
result.extend(value.get_as_tuple_list(key=key))
649+
else:
650+
result.append((key, value))
651+
return result
652+
629653
def http_request(
630654
self,
631655
verb: str,
@@ -688,6 +712,10 @@ def http_request(
688712
else:
689713
utils.copy_dict(src=kwargs, dest=params)
690714

715+
tuple_params = self._param_dict_to_param_tuples(params=params)
716+
if isinstance(post_data, dict):
717+
post_data = self._prepare_dict_for_api(in_dict=post_data)
718+
691719
opts = self._get_session_opts()
692720

693721
verify = opts.pop("verify")
@@ -710,7 +738,7 @@ def http_request(
710738
url=url,
711739
json=json,
712740
data=data,
713-
params=params,
741+
params=tuple_params,
714742
timeout=timeout,
715743
verify=verify,
716744
stream=streamed,
Collapse file

‎gitlab/types.py‎

Copy file name to clipboardExpand all lines: gitlab/types.py
+17Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ def set_from_cli(self, cli_value: Any) -> None:
6666
def get_for_api(self) -> Any:
6767
return self._value
6868

69+
def get_as_tuple_list(self, *, key: str) -> List[Tuple[str, Any]]:
70+
return [(key, self._value)]
71+
6972

7073
class _ListArrayAttribute(GitlabAttribute):
7174
"""Helper class to support `list` / `array` types."""
@@ -90,17 +93,31 @@ class ArrayAttribute(_ListArrayAttribute):
9093
"""To support `array` types as documented in
9194
https://docs.gitlab.com/ee/api/#array"""
9295

96+
def get_as_tuple_list(self, *, key: str) -> List[Tuple[str, str]]:
97+
if isinstance(self._value, str):
98+
return [(f"{key}[]", self._value)]
99+
100+
if TYPE_CHECKING:
101+
assert isinstance(self._value, list)
102+
return [(f"{key}[]", str(value)) for value in self._value]
103+
93104

94105
class CommaSeparatedListAttribute(_ListArrayAttribute):
95106
"""For values which are sent to the server as a Comma Separated Values
96107
(CSV) string. We allow them to be specified as a list and we convert it
97108
into a CSV"""
98109

110+
def get_as_tuple_list(self, *, key: str) -> List[Tuple[str, str]]:
111+
return [(key, self.get_for_api())]
112+
99113

100114
class LowercaseStringAttribute(GitlabAttribute):
101115
def get_for_api(self) -> str:
102116
return str(self._value).lower()
103117

118+
def get_as_tuple_list(self, *, key: str) -> List[Tuple[str, str]]:
119+
return [(key, self.get_for_api())]
120+
104121

105122
class FileAttribute(GitlabAttribute):
106123
@staticmethod
Collapse file

‎gitlab/utils.py‎

Copy file name to clipboardExpand all lines: gitlab/utils.py
+8-5Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,13 @@ def _transform_types(
7777

7878
type_obj = type_cls(data[attr_name])
7979

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))
80+
# if the type is FileAttribute we need to pass the data as file
81+
if isinstance(type_obj, types.FileAttribute):
82+
if transform_files:
83+
key = type_obj.get_file_name(attr_name)
84+
files[attr_name] = (key, data.pop(attr_name))
8485
else:
85-
data[attr_name] = type_obj.get_for_api()
86+
data[attr_name] = type_obj
8687

8788
return data, files
8889

@@ -94,6 +95,8 @@ def copy_dict(
9495
) -> None:
9596
for k, v in src.items():
9697
if isinstance(v, dict):
98+
# NOTE(jlvillal): This provides some support for the `hash` type
99+
# https://docs.gitlab.com/ee/api/#hash
97100
# Transform dict values to new attributes. For example:
98101
# custom_attributes: {'foo', 'bar'} =>
99102
# "custom_attributes['foo']": "bar"
Collapse file

‎tests/functional/api/test_groups.py‎

Copy file name to clipboardExpand all lines: tests/functional/api/test_groups.py
+5Lines changed: 5 additions & 0 deletions
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()
Collapse file

‎tests/unit/test_gitlab_http_methods.py‎

Copy file name to clipboardExpand all lines: tests/unit/test_gitlab_http_methods.py
+18Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import requests
66
import responses
77

8+
import gitlab
89
from gitlab import GitlabHttpError, GitlabList, GitlabParsingError, RedirectError
910
from gitlab.client import RETRYABLE_TRANSIENT_ERROR_CODES
1011
from tests.unit import helpers
@@ -742,3 +743,20 @@ def test_delete_request_404(gl):
742743
with pytest.raises(GitlabHttpError):
743744
gl.http_delete("/not_there")
744745
assert responses.assert_call_count(url, 1) is True
746+
747+
748+
@responses.activate
749+
def test_array_type_request(gl):
750+
url = "http://localhost/api/v4/projects"
751+
params = "array_var[]=1&array_var[]=2&array_var[]=3"
752+
full_url = f"{url}?array_var%5B%5D=1&array_var%5B%5D=2&array_var%5B%5D=3"
753+
responses.add(
754+
method=responses.GET,
755+
url=url,
756+
json={"name": "project1"},
757+
status=200,
758+
match=[responses.matchers.query_string_matcher(params)],
759+
)
760+
761+
gl.http_get("/projects", array_var=gitlab.types.ArrayAttribute([1, 2, 3]))
762+
assert responses.assert_call_count(full_url, 1) is True
Collapse file

‎tests/unit/test_types.py‎

Copy file name to clipboardExpand all lines: tests/unit/test_types.py
+34Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ def test_gitlab_attribute_get():
7474
o.set_from_cli("whatever2")
7575
assert o.get() == "whatever2"
7676
assert o.get_for_api() == "whatever2"
77+
assert o.get_as_tuple_list(key="foo") == [("foo", "whatever2")]
7778

7879
o = types.GitlabAttribute()
7980
assert o._value is None
@@ -113,6 +114,24 @@ def test_array_attribute_get_for_api_from_int_list():
113114
assert o.get_for_api() == "1,9,7"
114115

115116

117+
def test_array_attribute_get_as_tuple_list_from_list():
118+
o = types.ArrayAttribute(["foo", "bar", "baz"])
119+
assert o.get_as_tuple_list(key="identifier") == [
120+
("identifier[]", "foo"),
121+
("identifier[]", "bar"),
122+
("identifier[]", "baz"),
123+
]
124+
125+
126+
def test_array_attribute_get_as_tuple_list_from_int_list():
127+
o = types.ArrayAttribute([1, 9, 7])
128+
assert o.get_as_tuple_list(key="identifier") == [
129+
("identifier[]", "1"),
130+
("identifier[]", "9"),
131+
("identifier[]", "7"),
132+
]
133+
134+
116135
def test_array_attribute_does_not_split_string():
117136
o = types.ArrayAttribute("foo")
118137
assert o.get_for_api() == "foo"
@@ -135,7 +154,22 @@ def test_csv_string_attribute_get_for_api_from_int_list():
135154
assert o.get_for_api() == "1,9,7"
136155

137156

157+
def test_csv_string_attribute_get_as_tuple_list_from_list():
158+
o = types.CommaSeparatedListAttribute(["foo", "bar", "baz"])
159+
assert o.get_as_tuple_list(key="identifier") == [("identifier", "foo,bar,baz")]
160+
161+
162+
def test_csv_string_attribute_get_as_tuple_list_from_int_list():
163+
o = types.CommaSeparatedListAttribute([1, 9, 7])
164+
assert o.get_as_tuple_list(key="identifier") == [("identifier", "1,9,7")]
165+
166+
138167
# LowercaseStringAttribute tests
139168
def test_lowercase_string_attribute_get_for_api():
140169
o = types.LowercaseStringAttribute("FOO")
141170
assert o.get_for_api() == "foo"
171+
172+
173+
def test_lowercase_string_attribute_get_as_tuple():
174+
o = types.LowercaseStringAttribute("FOO")
175+
assert o.get_as_tuple_list(key="user_name") == [("user_name", "foo")]

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.