| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 1ecbc7c commit a939db1
6 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 | |
@@ -28,6 +29,7 @@ | |||
| 28 | 29 | import gitlab.config | |
| 29 | 30 | import gitlab.const | |
| 30 | 31 | import gitlab.exceptions | |
| 32 | + from gitlab import types as gl_types | ||
| 31 | 33 | from gitlab import utils | |
| 32 | 34 | ||
| 33 | 35 | REDIRECT_MSG = ( | |
@@ -626,6 +628,28 @@ def _prepare_send_data( | |||
| 626 | 628 | ||
| 627 | 629 | return (post_data, None, "application/json") | |
| 628 | 630 | ||
| 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 | + | ||
| 629 | 653 | def http_request( | |
| 630 | 654 | self, | |
| 631 | 655 | verb: str, | |
@@ -688,6 +712,10 @@ def http_request( | |||
| 688 | 712 | else: | |
| 689 | 713 | utils.copy_dict(src=kwargs, dest=params) | |
| 690 | 714 | ||
| 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 | + | ||
| 691 | 719 | opts = self._get_session_opts() | |
| 692 | 720 | ||
| 693 | 721 | verify = opts.pop("verify") | |
@@ -710,7 +738,7 @@ def http_request( | |||
| 710 | 738 | url=url, | |
| 711 | 739 | json=json, | |
| 712 | 740 | data=data, | |
| 713 | - params=params, | ||
| 741 | + params=tuple_params, | ||
| 714 | 742 | timeout=timeout, | |
| 715 | 743 | verify=verify, | |
| 716 | 744 | stream=streamed, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -66,6 +66,9 @@ def set_from_cli(self, cli_value: Any) -> None: | |||
| 66 | 66 | def get_for_api(self) -> Any: | |
| 67 | 67 | return self._value | |
| 68 | 68 | ||
| 69 | + def get_as_tuple_list(self, *, key: str) -> List[Tuple[str, Any]]: | ||
| 70 | + return [(key, self._value)] | ||
| 71 | + | ||
| 69 | 72 | ||
| 70 | 73 | class _ListArrayAttribute(GitlabAttribute): | |
| 71 | 74 | """Helper class to support `list` / `array` types.""" | |
@@ -90,17 +93,31 @@ class ArrayAttribute(_ListArrayAttribute): | |||
| 90 | 93 | """To support `array` types as documented in | |
| 91 | 94 | https://docs.gitlab.com/ee/api/#array""" | |
| 92 | 95 | ||
| 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 | + | ||
| 93 | 104 | ||
| 94 | 105 | class CommaSeparatedListAttribute(_ListArrayAttribute): | |
| 95 | 106 | """For values which are sent to the server as a Comma Separated Values | |
| 96 | 107 | (CSV) string. We allow them to be specified as a list and we convert it | |
| 97 | 108 | into a CSV""" | |
| 98 | 109 | ||
| 110 | + def get_as_tuple_list(self, *, key: str) -> List[Tuple[str, str]]: | ||
| 111 | + return [(key, self.get_for_api())] | ||
| 112 | + | ||
| 99 | 113 | ||
| 100 | 114 | class LowercaseStringAttribute(GitlabAttribute): | |
| 101 | 115 | def get_for_api(self) -> str: | |
| 102 | 116 | return str(self._value).lower() | |
| 103 | 117 | ||
| 118 | + def get_as_tuple_list(self, *, key: str) -> List[Tuple[str, str]]: | ||
| 119 | + return [(key, self.get_for_api())] | ||
| 120 | + | ||
| 104 | 121 | ||
| 105 | 122 | class FileAttribute(GitlabAttribute): | |
| 106 | 123 | @staticmethod | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -77,12 +77,13 @@ def _transform_types( | |||
| 77 | 77 | ||
| 78 | 78 | type_obj = type_cls(data[attr_name]) | |
| 79 | 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)) | ||
| 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)) | ||
| 84 | 85 | else: | |
| 85 | - data[attr_name] = type_obj.get_for_api() | ||
| 86 | + data[attr_name] = type_obj | ||
| 86 | 87 | ||
| 87 | 88 | return data, files | |
| 88 | 89 | ||
@@ -94,6 +95,8 @@ def copy_dict( | |||
| 94 | 95 | ) -> None: | |
| 95 | 96 | for k, v in src.items(): | |
| 96 | 97 | if isinstance(v, dict): | |
| 98 | + # NOTE(jlvillal): This provides some support for the `hash` type | ||
| 99 | + # https://docs.gitlab.com/ee/api/#hash | ||
| 97 | 100 | # Transform dict values to new attributes. For example: | |
| 98 | 101 | # custom_attributes: {'foo', 'bar'} => | |
| 99 | 102 | # "custom_attributes['foo']": "bar" | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -99,6 +99,11 @@ def test_groups(gl): | |||
| 99 | 99 | assert len(group1.members.list()) == 3 | |
| 100 | 100 | assert len(group2.members.list()) == 2 | |
| 101 | 101 | ||
| 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 | + | ||
| 102 | 107 | group1.members.delete(user.id) | |
| 103 | 108 | assert user not in group1.members.list() | |
| 104 | 109 | assert group1.members_all.list() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,6 +5,7 @@ | |||
| 5 | 5 | import requests | |
| 6 | 6 | import responses | |
| 7 | 7 | ||
| 8 | + import gitlab | ||
| 8 | 9 | from gitlab import GitlabHttpError, GitlabList, GitlabParsingError, RedirectError | |
| 9 | 10 | from gitlab.client import RETRYABLE_TRANSIENT_ERROR_CODES | |
| 10 | 11 | from tests.unit import helpers | |
@@ -742,3 +743,20 @@ def test_delete_request_404(gl): | |||
| 742 | 743 | with pytest.raises(GitlabHttpError): | |
| 743 | 744 | gl.http_delete("/not_there") | |
| 744 | 745 | 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 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -74,6 +74,7 @@ def test_gitlab_attribute_get(): | |||
| 74 | 74 | o.set_from_cli("whatever2") | |
| 75 | 75 | assert o.get() == "whatever2" | |
| 76 | 76 | assert o.get_for_api() == "whatever2" | |
| 77 | + assert o.get_as_tuple_list(key="foo") == [("foo", "whatever2")] | ||
| 77 | 78 | ||
| 78 | 79 | o = types.GitlabAttribute() | |
| 79 | 80 | assert o._value is None | |
@@ -113,6 +114,24 @@ def test_array_attribute_get_for_api_from_int_list(): | |||
| 113 | 114 | assert o.get_for_api() == "1,9,7" | |
| 114 | 115 | ||
| 115 | 116 | ||
| 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 | + | ||
| 116 | 135 | def test_array_attribute_does_not_split_string(): | |
| 117 | 136 | o = types.ArrayAttribute("foo") | |
| 118 | 137 | assert o.get_for_api() == "foo" | |
@@ -135,7 +154,22 @@ def test_csv_string_attribute_get_for_api_from_int_list(): | |||
| 135 | 154 | assert o.get_for_api() == "1,9,7" | |
| 136 | 155 | ||
| 137 | 156 | ||
| 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 | + | ||
| 138 | 167 | # LowercaseStringAttribute tests | |
| 139 | 168 | def test_lowercase_string_attribute_get_for_api(): | |
| 140 | 169 | o = types.LowercaseStringAttribute("FOO") | |
| 141 | 170 | 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")] | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments