Skip to content

Commit cd81aea

Browse files
fix: adds missing status check methods for merge requests
1 parent 6eee494 commit cd81aea

File tree

5 files changed

+223
-14
lines changed

5 files changed

+223
-14
lines changed

docs/gl_objects/status_checks.rst

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ Examples
2424

2525
List external status checks for a project::
2626

27-
status_checks = project.external_status_checks.list(get_all=True)
27+
external_status_checks = project.external_status_checks.list()
2828

2929
Create an external status check with shared secret::
3030

31-
status_checks = project.external_status_checks.create({
31+
external_status_checks = project.external_status_checks.create({
3232
"name": "mr_blocker",
3333
"external_url": "https://example.com/mr-status-check",
3434
"shared_secret": "secret-string"
@@ -38,7 +38,7 @@ Create an external status check with shared secret for protected branches::
3838

3939
protected_branch = project.protectedbranches.get('main')
4040

41-
status_check = project.external_status_checks.create({
41+
external_status_check = project.external_status_checks.create({
4242
"name": "mr_blocker",
4343
"external_url": "https://example.com/mr-status-check",
4444
"shared_secret": "secret-string",
@@ -48,10 +48,25 @@ Create an external status check with shared secret for protected branches::
4848

4949
Update an external status check::
5050

51-
status_check.external_url = "https://example.com/mr-blocker"
52-
status_check.save()
51+
external_status_check.external_url = "https://example.com/mr-blocker"
52+
external_status_check.save()
5353

5454
Delete an external status check::
5555

56-
status_check.delete(status_check_id)
56+
external_status_check.delete(externa_status_check_id)
5757

58+
List external status check for a project merge request::
59+
60+
merge_request = project.mergerequests.get(1)
61+
62+
merge_request.external_status_checks.list()
63+
64+
Set external status check for a project merge request::
65+
66+
merge_request = project.mergerequests.get(1)
67+
68+
merge_request.external_status_check_response.update({
69+
"external_status_check_id": external_status_check_id,
70+
"status": "passed",
71+
"sha": merge_request.sha
72+
})

gitlab/v4/objects/merge_requests.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,10 @@
4646
from .notes import ProjectMergeRequestNoteManager # noqa: F401
4747
from .pipelines import ProjectMergeRequestPipelineManager # noqa: F401
4848
from .reviewers import ProjectMergeRequestReviewerDetailManager
49-
from .status_checks import ProjectMergeRequestStatusCheckManager
49+
from .status_checks import (
50+
ProjectMergeRequestStatusCheckManager,
51+
ProjectMergeRequestStatusCheckResponseManager,
52+
)
5053

5154
__all__ = [
5255
"MergeRequest",
@@ -170,7 +173,8 @@ class ProjectMergeRequest(
170173
resourcemilestoneevents: ProjectMergeRequestResourceMilestoneEventManager
171174
resourcestateevents: ProjectMergeRequestResourceStateEventManager
172175
reviewer_details: ProjectMergeRequestReviewerDetailManager
173-
status_checks: ProjectMergeRequestStatusCheckManager
176+
external_status_checks: ProjectMergeRequestStatusCheckManager
177+
external_status_check_response: ProjectMergeRequestStatusCheckResponseManager
174178

175179
@cli.register_custom_action(cls_names="ProjectMergeRequest")
176180
@exc.on_http_error(exc.GitlabMROnBuildSuccessError)

gitlab/v4/objects/status_checks.py

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
from gitlab.base import RESTObject
1+
from typing import Any, Dict, Optional
2+
3+
from gitlab.base import RESTManager, RESTObject
24
from gitlab.mixins import (
35
CreateMixin,
46
DeleteMixin,
@@ -27,6 +29,7 @@ class ProjectExternalStatusCheckManager(
2729
CreateMixin[ProjectExternalStatusCheck],
2830
UpdateMixin[ProjectExternalStatusCheck],
2931
DeleteMixin[ProjectExternalStatusCheck],
32+
RESTManager[ProjectExternalStatusCheck],
3033
):
3134
_path = "/projects/{project_id}/external_status_checks"
3235
_obj_cls = ProjectExternalStatusCheck
@@ -41,15 +44,41 @@ class ProjectExternalStatusCheckManager(
4144
_types = {"protected_branch_ids": ArrayAttribute}
4245

4346

44-
class ProjectMergeRequestStatusCheck(SaveMixin, RESTObject):
47+
class ProjectMergeRequestStatusCheckResponse(SaveMixin, RESTObject):
4548
pass
4649

4750

48-
class ProjectMergeRequestStatusCheckManager(ListMixin[ProjectMergeRequestStatusCheck]):
49-
_path = "/projects/{project_id}/merge_requests/{merge_request_iid}/status_checks"
50-
_obj_cls = ProjectMergeRequestStatusCheck
51-
_from_parent_attrs = {"project_id": "project_id", "merge_request_iid": "iid"}
51+
class ProjectMergeRequestStatusCheckResponseManager(
52+
UpdateMixin[ProjectMergeRequestStatusCheckResponse],
53+
RESTManager[ProjectMergeRequestStatusCheckResponse],
54+
):
55+
_path = "/projects/{project_id}/merge_requests/{mr_iid}/status_check_responses"
56+
_obj_cls = ProjectMergeRequestStatusCheckResponse
57+
_from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
5258
_update_attrs = RequiredOptional(
5359
required=("sha", "external_status_check_id", "status")
5460
)
5561
_update_method = UpdateMethod.POST
62+
63+
def update( # type: ignore[override]
64+
self, new_data: Optional[Dict[str, Any]] = None, **kwargs: Any
65+
) -> Dict[str, Any]:
66+
"""Update a Label on the server.
67+
68+
Args:
69+
**kwargs: Extra options to send to the server (e.g. sudo)
70+
"""
71+
return super().update(id=None, new_data=new_data, **kwargs)
72+
73+
74+
class ProjectMergeRequestStatusCheck(RESTObject):
75+
pass
76+
77+
78+
class ProjectMergeRequestStatusCheckManager(
79+
ListMixin[ProjectMergeRequestStatusCheck],
80+
RESTManager[ProjectMergeRequestStatusCheck],
81+
):
82+
_path = "/projects/{project_id}/merge_requests/{mr_iid}/status_checks"
83+
_obj_cls = ProjectMergeRequestStatusCheck
84+
_from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}

tests/functional/api/test_merge_requests.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,3 +300,46 @@ def test_merge_request_merge_ref_should_fail(project, merge_request) -> None:
300300
with pytest.raises(gitlab.exceptions.GitlabGetError):
301301
response = merge_request.merge_ref()
302302
assert "commit_id" not in response
303+
304+
305+
@pytest.mark.gitlab_premium
306+
def test_merge_request_external_status_check_set_status(project, merge_request):
307+
project.external_status_checks.create(
308+
{
309+
"name": "external_status_check",
310+
"external_url": "https://example.com/mr-blocker",
311+
}
312+
)
313+
314+
mr_external_status_checks = merge_request.external_status_checks.list()
315+
assert len(mr_external_status_checks) == 1
316+
317+
expected_external_status_check = None
318+
319+
for mr_external_status_check in mr_external_status_checks:
320+
if mr_external_status_check.name == "external_status_check":
321+
expected_external_status_check = mr_external_status_check
322+
break
323+
324+
assert expected_external_status_check is not None
325+
326+
# set the external status check value to 'passed'
327+
merge_request.external_status_check_response.update(
328+
{
329+
"external_status_check_id": expected_external_status_check.id,
330+
"status": "passed",
331+
"sha": merge_request.sha,
332+
}
333+
)
334+
335+
time.sleep(2)
336+
337+
# Check the status again to validate the passed status
338+
mr_status_checks = merge_request.external_status_checks.list()
339+
340+
for mr_status_check in mr_status_checks:
341+
if mr_status_check.name == "external_status_check":
342+
expected_status_check = mr_status_check
343+
break
344+
345+
assert expected_status_check.status == "passed"

tests/unit/objects/test_status_checks.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,49 @@
55
import pytest
66
import responses
77

8+
mr_content = {
9+
"id": 1,
10+
"iid": 1,
11+
"project_id": 1,
12+
"title": "test1",
13+
"description": "fixed login page css paddings",
14+
"state": "merged",
15+
"sha": "somerandomstring",
16+
"merged_by": {
17+
"id": 87854,
18+
"name": "Douwe Maan",
19+
"username": "DouweM",
20+
"state": "active",
21+
"avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
22+
"web_url": "https://gitlab.com/DouweM",
23+
},
24+
"reviewers": [
25+
{
26+
"id": 2,
27+
"name": "Sam Bauch",
28+
"username": "kenyatta_oconnell",
29+
"state": "active",
30+
"avatar_url": "https://www.gravatar.com/avatar/956c92487c6f6f7616b536927e22c9a0?s=80&d=identicon",
31+
"web_url": "http://gitlab.example.com//kenyatta_oconnell",
32+
}
33+
],
34+
}
35+
36+
external_status_checks_content = [
37+
{
38+
"id": 2,
39+
"name": "Service 2",
40+
"external_url": "https://gitlab.example.com/test-endpoint-2",
41+
"status": "pending",
42+
},
43+
{
44+
"id": 1,
45+
"name": "Service 1",
46+
"external_url": "https://gitlab.example.com/test-endpoint-1",
47+
"status": "pending",
48+
},
49+
]
50+
851

952
@pytest.fixture
1053
def external_status_check():
@@ -104,6 +147,54 @@ def resp_delete_external_status_checks():
104147
content_type="application/json",
105148
status=200,
106149
)
150+
151+
yield rsps
152+
153+
154+
@pytest.fixture
155+
def resp_list_merge_requests_status_checks():
156+
with responses.RequestsMock() as rsps:
157+
rsps.add(
158+
method=responses.GET,
159+
url="http://localhost/api/v4/projects/1/merge_requests/1",
160+
json=mr_content,
161+
content_type="application/json",
162+
status=200,
163+
)
164+
rsps.add(
165+
method=responses.GET,
166+
url="http://localhost/api/v4/projects/1/merge_requests/1/status_checks",
167+
json=external_status_checks_content,
168+
content_type="application/json",
169+
status=200,
170+
)
171+
yield rsps
172+
173+
174+
@pytest.fixture
175+
def resp_list_merge_requests_status_checks_set_value():
176+
with responses.RequestsMock() as rsps:
177+
rsps.add(
178+
method=responses.GET,
179+
url="http://localhost/api/v4/projects/1/merge_requests/1",
180+
json=mr_content,
181+
content_type="application/json",
182+
status=200,
183+
)
184+
rsps.add(
185+
method=responses.GET,
186+
url="http://localhost/api/v4/projects/1/merge_requests/1/status_checks",
187+
json=external_status_checks_content,
188+
content_type="application/json",
189+
status=200,
190+
)
191+
rsps.add(
192+
method=responses.POST,
193+
url="http://localhost/api/v4/projects/1/merge_requests/1/status_check_responses",
194+
json={"status": "passed"},
195+
content_type="application/json",
196+
status=200,
197+
)
107198
yield rsps
108199

109200

@@ -125,3 +216,30 @@ def test_delete_external_status_checks(gl, resp_delete_external_status_checks):
125216
gl.projects.get(1, lazy=True).external_status_checks.delete(1)
126217
status_checks = gl.projects.get(1, lazy=True).external_status_checks.list()
127218
assert len(status_checks) == 0
219+
220+
221+
def test_get_merge_request_external_status_checks(
222+
gl, resp_list_merge_requests_status_checks
223+
):
224+
merge_request = gl.projects.get(1, lazy=True).mergerequests.get(1)
225+
external_status_checks = merge_request.external_status_checks.list()
226+
assert len(external_status_checks) == 2
227+
228+
229+
def test_get_merge_request_external_status_checks_set_value(
230+
gl, resp_list_merge_requests_status_checks_set_value
231+
):
232+
merge_request = gl.projects.get(1, lazy=True).mergerequests.get(1)
233+
external_status_checks = merge_request.external_status_checks.list()
234+
235+
assert len(external_status_checks) == 2
236+
for external_status_check in external_status_checks:
237+
if external_status_check.name == "Service 2":
238+
response = merge_request.external_status_check_response.update(
239+
{
240+
"external_status_check_id": external_status_check.id,
241+
"status": "passed",
242+
"sha": merge_request.sha,
243+
}
244+
)
245+
response["status"] == "passed"

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy