Skip to content

Commit 9353f54

Browse files
JacobHennerJohnVillalovos
authored andcommitted
feat(api): project/group hook test triggering
Add the ability to trigger tests of project and group hooks. Fixes #2924
1 parent 8d74b88 commit 9353f54

File tree

5 files changed

+113
-7
lines changed

5 files changed

+113
-7
lines changed

docs/gl_objects/groups.rst

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ Remove a group::
8989
group.delete()
9090

9191
Restore a Group marked for deletion (Premium only):::
92-
92+
9393
group.restore()
9494

9595

@@ -368,9 +368,9 @@ SAML group links
368368

369369
Add a SAML group link to an existing GitLab group::
370370

371-
saml_link = group.saml_group_links.create({
372-
"saml_group_name": "<your_saml_group_name>",
373-
"access_level": <chosen_access_level>
371+
saml_link = group.saml_group_links.create({
372+
"saml_group_name": "<your_saml_group_name>",
373+
"access_level": <chosen_access_level>
374374
})
375375

376376
List a group's SAML group links::
@@ -419,6 +419,10 @@ Update a group hook::
419419
hook.push_events = 0
420420
hook.save()
421421

422+
Test a group hook::
423+
424+
hook.test("push_events")
425+
422426
Delete a group hook::
423427

424428
group.hooks.delete(hook_id)

docs/gl_objects/projects.rst

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ Import the project using file stored on a remote URL::
353353
output = gl.projects.remote_import(
354354
url="https://whatever.com/url/file.tar.gz",
355355
path="my_new_remote_project",
356-
name="My New Remote Project",
356+
name="My New Remote Project",
357357
namespace="my-group",
358358
override_params={'visibility': 'private'},
359359
)
@@ -367,7 +367,7 @@ Import the project using file stored on AWS S3::
367367
file_key="aws-file-key",
368368
access_key_id="aws-access-key-id",
369369
secret_access_key="secret-access-key",
370-
name="My New Remote Project",
370+
name="My New Remote Project",
371371
namespace="my-group",
372372
override_params={'visibility': 'private'},
373373
)
@@ -449,7 +449,7 @@ Get file details from headers, without fetching its entire content::
449449
print(headers["X-Gitlab-Size"])
450450

451451
Get a raw file::
452-
452+
453453
raw_content = project.files.raw(file_path='README.rst', ref='main')
454454
print(raw_content)
455455
with open('/tmp/raw-download.txt', 'wb') as f:
@@ -689,6 +689,10 @@ Update a project hook::
689689
hook.push_events = 0
690690
hook.save()
691691

692+
Test a project hook::
693+
694+
hook.test("push_events")
695+
692696
Delete a project hook::
693697

694698
project.hooks.delete(hook_id)

gitlab/exceptions.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,10 @@ class GitlabDeploymentApprovalError(GitlabOperationError):
316316
pass
317317

318318

319+
class GitlabHookTestError(GitlabOperationError):
320+
pass
321+
322+
319323
# For an explanation of how these type-hints work see:
320324
# https://mypy.readthedocs.io/en/stable/generics.html#declaring-decorators
321325
#
@@ -370,6 +374,7 @@ def wrapped_f(*args: Any, **kwargs: Any) -> Any:
370374
"GitlabGetError",
371375
"GitlabGroupTransferError",
372376
"GitlabHeadError",
377+
"GitlabHookTestError",
373378
"GitlabHousekeepingError",
374379
"GitlabHttpError",
375380
"GitlabImportError",

gitlab/v4/objects/hooks.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from typing import Any, cast, Union
22

3+
from gitlab import exceptions as exc
34
from gitlab.base import RESTManager, RESTObject
45
from gitlab.mixins import CRUDMixin, NoUpdateMixin, ObjectDeleteMixin, SaveMixin
56
from gitlab.types import RequiredOptional
@@ -31,6 +32,20 @@ def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> Hook:
3132
class ProjectHook(SaveMixin, ObjectDeleteMixin, RESTObject):
3233
_repr_attr = "url"
3334

35+
@exc.on_http_error(exc.GitlabHookTestError)
36+
def test(self, trigger: str) -> None:
37+
"""
38+
Test a Project Hook
39+
40+
Args:
41+
trigger: Type of trigger event to test
42+
43+
Raises:
44+
GitlabHookTestError: If the hook test attempt failed
45+
"""
46+
path = f"{self.manager.path}/{self.encoded_id}/test/{trigger}"
47+
self.manager.gitlab.http_post(path)
48+
3449

3550
class ProjectHookManager(CRUDMixin, RESTManager):
3651
_path = "/projects/{project_id}/hooks"
@@ -78,6 +93,20 @@ def get(
7893
class GroupHook(SaveMixin, ObjectDeleteMixin, RESTObject):
7994
_repr_attr = "url"
8095

96+
@exc.on_http_error(exc.GitlabHookTestError)
97+
def test(self, trigger: str) -> None:
98+
"""
99+
Test a Group Hook
100+
101+
Args:
102+
trigger: Type of trigger event to test
103+
104+
Raises:
105+
GitlabHookTestError: If the hook test attempt failed
106+
"""
107+
path = f"{self.manager.path}/{self.encoded_id}/test/{trigger}"
108+
self.manager.gitlab.http_post(path)
109+
81110

82111
class GroupHookManager(CRUDMixin, RESTManager):
83112
_path = "/groups/{group_id}/hooks"

tests/unit/objects/test_hooks.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import pytest
1010
import responses
1111

12+
import gitlab
1213
from gitlab.v4.objects import GroupHook, Hook, ProjectHook
1314

1415
hooks_content = [
@@ -89,6 +90,58 @@ def resp_hook_update():
8990
yield rsps
9091

9192

93+
@pytest.fixture
94+
def resp_hook_test():
95+
with responses.RequestsMock() as rsps:
96+
hook_pattern = re.compile(
97+
r"http://localhost/api/v4/((groups|projects)/1/|)hooks/1"
98+
)
99+
test_pattern = re.compile(
100+
r"http://localhost/api/v4/((groups|projects)/1/|)hooks/1/test/[a-z_]+"
101+
)
102+
rsps.add(
103+
method=responses.GET,
104+
url=hook_pattern,
105+
json=hook_content,
106+
content_type="application/json",
107+
status=200,
108+
)
109+
rsps.add(
110+
method=responses.POST,
111+
url=test_pattern,
112+
json={"message": "201 Created"},
113+
content_type="application/json",
114+
status=201,
115+
)
116+
yield rsps
117+
118+
119+
@pytest.fixture
120+
def resp_hook_test_error():
121+
with responses.RequestsMock() as rsps:
122+
hook_pattern = re.compile(
123+
r"http://localhost/api/v4/((groups|projects)/1/|)hooks/1"
124+
)
125+
test_pattern = re.compile(
126+
r"http://localhost/api/v4/((groups|projects)/1/|)hooks/1/test/[a-z_]+"
127+
)
128+
rsps.add(
129+
method=responses.GET,
130+
url=hook_pattern,
131+
json=hook_content,
132+
content_type="application/json",
133+
status=200,
134+
)
135+
rsps.add(
136+
method=responses.POST,
137+
url=test_pattern,
138+
json={"message": "<html>error</html>"},
139+
content_type="application/json",
140+
status=422,
141+
)
142+
yield rsps
143+
144+
92145
@pytest.fixture
93146
def resp_hook_delete():
94147
with responses.RequestsMock() as rsps:
@@ -174,6 +227,17 @@ def test_delete_group_hook(group, resp_hook_delete):
174227
group.hooks.delete(1)
175228

176229

230+
def test_test_group_hook(group, resp_hook_test):
231+
hook = group.hooks.get(1)
232+
hook.test("push_events")
233+
234+
235+
def test_test_error_group_hook(group, resp_hook_test_error):
236+
hook = group.hooks.get(1)
237+
with pytest.raises(gitlab.exceptions.GitlabHookTestError):
238+
hook.test("push_events")
239+
240+
177241
def test_list_project_hooks(project, resp_hooks_list):
178242
hooks = project.hooks.list()
179243
assert hooks[0].id == 1

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