From 1dc9d0f91757eed9f28f0c7172654b9b2a730216 Mon Sep 17 00:00:00 2001 From: calve Date: Tue, 21 Jun 2022 16:41:45 +0200 Subject: [PATCH] feat: Add support for Protected Environments - https://docs.gitlab.com/ee/api/protected_environments.html - https://github.com/python-gitlab/python-gitlab/issues/1130 no write operation are implemented yet as I have no use case right now and am not sure how it should be done --- docs/api-objects.rst | 1 + docs/gl_objects/environments.rst | 4 +- docs/gl_objects/protected_environments.rst | 45 ++++++++++++++++++++++ gitlab/v4/objects/environments.py | 29 ++++++++++++++ gitlab/v4/objects/projects.py | 6 ++- tests/unit/objects/test_environments.py | 27 ++++++++++++- 6 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 docs/gl_objects/protected_environments.rst diff --git a/docs/api-objects.rst b/docs/api-objects.rst index 53491485a..e313bd8e8 100644 --- a/docs/api-objects.rst +++ b/docs/api-objects.rst @@ -43,6 +43,7 @@ API examples gl_objects/projects gl_objects/project_access_tokens gl_objects/protected_branches + gl_objects/protected_environments gl_objects/releases gl_objects/runners gl_objects/remote_mirrors diff --git a/docs/gl_objects/environments.rst b/docs/gl_objects/environments.rst index 6edde12a7..e6e3d729c 100644 --- a/docs/gl_objects/environments.rst +++ b/docs/gl_objects/environments.rst @@ -39,6 +39,8 @@ Delete an environment for a project:: # or environment.delete() -Stop an environments:: +Stop an environment:: environment.stop() + +To manage protected environments, see :doc:`/gl_objects/protected_environments`. diff --git a/docs/gl_objects/protected_environments.rst b/docs/gl_objects/protected_environments.rst new file mode 100644 index 000000000..a05cc1d02 --- /dev/null +++ b/docs/gl_objects/protected_environments.rst @@ -0,0 +1,45 @@ +###################### +Protected environments +###################### + +You can list and manage protected environments in a project. + +References +---------- + +* v4 API: + + + :class:`gitlab.v4.objects.ProjectProtectedEnvironment` + + :class:`gitlab.v4.objects.ProjectProtectedEnvironmentManager` + + :attr:`gitlab.v4.objects.Project.protected_environment` + +* GitLab API: https://docs.gitlab.com/ee/api/protected_environments.html + +Examples +-------- + +Get the list of protected environments for a project:: + + p_environments = project.protected_environments.list() + +Get a single protected environment:: + + p_environments = project.protected_environments.get('production') + +Protect an existing environment:: + + p_environment = project.protected_environments.create( + { + 'name': 'production', + 'deploy_access_levels': [ + {'access_level': 40} + ], + } + ) + + +Unprotect a protected environment:: + + p_environment = project.protected_environments.delete('production') + # or + p_environment.delete() diff --git a/gitlab/v4/objects/environments.py b/gitlab/v4/objects/environments.py index 7e2089fb6..a8bd9d5dd 100644 --- a/gitlab/v4/objects/environments.py +++ b/gitlab/v4/objects/environments.py @@ -18,6 +18,8 @@ __all__ = [ "ProjectEnvironment", "ProjectEnvironmentManager", + "ProjectProtectedEnvironment", + "ProjectProtectedEnvironmentManager", ] @@ -55,3 +57,30 @@ def get( self, id: Union[str, int], lazy: bool = False, **kwargs: Any ) -> ProjectEnvironment: return cast(ProjectEnvironment, super().get(id=id, lazy=lazy, **kwargs)) + + +class ProjectProtectedEnvironment(ObjectDeleteMixin, RESTObject): + _id_attr = "name" + _repr_attr = "name" + + +class ProjectProtectedEnvironmentManager( + RetrieveMixin, CreateMixin, DeleteMixin, RESTManager +): + _path = "/projects/{project_id}/protected_environments" + _obj_cls = ProjectProtectedEnvironment + _from_parent_attrs = {"project_id": "id"} + _create_attrs = RequiredOptional( + required=( + "name", + "deploy_access_levels", + ), + optional=("required_approval_count", "approval_rules"), + ) + + def get( + self, id: Union[str, int], lazy: bool = False, **kwargs: Any + ) -> ProjectProtectedEnvironment: + return cast( + ProjectProtectedEnvironment, super().get(id=id, lazy=lazy, **kwargs) + ) diff --git a/gitlab/v4/objects/projects.py b/gitlab/v4/objects/projects.py index 8674ee635..f32cf2257 100644 --- a/gitlab/v4/objects/projects.py +++ b/gitlab/v4/objects/projects.py @@ -31,7 +31,10 @@ from .deploy_keys import ProjectKeyManager # noqa: F401 from .deploy_tokens import ProjectDeployTokenManager # noqa: F401 from .deployments import ProjectDeploymentManager # noqa: F401 -from .environments import ProjectEnvironmentManager # noqa: F401 +from .environments import ( # noqa: F401 + ProjectEnvironmentManager, + ProjectProtectedEnvironmentManager, +) from .events import ProjectEventManager # noqa: F401 from .export_import import ProjectExportManager, ProjectImportManager # noqa: F401 from .files import ProjectFileManager # noqa: F401 @@ -175,6 +178,7 @@ class Project(RefreshMixin, SaveMixin, ObjectDeleteMixin, RepositoryMixin, RESTO pagesdomains: ProjectPagesDomainManager pipelines: ProjectPipelineManager pipelineschedules: ProjectPipelineScheduleManager + protected_environments: ProjectProtectedEnvironmentManager protectedbranches: ProjectProtectedBranchManager protectedtags: ProjectProtectedTagManager pushrules: ProjectPushRulesManager diff --git a/tests/unit/objects/test_environments.py b/tests/unit/objects/test_environments.py index b49a1db4e..5501471db 100644 --- a/tests/unit/objects/test_environments.py +++ b/tests/unit/objects/test_environments.py @@ -4,7 +4,7 @@ import pytest import responses -from gitlab.v4.objects import ProjectEnvironment +from gitlab.v4.objects import ProjectEnvironment, ProjectProtectedEnvironment @pytest.fixture @@ -22,9 +22,34 @@ def resp_get_environment(): yield rsps +@pytest.fixture +def resp_get_protected_environment(): + content = { + "name": "protected_environment_name", + "last_deployment": "my birthday", + } + + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/projects/1/protected_environments/2", + json=content, + content_type="application/json", + status=200, + ) + yield rsps + + def test_project_environments(project, resp_get_environment): environment = project.environments.get(1) assert isinstance(environment, ProjectEnvironment) assert environment.id == 1 assert environment.last_deployment == "sometime" assert environment.name == "environment_name" + + +def test_project_protected_environments(project, resp_get_protected_environment): + protected_environment = project.protected_environments.get(2) + assert isinstance(protected_environment, ProjectProtectedEnvironment) + assert protected_environment.last_deployment == "my birthday" + assert protected_environment.name == "protected_environment_name"