From 9dbf8704f35ed57fe1839a95bb6e975945501e6b Mon Sep 17 00:00:00 2001 From: Patrick Evans Date: Fri, 13 Sep 2024 17:08:19 -0500 Subject: [PATCH 1/2] fix(api): head requests for projectfilemanager --- gitlab/v4/objects/files.py | 44 ++++++++++++++++++----- tests/unit/objects/test_repositories.py | 47 +++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 9 deletions(-) diff --git a/gitlab/v4/objects/files.py b/gitlab/v4/objects/files.py index 43086b7fe..a986a1e7f 100644 --- a/gitlab/v4/objects/files.py +++ b/gitlab/v4/objects/files.py @@ -2,11 +2,11 @@ from typing import ( Any, Callable, - cast, Dict, Iterator, List, Optional, + Tuple, TYPE_CHECKING, Union, ) @@ -20,7 +20,6 @@ from gitlab.mixins import ( CreateMixin, DeleteMixin, - GetMixin, ObjectDeleteMixin, SaveMixin, UpdateMixin, @@ -96,10 +95,11 @@ def delete( # type: ignore self.manager.delete(file_path, branch, commit_message, **kwargs) -class ProjectFileManager(GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager): +class ProjectFileManager(CreateMixin, UpdateMixin, DeleteMixin, RESTManager): _path = "/projects/{project_id}/repository/files" _obj_cls = ProjectFile _from_parent_attrs = {"project_id": "id"} + _optional_get_attrs: Tuple[str, ...] = () _create_attrs = RequiredOptional( required=("file_path", "branch", "content", "commit_message"), optional=("encoding", "author_email", "author_name"), @@ -112,11 +112,7 @@ class ProjectFileManager(GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTMa @cli.register_custom_action( cls_names="ProjectFileManager", required=("file_path", "ref") ) - # NOTE(jlvillal): Signature doesn't match UpdateMixin.update() so ignore - # type error - def get( # type: ignore - self, file_path: str, ref: str, **kwargs: Any - ) -> ProjectFile: + def get(self, file_path: str, ref: str, **kwargs: Any) -> ProjectFile: """Retrieve a single file. Args: @@ -131,7 +127,37 @@ def get( # type: ignore Returns: The generated RESTObject """ - return cast(ProjectFile, GetMixin.get(self, file_path, ref=ref, **kwargs)) + if TYPE_CHECKING: + assert file_path is not None + file_path = utils.EncodedId(file_path) + path = f"{self.path}/{file_path}" + server_data = self.gitlab.http_get(path, ref=ref, **kwargs) + if TYPE_CHECKING: + assert isinstance(server_data, dict) + return self._obj_cls(self, server_data) + + def head( + self, file_path: str, ref: str, **kwargs: Any + ) -> "requests.structures.CaseInsensitiveDict[Any]": + """Retrieve just metadata for a single file. + + Args: + file_path: Path of the file to retrieve + ref: Name of the branch, tag or commit + **kwargs: Extra options to send to the server (e.g. sudo) + + Raises: + GitlabAuthenticationError: If authentication is not correct + GitlabGetError: If the file could not be retrieved + + Returns: + The generated RESTObject + """ + if TYPE_CHECKING: + assert file_path is not None + file_path = utils.EncodedId(file_path) + path = f"{self.path}/{file_path}" + return self.gitlab.http_head(path, ref=ref, **kwargs) @cli.register_custom_action( cls_names="ProjectFileManager", diff --git a/tests/unit/objects/test_repositories.py b/tests/unit/objects/test_repositories.py index f11bd64e4..f891d4d09 100644 --- a/tests/unit/objects/test_repositories.py +++ b/tests/unit/objects/test_repositories.py @@ -8,6 +8,7 @@ import pytest import responses +from requests.structures import CaseInsensitiveDict from gitlab.v4.objects import ProjectFile @@ -15,6 +16,52 @@ ref = "main" +@pytest.fixture +def resp_head_repository_file(): + header_response = { + "Cache-Control": "no-cache", + "Content-Length": "0", + "Content-Type": "application/json", + "Date": "Thu, 12 Sep 2024 14:27:49 GMT", + "Referrer-Policy": "strict-origin-when-cross-origin", + "Server": "nginx", + "Strict-Transport-Security": "max-age=63072000", + "Vary": "Origin", + "X-Content-Type-Options": "nosniff", + "X-Frame-Options": "SAMEORIGIN", + "X-Gitlab-Blob-Id": "79f7bbd25901e8334750839545a9bd021f0e4c83", + "X-Gitlab-Commit-Id": "d5a3ff139356ce33e37e73add446f16869741b50", + "X-Gitlab-Content-Sha256": "4c294617b60715c1d218e61164a3abd4808a4284cbc30e6728a01ad9aada4481", + "X-Gitlab-Encoding": "base64", + "X-Gitlab-Execute-Filemode": "false", + "X-Gitlab-File-Name": "key.rb", + "X-Gitlab-File-Path": file_path, + "X-Gitlab-Last-Commit-Id": "570e7b2abdd848b95f2f578043fc23bd6f6fd24d", + "X-Gitlab-Meta": '{"correlation_id":"01J7KFRPXBX65Y04HEH7MFX4GD","version":"1"}', + "X-Gitlab-Ref": ref, + "X-Gitlab-Size": "1476", + "X-Request-Id": "01J7KFRPXBX65Y04HEH7MFX4GD", + "X-Runtime": "0.083199", + "Connection": "keep-alive", + } + encoded_path = quote(file_path, safe="") + + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.HEAD, + url=f"http://localhost/api/v4/projects/1/repository/files/{encoded_path}", + headers=header_response, + status=200, + ) + yield rsps + + +def test_head_repository_file(project, resp_head_repository_file): + headers = project.files.head(file_path, ref=ref) + assert isinstance(headers, CaseInsensitiveDict) + assert headers["X-Gitlab-File-Path"] == file_path + + @pytest.fixture def resp_get_repository_file(): file_response = { From 5afd3321d61115073f232a974528a55dde2b2646 Mon Sep 17 00:00:00 2001 From: Nejc Habjan Date: Sat, 14 Sep 2024 18:15:24 +0200 Subject: [PATCH 2/2] chore: fix return type docs in docstring --- gitlab/v4/objects/files.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitlab/v4/objects/files.py b/gitlab/v4/objects/files.py index a986a1e7f..78476f5ad 100644 --- a/gitlab/v4/objects/files.py +++ b/gitlab/v4/objects/files.py @@ -151,7 +151,7 @@ def head( GitlabGetError: If the file could not be retrieved Returns: - The generated RESTObject + The response headers as a dictionary """ if TYPE_CHECKING: assert file_path is not None