diff options
| -rw-r--r-- | gitlab/_backends/requests_backend.py | 29 | ||||
| -rw-r--r-- | gitlab/client.py | 10 | ||||
| -rw-r--r-- | tests/unit/_backends/__init__.py | 0 | ||||
| -rw-r--r-- | tests/unit/_backends/test_requests_backend.py | 38 |
4 files changed, 62 insertions, 15 deletions
diff --git a/gitlab/_backends/requests_backend.py b/gitlab/_backends/requests_backend.py index fd40baa..d70cf42 100644 --- a/gitlab/_backends/requests_backend.py +++ b/gitlab/_backends/requests_backend.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import Any, Dict, Optional, Tuple, TYPE_CHECKING, Union +import dataclasses +from typing import Any, Dict, Optional, TYPE_CHECKING, Union import requests from requests.structures import CaseInsensitiveDict @@ -9,6 +10,20 @@ from requests_toolbelt.multipart.encoder import MultipartEncoder # type: ignore from . import protocol +@dataclasses.dataclass +class SendData: + content_type: str + data: Optional[Union[Dict[str, Any], MultipartEncoder]] = None + json: Optional[Union[Dict[str, Any], bytes]] = None + + def __post_init__(self) -> None: + if self.json is not None and self.data is not None: + raise ValueError( + f"`json` and `data` are mutually exclusive. Only one can be set. " + f"json={self.json!r} data={self.data!r}" + ) + + class RequestsResponse(protocol.BackendResponse): def __init__(self, response: requests.Response) -> None: self._response: requests.Response = response @@ -50,11 +65,7 @@ class RequestsBackend(protocol.Backend): files: Optional[Dict[str, Any]] = None, post_data: Optional[Union[Dict[str, Any], bytes]] = None, raw: bool = False, - ) -> Tuple[ - Optional[Union[Dict[str, Any], bytes]], - Optional[Union[Dict[str, Any], MultipartEncoder]], - str, - ]: + ) -> SendData: if files: if post_data is None: post_data = {} @@ -70,12 +81,12 @@ class RequestsBackend(protocol.Backend): post_data["avatar"] = files.get("avatar") data = MultipartEncoder(post_data) - return (None, data, data.content_type) + return SendData(data=data, content_type=data.content_type) if raw and post_data: - return (None, post_data, "application/octet-stream") + return SendData(data=post_data, content_type="application/octet-stream") - return (post_data, None, "application/json") + return SendData(json=post_data, content_type="application/json") def http_request( self, diff --git a/gitlab/client.py b/gitlab/client.py index c3982f3..495ed44 100644 --- a/gitlab/client.py +++ b/gitlab/client.py @@ -716,10 +716,8 @@ class Gitlab: retry_transient_errors = self.retry_transient_errors # We need to deal with json vs. data when uploading files - json, data, content_type = self._backend.prepare_send_data( - files, post_data, raw - ) - opts["headers"]["Content-type"] = content_type + send_data = self._backend.prepare_send_data(files, post_data, raw) + opts["headers"]["Content-type"] = send_data.content_type cur_retries = 0 while True: @@ -727,8 +725,8 @@ class Gitlab: result = self._backend.http_request( method=verb, url=url, - json=json, - data=data, + json=send_data.json, + data=send_data.data, params=params, timeout=timeout, verify=verify, diff --git a/tests/unit/_backends/__init__.py b/tests/unit/_backends/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/unit/_backends/__init__.py diff --git a/tests/unit/_backends/test_requests_backend.py b/tests/unit/_backends/test_requests_backend.py new file mode 100644 index 0000000..d83703d --- /dev/null +++ b/tests/unit/_backends/test_requests_backend.py @@ -0,0 +1,38 @@ +import pytest +from requests_toolbelt.multipart.encoder import MultipartEncoder # type: ignore + +from gitlab._backends import requests_backend + + +class TestSendData: + def test_senddata_json(self) -> None: + result = requests_backend.SendData( + json={"a": 1}, content_type="application/json" + ) + assert result.data is None + + def test_senddata_data(self) -> None: + result = requests_backend.SendData( + data={"b": 2}, content_type="application/octet-stream" + ) + assert result.json is None + + def test_senddata_json_and_data(self) -> None: + with pytest.raises(ValueError, match=r"json={'a': 1} data={'b': 2}"): + requests_backend.SendData( + json={"a": 1}, data={"b": 2}, content_type="application/json" + ) + + +class TestRequestsBackend: + def test_prepare_send_data_str_parentid(self) -> None: + file = "12345" + files = {"file": ("file.tar.gz", file, "application/octet-stream")} + post_data = {"parent_id": "12"} + + result = requests_backend.RequestsBackend.prepare_send_data( + files=files, post_data=post_data, raw=False + ) + assert result.json is None + assert result.content_type.startswith("multipart/form-data") + assert isinstance(result.data, MultipartEncoder) |
