Skip to content

Commit

Permalink
feat: add feature to get inherited member for project/group
Browse files Browse the repository at this point in the history
  • Loading branch information
Oleksii Shkurupii authored and nejch committed May 6, 2021
1 parent 0d3b8ae commit e444b39
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 62 deletions.
14 changes: 11 additions & 3 deletions docs/gl_objects/groups.rst
Original file line number Diff line number Diff line change
Expand Up @@ -225,27 +225,35 @@ Reference

+ :class:`gitlab.v4.objects.GroupMember`
+ :class:`gitlab.v4.objects.GroupMemberManager`
+ :class:`gitlab.v4.objects.GroupMemberAllManager`
+ :attr:`gitlab.v4.objects.Group.members`
+ :attr:`gitlab.v4.objects.Group.members_all`

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


Examples
--------

List group members::
List only direct group members::

members = group.members.list()

List the group members recursively (including inherited members through
ancestor groups)::

members = group.members.all(all=True)
members = group.members_all.list(all=True)
# or
members = group.members.all(all=True) # Deprecated

Get a group member::
Get only direct group member::

members = group.members.get(member_id)

Get a member of a group, including members inherited through ancestor groups::

members = group.members_all.get(member_id)

Add a member to the group::

member = group.members.create({'user_id': user_id,
Expand Down
15 changes: 12 additions & 3 deletions docs/gl_objects/projects.rst
Original file line number Diff line number Diff line change
Expand Up @@ -502,30 +502,39 @@ Reference

+ :class:`gitlab.v4.objects.ProjectMember`
+ :class:`gitlab.v4.objects.ProjectMemberManager`
+ :class:`gitlab.v4.objects.ProjectMemberAllManager`
+ :attr:`gitlab.v4.objects.Project.members`
+ :attr:`gitlab.v4.objects.Project.members_all`

* GitLab API: https://docs.gitlab.com/ce/api/members.html

Examples
--------

List the project members::
List only direct project members::

members = project.members.list()

List the project members recursively (including inherited members through
ancestor groups)::

members = project.members.all(all=True)
members = project.members_all.list(all=True)
# or
members = project.members.all(all=True) # Deprecated

Search project members matching a query string::

members = project.members.list(query='bar')

Get a single project member::
Get only direct project member::

member = project.members.get(user_id)

Get a member of a project, including members inherited through ancestor groups::

members = project.members_all.get(member_id)


Add a project member::

member = project.members.create({'user_id': user.id, 'access_level':
Expand Down
49 changes: 48 additions & 1 deletion gitlab/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
from gitlab import exceptions as exc
from gitlab import types as g_types
from gitlab import utils

import warnings

__all__ = [
"GetMixin",
Expand Down Expand Up @@ -928,3 +928,50 @@ def render(self, link_url: str, image_url: str, **kwargs: Any) -> Dict[str, Any]
if TYPE_CHECKING:
assert not isinstance(result, requests.Response)
return result


class MemberAllMixin(_RestManagerBase):
"""This mixin is deprecated."""

_computed_path: Optional[str]
_from_parent_attrs: Dict[str, Any]
_obj_cls: Optional[Type[base.RESTObject]]
_parent: Optional[base.RESTObject]
_parent_attrs: Dict[str, Any]
_path: Optional[str]
gitlab: gitlab.Gitlab

@cli.register_custom_action(("GroupMemberManager", "ProjectMemberManager"))
@exc.on_http_error(exc.GitlabListError)
def all(self, **kwargs: Any) -> List[base.RESTObject]:
"""List all the members, included inherited ones.
This Method is deprecated.
Args:
all (bool): If True, return all the items, without pagination
per_page (int): Number of items to retrieve per request
page (int): ID of the page to return (starts with page 1)
as_list (bool): If set to False and no pagination option is
defined, return a generator instead of a list
**kwargs: Extra options to send to the server (e.g. sudo)
Raises:
GitlabAuthenticationError: If authentication is not correct
GitlabListError: If the list could not be retrieved
Returns:
RESTObjectList: The list of members
"""

warnings.warn(
"The all() method for this object is deprecated "
"and will be removed in a future version.",
DeprecationWarning,
)
path = "%s/all" % self.path

if TYPE_CHECKING:
assert self._obj_cls is not None
obj = self.gitlab.http_list(path, **kwargs)
return [self._obj_cls(self, item) for item in obj]
3 changes: 2 additions & 1 deletion gitlab/v4/objects/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from .epics import GroupEpicManager # noqa: F401
from .issues import GroupIssueManager # noqa: F401
from .labels import GroupLabelManager # noqa: F401
from .members import GroupMemberManager # noqa: F401
from .members import GroupMemberManager, GroupMemberAllManager # noqa: F401
from .merge_requests import GroupMergeRequestManager # noqa: F401
from .milestones import GroupMilestoneManager # noqa: F401
from .notification_settings import GroupNotificationSettingsManager # noqa: F401
Expand Down Expand Up @@ -45,6 +45,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
("issues", "GroupIssueManager"),
("labels", "GroupLabelManager"),
("members", "GroupMemberManager"),
("members_all", "GroupMemberAllManager"),
("mergerequests", "GroupMergeRequestManager"),
("milestones", "GroupMilestoneManager"),
("notificationsettings", "GroupNotificationSettingsManager"),
Expand Down
72 changes: 20 additions & 52 deletions gitlab/v4/objects/members.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
from gitlab import cli, types
from gitlab import exceptions as exc
from gitlab import types
from gitlab.base import RequiredOptional, RESTManager, RESTObject
from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, SaveMixin

from gitlab.mixins import (
CRUDMixin,
ObjectDeleteMixin,
SaveMixin,
RetrieveMixin,
MemberAllMixin,
)

__all__ = [
"GroupMember",
"GroupMemberManager",
"GroupMemberAllManager",
"ProjectMember",
"ProjectMemberManager",
"ProjectMemberAllManager",
]


class GroupMember(SaveMixin, ObjectDeleteMixin, RESTObject):
_short_print_attr = "username"


class GroupMemberManager(CRUDMixin, RESTManager):
class GroupMemberManager(MemberAllMixin, CRUDMixin, RESTManager):
_path = "/groups/%(group_id)s/members"
_obj_cls = GroupMember
_from_parent_attrs = {"group_id": "id"}
Expand All @@ -28,37 +34,18 @@ class GroupMemberManager(CRUDMixin, RESTManager):
)
_types = {"user_ids": types.ListAttribute}

@cli.register_custom_action("GroupMemberManager")
@exc.on_http_error(exc.GitlabListError)
def all(self, **kwargs):
"""List all the members, included inherited ones.
Args:
all (bool): If True, return all the items, without pagination
per_page (int): Number of items to retrieve per request
page (int): ID of the page to return (starts with page 1)
as_list (bool): If set to False and no pagination option is
defined, return a generator instead of a list
**kwargs: Extra options to send to the server (e.g. sudo)
Raises:
GitlabAuthenticationError: If authentication is not correct
GitlabListError: If the list could not be retrieved

Returns:
RESTObjectList: The list of members
"""

path = "%s/all" % self.path
obj = self.gitlab.http_list(path, **kwargs)
return [self._obj_cls(self, item) for item in obj]
class GroupMemberAllManager(RetrieveMixin, RESTManager):
_path = "/groups/%(group_id)s/members/all"
_obj_cls = GroupMember
_from_parent_attrs = {"group_id": "id"}


class ProjectMember(SaveMixin, ObjectDeleteMixin, RESTObject):
_short_print_attr = "username"


class ProjectMemberManager(CRUDMixin, RESTManager):
class ProjectMemberManager(MemberAllMixin, CRUDMixin, RESTManager):
_path = "/projects/%(project_id)s/members"
_obj_cls = ProjectMember
_from_parent_attrs = {"project_id": "id"}
Expand All @@ -70,27 +57,8 @@ class ProjectMemberManager(CRUDMixin, RESTManager):
)
_types = {"user_ids": types.ListAttribute}

@cli.register_custom_action("ProjectMemberManager")
@exc.on_http_error(exc.GitlabListError)
def all(self, **kwargs):
"""List all the members, included inherited ones.

Args:
all (bool): If True, return all the items, without pagination
per_page (int): Number of items to retrieve per request
page (int): ID of the page to return (starts with page 1)
as_list (bool): If set to False and no pagination option is
defined, return a generator instead of a list
**kwargs: Extra options to send to the server (e.g. sudo)
Raises:
GitlabAuthenticationError: If authentication is not correct
GitlabListError: If the list could not be retrieved
Returns:
RESTObjectList: The list of members
"""

path = "%s/all" % self.path
obj = self.gitlab.http_list(path, **kwargs)
return [self._obj_cls(self, item) for item in obj]
class ProjectMemberAllManager(RetrieveMixin, RESTManager):
_path = "/projects/%(project_id)s/members/all"
_obj_cls = ProjectMember
_from_parent_attrs = {"project_id": "id"}
3 changes: 2 additions & 1 deletion gitlab/v4/objects/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from .issues import ProjectIssueManager # noqa: F401
from .jobs import ProjectJobManager # noqa: F401
from .labels import ProjectLabelManager # noqa: F401
from .members import ProjectMemberManager # noqa: F401
from .members import ProjectMemberManager, ProjectMemberAllManager # noqa: F401
from .merge_request_approvals import ( # noqa: F401
ProjectApprovalManager,
ProjectApprovalRuleManager,
Expand Down Expand Up @@ -130,6 +130,7 @@ class Project(RefreshMixin, SaveMixin, ObjectDeleteMixin, RepositoryMixin, RESTO
("issues", "ProjectIssueManager"),
("labels", "ProjectLabelManager"),
("members", "ProjectMemberManager"),
("members_all", "ProjectMemberAllManager"),
("mergerequests", "ProjectMergeRequestManager"),
("milestones", "ProjectMilestoneManager"),
("notes", "ProjectNoteManager"),
Expand Down
3 changes: 2 additions & 1 deletion tools/functional/api/test_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ def test_groups(gl):

group1.members.delete(user.id)
assert len(group1.members.list()) == 2
assert len(group1.members.all())
assert len(group1.members.all()) # Deprecated
assert len(group1.members_all.list())
member = group1.members.get(user2.id)
member.access_level = gitlab.const.OWNER_ACCESS
member.save()
Expand Down

0 comments on commit e444b39

Please sign in to comment.