Skip to content

Commit d0abf43

Browse files
fix: adds missing status check methods for merge requests
1 parent f62dda7 commit d0abf43

File tree

5 files changed

+225
-14
lines changed

5 files changed

+225
-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()
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: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,3 +300,55 @@ 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_list(project, merge_request):
307+
308+
time.sleep(2)
309+
310+
mr_status_checks = merge_request.status_checks.list()
311+
assert len(mr_status_checks) == 1
312+
313+
314+
@pytest.mark.gitlab_premium
315+
def test_merge_request_external_status_check_set_status(project, merge_request):
316+
project.external_status_checks.create(
317+
{
318+
"name": "external_status_check",
319+
"external_url": "https://example.com/mr-blocker",
320+
}
321+
)
322+
323+
mr_external_status_checks = merge_request.external_status_checks.list()
324+
assert len(mr_external_status_checks) == 1
325+
326+
expected_external_status_check = None
327+
328+
for mr_external_status_check in mr_external_status_checks:
329+
if mr_external_status_check.name == "external_status_check":
330+
expected_external_status_check = mr_external_status_check
331+
break
332+
333+
assert expected_external_status_check is not None
334+
335+
# set the external status check value to 'passed'
336+
expected_external_status_check.external_status_check_response.update(
337+
{
338+
"external_status_check_id": expected_external_status_check.id,
339+
"status": "passed",
340+
"sha": merge_request.sha,
341+
}
342+
)
343+
344+
time.sleep(2)
345+
346+
# Check the status again to validate the passed status
347+
mr_status_checks = merge_request.external_status_checks.list()
348+
349+
for mr_status_check in mr_status_checks:
350+
if mr_status_check.name == "external_status_check":
351+
expected_status_check = mr_status_check
352+
break
353+
354+
assert expected_status_check.status == "passed"

tests/unit/objects/test_status_checks.py

Lines changed: 111 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,48 @@ def resp_delete_external_status_checks():
104147
content_type="application/json",
105148
status=200,
106149
)
150+
151+
yield rsps
152+
153+
154+
def before_passing_exeternal_status_check_matcher(request):
155+
data = request.json().get("data", {})
156+
if "a" in data.keys():
157+
return True
158+
return None
159+
160+
161+
def after_passing_exeternal_status_check_matcher(request):
162+
data = request.json().get("data", {})
163+
if "b" in data.keys():
164+
return True
165+
return None
166+
167+
168+
@pytest.fixture
169+
def resp_list_merge_requests_status_checks():
170+
with responses.RequestsMock() as rsps:
171+
rsps.add(
172+
method=responses.GET,
173+
url="http://localhost/api/v4/projects/1/merge_requests/1",
174+
json=mr_content,
175+
content_type="application/json",
176+
status=200,
177+
)
178+
rsps.add(
179+
method=responses.GET,
180+
url="http://localhost/api/v4/projects/1/merge_requests/1/status_checks",
181+
json=external_status_checks_content,
182+
content_type="application/json",
183+
status=200,
184+
)
185+
rsps.add(
186+
method=responses.POST,
187+
url="http://localhost/api/v4/projects/1/merge_requests/1/status_check_responses",
188+
json={"status": "passed"},
189+
content_type="application/json",
190+
status=200,
191+
)
107192
yield rsps
108193

109194

@@ -125,3 +210,29 @@ def test_delete_external_status_checks(gl, resp_delete_external_status_checks):
125210
gl.projects.get(1, lazy=True).external_status_checks.delete(1)
126211
status_checks = gl.projects.get(1, lazy=True).external_status_checks.list()
127212
assert len(status_checks) == 0
213+
214+
215+
def test_get_merge_request_external_status_checks(
216+
gl, resp_list_merge_requests_status_checks
217+
):
218+
merge_request = gl.projects.get(1, lazy=True).mergerequests.get(1)
219+
external_status_checks = merge_request.external_status_checks.list()
220+
assert len(external_status_checks) == 2
221+
222+
223+
def test_get_merge_request_status_checks_set_value(
224+
gl, resp_list_merge_requests_status_checks
225+
):
226+
merge_request = gl.projects.get(1, lazy=True).mergerequests.get(1)
227+
external_status_checks = merge_request.status_checks.list()
228+
229+
for external_status_check in external_status_checks:
230+
if external_status_check == "Service 2":
231+
response = external_status_check.external_status_check_response.update(
232+
{
233+
"external_status_check_id": external_status_check.id,
234+
"status": "passed",
235+
"sha": merge_request.sha,
236+
}
237+
)
238+
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