summaryrefslogtreecommitdiff
path: root/gitlab
diff options
context:
space:
mode:
authorJohn L. Villalovos <john@sodarock.com>2022-07-27 16:08:25 -0700
committerJohn L. Villalovos <john@sodarock.com>2022-07-27 16:08:25 -0700
commit1af44ce8761e6ee8a9467a3e192f6c4d19e5cefe (patch)
treeda3116aa356818cb2a865b047de9ef160bbfffa7 /gitlab
parent194ee0100c2868c1a9afb161c15f3145efb01c7c (diff)
downloadgitlab-1af44ce8761e6ee8a9467a3e192f6c4d19e5cefe.tar.gz
fix: use the [] after key names for array variables in `params`
1. If a value is of type ArrayAttribute then append '[]' to the name of the value for query parameters (`params`). 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 5127b1594c00c7364e9af15e42d2e2f2d909449b Step two was: commit a57334f1930752c70ea15847a39324fa94042460 Fixes: #1698 [1] https://docs.gitlab.com/ee/api/#encoding-api-parameters-of-array-and-hash-types
Diffstat (limited to 'gitlab')
-rw-r--r--gitlab/mixins.py15
-rw-r--r--gitlab/types.py22
-rw-r--r--gitlab/utils.py39
3 files changed, 57 insertions, 19 deletions
diff --git a/gitlab/mixins.py b/gitlab/mixins.py
index f33a1fc..a48c032 100644
--- a/gitlab/mixins.py
+++ b/gitlab/mixins.py
@@ -238,7 +238,12 @@ class ListMixin(HeadMixin, _RestManagerBase):
GitlabListError: If the server cannot perform the request
"""
- data, _ = utils._transform_types(kwargs, self._types, transform_files=False)
+ data, _ = utils._transform_types(
+ data=kwargs,
+ custom_types=self._types,
+ transform_data=True,
+ transform_files=False,
+ )
if self.gitlab.per_page:
data.setdefault("per_page", self.gitlab.per_page)
@@ -303,7 +308,9 @@ class CreateMixin(_RestManagerBase):
data = {}
self._create_attrs.validate_attrs(data=data)
- data, files = utils._transform_types(data, self._types)
+ data, files = utils._transform_types(
+ data=data, custom_types=self._types, transform_data=False
+ )
# Handle specific URL for creation
path = kwargs.pop("path", self.path)
@@ -370,7 +377,9 @@ class UpdateMixin(_RestManagerBase):
if self._obj_cls is not None and self._obj_cls._id_attr is not None:
excludes = [self._obj_cls._id_attr]
self._update_attrs.validate_attrs(data=new_data, excludes=excludes)
- new_data, files = utils._transform_types(new_data, self._types)
+ new_data, files = utils._transform_types(
+ data=new_data, custom_types=self._types, transform_data=False
+ )
http_method = self._get_update_method()
result = http_method(path, post_data=new_data, files=files, **kwargs)
diff --git a/gitlab/types.py b/gitlab/types.py
index f811a6f..d683b70 100644
--- a/gitlab/types.py
+++ b/gitlab/types.py
@@ -63,8 +63,8 @@ class GitlabAttribute:
def set_from_cli(self, cli_value: Any) -> None:
self._value = cli_value
- def get_for_api(self) -> Any:
- return self._value
+ def get_for_api(self, *, key: str) -> Tuple[str, Any]:
+ return (key, self._value)
class _ListArrayAttribute(GitlabAttribute):
@@ -76,20 +76,28 @@ class _ListArrayAttribute(GitlabAttribute):
else:
self._value = [item.strip() for item in cli_value.split(",")]
- def get_for_api(self) -> str:
+ def get_for_api(self, *, key: str) -> Tuple[str, str]:
# Do not comma-split single value passed as string
if isinstance(self._value, str):
- return self._value
+ return (key, self._value)
if TYPE_CHECKING:
assert isinstance(self._value, list)
- return ",".join([str(x) for x in self._value])
+ return (key, ",".join([str(x) for x in self._value]))
class ArrayAttribute(_ListArrayAttribute):
"""To support `array` types as documented in
https://docs.gitlab.com/ee/api/#array"""
+ def get_for_api(self, *, key: str) -> Tuple[str, Any]:
+ if isinstance(self._value, str):
+ return (f"{key}[]", self._value)
+
+ if TYPE_CHECKING:
+ assert isinstance(self._value, list)
+ return (f"{key}[]", self._value)
+
class CommaSeparatedListAttribute(_ListArrayAttribute):
"""For values which are sent to the server as a Comma Separated Values
@@ -98,8 +106,8 @@ class CommaSeparatedListAttribute(_ListArrayAttribute):
class LowercaseStringAttribute(GitlabAttribute):
- def get_for_api(self) -> str:
- return str(self._value).lower()
+ def get_for_api(self, *, key: str) -> Tuple[str, str]:
+ return (key, str(self._value).lower())
class FileAttribute(GitlabAttribute):
diff --git a/gitlab/utils.py b/gitlab/utils.py
index 4d2ec8d..f3d97f7 100644
--- a/gitlab/utils.py
+++ b/gitlab/utils.py
@@ -55,34 +55,53 @@ def response_content(
def _transform_types(
- data: Dict[str, Any], custom_types: dict, *, transform_files: Optional[bool] = True
+ data: Dict[str, Any],
+ custom_types: dict,
+ *,
+ transform_data: bool,
+ transform_files: Optional[bool] = True,
) -> Tuple[dict, dict]:
"""Copy the data dict with attributes that have custom types and transform them
before being sent to the server.
- If ``transform_files`` is ``True`` (default), also populates the ``files`` dict for
+ ``transform_files``: If ``True`` (default), also populates the ``files`` dict for
FileAttribute types with tuples to prepare fields for requests' MultipartEncoder:
https://toolbelt.readthedocs.io/en/latest/user.html#multipart-form-data-encoder
+ ``transform_data``: If ``True`` transforms the ``data`` dict with fields
+ suitable for encoding as query parameters for GitLab's API:
+ https://docs.gitlab.com/ee/api/#encoding-api-parameters-of-array-and-hash-types
+
Returns:
A tuple of the transformed data dict and files dict"""
# Duplicate data to avoid messing with what the user sent us
data = data.copy()
+ if not transform_files and not transform_data:
+ return data, {}
+
files = {}
- for attr_name, type_cls in custom_types.items():
+ for attr_name, attr_class in custom_types.items():
if attr_name not in data:
continue
- type_obj = type_cls(data[attr_name])
+ gitlab_attribute = attr_class(data[attr_name])
- # if the type if FileAttribute we need to pass the data as file
- if transform_files and isinstance(type_obj, types.FileAttribute):
- key = type_obj.get_file_name(attr_name)
+ # if the type is FileAttribute we need to pass the data as file
+ if isinstance(gitlab_attribute, types.FileAttribute) and transform_files:
+ key = gitlab_attribute.get_file_name(attr_name)
files[attr_name] = (key, data.pop(attr_name))
- else:
- data[attr_name] = type_obj.get_for_api()
+ continue
+
+ if not transform_data:
+ continue
+
+ if isinstance(gitlab_attribute, types.GitlabAttribute):
+ key, value = gitlab_attribute.get_for_api(key=attr_name)
+ if key != attr_name:
+ del data[attr_name]
+ data[key] = value
return data, files
@@ -94,6 +113,8 @@ def copy_dict(
) -> None:
for k, v in src.items():
if isinstance(v, dict):
+ # NOTE(jlvillal): This provides some support for the `hash` type
+ # https://docs.gitlab.com/ee/api/#hash
# Transform dict values to new attributes. For example:
# custom_attributes: {'foo', 'bar'} =>
# "custom_attributes['foo']": "bar"