Source code for users.api

"""API viewsets for the users app."""

from typing import TYPE_CHECKING, List

from django.urls import reverse

from rest_framework.authtoken.serializers import AuthTokenSerializer
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet, mixins

from api.v2.mixins import CORSMixin
from api.v2.schema import OpenAPISchema

from .models import ApiKey, UserProfile
from .serializers import (
    BookmarkPagination, BookmarkSerializer, ProfileSerializer
)

if TYPE_CHECKING:  # pragma: no cover
    from django.db.models.query import QuerySet  # isort:skip
    from rest_framework.request import Request  # isort:skip


[docs]class BookmarkViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, mixins.DestroyModelMixin, CORSMixin, GenericViewSet): """ API endpoints for bookmarks. * list: List your bookmarked series and the feed URLs. * create: Bookmark the given series. * delete: Unbookmark the given series. """ schema = OpenAPISchema(tags=('bookmarks',)) permission_classes = (IsAuthenticated,) serializer_class = BookmarkSerializer pagination_class = BookmarkPagination lookup_field = 'series__slug' lookup_url_kwarg = 'slug' _restrict = True
[docs] def get_permissions(self) -> List: if self.request.method == 'OPTIONS': return [] return super().get_permissions()
[docs] def get_queryset(self) -> 'QuerySet': return self.request.user.bookmarks.all()
[docs] def list(self, request: 'Request', *args, **kwargs) -> Response: token = request.user.profile.token rss = request.build_absolute_uri( reverse('user_bookmarks.rss') + '?token=' + token ) atom = request.build_absolute_uri( reverse('user_bookmarks.atom') + '?token=' + token ) bookmarks = self.get_serializer(self.get_queryset(), many=True).data return Response({'rss': rss, 'atom': atom, 'bookmarks': bookmarks})
class ProfileViewSet(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, CORSMixin, GenericViewSet): """ API endpoints for user profiles. * read: View your profile. * update: Edit your profile. * patch: Patch your profile. * delete: Delete your profile. """ schema = OpenAPISchema(tags=('profile',)) permission_classes = (IsAuthenticated,) serializer_class = ProfileSerializer lookup_field = None _restrict = True def get_permissions(self) -> List: if self.request.method == 'OPTIONS': return [] return super().get_permissions() def get_object(self) -> UserProfile: return UserProfile.objects.select_related('user') \ .get_or_create(user_id=self.request.user.id)[0] def perform_update(self, serializer: ProfileSerializer): data = dict(serializer.validated_data) fields = data.pop('user', {}) profile = serializer.instance user = profile.user if fields: # update the underlying user first for k, v in fields.items(): setattr(user, k, v) user.save(update_fields=list(fields)) if data: # and then update the profile for k, v in data.items(): setattr(profile, k, v) profile.save(update_fields=list(data)) def perform_destroy(self, instance: UserProfile): # deactivate and anonymize the user instance.user.is_active = False instance.user.first_name = '' instance.user.last_name = '' instance.user.api_key.delete() instance.user.save(update_fields=( 'is_active', 'first_name', 'last_name' )) instance.delete() @classmethod def as_view(cls, **initkwargs): return super().as_view(actions={ 'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy', 'options': 'options' }, **initkwargs)
[docs]class ApiKeyViewSet(mixins.CreateModelMixin, CORSMixin, GenericViewSet): """ API endpoints for API keys. * create: Create or retrieve your API key. """ schema = OpenAPISchema( operation_id_base='ApiKey', tags=('token',), component_name='ApiKey' ) serializer_class = AuthTokenSerializer permission_classes = ()
[docs] def create(self, request: 'Request', *args, **kwargs) -> Response: serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) user = serializer.validated_data['user'] token, created = ApiKey.objects.get_or_create(user=user) return Response({'token': token.key}, 201 if created else 200)
__all__ = ['BookmarkViewSet', 'ApiKeyViewSet']