X Tutup
""" GitLab API: https://docs.gitlab.com/ee/api/users.html https://docs.gitlab.com/ee/api/projects.html#list-projects-starred-by-a-user """ from __future__ import annotations from typing import Any, cast, Literal, Optional, overload import requests from gitlab import cli from gitlab import exceptions as exc from gitlab import types from gitlab.base import RESTObject, RESTObjectList from gitlab.mixins import ( CreateMixin, CRUDMixin, DeleteMixin, GetWithoutIdMixin, ListMixin, NoUpdateMixin, ObjectDeleteMixin, RetrieveMixin, SaveMixin, UpdateMixin, ) from gitlab.types import ArrayAttribute, RequiredOptional from .custom_attributes import UserCustomAttributeManager # noqa: F401 from .events import UserEventManager # noqa: F401 from .personal_access_tokens import UserPersonalAccessTokenManager # noqa: F401 __all__ = [ "CurrentUserEmail", "CurrentUserEmailManager", "CurrentUserGPGKey", "CurrentUserGPGKeyManager", "CurrentUserKey", "CurrentUserKeyManager", "CurrentUserRunner", "CurrentUserRunnerManager", "CurrentUserStatus", "CurrentUserStatusManager", "CurrentUser", "CurrentUserManager", "User", "UserManager", "ProjectUser", "ProjectUserManager", "StarredProject", "StarredProjectManager", "UserEmail", "UserEmailManager", "UserActivities", "UserStatus", "UserStatusManager", "UserActivitiesManager", "UserGPGKey", "UserGPGKeyManager", "UserKey", "UserKeyManager", "UserIdentityProviderManager", "UserImpersonationToken", "UserImpersonationTokenManager", "UserMembership", "UserMembershipManager", "UserProject", "UserProjectManager", "UserContributedProject", "UserContributedProjectManager", ] class CurrentUserEmail(ObjectDeleteMixin, RESTObject): _repr_attr = "email" class CurrentUserEmailManager( RetrieveMixin[CurrentUserEmail], CreateMixin[CurrentUserEmail], DeleteMixin[CurrentUserEmail], ): _path = "/user/emails" _obj_cls = CurrentUserEmail _create_attrs = RequiredOptional(required=("email",)) class CurrentUserGPGKey(ObjectDeleteMixin, RESTObject): pass class CurrentUserGPGKeyManager( RetrieveMixin[CurrentUserGPGKey], CreateMixin[CurrentUserGPGKey], DeleteMixin[CurrentUserGPGKey], ): _path = "/user/gpg_keys" _obj_cls = CurrentUserGPGKey _create_attrs = RequiredOptional(required=("key",)) class CurrentUserKey(ObjectDeleteMixin, RESTObject): _repr_attr = "title" class CurrentUserKeyManager( RetrieveMixin[CurrentUserKey], CreateMixin[CurrentUserKey], DeleteMixin[CurrentUserKey], ): _path = "/user/keys" _obj_cls = CurrentUserKey _create_attrs = RequiredOptional(required=("title", "key")) class CurrentUserRunner(RESTObject): pass class CurrentUserRunnerManager(CreateMixin[CurrentUserRunner]): _path = "/user/runners" _obj_cls = CurrentUserRunner _types = {"tag_list": types.CommaSeparatedListAttribute} _create_attrs = RequiredOptional( required=("runner_type",), optional=( "group_id", "project_id", "description", "paused", "locked", "run_untagged", "tag_list", "access_level", "maximum_timeout", "maintenance_note", ), ) class CurrentUserStatus(SaveMixin, RESTObject): _id_attr = None _repr_attr = "message" class CurrentUserStatusManager( GetWithoutIdMixin[CurrentUserStatus], UpdateMixin[CurrentUserStatus] ): _path = "/user/status" _obj_cls = CurrentUserStatus _update_attrs = RequiredOptional(optional=("emoji", "message")) class CurrentUser(RESTObject): _id_attr = None _repr_attr = "username" emails: CurrentUserEmailManager gpgkeys: CurrentUserGPGKeyManager keys: CurrentUserKeyManager runners: CurrentUserRunnerManager status: CurrentUserStatusManager class CurrentUserManager(GetWithoutIdMixin[CurrentUser]): _path = "/user" _obj_cls = CurrentUser class User(SaveMixin, ObjectDeleteMixin, RESTObject): _repr_attr = "username" customattributes: UserCustomAttributeManager emails: UserEmailManager events: UserEventManager followers_users: UserFollowersManager following_users: UserFollowingManager gpgkeys: UserGPGKeyManager identityproviders: UserIdentityProviderManager impersonationtokens: UserImpersonationTokenManager keys: UserKeyManager memberships: UserMembershipManager personal_access_tokens: UserPersonalAccessTokenManager projects: UserProjectManager contributed_projects: UserContributedProjectManager starred_projects: StarredProjectManager status: UserStatusManager @cli.register_custom_action(cls_names="User") @exc.on_http_error(exc.GitlabBlockError) def block(self, **kwargs: Any) -> bool | None: """Block the user. Args: **kwargs: Extra options to send to the server (e.g. sudo) Raises: GitlabAuthenticationError: If authentication is not correct GitlabBlockError: If the user could not be blocked Returns: Whether the user status has been changed """ path = f"/users/{self.encoded_id}/block" # NOTE: Undocumented behavior of the GitLab API is that it returns a # boolean or None server_data = cast( Optional[bool], self.manager.gitlab.http_post(path, **kwargs) ) if server_data is True: self._attrs["state"] = "blocked" return server_data @cli.register_custom_action(cls_names="User") @exc.on_http_error(exc.GitlabFollowError) def follow(self, **kwargs: Any) -> dict[str, Any] | requests.Response: """Follow the user. Args: **kwargs: Extra options to send to the server (e.g. sudo) Raises: GitlabAuthenticationError: If authentication is not correct GitlabFollowError: If the user could not be followed Returns: The new object data (*not* a RESTObject) """ path = f"/users/{self.encoded_id}/follow" return self.manager.gitlab.http_post(path, **kwargs) @cli.register_custom_action(cls_names="User") @exc.on_http_error(exc.GitlabUnfollowError) def unfollow(self, **kwargs: Any) -> dict[str, Any] | requests.Response: """Unfollow the user. Args: **kwargs: Extra options to send to the server (e.g. sudo) Raises: GitlabAuthenticationError: If authentication is not correct GitlabUnfollowError: If the user could not be followed Returns: The new object data (*not* a RESTObject) """ path = f"/users/{self.encoded_id}/unfollow" return self.manager.gitlab.http_post(path, **kwargs) @cli.register_custom_action(cls_names="User") @exc.on_http_error(exc.GitlabUnblockError) def unblock(self, **kwargs: Any) -> bool | None: """Unblock the user. Args: **kwargs: Extra options to send to the server (e.g. sudo) Raises: GitlabAuthenticationError: If authentication is not correct GitlabUnblockError: If the user could not be unblocked Returns: Whether the user status has been changed """ path = f"/users/{self.encoded_id}/unblock" # NOTE: Undocumented behavior of the GitLab API is that it returns a # boolean or None server_data = cast( Optional[bool], self.manager.gitlab.http_post(path, **kwargs) ) if server_data is True: self._attrs["state"] = "active" return server_data @cli.register_custom_action(cls_names="User") @exc.on_http_error(exc.GitlabDeactivateError) def deactivate(self, **kwargs: Any) -> dict[str, Any] | requests.Response: """Deactivate the user. Args: **kwargs: Extra options to send to the server (e.g. sudo) Raises: GitlabAuthenticationError: If authentication is not correct GitlabDeactivateError: If the user could not be deactivated Returns: Whether the user status has been changed """ path = f"/users/{self.encoded_id}/deactivate" server_data = self.manager.gitlab.http_post(path, **kwargs) if server_data: self._attrs["state"] = "deactivated" return server_data @cli.register_custom_action(cls_names="User") @exc.on_http_error(exc.GitlabActivateError) def activate(self, **kwargs: Any) -> dict[str, Any] | requests.Response: """Activate the user. Args: **kwargs: Extra options to send to the server (e.g. sudo) Raises: GitlabAuthenticationError: If authentication is not correct GitlabActivateError: If the user could not be activated Returns: Whether the user status has been changed """ path = f"/users/{self.encoded_id}/activate" server_data = self.manager.gitlab.http_post(path, **kwargs) if server_data: self._attrs["state"] = "active" return server_data @cli.register_custom_action(cls_names="User") @exc.on_http_error(exc.GitlabUserApproveError) def approve(self, **kwargs: Any) -> dict[str, Any] | requests.Response: """Approve a user creation request. Args: **kwargs: Extra options to send to the server (e.g. sudo) Raises: GitlabAuthenticationError: If authentication is not correct GitlabUserApproveError: If the user could not be activated Returns: The new object data (*not* a RESTObject) """ path = f"/users/{self.encoded_id}/approve" return self.manager.gitlab.http_post(path, **kwargs) @cli.register_custom_action(cls_names="User") @exc.on_http_error(exc.GitlabUserRejectError) def reject(self, **kwargs: Any) -> dict[str, Any] | requests.Response: """Reject a user creation request. Args: **kwargs: Extra options to send to the server (e.g. sudo) Raises: GitlabAuthenticationError: If authentication is not correct GitlabUserRejectError: If the user could not be rejected Returns: The new object data (*not* a RESTObject) """ path = f"/users/{self.encoded_id}/reject" return self.manager.gitlab.http_post(path, **kwargs) @cli.register_custom_action(cls_names="User") @exc.on_http_error(exc.GitlabBanError) def ban(self, **kwargs: Any) -> dict[str, Any] | requests.Response: """Ban the user. Args: **kwargs: Extra options to send to the server (e.g. sudo) Raises: GitlabAuthenticationError: If authentication is not correct GitlabBanError: If the user could not be banned Returns: Whether the user has been banned """ path = f"/users/{self.encoded_id}/ban" server_data = self.manager.gitlab.http_post(path, **kwargs) if server_data: self._attrs["state"] = "banned" return server_data @cli.register_custom_action(cls_names="User") @exc.on_http_error(exc.GitlabUnbanError) def unban(self, **kwargs: Any) -> dict[str, Any] | requests.Response: """Unban the user. Args: **kwargs: Extra options to send to the server (e.g. sudo) Raises: GitlabAuthenticationError: If authentication is not correct GitlabUnbanError: If the user could not be unbanned Returns: Whether the user has been unbanned """ path = f"/users/{self.encoded_id}/unban" server_data = self.manager.gitlab.http_post(path, **kwargs) if server_data: self._attrs["state"] = "active" return server_data class UserManager(CRUDMixin[User]): _path = "/users" _obj_cls = User _list_filters = ( "username", "public_email", "search", "active", "external", "blocked", "humans", "created_after", "created_before", "exclude_active", "exclude_external", "exclude_humans", "exclude_internal", "without_project_bots", "extern_uid", "provider", "two_factor", "without_projects", "admins", "auditors", "skip_ldap", "custom_attributes", "status", ) _create_attrs = RequiredOptional( optional=( "email", "username", "name", "password", "reset_password", "skype", "linkedin", "twitter", "projects_limit", "extern_uid", "provider", "bio", "admin", "can_create_group", "website_url", "skip_confirmation", "external", "organization", "location", "avatar", "public_email", "private_profile", "color_scheme_id", "theme_id", ) ) _update_attrs = RequiredOptional( required=("email", "username", "name"), optional=( "password", "skype", "linkedin", "twitter", "projects_limit", "extern_uid", "provider", "bio", "admin", "can_create_group", "website_url", "skip_reconfirmation", "external", "organization", "location", "avatar", "public_email", "private_profile", "color_scheme_id", "theme_id", ), ) _types = {"confirm": types.LowercaseStringAttribute, "avatar": types.ImageAttribute} class ProjectUser(RESTObject): pass class ProjectUserManager(ListMixin[ProjectUser]): _path = "/projects/{project_id}/users" _obj_cls = ProjectUser _from_parent_attrs = {"project_id": "id"} _list_filters = ("search", "skip_users") _types = {"skip_users": types.ArrayAttribute} class UserEmail(ObjectDeleteMixin, RESTObject): _repr_attr = "email" class UserEmailManager( RetrieveMixin[UserEmail], CreateMixin[UserEmail], DeleteMixin[UserEmail] ): _path = "/users/{user_id}/emails" _obj_cls = UserEmail _from_parent_attrs = {"user_id": "id"} _create_attrs = RequiredOptional( required=("email",), optional=("skip_confirmation",) ) class UserActivities(RESTObject): _id_attr = "username" class UserStatus(RESTObject): _id_attr = None _repr_attr = "message" class UserStatusManager(GetWithoutIdMixin[UserStatus]): _path = "/users/{user_id}/status" _obj_cls = UserStatus _from_parent_attrs = {"user_id": "id"} class UserActivitiesManager(ListMixin[UserActivities]): _path = "/user/activities" _obj_cls = UserActivities class UserGPGKey(ObjectDeleteMixin, RESTObject): pass class UserGPGKeyManager( RetrieveMixin[UserGPGKey], CreateMixin[UserGPGKey], DeleteMixin[UserGPGKey] ): _path = "/users/{user_id}/gpg_keys" _obj_cls = UserGPGKey _from_parent_attrs = {"user_id": "id"} _create_attrs = RequiredOptional(required=("key",)) class UserKey(ObjectDeleteMixin, RESTObject): pass class UserKeyManager( RetrieveMixin[UserKey], CreateMixin[UserKey], DeleteMixin[UserKey] ): _path = "/users/{user_id}/keys" _obj_cls = UserKey _from_parent_attrs = {"user_id": "id"} _create_attrs = RequiredOptional(required=("title", "key")) class UserIdentityProviderManager(DeleteMixin[User]): """Manager for user identities. This manager does not actually manage objects but enables functionality for deletion of user identities by provider. """ _path = "/users/{user_id}/identities" _obj_cls = User _from_parent_attrs = {"user_id": "id"} class UserImpersonationToken(ObjectDeleteMixin, RESTObject): pass class UserImpersonationTokenManager(NoUpdateMixin[UserImpersonationToken]): _path = "/users/{user_id}/impersonation_tokens" _obj_cls = UserImpersonationToken _from_parent_attrs = {"user_id": "id"} _create_attrs = RequiredOptional( required=("name", "scopes"), optional=("expires_at",) ) _list_filters = ("state",) _types = {"scopes": ArrayAttribute} class UserMembership(RESTObject): _id_attr = "source_id" class UserMembershipManager(RetrieveMixin[UserMembership]): _path = "/users/{user_id}/memberships" _obj_cls = UserMembership _from_parent_attrs = {"user_id": "id"} _list_filters = ("type",) # Having this outside projects avoids circular imports due to ProjectUser class UserProject(RESTObject): pass class UserProjectManager(ListMixin[UserProject], CreateMixin[UserProject]): _path = "/projects/user/{user_id}" _obj_cls = UserProject _from_parent_attrs = {"user_id": "id"} _create_attrs = RequiredOptional( required=("name",), optional=( "default_branch", "issues_enabled", "wall_enabled", "merge_requests_enabled", "wiki_enabled", "snippets_enabled", "squash_option", "public", "visibility", "description", "builds_enabled", "public_builds", "import_url", "only_allow_merge_if_build_succeeds", ), ) _list_filters = ( "archived", "visibility", "order_by", "sort", "search", "simple", "owned", "membership", "starred", "statistics", "with_issues_enabled", "with_merge_requests_enabled", "with_custom_attributes", "with_programming_language", "wiki_checksum_failed", "repository_checksum_failed", "min_access_level", "id_after", "id_before", ) @overload def list( self, *, iterator: Literal[False] = False, **kwargs: Any ) -> list[UserProject]: ... @overload def list( self, *, iterator: Literal[True] = True, **kwargs: Any ) -> RESTObjectList[UserProject]: ... @overload def list( self, *, iterator: bool = False, **kwargs: Any ) -> RESTObjectList[UserProject] | list[UserProject]: ... def list( self, *, iterator: bool = False, **kwargs: Any ) -> RESTObjectList[UserProject] | list[UserProject]: """Retrieve a list of objects. Args: get_all: If True, return all the items, without pagination per_page: Number of items to retrieve per request page: ID of the page to return (starts with page 1) iterator: If set to True and no pagination option is defined, return a generator instead of a list **kwargs: Extra options to send to the server (e.g. sudo) Returns: The list of objects, or a generator if `iterator` is True Raises: GitlabAuthenticationError: If authentication is not correct GitlabListError: If the server cannot perform the request """ if self._parent: path = f"/users/{self._parent.id}/projects" else: path = f"/users/{self._from_parent_attrs['user_id']}/projects" return super().list(path=path, iterator=iterator, **kwargs) class UserContributedProject(RESTObject): _id_attr = "id" _repr_attr = "path_with_namespace" class UserContributedProjectManager(ListMixin[UserContributedProject]): _path = "/users/{user_id}/contributed_projects" _obj_cls = UserContributedProject _from_parent_attrs = {"user_id": "id"} class StarredProject(RESTObject): pass class StarredProjectManager(ListMixin[StarredProject]): _path = "/users/{user_id}/starred_projects" _obj_cls = StarredProject _from_parent_attrs = {"user_id": "id"} _list_filters = ( "archived", "membership", "min_access_level", "order_by", "owned", "search", "simple", "sort", "starred", "statistics", "visibility", "with_custom_attributes", "with_issues_enabled", "with_merge_requests_enabled", ) class UserFollowersManager(ListMixin[User]): _path = "/users/{user_id}/followers" _obj_cls = User _from_parent_attrs = {"user_id": "id"} class UserFollowingManager(ListMixin[User]): _path = "/users/{user_id}/following" _obj_cls = User _from_parent_attrs = {"user_id": "id"}
X Tutup