Skip to content

feat(api): add group hooks #1533

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions docs/gl_objects/groups.rst
Original file line number Diff line number Diff line change
Expand Up @@ -338,3 +338,43 @@ You can use the ``ldapgroups`` manager to list available LDAP groups::

# list the groups for a specific LDAP provider
ldap_groups = gl.ldapgroups.list(search='foo', provider='ldapmain')

Groups hooks
============

Reference
---------

* v4 API:

+ :class:`gitlab.v4.objects.GroupHook`
+ :class:`gitlab.v4.objects.GroupHookManager`
+ :attr:`gitlab.v4.objects.Group.hooks`

* GitLab API: https://docs.gitlab.com/ce/api/groups.html#hooks

Examples
--------

List the group hooks::

hooks = group.hooks.list()

Get a group hook::

hook = group.hooks.get(hook_id)

Create a group hook::

hook = group.hooks.create({'url': 'http://my/action/url', 'push_events': 1})

Update a group hook::

hook.push_events = 0
hook.save()

Delete a group hook::

group.hooks.delete(hook_id)
# or
hook.delete()
2 changes: 2 additions & 0 deletions gitlab/v4/objects/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from .deploy_tokens import GroupDeployTokenManager # noqa: F401
from .epics import GroupEpicManager # noqa: F401
from .export_import import GroupExportManager, GroupImportManager # noqa: F401
from .hooks import GroupHookManager # noqa: F401
from .issues import GroupIssueManager # noqa: F401
from .labels import GroupLabelManager # noqa: F401
from .members import ( # noqa: F401
Expand Down Expand Up @@ -52,6 +53,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
("descendant_groups", "GroupDescendantGroupManager"),
("exports", "GroupExportManager"),
("epics", "GroupEpicManager"),
("hooks", "GroupHookManager"),
("imports", "GroupImportManager"),
("issues", "GroupIssueManager"),
("issues_statistics", "GroupIssuesStatisticsManager"),
Expand Down
52 changes: 52 additions & 0 deletions gitlab/v4/objects/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"HookManager",
"ProjectHook",
"ProjectHookManager",
"GroupHook",
"GroupHookManager",
]


Expand Down Expand Up @@ -60,3 +62,53 @@ class ProjectHookManager(CRUDMixin, RESTManager):
"token",
),
)


class GroupHook(SaveMixin, ObjectDeleteMixin, RESTObject):
_short_print_attr = "url"


class GroupHookManager(CRUDMixin, RESTManager):
_path = "/groups/%(group_id)s/hooks"
_obj_cls = GroupHook
_from_parent_attrs = {"group_id": "id"}
_create_attrs = RequiredOptional(
required=("url",),
optional=(
"push_events",
"issues_events",
"confidential_issues_events",
"merge_requests_events",
"tag_push_events",
"note_events",
"confidential_note_events",
"job_events",
"pipeline_events",
"wiki_page_events",
"deployment_events",
"releases_events",
"subgroup_events",
"enable_ssl_verification",
"token",
),
)
_update_attrs = RequiredOptional(
required=("url",),
optional=(
"push_events",
"issues_events",
"confidential_issues_events",
"merge_requests_events",
"tag_push_events",
"note_events",
"confidential_note_events",
"job_events",
"pipeline_events",
"wiki_page_events",
"deployment_events",
"releases_events",
"subgroup_events",
"enable_ssl_verification",
"token",
),
)
13 changes: 13 additions & 0 deletions tests/functional/api/test_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,16 @@ def test_group_wiki(group):
wiki.save()
wiki.delete()
assert len(group.wikis.list()) == 0


@pytest.mark.skip(reason="EE feature")
def test_group_hooks(group):
hook = group.hooks.create({"url": "http://hook.url"})
assert len(group.hooks.list()) == 1

hook.note_events = True
hook.save()

hook = group.hooks.get(hook.id)
assert hook.note_events is True
hook.delete()
192 changes: 186 additions & 6 deletions tests/unit/objects/test_hooks.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,209 @@
"""
GitLab API: https://docs.gitlab.com/ce/api/system_hooks.html
GitLab API: https://docs.gitlab.com/ce/api/groups.html#hooks
GitLab API: https://docs.gitlab.com/ee/api/projects.html#hooks
"""

import re

import pytest
import responses

from gitlab.v4.objects import Hook
from gitlab.v4.objects import GroupHook, Hook, ProjectHook

hooks_content = [
{
"id": 1,
"url": "testurl",
"push_events": True,
"tag_push_events": True,
},
{
"id": 2,
"url": "testurl_second",
"push_events": False,
"tag_push_events": False,
},
]

hook_content = hooks_content[0]


@pytest.fixture
def resp_hooks_list():
with responses.RequestsMock() as rsps:
rsps.add(
method=responses.GET,
url=re.compile(r"http://localhost/api/v4/((groups|projects)/1/|)hooks"),
json=hooks_content,
content_type="application/json",
status=200,
)
yield rsps


