diff options
author | 2020-10-10 12:05:49 +0200 | |
---|---|---|
committer | 2020-10-10 12:05:49 +0200 | |
commit | 191242049e307de48b98861c293749552e3499fb (patch) | |
tree | ff7db8ed7ba47e660c2a5f327cfd2a3ecb720fca /pydis_site/apps/api/viewsets | |
parent | Broke out metricity connection into an (diff) | |
parent | Merge pull request #378 from RohanJnr/user_endpoint (diff) |
Merge remote-tracking branch 'upstream/master' into feat/397-398-metricity-db-and-api
# Conflicts:
# pydis_site/apps/api/tests/test_users.py
# pydis_site/apps/api/viewsets/bot/user.py
Diffstat (limited to 'pydis_site/apps/api/viewsets')
-rw-r--r-- | pydis_site/apps/api/viewsets/bot/user.py | 116 |
1 files changed, 107 insertions, 9 deletions
diff --git a/pydis_site/apps/api/viewsets/bot/user.py b/pydis_site/apps/api/viewsets/bot/user.py index 352d77c0..3ab71186 100644 --- a/pydis_site/apps/api/viewsets/bot/user.py +++ b/pydis_site/apps/api/viewsets/bot/user.py @@ -1,26 +1,65 @@ +import typing +from collections import OrderedDict + from rest_framework import status from rest_framework.decorators import action +from rest_framework.pagination import PageNumberPagination from rest_framework.request import Request from rest_framework.response import Response +from rest_framework.serializers import ModelSerializer from rest_framework.viewsets import ModelViewSet -from rest_framework_bulk import BulkCreateModelMixin from pydis_site.apps.api.models.bot.metricity import Metricity, NotFound from pydis_site.apps.api.models.bot.user import User from pydis_site.apps.api.serializers import UserSerializer -class UserViewSet(BulkCreateModelMixin, ModelViewSet): +class UserListPagination(PageNumberPagination): + """Custom pagination class for the User Model.""" + + page_size = 10000 + page_size_query_param = "page_size" + + def get_next_page_number(self) -> typing.Optional[int]: + """Get the next page number.""" + if not self.page.has_next(): + return None + page_number = self.page.next_page_number() + return page_number + + def get_previous_page_number(self) -> typing.Optional[int]: + """Get the previous page number.""" + if not self.page.has_previous(): + return None + + page_number = self.page.previous_page_number() + return page_number + + def get_paginated_response(self, data: list) -> Response: + """Override method to send modified response.""" + return Response(OrderedDict([ + ('count', self.page.paginator.count), + ('next_page_no', self.get_next_page_number()), + ('previous_page_no', self.get_previous_page_number()), + ('results', data) + ])) + + +class UserViewSet(ModelViewSet): """ View providing CRUD operations on Discord users through the bot. ## Routes ### GET /bot/users - Returns all users currently known. + Returns all users currently known with pagination. #### Response format - >>> [ - ... { + >>> { + ... 'count': 95000, + ... 'next_page_no': "2", + ... 'previous_page_no': None, + ... 'results': [ + ... { ... 'id': 409107086526644234, ... 'name': "Python", ... 'discriminator': 4329, @@ -31,8 +70,13 @@ class UserViewSet(BulkCreateModelMixin, ModelViewSet): ... 458226699344019457 ... ], ... 'in_guild': True - ... } - ... ] + ... }, + ... ] + ... } + + #### Optional Query Parameters + - page_size: number of Users in one page, defaults to 10,000 + - page: page number #### Status codes - 200: returned on success @@ -74,6 +118,7 @@ class UserViewSet(BulkCreateModelMixin, ModelViewSet): ### POST /bot/users Adds a single or multiple new users. The roles attached to the user(s) must be roles known by the site. + Users that already exist in the database will be skipped. #### Request body >>> { @@ -85,11 +130,13 @@ class UserViewSet(BulkCreateModelMixin, ModelViewSet): ... } Alternatively, request users can be POSTed as a list of above objects, - in which case multiple users will be created at once. + in which case multiple users will be created at once. In this case, + the response is an empty list. #### Status codes - 201: returned on success - 400: if one of the given roles does not exist, or one of the given fields is invalid + - 400: if multiple user objects with the same id are given ### PUT /bot/users/<snowflake:int> Update the user with the given `snowflake`. @@ -127,6 +174,34 @@ class UserViewSet(BulkCreateModelMixin, ModelViewSet): - 400: if the request body was invalid, see response body for details - 404: if the user with the given `snowflake` could not be found + ### BULK PATCH /bot/users/bulk_patch + Update users with the given `ids` and `details`. + `id` field and at least one other field is mandatory. + + #### Request body + >>> [ + ... { + ... 'id': int, + ... 'name': str, + ... 'discriminator': int, + ... 'roles': List[int], + ... 'in_guild': bool + ... }, + ... { + ... 'id': int, + ... 'name': str, + ... 'discriminator': int, + ... 'roles': List[int], + ... 'in_guild': bool + ... }, + ... ] + + #### Status codes + - 200: returned on success + - 400: if the request body was invalid, see response body for details + - 400: if multiple user objects with the same id are given + - 404: if the user with the given id does not exist + ### DELETE /bot/users/<snowflake:int> Deletes the user with the given `snowflake`. @@ -136,7 +211,30 @@ class UserViewSet(BulkCreateModelMixin, ModelViewSet): """ serializer_class = UserSerializer - queryset = User.objects + queryset = User.objects.all().order_by("id") + pagination_class = UserListPagination + + def get_serializer(self, *args, **kwargs) -> ModelSerializer: + """Set Serializer many attribute to True if request body contains a list.""" + if isinstance(kwargs.get('data', {}), list): + kwargs['many'] = True + + return super().get_serializer(*args, **kwargs) + + @action(detail=False, methods=["PATCH"], name='user-bulk-patch') + def bulk_patch(self, request: Request) -> Response: + """Update multiple User objects in a single request.""" + serializer = self.get_serializer( + instance=self.get_queryset(), + data=request.data, + many=True, + partial=True + ) + + serializer.is_valid(raise_exception=True) + serializer.save() + + return Response(serializer.data, status=status.HTTP_200_OK) @action(detail=True) def metricity_data(self, request: Request, pk: str = None) -> Response: |