diff --git a/docs/gl_objects/groups.rst b/docs/gl_objects/groups.rst index 1880a6bbd..cd8ab45d0 100644 --- a/docs/gl_objects/groups.rst +++ b/docs/gl_objects/groups.rst @@ -225,7 +225,9 @@ 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 @@ -233,19 +235,25 @@ Reference 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, diff --git a/docs/gl_objects/projects.rst b/docs/gl_objects/projects.rst index e61bb6a53..42dbedf82 100644 --- a/docs/gl_objects/projects.rst +++ b/docs/gl_objects/projects.rst @@ -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': diff --git a/gitlab/mixins.py b/gitlab/mixins.py index b9026c51a..a4ed9f593 100644 --- a/gitlab/mixins.py +++ b/gitlab/mixins.py @@ -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", @@ -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] diff --git a/gitlab/v4/objects/groups.py b/gitlab/v4/objects/groups.py index 9e2760156..574c57b50 100644 --- a/gitlab/v4/objects/groups.py +++ b/gitlab/v4/objects/groups.py @@ -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 @@ -45,6 +45,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject): ("issues", "GroupIssueManager"), ("labels", "GroupLabelManager"), ("members", "GroupMemberManager"), + ("members_all", "GroupMemberAllManager"), ("mergerequests", "GroupMergeRequestManager"), ("milestones", "GroupMilestoneManager"), ("notificationsettings", "GroupNotificationSettingsManager"), diff --git a/gitlab/v4/objects/members.py b/gitlab/v4/objects/members.py index 2686587f0..839c89ef8 100644 --- a/gitlab/v4/objects/members.py +++ b/gitlab/v4/objects/members.py @@ -1,14 +1,20 @@ -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", ] @@ -16,7 +22,7 @@ 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"} @@ -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"} @@ -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"} diff --git a/gitlab/v4/objects/projects.py b/gitlab/v4/objects/projects.py index 4618292ab..4223b1882 100644 --- a/gitlab/v4/objects/projects.py +++ b/gitlab/v4/objects/projects.py @@ -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, @@ -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"), diff --git a/tools/functional/api/test_groups.py b/tools/functional/api/test_groups.py index c44c175f9..eae2d9bfe 100644 --- a/tools/functional/api/test_groups.py +++ b/tools/functional/api/test_groups.py @@ -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()