diff options
Diffstat (limited to 'gitlab')
-rw-r--r-- | gitlab/exceptions.py | 8 | ||||
-rw-r--r-- | gitlab/tests/objects/test_users.py | 80 | ||||
-rw-r--r-- | gitlab/v4/objects/users.py | 50 |
3 files changed, 138 insertions, 0 deletions
diff --git a/gitlab/exceptions.py b/gitlab/exceptions.py index fd2ff2a..f5b3600 100644 --- a/gitlab/exceptions.py +++ b/gitlab/exceptions.py @@ -261,6 +261,14 @@ class GitlabLicenseError(GitlabOperationError): pass +class GitlabFollowError(GitlabOperationError): + pass + + +class GitlabUnfollowError(GitlabOperationError): + pass + + def on_http_error(error): """Manage GitlabHttpError exceptions. diff --git a/gitlab/tests/objects/test_users.py b/gitlab/tests/objects/test_users.py index f84e877..e46a315 100644 --- a/gitlab/tests/objects/test_users.py +++ b/gitlab/tests/objects/test_users.py @@ -108,6 +108,72 @@ def resp_delete_user_identity(no_content): yield rsps +@pytest.fixture +def resp_follow_unfollow(): + user = { + "id": 1, + "username": "john_smith", + "name": "John Smith", + "state": "active", + "avatar_url": "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg", + "web_url": "http://localhost:3000/john_smith", + } + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/users/1/follow", + json=user, + content_type="application/json", + status=201, + ) + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/users/1/unfollow", + json=user, + content_type="application/json", + status=201, + ) + yield rsps + + +@pytest.fixture +def resp_followers_following(): + content = [ + { + "id": 2, + "name": "Lennie Donnelly", + "username": "evette.kilback", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/7955171a55ac4997ed81e5976287890a?s=80&d=identicon", + "web_url": "http://127.0.0.1:3000/evette.kilback", + }, + { + "id": 4, + "name": "Serena Bradtke", + "username": "cammy", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/a2daad869a7b60d3090b7b9bef4baf57?s=80&d=identicon", + "web_url": "http://127.0.0.1:3000/cammy", + }, + ] + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/users/1/followers", + json=content, + content_type="application/json", + status=200, + ) + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/users/1/following", + json=content, + content_type="application/json", + status=200, + ) + yield rsps + + def test_get_user(gl, resp_get_user): user = gl.users.get(1) assert isinstance(user, User) @@ -135,3 +201,17 @@ def test_user_activate_deactivate(user, resp_activate): def test_delete_user_identity(user, resp_delete_user_identity): user.identityproviders.delete("test_provider") + + +def test_user_follow_unfollow(user, resp_follow_unfollow): + user.follow() + user.unfollow() + + +def test_list_followers(user, resp_followers_following): + followers = user.followers_users.list() + followings = user.following_users.list() + assert isinstance(followers[0], User) + assert followers[0].id == 2 + assert isinstance(followings[0], User) + assert followings[1].id == 4 diff --git a/gitlab/v4/objects/users.py b/gitlab/v4/objects/users.py index c332435..530383d 100644 --- a/gitlab/v4/objects/users.py +++ b/gitlab/v4/objects/users.py @@ -106,6 +106,8 @@ class User(SaveMixin, ObjectDeleteMixin, RESTObject): _managers = ( ("customattributes", "UserCustomAttributeManager"), ("emails", "UserEmailManager"), + ("followers_users", "UserFollowersManager"), + ("following_users", "UserFollowingManager"), ("events", "UserEventManager"), ("gpgkeys", "UserGPGKeyManager"), ("identityproviders", "UserIdentityProviderManager"), @@ -138,6 +140,42 @@ class User(SaveMixin, ObjectDeleteMixin, RESTObject): return server_data @cli.register_custom_action("User") + @exc.on_http_error(exc.GitlabFollowError) + def follow(self, **kwargs): + """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: + dict: The new object data (*not* a RESTObject) + """ + path = "/users/%s/follow" % self.id + return self.manager.gitlab.http_post(path, **kwargs) + + @cli.register_custom_action("User") + @exc.on_http_error(exc.GitlabUnfollowError) + def unfollow(self, **kwargs): + """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: + dict: The new object data (*not* a RESTObject) + """ + path = "/users/%s/unfollow" % self.id + return self.manager.gitlab.http_post(path, **kwargs) + + @cli.register_custom_action("User") @exc.on_http_error(exc.GitlabUnblockError) def unblock(self, **kwargs): """Unblock the user. @@ -454,3 +492,15 @@ class UserProjectManager(ListMixin, CreateMixin, RESTManager): else: path = "/users/%s/projects" % kwargs["user_id"] return ListMixin.list(self, path=path, **kwargs) + + +class UserFollowersManager(ListMixin, RESTManager): + _path = "/users/%(user_id)s/followers" + _obj_cls = User + _from_parent_attrs = {"user_id": "id"} + + +class UserFollowingManager(ListMixin, RESTManager): + _path = "/users/%(user_id)s/following" + _obj_cls = User + _from_parent_attrs = {"user_id": "id"} |