diff --git a/README.md b/README.md index d2303b1..ea6674c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Django CI](https://github.com/NoobCoder12/DevSocial/actions/workflows/django-tests.yml/badge.svg)](https://github.com/NoobCoder12/DevSocial/actions/workflows/django-tests.yml) -> Version 1.2.0 +> Version 1.2.1 DevSocialApp is a social media platform designed for developers to share posts, interact with each other through likes and comments, and follow their peers. @@ -132,6 +132,7 @@ docker compose up ``` The application will be accessible at `http://127.0.0.1:8000/`. +API documentation available at `http://127.0.0.1:8000/api/schema/swagger-ui/` ## Future Improvements @@ -141,9 +142,15 @@ Things I'd add if I continue this project: - Notifications about interactions with your post or profile - Direct messages system - Hashtag system for post discovery +- Pagination for feed, posts and search endpoints +- Caching with Redis for feed queries +- Deployment to AWS (ECS, RDS) ## Changelog +### v1.2.1 +- Improved Swagger documentation with endpoint descriptions and parameter types + ### v1.2.0 - Django Rest Framework added - JWT Authentication for most endpoints diff --git a/backend/apps/api/serializers.py b/backend/apps/api/serializers.py index e9e29ed..88f0775 100644 --- a/backend/apps/api/serializers.py +++ b/backend/apps/api/serializers.py @@ -9,10 +9,10 @@ class UserSerializer(serializers.ModelSerializer): followers_count = serializers.SerializerMethodField() # DRF looks for a method named 'get_' following_count = serializers.SerializerMethodField() - def get_followers_count(self, obj): + def get_followers_count(self, obj) -> int: return obj.followers.count() - def get_following_count(self, obj): + def get_following_count(self, obj) -> int: return obj.following.count() class Meta: @@ -24,10 +24,10 @@ class PostSerializer(serializers.ModelSerializer): likes_count = serializers.SerializerMethodField() # DRF looks for a method named 'get_' comments_count = serializers.SerializerMethodField() - def get_likes_count(self, obj): + def get_likes_count(self, obj) -> int: return obj.likes_count() # Model method - def get_comments_count(self, obj): + def get_comments_count(self, obj) -> int: return obj.comments_count() class Meta: diff --git a/backend/apps/api/views.py b/backend/apps/api/views.py index db4e871..de65611 100644 --- a/backend/apps/api/views.py +++ b/backend/apps/api/views.py @@ -15,15 +15,20 @@ ) from rest_framework import viewsets from rest_framework import status +from drf_spectacular.utils import extend_schema, OpenApiParameter +from rest_framework.mixins import ListModelMixin - +@extend_schema(exclude=True) # Do not add it to Swagger @api_view(['GET']) @permission_classes([AllowAny]) # Overwrites permision class for this view def health_check(request): return Response({'status': 'ok'}) -class CurrentUserViewSet(viewsets.ReadOnlyModelViewSet): +class CurrentUserViewSet(ListModelMixin, viewsets.GenericViewSet): # RetrieveModelMixin removed + """ + Returns profile data of currently authenticated user. + """ serializer_class = UserSerializer def get_queryset(self): @@ -31,6 +36,10 @@ def get_queryset(self): class UserPostViewSet(viewsets.ModelViewSet): + """ + Manage posts of currently authenticated user. + Allows creating, reading and deleting own posts. + """ serializer_class = PostSerializer def get_queryset(self): @@ -55,8 +64,8 @@ def destroy(self, request, *args, **kwargs): # Only to read class FeedViewSet(viewsets.ReadOnlyModelViewSet): """ - Class allows to see posts in current user feed. - With post ID comment and like can be created + Returns posts from users that current user follows. + Use post ID to add or remove a like, or to get/add comments. """ serializer_class = PostSerializer @@ -65,7 +74,12 @@ def get_queryset(self): following = self.request.user.following.all().values_list("following_id", flat=True) # Return posts of followed users return Post.objects.filter(author__in=following) - + + # Decorator for Swagger documentation, defining data type + # 'id' is a name of variable to define + # int is a type of defined variable + # OpenApiParameter.PATH Where does it show up - in URL + @extend_schema(parameters=[OpenApiParameter('id', int, OpenApiParameter.PATH)]) # @action decorator adds action to basic URL in ViewSet @action(detail=True, methods=['post', 'delete', 'get']) # /like/ is created in URL from method name @@ -82,6 +96,7 @@ def like(self, request, pk=None): serializer = LikeSerializer(likes, many=True) return Response(serializer.data) + @extend_schema(parameters=[OpenApiParameter('id', int, OpenApiParameter.PATH)]) @action(detail=True, methods=['post', 'get']) def comment(self, request, pk=None): post = self.get_object() @@ -96,6 +111,9 @@ def comment(self, request, pk=None): class LikeViewSet(viewsets.ReadOnlyModelViewSet): + """ + Returns all likes added by currently authenticated user. + """ serializer_class = LikeSerializer # Get likes of current user @@ -108,7 +126,10 @@ def get_queryset(self): # serializer.save(user=self.request.user) -class FollowViewSet(viewsets.ModelViewSet): +class FollowViewSet(viewsets.ReadOnlyModelViewSet): + """ + Returns follows of current user. + """ serializer_class = FollowSerializer def get_queryset(self): @@ -116,6 +137,9 @@ def get_queryset(self): class CommentViewSet(viewsets.ReadOnlyModelViewSet): + """ + Returns all comments added by currently authenticated user. + """ serializer_class = CommentSerializer def get_queryset(self): @@ -123,6 +147,11 @@ def get_queryset(self): class SearchUsersViewSet(viewsets.ReadOnlyModelViewSet): + """ + Search for users by username. + Use query parameter 'q' to filter results. e.g. /search/?q=john + Use user ID to follow or unfollow a user. + """ serializer_class = ProfileSerializer def get_queryset(self): @@ -130,6 +159,7 @@ def get_queryset(self): query = self.request.query_params.get("q", "") return Profile.objects.filter(user__username__icontains=query) + @extend_schema(parameters=[OpenApiParameter('id', int, OpenApiParameter.PATH)]) @action(detail=True, methods=['post', 'delete']) def follow(self, request, pk=None): followed_user = self.get_object().user # Gets user by profile