@@ -144,6 +144,10 @@ List the changes of a MR:: | |||
| 144 | 144 | ||
| 145 | 145 | changes = mr.changes() | |
| 146 | 146 | ||
| 147 | + List issues related to this merge request:: | ||
| 148 | + | ||
| 149 | + related_issues = mr.related_issues() | ||
| 150 | + | ||
| 147 | 151 | List issues that will close on merge:: | |
| 148 | 152 | ||
| 149 | 153 | mr.closes_issues() | |
@@ -197,6 +197,35 @@ def cancel_merge_when_pipeline_succeeds(self, **kwargs: Any) -> Dict[str, str]: | |||
| 197 | 197 | assert isinstance(server_data, dict) | |
| 198 | 198 | return server_data | |
| 199 | 199 | ||
| 200 | + @cli.register_custom_action(cls_names="ProjectMergeRequest") | ||
| 201 | + @exc.on_http_error(exc.GitlabListError) | ||
| 202 | + def related_issues(self, **kwargs: Any) -> RESTObjectList: | ||
| 203 | + """List issues related to this merge request." | ||
| 204 | + | ||
| 205 | + Args: | ||
| 206 | + all: If True, return all the items, without pagination | ||
| 207 | + per_page: Number of items to retrieve per request | ||
| 208 | + page: ID of the page to return (starts with page 1) | ||
| 209 | + **kwargs: Extra options to send to the server (e.g. sudo) | ||
| 210 | + | ||
| 211 | + Raises: | ||
| 212 | + GitlabAuthenticationError: If authentication is not correct | ||
| 213 | + GitlabListError: If the list could not be retrieved | ||
| 214 | + | ||
| 215 | + Returns: | ||
| 216 | + List of issues | ||
| 217 | + """ | ||
| 218 | + | ||
| 219 | + path = f"{self.manager.path}/{self.encoded_id}/related_issues" | ||
| 220 | + data_list = self.manager.gitlab.http_list(path, iterator=True, **kwargs) | ||
| 221 | + | ||
| 222 | + if TYPE_CHECKING: | ||
| 223 | + assert isinstance(data_list, gitlab.GitlabList) | ||
| 224 | + | ||
| 225 | + manager = ProjectIssueManager(self.manager.gitlab, parent=self.manager._parent) | ||
| 226 | + | ||
| 227 | + return RESTObjectList(manager, ProjectIssue, data_list) | ||
| 228 | + | ||
| 200 | 229 | @cli.register_custom_action(cls_names="ProjectMergeRequest") | |
| 201 | 230 | @exc.on_http_error(exc.GitlabListError) | |
| 202 | 231 | def closes_issues(self, **kwargs: Any) -> RESTObjectList: | |
@@ -9,8 +9,10 @@ | |||
| 9 | 9 | import pytest | |
| 10 | 10 | import responses | |
| 11 | 11 | ||
| 12 | + from gitlab.base import RESTObjectList | ||
| 12 | 13 | from gitlab.v4.objects import ( | |
| 13 | 14 | ProjectDeploymentMergeRequest, | |
| 15 | + ProjectIssue, | ||
| 14 | 16 | ProjectMergeRequest, | |
| 15 | 17 | ProjectMergeRequestReviewerDetail, | |
| 16 | 18 | ) | |
@@ -57,6 +59,78 @@ | |||
| 57 | 59 | } | |
| 58 | 60 | ] | |
| 59 | 61 | ||
| 62 | + related_issues = [ | ||
| 63 | + { | ||
| 64 | + "id": 1, | ||
| 65 | + "iid": 1, | ||
| 66 | + "project_id": 1, | ||
| 67 | + "title": "Fake Title for Merge Requests via API", | ||
| 68 | + "description": "Something here", | ||
| 69 | + "state": "closed", | ||
| 70 | + "created_at": "2024-05-14T04:01:40.042Z", | ||
| 71 | + "updated_at": "2024-06-13T05:29:13.661Z", | ||
| 72 | + "closed_at": "2024-06-13T05:29:13.602Z", | ||
| 73 | + "closed_by": { | ||
| 74 | + "id": 2, | ||
| 75 | + "name": "Sam Bauch", | ||
| 76 | + "username": "kenyatta_oconnell", | ||
| 77 | + "state": "active", | ||
| 78 | + "avatar_url": "https://www.gravatar.com/avatar/956c92487c6f6f7616b536927e22c9a0?s=80&d=identicon", | ||
| 79 | + "web_url": "http://gitlab.example.com/kenyatta_oconnell", | ||
| 80 | + }, | ||
| 81 | + "labels": [ | ||
| 82 | + "FakeCategory", | ||
| 83 | + "fake:ml", | ||
| 84 | + ], | ||
| 85 | + "assignees": [ | ||
| 86 | + { | ||
| 87 | + "id": 2, | ||
| 88 | + "name": "Sam Bauch", | ||
| 89 | + "username": "kenyatta_oconnell", | ||
| 90 | + "state": "active", | ||
| 91 | + "avatar_url": "https://www.gravatar.com/avatar/956c92487c6f6f7616b536927e22c9a0?s=80&d=identicon", | ||
| 92 | + "web_url": "http://gitlab.example.com/kenyatta_oconnell", | ||
| 93 | + } | ||
| 94 | + ], | ||
| 95 | + "author": { | ||
| 96 | + "id": 2, | ||
| 97 | + "name": "Sam Bauch", | ||
| 98 | + "username": "kenyatta_oconnell", | ||
| 99 | + "state": "active", | ||
| 100 | + "avatar_url": "https://www.gravatar.com/avatar/956c92487c6f6f7616b536927e22c9a0?s=80&d=identicon", | ||
| 101 | + "web_url": "http://gitlab.example.com//kenyatta_oconnell", | ||
| 102 | + }, | ||
| 103 | + "type": "ISSUE", | ||
| 104 | + "assignee": { | ||
| 105 | + "id": 4459593, | ||
| 106 | + "username": "fakeuser", | ||
| 107 | + "name": "Fake User", | ||
| 108 | + "state": "active", | ||
| 109 | + "locked": False, | ||
| 110 | + "avatar_url": "https://example.com/uploads/-/system/user/avatar/4459593/avatar.png", | ||
| 111 | + "web_url": "https://example.com/fakeuser", | ||
| 112 | + }, | ||
| 113 | + "user_notes_count": 9, | ||
| 114 | + "merge_requests_count": 0, | ||
| 115 | + "upvotes": 1, | ||
| 116 | + "downvotes": 0, | ||
| 117 | + "due_date": None, | ||
| 118 | + "confidential": False, | ||
| 119 | + "discussion_locked": None, | ||
| 120 | + "issue_type": "issue", | ||
| 121 | + "web_url": "https://example.com/fakeorg/fakeproject/-/issues/461536", | ||
| 122 | + "time_stats": { | ||
| 123 | + "time_estimate": 0, | ||
| 124 | + "total_time_spent": 0, | ||
| 125 | + "human_time_estimate": None, | ||
| 126 | + "human_total_time_spent": None, | ||
| 127 | + }, | ||
| 128 | + "task_completion_status": {"count": 0, "completed_count": 0}, | ||
| 129 | + "weight": None, | ||
| 130 | + "blocking_issues_count": 0, | ||
| 131 | + } | ||
| 132 | + ] | ||
| 133 | + | ||
| 60 | 134 | ||
| 61 | 135 | @pytest.fixture | |
| 62 | 136 | def resp_list_merge_requests(): | |
@@ -93,6 +167,26 @@ def resp_get_merge_request_reviewers(): | |||
| 93 | 167 | yield rsps | |
| 94 | 168 | ||
| 95 | 169 | ||
| 170 | + @pytest.fixture | ||
| 171 | + def resp_list_merge_requests_related_issues(): | ||
| 172 | + with responses.RequestsMock() as rsps: | ||
| 173 | + rsps.add( | ||
| 174 | + method=responses.GET, | ||
| 175 | + url="http://localhost/api/v4/projects/1/merge_requests/1", | ||
| 176 | + json=mr_content, | ||
| 177 | + content_type="application/json", | ||
| 178 | + status=200, | ||
| 179 | + ) | ||
| 180 | + rsps.add( | ||
| 181 | + method=responses.GET, | ||
| 182 | + url="http://localhost/api/v4/projects/1/merge_requests/1/related_issues", | ||
| 183 | + json=related_issues, | ||
| 184 | + content_type="application/json", | ||
| 185 | + status=200, | ||
| 186 | + ) | ||
| 187 | + yield rsps | ||
| 188 | + | ||
| 189 | + | ||
| 96 | 190 | def test_list_project_merge_requests(project, resp_list_merge_requests): | |
| 97 | 191 | mrs = project.mergerequests.list() | |
| 98 | 192 | assert isinstance(mrs[0], ProjectMergeRequest) | |
@@ -115,3 +209,13 @@ def test_get_merge_request_reviewers(project, resp_get_merge_request_reviewers): | |||
| 115 | 209 | assert mr.reviewers[0]["name"] == reviewers_details[0].user["name"] | |
| 116 | 210 | assert reviewers_details[0].state == "unreviewed" | |
| 117 | 211 | assert reviewers_details[0].created_at == "2022-07-27T17:03:27.684Z" | |
| 212 | + | ||
| 213 | + | ||
| 214 | + def test_list_related_issues(project, resp_list_merge_requests_related_issues): | ||
| 215 | + mr = project.mergerequests.get(1) | ||
| 216 | + this_mr_related_issues = mr.related_issues() | ||
| 217 | + the_issue = next(iter(this_mr_related_issues)) | ||
| 218 | + assert isinstance(mr, ProjectMergeRequest) | ||
| 219 | + assert isinstance(this_mr_related_issues, RESTObjectList) | ||
| 220 | + assert isinstance(the_issue, ProjectIssue) | ||
| 221 | + assert the_issue.title == related_issues[0]["title"] | ||
0 commit comments