Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

POST API Endpoints for Sending and Accepting Friend invitations between two Users; GET request endpoint for listview for Friend Invites #370

Open
wants to merge 37 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
90d71a7
Create serializers.py
Esterello2 Oct 30, 2023
d9792c7
Update urls.py
Esterello2 Oct 30, 2023
fd712df
Update views.py
Esterello2 Oct 30, 2023
734d40d
Update views.py
Esterello2 Nov 6, 2023
e48159f
Update
Esterello2 Nov 13, 2023
f3bc490
Deleted some files
Esterello2 Nov 13, 2023
7ec1924
Merge branch 'dev' of https://github.com/uchicago-cs/chigame into api…
Esterello2 Nov 27, 2023
6525622
Fixed CI error
Esterello2 Dec 1, 2023
ed8943a
Merge branch 'dev' into apis/post-users
Esterello2 Dec 1, 2023
d4d41c1
Fixing CI issues
Esterello2 Dec 1, 2023
23ae6ea
fixing CI issue in urls.py
Esterello2 Dec 1, 2023
b04b0fa
Fixing CI issues
Esterello2 Dec 1, 2023
6ee326c
Fixing CI
Esterello2 Dec 1, 2023
8be6de9
Fix CI
Esterello2 Dec 1, 2023
d05711d
CI ISsue
Esterello2 Dec 1, 2023
2a5fa11
Fixing CI
Esterello2 Dec 1, 2023
2759d2a
sd
Esterello2 Dec 1, 2023
554e5e8
djsdf
Esterello2 Dec 1, 2023
f17eda4
update:
Esterello2 Dec 1, 2023
8a2f865
Merge branch 'dev' into apis/post-users
Esterello2 Dec 1, 2023
4deeba5
update
Esterello2 Dec 1, 2023
86aad2a
Fixing CI
Esterello2 Dec 2, 2023
e2bb59c
Merge branch 'dev' into apis/create-friend-requests
abrahmasandra Dec 2, 2023
e355d91
Adjustments
Esterello2 Dec 3, 2023
f2e7ef4
update
Esterello2 Dec 5, 2023
a845ea4
Merge branch 'dev' into apis/create-friend-requests
Esterello2 Dec 5, 2023
8e8dbeb
Merge branch 'apis/create-friend-requests' of https://github.com/uchi…
Esterello2 Dec 5, 2023
888ac68
Merge branch 'dev' into apis/create-friend-requests
majorsylvie Dec 5, 2023
a472162
update
Esterello2 Dec 5, 2023
e46b2d5
Merge branch 'dev' into apis/create-friend-requests
Esterello2 Dec 6, 2023
a50ea82
lint
Esterello2 Dec 5, 2023
615d142
Merge branch 'dev' into apis/create-friend-requests
Esterello2 Dec 6, 2023
28cad16
update
Esterello2 Dec 5, 2023
23e62f8
deleting extra code
Esterello2 Dec 7, 2023
c804cfa
updatre
Esterello2 Dec 7, 2023
9506857
update
Esterello2 Dec 7, 2023
b4ca844
Merge branch 'dev' into apis/create-friend-requests
majorsylvie Dec 8, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ django-allauth==0.57.0 # https://github.com/pennersr/django-allauth
django-crispy-forms==2.0 # https://github.com/django-crispy-forms/django-crispy-forms
crispy-bootstrap5==0.7 # https://github.com/django-crispy-forms/crispy-bootstrap5
django-machina==1.3.1 # https://github.com/ellmetha/django-machina
djangorestframework-simplejwt==5.2.0
dj-rest-auth==1.0.0
6 changes: 6 additions & 0 deletions src/chigame/api/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from rest_framework.permissions import BasePermission


class IsUnauthenticated(BasePermission):
def has_permission(self, request, view):
return not request.user or not request.user.is_authenticated
43 changes: 37 additions & 6 deletions src/chigame/api/serializers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from rest_framework import serializers

from chigame.games.models import Chat, Game, Lobby, Message, Tournament, User
from chigame.users.models import FriendInvitation, UserProfile


class GameSerializer(serializers.ModelSerializer):
Expand All @@ -9,6 +10,36 @@ class Meta:
fields = "__all__"


class TournamentSerializer(serializers.ModelSerializer):
class Meta:
model = Tournament
fields = (
"game",
"start_date",
"end_date",
"max_players",
"description",
"rules",
"draw_rules",
"matches",
"winners",
"players",
)


class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ("id", "email", "password", "name", "is_staff")
extra_kwargs = {"password": {"write_only": True}, "id": {"read_only": True}}


class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = "__all__"


class LobbySerializer(serializers.ModelSerializer):
class Meta:
model = Lobby
Expand All @@ -26,12 +57,6 @@ class Meta:
)


class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ("id", "name", "email")


class MessageSerializer(serializers.ModelSerializer):
sender = serializers.EmailField(write_only=True)

Expand All @@ -54,3 +79,9 @@ def create(self, validated_data):

message = Message.objects.create(**validated_data)
return message


