Skip to content

Commit 174d992

Browse files
authored
feat: introduce related_issues to merge requests (#2996)
1 parent a2ab54c commit 174d992

File tree

3 files changed

+137
-0
lines changed

3 files changed

+137
-0
lines changed

docs/gl_objects/merge_requests.rst

+4
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ List the changes of a MR::
144144

145145
changes = mr.changes()
146146

147+
List issues related to this merge request::
148+
149+
related_issues = mr.related_issues()
150+
147151
List issues that will close on merge::
148152

149153
mr.closes_issues()

gitlab/v4/objects/merge_requests.py

+29
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,35 @@ def cancel_merge_when_pipeline_succeeds(self, **kwargs: Any) -> Dict[str, str]:
197197
assert isinstance(server_data, dict)
198198
return server_data
199199

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+
200229
@cli.register_custom_action(cls_names="ProjectMergeRequest")
201230
@exc.on_http_error(exc.GitlabListError)
202231
def closes_issues(self, **kwargs: Any) -> RESTObjectList:

tests/unit/objects/test_merge_requests.py

+104
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
import pytest
1010
import responses
1111

12+
from gitlab.base import RESTObjectList
1213
from gitlab.v4.objects import (
1314
ProjectDeploymentMergeRequest,
15+
ProjectIssue,
1416
ProjectMergeRequest,
1517
ProjectMergeRequestReviewerDetail,
1618
)
@@ -57,6 +59,78 @@
5759
}
5860
]
5961

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+
60134

61135
@pytest.fixture
62136
def resp_list_merge_requests():
@@ -93,6 +167,26 @@ def resp_get_merge_request_reviewers():
93167
yield rsps
94168

95169

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+
96190
def test_list_project_merge_requests(project, resp_list_merge_requests):
97191
mrs = project.mergerequests.list()
98192
assert isinstance(mrs[0], ProjectMergeRequest)
@@ -115,3 +209,13 @@ def test_get_merge_request_reviewers(project, resp_get_merge_request_reviewers):
115209
assert mr.reviewers[0]["name"] == reviewers_details[0].user["name"]
116210
assert reviewers_details[0].state == "unreviewed"
117211
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

Comments
 (0)