@pytest.fixture
def resp_hook_get():
with responses.RequestsMock() as rsps:
rsps.add(
method=responses.GET,
url=re.compile(r"http://localhost/api/v4/((groups|projects)/1/|)hooks/1"),
json=hook_content,
content_type="application/json",
status=200,
)
yield rsps


@pytest.fixture
def resp_hook_create():
with responses.RequestsMock() as rsps:
rsps.add(
method=responses.POST,
url=re.compile(r"http://localhost/api/v4/((groups|projects)/1/|)hooks"),
json=hook_content,
content_type="application/json",
status=200,
)
yield rsps


@pytest.fixture
def resp_get_hook():
content = {"url": "testurl", "id": 1}
def resp_hook_update():
with responses.RequestsMock() as rsps:
pattern = re.compile(r"http://localhost/api/v4/((groups|projects)/1/|)hooks/1")
rsps.add(
method=responses.GET,
url=pattern,
json=hook_content,
content_type="application/json",
status=200,
)
rsps.add(
method=responses.PUT,
url=pattern,
json=hook_content,
content_type="application/json",
status=200,
)
yield rsps


@pytest.fixture
def resp_hook_delete():
with responses.RequestsMock() as rsps:
pattern = re.compile(r"http://localhost/api/v4/((groups|projects)/1/|)hooks/1")
rsps.add(
method=responses.GET,
url="http://localhost/api/v4/hooks/1",
json=content,
url=pattern,
json=hook_content,
content_type="application/json",
status=200,
)
rsps.add(
method=responses.DELETE,
url=pattern,
status=204,
)
yield rsps


def test_hooks(gl, resp_get_hook):
def test_list_system_hooks(gl, resp_hooks_list):
hooks = gl.hooks.list()
assert hooks[0].id == 1
assert hooks[0].url == "testurl"
assert hooks[1].id == 2
assert hooks[1].url == "testurl_second"


def test_get_system_hook(gl, resp_hook_get):
data = gl.hooks.get(1)
assert isinstance(data, Hook)
assert data.url == "testurl"
assert data.id == 1


def test_create_system_hook(gl, resp_hook_create):
hook = gl.hooks.create(hook_content)
assert hook.url == "testurl"
assert hook.push_events is True
assert hook.tag_push_events is True


# there is no update method for system hooks


def test_delete_system_hook(gl, resp_hook_delete):
hook = gl.hooks.get(1)
hook.delete()
gl.hooks.delete(1)


def test_list_group_hooks(group, resp_hooks_list):
hooks = group.hooks.list()
assert hooks[0].id == 1
assert hooks[0].url == "testurl"
assert hooks[1].id == 2
assert hooks[1].url == "testurl_second"


def test_get_group_hook(group, resp_hook_get):
data = group.hooks.get(1)
assert isinstance(data, GroupHook)
assert data.url == "testurl"
assert data.id == 1


def test_create_group_hook(group, resp_hook_create):
hook = group.hooks.create(hook_content)
assert hook.url == "testurl"
assert hook.push_events is True
assert hook.tag_push_events is True


def test_update_group_hook(group, resp_hook_update):
hook = group.hooks.get(1)
assert hook.id == 1
hook.url = "testurl_more"
hook.save()


def test_delete_group_hook(group, resp_hook_delete):
hook = group.hooks.get(1)
hook.delete()
group.hooks.delete(1)


def test_list_project_hooks(project, resp_hooks_list):
hooks = project.hooks.list()
assert hooks[0].id == 1
assert hooks[0].url == "testurl"
assert hooks[1].id == 2
assert hooks[1].url == "testurl_second"


def test_get_project_hook(project, resp_hook_get):
data = project.hooks.get(1)
assert isinstance(data, ProjectHook)
assert data.url == "testurl"
assert data.id == 1


def test_create_project_hook(project, resp_hook_create):
hook = project.hooks.create(hook_content)
assert hook.url == "testurl"
assert hook.push_events is True
assert hook.tag_push_events is True


def test_update_project_hook(project, resp_hook_update):
hook = project.hooks.get(1)
assert hook.id == 1
hook.url = "testurl_more"
hook.save()


def test_delete_project_hook(project, resp_hook_delete):
hook = project.hooks.get(1)
hook.delete()
project.hooks.delete(1)
25 changes: 0 additions & 25 deletions tests/unit/objects/test_projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,31 +177,6 @@ def test_delete_shared_project_link(gl):
pass


@pytest.mark.skip(reason="missing test")
def test_list_project_hooks(gl):
pass


@pytest.mark.skip(reason="missing test")
def test_get_project_hook(gl):
pass


@pytest.mark.skip(reason="missing test")
def test_create_project_hook(gl):
pass


@pytest.mark.skip(reason="missing test")
def test_update_project_hook(gl):
pass


@pytest.mark.skip(reason="missing test")
def test_delete_project_hook(gl):
pass


@pytest.mark.skip(reason="missing test")
def test_create_forked_from_relationship(gl):
pass
Expand Down