| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 9fe60f7 commit 81d8c46
7 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -16,6 +16,7 @@ | |||
| 16 | 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| 17 | 17 | """Wrapper for the GitLab API.""" | |
| 18 | 18 | ||
| 19 | + import copy | ||
| 19 | 20 | import os | |
| 20 | 21 | import time | |
| 21 | 22 | from typing import Any, cast, Dict, List, Optional, Tuple, TYPE_CHECKING, Union | |
@@ -27,6 +28,7 @@ | |||
| 27 | 28 | import gitlab.config | |
| 28 | 29 | import gitlab.const | |
| 29 | 30 | import gitlab.exceptions | |
| 31 | + from gitlab import types as gl_types | ||
| 30 | 32 | from gitlab import utils | |
| 31 | 33 | ||
| 32 | 34 | REDIRECT_MSG = ( | |
@@ -604,6 +606,28 @@ def _prepare_send_data( | |||
| 604 | 606 | ||
| 605 | 607 | return (post_data, None, "application/json") | |
| 606 | 608 | ||
| 609 | + @staticmethod | ||
| 610 | + def _prepare_dict_for_api(*, in_dict: Dict[str, Any]) -> Dict[str, Any]: | ||
| 611 | + result: Dict[str, Any] = {} | ||
| 612 | + for key, value in in_dict.items(): | ||
| 613 | + if isinstance(value, gl_types.GitlabAttribute): | ||
| 614 | + result[key] = value.get_for_api() | ||
| 615 | + else: | ||
| 616 | + result[key] = copy.deepcopy(in_dict[key]) | ||
| 617 | + return result | ||
| 618 | + | ||
| 619 | + @staticmethod | ||
| 620 | + def _param_dict_to_param_tuples(*, params: Dict[str, Any]) -> List[Tuple[str, Any]]: | ||
| 621 | + """Convert a dict to a list of key/values. This will be used to pass | ||
| 622 | + values to requests""" | ||
| 623 | + result: List[Tuple[str, Any]] = [] | ||
| 624 | + for key, value in params.items(): | ||
| 625 | + if isinstance(value, gl_types.GitlabAttribute): | ||
| 626 | + result.extend(value.get_as_tuple_list(key=key)) | ||
| 627 | + else: | ||
| 628 | + result.append((key, value)) | ||
| 629 | + return result | ||
| 630 | + | ||
| 607 | 631 | def http_request( | |
| 608 | 632 | self, | |
| 609 | 633 | verb: str, | |
@@ -663,6 +687,10 @@ def http_request( | |||
| 663 | 687 | else: | |
| 664 | 688 | utils.copy_dict(src=kwargs, dest=params) | |
| 665 | 689 | ||
| 690 | + tuple_params = self._param_dict_to_param_tuples(params=params) | ||
| 691 | + if isinstance(post_data, dict): | ||
| 692 | + post_data = self._prepare_dict_for_api(in_dict=post_data) | ||
| 693 | + | ||
| 666 | 694 | opts = self._get_session_opts() | |
| 667 | 695 | ||
| 668 | 696 | verify = opts.pop("verify") | |
@@ -682,7 +710,7 @@ def http_request( | |||
| 682 | 710 | url=url, | |
| 683 | 711 | json=json, | |
| 684 | 712 | data=data, | |
| 685 | - params=params, | ||
| 713 | + params=tuple_params, | ||
| 686 | 714 | timeout=timeout, | |
| 687 | 715 | verify=verify, | |
| 688 | 716 | stream=streamed, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -230,8 +230,7 @@ def list(self, **kwargs: Any) -> Union[base.RESTObjectList, List[base.RESTObject | |||
| 230 | 230 | if self._types: | |
| 231 | 231 | for attr_name, type_cls in self._types.items(): | |
| 232 | 232 | if attr_name in data.keys(): | |
| 233 | - type_obj = type_cls(data[attr_name]) | ||
| 234 | - data[attr_name] = type_obj.get_for_api() | ||
| 233 | + data[attr_name] = type_cls(data[attr_name]) | ||
| 235 | 234 | ||
| 236 | 235 | # Allow to overwrite the path, handy for custom listings | |
| 237 | 236 | path = data.pop("path", self.path) | |
@@ -307,14 +306,13 @@ def create( | |||
| 307 | 306 | for attr_name, type_cls in self._types.items(): | |
| 308 | 307 | if attr_name in data.keys(): | |
| 309 | 308 | type_obj = type_cls(data[attr_name]) | |
| 310 | - | ||
| 311 | 309 | # if the type if FileAttribute we need to pass the data as | |
| 312 | 310 | # file | |
| 313 | 311 | if isinstance(type_obj, g_types.FileAttribute): | |
| 314 | 312 | k = type_obj.get_file_name(attr_name) | |
| 315 | 313 | files[attr_name] = (k, data.pop(attr_name)) | |
| 316 | 314 | else: | |
| 317 | - data[attr_name] = type_obj.get_for_api() | ||
| 315 | + data[attr_name] = type_obj | ||
| 318 | 316 | ||
| 319 | 317 | # Handle specific URL for creation | |
| 320 | 318 | path = kwargs.pop("path", self.path) | |
@@ -410,7 +408,7 @@ def update( | |||
| 410 | 408 | k = type_obj.get_file_name(attr_name) | |
| 411 | 409 | files[attr_name] = (k, new_data.pop(attr_name)) | |
| 412 | 410 | else: | |
| 413 | - new_data[attr_name] = type_obj.get_for_api() | ||
| 411 | + new_data[attr_name] = type_obj | ||
| 414 | 412 | ||
| 415 | 413 | http_method = self._get_update_method() | |
| 416 | 414 | result = http_method(path, post_data=new_data, files=files, **kwargs) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -15,7 +15,7 @@ | |||
| 15 | 15 | # You should have received a copy of the GNU Lesser General Public License | |
| 16 | 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| 17 | 17 | ||
| 18 | - from typing import Any, Optional, TYPE_CHECKING | ||
| 18 | + from typing import Any, List, Optional, Tuple, TYPE_CHECKING | ||
| 19 | 19 | ||
| 20 | 20 | ||
| 21 | 21 | class GitlabAttribute: | |
@@ -31,6 +31,9 @@ def set_from_cli(self, cli_value: Any) -> None: | |||
| 31 | 31 | def get_for_api(self) -> Any: | |
| 32 | 32 | return self._value | |
| 33 | 33 | ||
| 34 | + def get_as_tuple_list(self, *, key: str) -> List[Tuple[str, Any]]: | ||
| 35 | + return [(key, self._value)] | ||
| 36 | + | ||
| 34 | 37 | ||
| 35 | 38 | class _ListArrayAttribute(GitlabAttribute): | |
| 36 | 39 | """Helper class to support `list` / `array` types.""" | |
@@ -55,17 +58,31 @@ class ArrayAttribute(_ListArrayAttribute): | |||
| 55 | 58 | """To support `array` types as documented in | |
| 56 | 59 | https://docs.gitlab.com/ee/api/#array""" | |
| 57 | 60 | ||
| 61 | + def get_as_tuple_list(self, *, key: str) -> List[Tuple[str, str]]: | ||
| 62 | + if isinstance(self._value, str): | ||
| 63 | + return [(f"{key}[]", self._value)] | ||
| 64 | + | ||
| 65 | + if TYPE_CHECKING: | ||
| 66 | + assert isinstance(self._value, list) | ||
| 67 | + return [(f"{key}[]", str(value)) for value in self._value] | ||
| 68 | + | ||
| 58 | 69 | ||
| 59 | 70 | class CommaSeparatedListAttribute(_ListArrayAttribute): | |
| 60 | 71 | """For values which are sent to the server as a Comma Separated Values | |
| 61 | 72 | (CSV) string. We allow them to be specified as a list and we convert it | |
| 62 | 73 | into a CSV""" | |
| 63 | 74 | ||
| 75 | + def get_as_tuple_list(self, *, key: str) -> List[Tuple[str, str]]: | ||
| 76 | + return [(key, self.get_for_api())] | ||
| 77 | + | ||
| 64 | 78 | ||
| 65 | 79 | class LowercaseStringAttribute(GitlabAttribute): | |
| 66 | 80 | def get_for_api(self) -> str: | |
| 67 | 81 | return str(self._value).lower() | |
| 68 | 82 | ||
| 83 | + def get_as_tuple_list(self, *, key: str) -> List[Tuple[str, str]]: | ||
| 84 | + return [(key, self.get_for_api())] | ||
| 85 | + | ||
| 69 | 86 | ||
| 70 | 87 | class FileAttribute(GitlabAttribute): | |
| 71 | 88 | def get_file_name(self, attr_name: Optional[str] = None) -> Optional[str]: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -54,6 +54,8 @@ def copy_dict( | |||
| 54 | 54 | ) -> None: | |
| 55 | 55 | for k, v in src.items(): | |
| 56 | 56 | if isinstance(v, dict): | |
| 57 | + # NOTE(jlvillal): This provides some support for the `hash` type | ||
| 58 | + # https://docs.gitlab.com/ee/api/#hash | ||
| 57 | 59 | # Transform dict values to new attributes. For example: | |
| 58 | 60 | # custom_attributes: {'foo', 'bar'} => | |
| 59 | 61 | # "custom_attributes['foo']": "bar" | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -91,6 +91,11 @@ def test_groups(gl): | |||
| 91 | 91 | assert len(group1.members.list()) == 3 | |
| 92 | 92 | assert len(group2.members.list()) == 2 | |
| 93 | 93 | ||
| 94 | + # Test `user_ids` array | ||
| 95 | + result = group1.members.list(user_ids=[user.id, 99999]) | ||
| 96 | + assert len(result) == 1 | ||
| 97 | + assert result[0].id == user.id | ||
| 98 | + | ||
| 94 | 99 | group1.members.delete(user.id) | |
| 95 | 100 | assert len(group1.members.list()) == 2 | |
| 96 | 101 | assert len(group1.members_all.list()) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,6 +2,7 @@ | |||
| 2 | 2 | import requests | |
| 3 | 3 | import responses | |
| 4 | 4 | ||
| 5 | + import gitlab | ||
| 5 | 6 | from gitlab import GitlabHttpError, GitlabList, GitlabParsingError, RedirectError | |
| 6 | 7 | from tests.unit import helpers | |
| 7 | 8 | ||
@@ -507,3 +508,20 @@ def test_delete_request_404(gl): | |||
| 507 | 508 | with pytest.raises(GitlabHttpError): | |
| 508 | 509 | gl.http_delete("/not_there") | |
| 509 | 510 | assert responses.assert_call_count(url, 1) is True | |
| 511 | + | ||
| 512 | + | ||
| 513 | + @responses.activate | ||
| 514 | + def test_array_type_request(gl): | ||
| 515 | + url = "http://localhost/api/v4/projects" | ||
| 516 | + params = "array_var[]=1&array_var[]=2&array_var[]=3" | ||
| 517 | + full_url = f"{url}?array_var%5B%5D=1&array_var%5B%5D=2&array_var%5B%5D=3" | ||
| 518 | + responses.add( | ||
| 519 | + method=responses.GET, | ||
| 520 | + url=url, | ||
| 521 | + json={"name": "project1"}, | ||
| 522 | + status=200, | ||
| 523 | + match=[responses.matchers.query_string_matcher(params)], | ||
| 524 | + ) | ||
| 525 | + | ||
| 526 | + gl.http_get("/projects", array_var=gitlab.types.ArrayAttribute([1, 2, 3])) | ||
| 527 | + assert responses.assert_call_count(full_url, 1) is True | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -25,6 +25,7 @@ def test_gitlab_attribute_get(): | |||
| 25 | 25 | o.set_from_cli("whatever2") | |
| 26 | 26 | assert o.get() == "whatever2" | |
| 27 | 27 | assert o.get_for_api() == "whatever2" | |
| 28 | + assert o.get_as_tuple_list(key="foo") == [("foo", "whatever2")] | ||
| 28 | 29 | ||
| 29 | 30 | o = types.GitlabAttribute() | |
| 30 | 31 | assert o._value is None | |
@@ -64,6 +65,24 @@ def test_array_attribute_get_for_api_from_int_list(): | |||
| 64 | 65 | assert o.get_for_api() == "1,9,7" | |
| 65 | 66 | ||
| 66 | 67 | ||
| 68 | + def test_array_attribute_get_as_tuple_list_from_list(): | ||
| 69 | + o = types.ArrayAttribute(["foo", "bar", "baz"]) | ||
| 70 | + assert o.get_as_tuple_list(key="identifier") == [ | ||
| 71 | + ("identifier[]", "foo"), | ||
| 72 | + ("identifier[]", "bar"), | ||
| 73 | + ("identifier[]", "baz"), | ||
| 74 | + ] | ||
| 75 | + | ||
| 76 | + | ||
| 77 | + def test_array_attribute_get_as_tuple_list_from_int_list(): | ||
| 78 | + o = types.ArrayAttribute([1, 9, 7]) | ||
| 79 | + assert o.get_as_tuple_list(key="identifier") == [ | ||
| 80 | + ("identifier[]", "1"), | ||
| 81 | + ("identifier[]", "9"), | ||
| 82 | + ("identifier[]", "7"), | ||
| 83 | + ] | ||
| 84 | + | ||
| 85 | + | ||
| 67 | 86 | def test_array_attribute_does_not_split_string(): | |
| 68 | 87 | o = types.ArrayAttribute("foo") | |
| 69 | 88 | assert o.get_for_api() == "foo" | |
@@ -86,7 +105,22 @@ def test_csv_string_attribute_get_for_api_from_int_list(): | |||
| 86 | 105 | assert o.get_for_api() == "1,9,7" | |
| 87 | 106 | ||
| 88 | 107 | ||
| 108 | + def test_csv_string_attribute_get_as_tuple_list_from_list(): | ||
| 109 | + o = types.CommaSeparatedListAttribute(["foo", "bar", "baz"]) | ||
| 110 | + assert o.get_as_tuple_list(key="identifier") == [("identifier", "foo,bar,baz")] | ||
| 111 | + | ||
| 112 | + | ||
| 113 | + def test_csv_string_attribute_get_as_tuple_list_from_int_list(): | ||
| 114 | + o = types.CommaSeparatedListAttribute([1, 9, 7]) | ||
| 115 | + assert o.get_as_tuple_list(key="identifier") == [("identifier", "1,9,7")] | ||
| 116 | + | ||
| 117 | + | ||
| 89 | 118 | # LowercaseStringAttribute tests | |
| 90 | 119 | def test_lowercase_string_attribute_get_for_api(): | |
| 91 | 120 | o = types.LowercaseStringAttribute("FOO") | |
| 92 | 121 | assert o.get_for_api() == "foo" | |
| 122 | + | ||
| 123 | + | ||
| 124 | + def test_lowercase_string_attribute_get_as_tuple(): | ||
| 125 | + o = types.LowercaseStringAttribute("FOO") | ||
| 126 | + assert o.get_as_tuple_list(key="user_name") == [("user_name", "foo")] | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments