From b27f4ee4ce36967b826517d016133a0236d9c143 Mon Sep 17 00:00:00 2001 From: "John L. Villalovos" Date: Fri, 3 Feb 2023 17:16:05 -0800 Subject: [PATCH] chore: add `http_patch` method In order to support some new API calls we need to support the HTTP `PATCH` method. Closes: #2469 --- docs/api-levels.rst | 1 + gitlab/client.py | 48 +++++++++++++++++++++++++ pyproject.toml | 1 + tests/unit/test_gitlab_http_methods.py | 50 ++++++++++++++++++++++++++ 4 files changed, 100 insertions(+) diff --git a/docs/api-levels.rst b/docs/api-levels.rst index 5a52b78b4..2fdc42dbd 100644 --- a/docs/api-levels.rst +++ b/docs/api-levels.rst @@ -60,6 +60,7 @@ The available methods are: * ``http_get()`` * ``http_post()`` * ``http_put()`` +* ``http_patch()`` * ``http_delete()`` * ``http_list()`` (a wrapper around ``http_get`` handling pagination, including with lazy generators) * ``http_head()`` (only returns the header dictionary) diff --git a/gitlab/client.py b/gitlab/client.py index 5e6c71af0..c3982f39d 100644 --- a/gitlab/client.py +++ b/gitlab/client.py @@ -1094,6 +1094,54 @@ def http_put( error_message="Failed to parse the server message" ) from e + def http_patch( + self, + path: str, + *, + query_data: Optional[Dict[str, Any]] = None, + post_data: Optional[Union[Dict[str, Any], bytes]] = None, + raw: bool = False, + **kwargs: Any, + ) -> Union[Dict[str, Any], requests.Response]: + """Make a PATCH request to the Gitlab server. + + Args: + path: Path or full URL to query ('/projects' or + 'http://whatever/v4/api/projecs') + query_data: Data to send as query parameters + post_data: Data to send in the body (will be converted to + json by default) + raw: If True, do not convert post_data to json + **kwargs: Extra options to send to the server (e.g. sudo) + + Returns: + The parsed json returned by the server. + + Raises: + GitlabHttpError: When the return code is not 2xx + GitlabParsingError: If the json data could not be parsed + """ + query_data = query_data or {} + post_data = post_data or {} + + result = self.http_request( + "patch", + path, + query_data=query_data, + post_data=post_data, + raw=raw, + **kwargs, + ) + try: + json_result = result.json() + if TYPE_CHECKING: + assert isinstance(json_result, dict) + return json_result + except Exception as e: + raise gitlab.exceptions.GitlabParsingError( + error_message="Failed to parse the server message" + ) from e + def http_delete(self, path: str, **kwargs: Any) -> requests.Response: """Make a DELETE request to the Gitlab server. diff --git a/pyproject.toml b/pyproject.toml index 744113051..75b8c1de6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,6 +58,7 @@ disable = [ "too-many-instance-attributes", "too-many-lines", "too-many-locals", + "too-many-public-methods", "too-many-statements", "unsubscriptable-object", ] diff --git a/tests/unit/test_gitlab_http_methods.py b/tests/unit/test_gitlab_http_methods.py index 569df8149..d7e375af1 100644 --- a/tests/unit/test_gitlab_http_methods.py +++ b/tests/unit/test_gitlab_http_methods.py @@ -809,6 +809,56 @@ def test_put_request_invalid_data(gl): assert responses.assert_call_count(url, 1) is True +@responses.activate +def test_patch_request(gl): + url = "http://localhost/api/v4/projects" + responses.add( + method=responses.PATCH, + url=url, + json={"name": "project1"}, + status=200, + match=helpers.MATCH_EMPTY_QUERY_PARAMS, + ) + + result = gl.http_patch("/projects") + assert isinstance(result, dict) + assert result["name"] == "project1" + assert responses.assert_call_count(url, 1) is True + + +@responses.activate +def test_patch_request_404(gl): + url = "http://localhost/api/v4/not_there" + responses.add( + method=responses.PATCH, + url=url, + json=[], + status=404, + match=helpers.MATCH_EMPTY_QUERY_PARAMS, + ) + + with pytest.raises(GitlabHttpError): + gl.http_patch("/not_there") + assert responses.assert_call_count(url, 1) is True + + +@responses.activate +def test_patch_request_invalid_data(gl): + url = "http://localhost/api/v4/projects" + responses.add( + method=responses.PATCH, + url=url, + body='["name": "project1"]', + content_type="application/json", + status=200, + match=helpers.MATCH_EMPTY_QUERY_PARAMS, + ) + + with pytest.raises(GitlabParsingError): + gl.http_patch("/projects") + assert responses.assert_call_count(url, 1) is True + + @responses.activate def test_delete_request(gl): url = "http://localhost/api/v4/projects"