aboutsummaryrefslogtreecommitdiffstats
path: root/pydis_site/apps/api/viewsets
diff options
context:
space:
mode:
authorGravatar dementati <[email protected]>2020-10-10 12:05:49 +0200
committerGravatar dementati <[email protected]>2020-10-10 12:05:49 +0200
commit191242049e307de48b98861c293749552e3499fb (patch)
treeff7db8ed7ba47e660c2a5f327cfd2a3ecb720fca /pydis_site/apps/api/viewsets
parentBroke out metricity connection into an (diff)
parentMerge 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.py116
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: