From 954357c49963ef51945c81c41fd4345002f9fb98 Mon Sep 17 00:00:00 2001 From: Nejc Habjan Date: Wed, 24 Feb 2021 00:18:49 +0100 Subject: [PATCH] feat(api): add MR pipeline manager in favor of pipelines() method --- docs/gl_objects/mrs.rst | 29 +++++++-- gitlab/cli.py | 3 +- gitlab/v4/objects/merge_requests.py | 21 +----- gitlab/v4/objects/pipelines.py | 41 ++++++++++++ .../objects/test_merge_request_pipelines.py | 64 +++++++++++++++++++ 5 files changed, 134 insertions(+), 24 deletions(-) create mode 100644 tests/unit/objects/test_merge_request_pipelines.py diff --git a/docs/gl_objects/mrs.rst b/docs/gl_objects/mrs.rst index be93f1240..29cbced88 100644 --- a/docs/gl_objects/mrs.rst +++ b/docs/gl_objects/mrs.rst @@ -127,10 +127,6 @@ List the changes of a MR:: changes = mr.changes() -List the pipelines for a MR:: - - pipelines = mr.pipelines() - List issues that will close on merge:: mr.closes_issues() @@ -185,3 +181,28 @@ Get user agent detail for the issue (admin only):: Attempt to rebase an MR:: mr.rebase() + +Merge Request Pipelines +======================= + +Reference +--------- + +* v4 API: + + + :class:`gitlab.v4.objects.ProjectMergeRequestPipeline` + + :class:`gitlab.v4.objects.ProjectMergeRequestPipelineManager` + + :attr:`gitlab.v4.objects.ProjectMergeRequest.pipelines` + +* GitLab API: https://docs.gitlab.com/ee/api/merge_requests.html#list-mr-pipelines + +Examples +-------- + +List pipelines for a merge request:: + + pipelines = mr.pipelines.list() + +Create a pipeline for a merge request:: + + pipeline = mr.pipelines.create() diff --git a/gitlab/cli.py b/gitlab/cli.py index a5044ffda..c053a38d5 100644 --- a/gitlab/cli.py +++ b/gitlab/cli.py @@ -53,6 +53,7 @@ def register_custom_action( cls_names: Union[str, Tuple[str, ...]], mandatory: Tuple[str, ...] = tuple(), optional: Tuple[str, ...] = tuple(), + custom_action: Optional[str] = None, ) -> Callable[[__F], __F]: def wrap(f: __F) -> __F: @functools.wraps(f) @@ -74,7 +75,7 @@ def wrapped_f(*args: Any, **kwargs: Any) -> Any: if final_name not in custom_actions: custom_actions[final_name] = {} - action = f.__name__.replace("_", "-") + action = custom_action or f.__name__.replace("_", "-") custom_actions[final_name][action] = (mandatory, optional, in_obj) return cast(__F, wrapped_f) diff --git a/gitlab/v4/objects/merge_requests.py b/gitlab/v4/objects/merge_requests.py index dd118d0db..e8c2a96c8 100644 --- a/gitlab/v4/objects/merge_requests.py +++ b/gitlab/v4/objects/merge_requests.py @@ -28,6 +28,7 @@ ProjectMergeRequestApprovalRuleManager, ) from .notes import ProjectMergeRequestNoteManager # noqa: F401 +from .pipelines import ProjectMergeRequestPipelineManager # noqa: F401 __all__ = [ "MergeRequest", @@ -145,6 +146,7 @@ class ProjectMergeRequest( ("diffs", "ProjectMergeRequestDiffManager"), ("discussions", "ProjectMergeRequestDiscussionManager"), ("notes", "ProjectMergeRequestNoteManager"), + ("pipelines", "ProjectMergeRequestPipelineManager"), ("resourcelabelevents", "ProjectMergeRequestResourceLabelEventManager"), ("resourcemilestoneevents", "ProjectMergeRequestResourceMilestoneEventManager"), ("resourcestateevents", "ProjectMergeRequestResourceStateEventManager"), @@ -240,25 +242,6 @@ def changes(self, **kwargs): path = "%s/%s/changes" % (self.manager.path, self.get_id()) return self.manager.gitlab.http_get(path, **kwargs) - @cli.register_custom_action("ProjectMergeRequest") - @exc.on_http_error(exc.GitlabListError) - def pipelines(self, **kwargs): - """List the merge request pipelines. - - Args: - **kwargs: Extra options to send to the server (e.g. sudo) - - Raises: - GitlabAuthenticationError: If authentication is not correct - GitlabListError: If the list could not be retrieved - - Returns: - RESTObjectList: List of changes - """ - - path = "%s/%s/pipelines" % (self.manager.path, self.get_id()) - return self.manager.gitlab.http_get(path, **kwargs) - @cli.register_custom_action("ProjectMergeRequest", tuple(), ("sha",)) @exc.on_http_error(exc.GitlabMRApprovalError) def approve(self, sha=None, **kwargs): diff --git a/gitlab/v4/objects/pipelines.py b/gitlab/v4/objects/pipelines.py index 79b080245..5118e7831 100644 --- a/gitlab/v4/objects/pipelines.py +++ b/gitlab/v4/objects/pipelines.py @@ -1,3 +1,5 @@ +import warnings + from gitlab import cli from gitlab import exceptions as exc from gitlab.base import RequiredOptional, RESTManager, RESTObject @@ -15,6 +17,8 @@ ) __all__ = [ + "ProjectMergeRequestPipeline", + "ProjectMergeRequestPipelineManager", "ProjectPipeline", "ProjectPipelineManager", "ProjectPipelineJob", @@ -32,6 +36,43 @@ ] +class ProjectMergeRequestPipeline(RESTObject): + pass + + +class ProjectMergeRequestPipelineManager(CreateMixin, ListMixin, RESTManager): + _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/pipelines" + _obj_cls = ProjectMergeRequestPipeline + _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"} + + # If the manager was called directly as a callable via + # mr.pipelines(), execute the deprecated method for now. + # TODO: in python-gitlab 3.0.0, remove this method entirely. + + @cli.register_custom_action("ProjectMergeRequest", custom_action="pipelines") + @exc.on_http_error(exc.GitlabListError) + def __call__(self, **kwargs): + """List the merge request pipelines. + + Args: + **kwargs: Extra options to send to the server (e.g. sudo) + + Raises: + GitlabAuthenticationError: If authentication is not correct + GitlabListError: If the list could not be retrieved + + Returns: + RESTObjectList: List of changes + """ + warnings.warn( + "Calling the ProjectMergeRequest.pipelines() method on " + "merge request objects directly is deprecated and will be replaced " + "by ProjectMergeRequest.pipelines.list() in python-gitlab 3.0.0.\n", + DeprecationWarning, + ) + return self.list(**kwargs) + + class ProjectPipeline(RefreshMixin, ObjectDeleteMixin, RESTObject): _managers = ( ("jobs", "ProjectPipelineJobManager"), diff --git a/tests/unit/objects/test_merge_request_pipelines.py b/tests/unit/objects/test_merge_request_pipelines.py new file mode 100644 index 000000000..c620cb027 --- /dev/null +++ b/tests/unit/objects/test_merge_request_pipelines.py @@ -0,0 +1,64 @@ +""" +GitLab API: https://docs.gitlab.com/ee/api/merge_requests.html#list-mr-pipelines +""" +import pytest +import responses + +from gitlab.v4.objects import ProjectMergeRequestPipeline + +pipeline_content = { + "id": 1, + "sha": "959e04d7c7a30600c894bd3c0cd0e1ce7f42c11d", + "ref": "master", + "status": "success", +} + + +@pytest.fixture() +def resp_list_merge_request_pipelines(): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/projects/1/merge_requests/1/pipelines", + json=[pipeline_content], + content_type="application/json", + status=200, + ) + yield rsps + + +@pytest.fixture() +def resp_create_merge_request_pipeline(): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/projects/1/merge_requests/1/pipelines", + json=pipeline_content, + content_type="application/json", + status=201, + ) + yield rsps + + +def test_merge_requests_pipelines_deprecated_raises_warning( + project, resp_list_merge_request_pipelines +): + with pytest.deprecated_call(): + pipelines = project.mergerequests.get(1, lazy=True).pipelines() + + assert len(pipelines) == 1 + assert isinstance(pipelines[0], ProjectMergeRequestPipeline) + assert pipelines[0].sha == pipeline_content["sha"] + + +def test_list_merge_requests_pipelines(project, resp_list_merge_request_pipelines): + pipelines = project.mergerequests.get(1, lazy=True).pipelines.list() + assert len(pipelines) == 1 + assert isinstance(pipelines[0], ProjectMergeRequestPipeline) + assert pipelines[0].sha == pipeline_content["sha"] + + +def test_create_merge_requests_pipelines(project, resp_create_merge_request_pipeline): + pipeline = project.mergerequests.get(1, lazy=True).pipelines.create() + assert isinstance(pipeline, ProjectMergeRequestPipeline) + assert pipeline.sha == pipeline_content["sha"]