summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Brown <ben@demerara.io>2022-11-07 22:22:26 +0000
committerNejc Habjan <hab.nejc@gmail.com>2022-11-10 13:16:57 +0100
commitd0a034878fabfd8409134aa8b7ffeeb40219683c (patch)
treed3291921754730426ee36a659596fa151d665d25
parentfcd72fe243daa0623abfde267c7ab1c6866bcd52 (diff)
downloadgitlab-d0a034878fabfd8409134aa8b7ffeeb40219683c.tar.gz
feat: implement secure files API
-rw-r--r--docs/api-objects.rst1
-rw-r--r--docs/gl_objects/secure_files.rst47
-rw-r--r--gitlab/v4/objects/__init__.py1
-rw-r--r--gitlab/v4/objects/projects.py2
-rw-r--r--gitlab/v4/objects/secure_files.py69
-rw-r--r--tests/unit/objects/test_secure_files.py101
6 files changed, 221 insertions, 0 deletions
diff --git a/docs/api-objects.rst b/docs/api-objects.rst
index c025056..cccf1c6 100644
--- a/docs/api-objects.rst
+++ b/docs/api-objects.rst
@@ -52,6 +52,7 @@ API examples
gl_objects/repositories
gl_objects/repository_tags
gl_objects/search
+ gl_objects/secure_files
gl_objects/settings
gl_objects/snippets
gl_objects/statistics
diff --git a/docs/gl_objects/secure_files.rst b/docs/gl_objects/secure_files.rst
new file mode 100644
index 0000000..e7374bb
--- /dev/null
+++ b/docs/gl_objects/secure_files.rst
@@ -0,0 +1,47 @@
+############
+Secure Files
+############
+
+secure files
+============
+
+References
+----------
+
+* v4 API:
+
+ + :class:`gitlab.v4.objects.SecureFile`
+ + :class:`gitlab.v4.objects.SecureFileManager`
+ + :attr:`gitlab.v4.objects.Project.secure_files`
+
+* GitLab API: https://docs.gitlab.com/ee/api/secure_files.html
+
+Examples
+--------
+
+Get a project secure file::
+
+ secure_files = gl.projects.get(1, lazy=True).secure_files.get(1)
+ print(secure_files.name)
+
+List project secure files::
+
+ secure_files = gl.projects.get(1, lazy=True).secure_files.list()
+ print(secure_files[0].name)
+
+Create project secure file::
+
+ secure_file = gl.projects.get(1).secure_files.create({"name": "test", "file": "secure.txt"})
+
+Download a project secure file::
+
+ content = secure_file.download()
+ print(content)
+ with open("/tmp/secure.txt", "wb") as f:
+ secure_file.download(streamed=True, action=f.write)
+
+Remove a project secure file::
+
+ gl.projects.get(1).secure_files.delete(1)
+ # or
+ secure_file.delete()
diff --git a/gitlab/v4/objects/__init__.py b/gitlab/v4/objects/__init__.py
index f1012a0..56c17d5 100644
--- a/gitlab/v4/objects/__init__.py
+++ b/gitlab/v4/objects/__init__.py
@@ -53,6 +53,7 @@ from .push_rules import *
from .releases import *
from .repositories import *
from .runners import *
+from .secure_files import *
from .settings import *
from .sidekiq import *
from .snippets import *
diff --git a/gitlab/v4/objects/projects.py b/gitlab/v4/objects/projects.py
index daba896..fad21eb 100644
--- a/gitlab/v4/objects/projects.py
+++ b/gitlab/v4/objects/projects.py
@@ -82,6 +82,7 @@ from .push_rules import ProjectPushRulesManager # noqa: F401
from .releases import ProjectReleaseManager # noqa: F401
from .repositories import RepositoryMixin
from .runners import ProjectRunnerManager # noqa: F401
+from .secure_files import SecureFileManager # noqa: F401
from .snippets import ProjectSnippetManager # noqa: F401
from .statistics import ( # noqa: F401
ProjectAdditionalStatisticsManager,
@@ -209,6 +210,7 @@ class Project(RefreshMixin, SaveMixin, ObjectDeleteMixin, RepositoryMixin, RESTO
remote_mirrors: "ProjectRemoteMirrorManager"
repositories: ProjectRegistryRepositoryManager
runners: ProjectRunnerManager
+ secure_files: SecureFileManager
services: ProjectServiceManager
snippets: ProjectSnippetManager
storage: "ProjectStorageManager"
diff --git a/gitlab/v4/objects/secure_files.py b/gitlab/v4/objects/secure_files.py
new file mode 100644
index 0000000..ce8d12d
--- /dev/null
+++ b/gitlab/v4/objects/secure_files.py
@@ -0,0 +1,69 @@
+"""
+GitLab API:
+https://docs.gitlab.com/ee/api/secure_files.html
+"""
+from typing import Any, Callable, cast, Iterator, Optional, TYPE_CHECKING, Union
+
+import requests
+
+from gitlab import cli
+from gitlab import exceptions as exc
+from gitlab import utils
+from gitlab.base import RESTManager, RESTObject
+from gitlab.mixins import NoUpdateMixin, ObjectDeleteMixin
+from gitlab.types import FileAttribute, RequiredOptional
+
+__all__ = ["SecureFile", "SecureFileManager"]
+
+
+class SecureFile(ObjectDeleteMixin, RESTObject):
+ @cli.register_custom_action("SecureFile")
+ @exc.on_http_error(exc.GitlabGetError)
+ def download(
+ self,
+ streamed: bool = False,
+ action: Optional[Callable[[bytes], None]] = None,
+ chunk_size: int = 1024,
+ *,
+ iterator: bool = False,
+ **kwargs: Any,
+ ) -> Optional[Union[bytes, Iterator[Any]]]:
+ """Download the secure file.
+
+ Args:
+ streamed: If True the data will be processed by chunks of
+ `chunk_size` and each chunk is passed to `action` for
+ treatment
+ iterator: If True directly return the underlying response
+ iterator
+ action: Callable responsible of dealing with chunk of
+ data
+ chunk_size: Size of each chunk
+ **kwargs: Extra options to send to the server (e.g. sudo)
+
+ Raises:
+ GitlabAuthenticationError: If authentication is not correct
+ GitlabGetError: If the artifacts could not be retrieved
+
+ Returns:
+ The artifacts if `streamed` is False, None otherwise."""
+ path = f"{self.manager.path}/{self.id}/download"
+ result = self.manager.gitlab.http_get(
+ path, streamed=streamed, raw=True, **kwargs
+ )
+ if TYPE_CHECKING:
+ assert isinstance(result, requests.Response)
+ return utils.response_content(
+ result, streamed, action, chunk_size, iterator=iterator
+ )
+
+
+class SecureFileManager(NoUpdateMixin, RESTManager):
+ _path = "/projects/{project_id}/secure_files"
+ _obj_cls = SecureFile
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = RequiredOptional(required=("name", "file"))
+ _types = {"file": FileAttribute}
+
+ def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> SecureFile:
+ return cast(SecureFile, super().get(id=id, lazy=lazy, **kwargs))
diff --git a/tests/unit/objects/test_secure_files.py b/tests/unit/objects/test_secure_files.py
new file mode 100644
index 0000000..b614dc2
--- /dev/null
+++ b/tests/unit/objects/test_secure_files.py
@@ -0,0 +1,101 @@
+"""
+GitLab API: https://docs.gitlab.com/ee/api/secure_files.html
+"""
+
+import pytest
+import responses
+
+from gitlab.v4.objects import SecureFile
+
+secure_file_content = {
+ "id": 1,
+ "name": "myfile.jks",
+ "checksum": "16630b189ab34b2e3504f4758e1054d2e478deda510b2b08cc0ef38d12e80aac",
+ "checksum_algorithm": "sha256",
+ "created_at": "2022-02-22T22:22:22.222Z",
+ "expires_at": None,
+ "metadata": None,
+}
+
+
+@pytest.fixture
+def resp_list_secure_files():
+ with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
+ rsps.add(
+ method=responses.GET,
+ url="http://localhost/api/v4/projects/1/secure_files",
+ json=[secure_file_content],
+ content_type="application/json",
+ status=200,
+ )
+ yield rsps
+
+
+@pytest.fixture
+def resp_create_secure_file():
+ with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
+ rsps.add(
+ method=responses.POST,
+ url="http://localhost/api/v4/projects/1/secure_files",
+ json=secure_file_content,
+ content_type="application/json",
+ status=200,
+ )
+ yield rsps
+
+
+@pytest.fixture
+def resp_download_secure_file(binary_content):
+ with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
+ rsps.add(
+ method=responses.GET,
+ url="http://localhost/api/v4/projects/1/secure_files/1",
+ json=secure_file_content,
+ content_type="application/json",
+ status=200,
+ )
+ rsps.add(
+ method=responses.GET,
+ url="http://localhost/api/v4/projects/1/secure_files/1/download",
+ body=binary_content,
+ content_type="application/octet-stream",
+ status=200,
+ )
+ yield rsps
+
+
+@pytest.fixture
+def resp_remove_secure_file(no_content):
+ with responses.RequestsMock() as rsps:
+ rsps.add(
+ method=responses.DELETE,
+ url="http://localhost/api/v4/projects/1/secure_files/1",
+ json=no_content,
+ content_type="application/json",
+ status=204,
+ )
+ yield rsps
+
+
+def test_list_secure_files(project, resp_list_secure_files):
+ secure_files = project.secure_files.list()
+ assert len(secure_files) == 1
+ assert secure_files[0].id == 1
+ assert secure_files[0].name == "myfile.jks"
+
+
+def test_create_secure_file(project, resp_create_secure_file):
+ secure_files = project.secure_files.create({"name": "test", "file": "myfile.jks"})
+ assert secure_files.id == 1
+ assert secure_files.name == "myfile.jks"
+
+
+def test_download_secure_file(project, binary_content, resp_download_secure_file):
+ secure_file = project.secure_files.get(1)
+ secure_content = secure_file.download()
+ assert isinstance(secure_file, SecureFile)
+ assert secure_content == binary_content
+
+
+def test_remove_secure_file(project, resp_remove_secure_file):
+ project.secure_files.delete(1)