Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
4 changes: 2 additions & 2 deletions users/v1/urls.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from django.urls import path

from users.v1.views import HelloWorldView, LoginUserView
from users.v1.views import LoginUserView, LoginWithEmailView

urlpatterns = [
path('login', LoginUserView.as_view(), name='login_user'),
path('hello', HelloWorldView.as_view(), name='hello_world'),
path('login/email', LoginWithEmailView.as_view(), name='login_with_email'),
]
112 changes: 101 additions & 11 deletions users/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from rest_framework_simplejwt.tokens import RefreshToken

from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth import authenticate, get_user_model

from arbisoft_sessions_portal.services.google.google_user_info import GoogleUserInfoService
from users.v1.serializers import LoginUserSerializer
Expand Down Expand Up @@ -110,16 +110,106 @@ def post(self, request):
}
})

class LoginWithEmailView(APIView):
""" View for logging in the user with email """

class HelloWorldView(APIView):
""" View for testing the API """

permission_classes = []

@extend_schema(
responses={200: {"type": "string", "example": "Hello World"}}
request={
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email",
"description": "User email address",
"example": "user@example.com"
},
"password": {
"type": "string",
"format": "password",
"description": "User password",
"example": "securepassword123"
}
},
"required": ["email", "password"]
},
responses={
200: {
"type": "object",
"properties": {
"refresh": {
"type": "string",
"description": "JWT refresh token",
"example": "eyJ0eXAiOiJKV1QiLCJhbGc..."
},
"access": {
"type": "string",
"description": "JWT access token",
"example": "eyJ0eXAiOiJKV1QiLCJhbGc..."
},
"user_info": {
"type": "object",
"properties": {
"full_name": {
"type": "string",
"example": "John Doe"
},
"first_name": {
"type": "string",
"example": "John"
},
"last_name": {
"type": "string",
"example": "Doe"
},
"avatar": {
"type": ["string", "null"],
"format": "uri",
"example": None,
"description": "User avatar URL, can be null"
}
}
}
}
},
Comment on lines 119 to +159
Copy link
Member

Choose a reason for hiding this comment

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

Consider adding a response serializer (e.g., LoginResponseSerializer) instead of manually constructing the response dictionary and documenting it inline in @extend_schema.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since this is not a serialized model and involves custom fields and calculations, I believe keeping it as is would be better for maintainability and readability.

400: {
"type": "object",
"properties": {
"detail": {
"type": "string",
"enum": [
"Email and password are required",
"Invalid email or password"
]
}
}
}
},
description="Authenticate user using email and password and return JWT tokens",
)
def get(self, request):
"""
This endpoint returns a simple "Hello World" response.
It can be used to verify that the API is up and running.
"""
return Response("Hello World")
def post(self, request):
""" Log in the user with email """
email = request.data.get('email')
password = request.data.get('password')

if not email or not password:
raise ValidationError("Email and password are required")

user = user_model.objects.filter(email=email, is_active=True).first()
if not user:
raise ValidationError("Invalid email or password")
if not user.check_password(password):
raise ValidationError("Invalid email or password")

refresh = RefreshToken.for_user(user)
return Response({
'refresh': str(refresh),
'access': str(refresh.access_token),
'user_info': {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: Consider including a stable identifier (e.g. user_id) in user_info to simplify client-side user state management.

'full_name': user.get_full_name(),
'first_name': user.first_name,
'last_name': user.last_name,
'avatar': None
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: avatar is always returned as None. If no avatar is available, consider omitting the field rather than returning a constant null value.

}
})
Loading