class FriendInvitationSerializer(serializers.ModelSerializer):
class Meta:
model = FriendInvitation
fields = "__all__"
28 changes: 24 additions & 4 deletions src/chigame/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,36 @@
from . import views

urlpatterns = [
# GAME API URLS
path("games/", views.GameListView.as_view(), name="api-game-list"),
path("games/<int:pk>/", views.GameDetailView.as_view(), name="api-game-detail"),
# LOBBY API URLS
path("tournaments/create/", views.TournamentCreateView.as_view(), name="create-tournament"),
path("tournaments/", views.TournamentListView.as_view(), name="tournament-list"),
path("register/", views.UserRegistrationView.as_view(), name="user-registration"),
path("lobbies/", views.LobbyListView.as_view(), name="api-lobby-list"),
path("lobbies/<int:pk>/", views.LobbyDetailView.as_view(), name="api-lobby-detail"),
# USER API URLS
path("users/", views.UserListView.as_view(), name="api-user-list"),
path("users/<int:pk>/", views.UserDetailView.as_view(), name="api-user-detail"),
path("token/", views.CustomTokenObtainPairView.as_view(), name="token_obtain_pair"),
path("token/refresh/", views.CustomTokenRefreshView.as_view(), name="token_refresh"),
path("token/verify/", views.CustomTokenVerifyView.as_view(), name="token_verify"),
path("users/<int:pk>/friends/", views.UserFriendsAPIView.as_view(), name="api-user-friends"),
# CHAT API URLS
path("tournaments/chat/", views.MessageView.as_view(), name="api-chat-list"),
path(
"friend-invitations/send/<int:sender_pk>/<int:receiver_pk>/",
views.SendFriendInvitationView.as_view(),
name="send-friend-invitation",
),
path(
"friend-invitations/accept/<int:invitation_pk>/",
views.AcceptFriendInvitationView.as_view(),
name="accept-friend-invitation",
),
path("friend-invitations/", views.FriendInvitationList.as_view(), name="friend-invitation-list"),
path("user-profiles/create/<int:user_pk>/", views.UserProfileCreateView.as_view(), name="create-user-profile"),
path("user-profiles/", views.UserProfileListView.as_view(), name="user-profile-list"),
path(
"user-profiles/update/<int:user_profile_pk>/",
views.UserProfileUpdateView.as_view(),
name="update-user-profile",
),
]
152 changes: 144 additions & 8 deletions src/chigame/api/views.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
# from django.shortcuts import render

from dj_rest_auth.models import TokenModel
from django.shortcuts import get_object_or_404
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import generics
from rest_framework import generics, status
from rest_framework.pagination import PageNumberPagination
from rest_framework.permissions import IsAdminUser, IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView, TokenVerifyView

from chigame.api.filters import GameFilter
from chigame.api.serializers import GameSerializer, LobbySerializer, MessageSerializer, UserSerializer
from chigame.games.models import Game, Lobby, Message, User
from chigame.users.models import UserProfile
from chigame.api.serializers import (
FriendInvitationSerializer,
GameSerializer,
LobbySerializer,
MessageSerializer,
TournamentSerializer,
UserProfileSerializer,
UserSerializer,
)
from chigame.games.models import Game, Lobby, Message, Tournament
from chigame.users.models import FriendInvitation, User, UserProfile


class GameListView(generics.ListCreateAPIView):
Expand All @@ -22,6 +38,39 @@ class GameDetailView(generics.RetrieveUpdateDestroyAPIView):
serializer_class = GameSerializer


class UserListView(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsAuthenticated]
authentication_classes = [JWTAuthentication]


class UserRegistrationView(APIView):
permission_classes = [IsAuthenticated]

def post(self, request, *args, **kwargs):
serializer_class = UserSerializer(data=request.data)
if serializer_class.is_valid():
user = serializer_class.save()
UserProfile.objects.create(user=user, display_name=user.name)
refresh = TokenModel.objects.create(user=user)
access_token = str(refresh.key)

return Response({"access_token": access_token}, status=status.HTTP_201_CREATED)
return Response(serializer_class.errors, status=status.HTTP_400_BAD_REQUEST)


class TournamentCreateView(generics.CreateAPIView):
queryset = Tournament.objects.all()
serializer_class = TournamentSerializer
permission_classes = [IsAdminUser]


class TournamentListView(generics.ListAPIView):
queryset = Tournament.objects.all()
serializer_class = TournamentSerializer


class UserFriendsAPIView(generics.RetrieveAPIView):
serializer_class = UserSerializer

Expand All @@ -42,17 +91,104 @@ class LobbyDetailView(generics.RetrieveUpdateDestroyAPIView):
serializer_class = LobbySerializer


class UserListView(generics.ListCreateAPIView):
class UserDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
pagination_class = PageNumberPagination


class UserDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class CustomTokenObtainPairView(TokenObtainPairView):
# Add any custom behavior if needed
pass


class CustomTokenRefreshView(TokenRefreshView):
# Add any custom behavior if needed
pass


class CustomTokenVerifyView(TokenVerifyView):
# Add any custom behavior if needed
pass


class MessageView(generics.CreateAPIView):
queryset = Message.objects.all()
serializer_class = MessageSerializer


class SendFriendInvitationView(APIView):
def post(self, request, *args, **kwargs):
sender_pk = self.kwargs["sender_pk"]
receiver_pk = self.kwargs["receiver_pk"]

sender = get_object_or_404(User, pk=sender_pk)
receiver = get_object_or_404(User, pk=receiver_pk)

if sender == receiver:
return Response(
{"detail": "You cannot send an invitation to yourself."}, status=status.HTTP_400_BAD_REQUEST
)

invitation = FriendInvitation(sender=sender, receiver=receiver)
invitation.save()

serializer = FriendInvitationSerializer(invitation)
return Response(serializer.data, status=status.HTTP_201_CREATED)


class AcceptFriendInvitationView(APIView):
def post(self, request, *args, **kwargs):
invitation_pk = self.kwargs["invitation_pk"]
invitation = get_object_or_404(FriendInvitation, pk=invitation_pk)

if invitation.accepted:
return Response(
{"detail": "This invitation has already been accepted."}, status=status.HTTP_400_BAD_REQUEST
)
invitation.accept_invitation() # Error with this method in the users.models
serializer = FriendInvitationSerializer(invitation)
return Response(serializer.data, status=status.HTTP_200_OK)


class FriendInvitationList(generics.ListAPIView):
queryset = FriendInvitation.objects.all()
serializer_class = FriendInvitationSerializer


class UserProfileCreateView(APIView):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this class have to do with friend invitations?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IT is because Friend Invitations use User Profile pks rather than User class, so I had to create a way to make these UserProfile objects

def post(self, request, *args, **kwargs):
user_pk = self.kwargs["user_pk"]
user = get_object_or_404(User, pk=user_pk)
existing_profile = UserProfile.objects.filter(user=user).first()
if existing_profile:
serializer = UserProfileSerializer(existing_profile)
return Response(serializer.data, status=status.HTTP_200_OK)

serializer = UserProfileSerializer(data=request.data)
if serializer.is_valid():
profile = serializer.save(user=user)
return Response(UserProfileSerializer(profile).data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class UserProfileListView(generics.ListAPIView):
queryset = UserProfile.objects.all()
serializer_class = UserProfileSerializer
permission_classes = [IsAuthenticated]
authentication_classes = [JWTAuthentication]


class UserProfileUpdateView(APIView):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both this update and user profile list view are relevant for JWT and are related to authentication.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I am removing the permission classes

permission_classes = [IsAuthenticated]
authentication_classes = [JWTAuthentication]

def patch(self, request, *args, **kwargs):
user_profile_pk = self.kwargs["user_profile_pk"]
user_profile = get_object_or_404(UserProfile, pk=user_profile_pk)

serializer = UserProfileSerializer(user_profile, data=request.data, partial=True)
if serializer.is_valid():
updated_profile = serializer.save()
return Response(UserProfileSerializer(updated_profile).data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
38 changes: 38 additions & 0 deletions src/config/settings/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Base settings to build other settings files upon.
"""
from datetime import timedelta
from pathlib import Path

import environ
Expand Down Expand Up @@ -77,6 +78,9 @@
"rest_framework",
"django_filters",
"django_tables2",
"rest_framework_simplejwt",
"rest_framework.authtoken",
"dj_rest_auth",
]
THIRD_PARTY_APPS = [
"crispy_forms",
Expand Down Expand Up @@ -113,7 +117,41 @@
# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS

REST_FRAMEWORK = {"DEFAULT_AUTHENTICATION_CLASSES": ("rest_framework_simplejwt.authentication.JWTAuthentication",)}


SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(days=1),
"REFRESH_TOKEN_LIFETIME": timedelta(days=7),
"ROTATE_REFRESH_TOKENS": False,
"BLACKLIST_AFTER_ROTATION": False,
"UPDATE_LAST_LOGIN": False,
"ALGORITHM": "HS256",
"VERIFYING_KEY": "",
"AUDIENCE": None,
"ISSUER": None,
"JSON_ENCODER": None,
"JWK_URL": None,
"LEEWAY": 0,
"AUTH_HEADER_TYPES": ("Bearer",),
"AUTH_HEADER_NAME": "HTTP_AUTHORIZATION",
"USER_ID_FIELD": "id",
"USER_ID_CLAIM": "user_id",
"USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule",
"AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),
"TOKEN_TYPE_CLAIM": "token_type",
"TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",
"JTI_CLAIM": "jti",
"SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp",
"SLIDING_TOKEN_LIFETIME": timedelta(minutes=5),
"SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=1),
"TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer",
"TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSerializer",
"TOKEN_VERIFY_SERIALIZER": "rest_framework_simplejwt.serializers.TokenVerifySerializer",
"TOKEN_BLACKLIST_SERIALIZER": "rest_framework_simplejwt.serializers.TokenBlacklistSerializer",
"SLIDING_TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer",
"SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer",
}
# AUTHENTICATION
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#authentication-backends
Expand Down