From e3f8df0d593aa622309954f6a923eddaea925133 Mon Sep 17 00:00:00 2001 From: johnson-oragui Date: Thu, 22 Aug 2024 22:29:30 +0100 Subject: [PATCH 1/2] fix: added profile, and organisations in response data --- api/v1/schemas/user.py | 64 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/api/v1/schemas/user.py b/api/v1/schemas/user.py index 5af9bdb48..c36eddc90 100644 --- a/api/v1/schemas/user.py +++ b/api/v1/schemas/user.py @@ -1,7 +1,9 @@ from email_validator import validate_email, EmailNotValidError import dns.resolver from datetime import datetime -from typing import Optional, Union, List, Annotated +from typing import (Optional, Union, + List, Annotated, Dict, + Literal) from pydantic import (BaseModel, EmailStr, field_validator, ConfigDict, @@ -115,6 +117,66 @@ class UserData(BaseModel): model_config = ConfigDict(from_attributes=True) +class UserData2(BaseModel): + """ + Schema for users to be returned to superadmin + """ + id: str + email: EmailStr + first_name: str + last_name: str + is_active: bool + is_deleted: bool + is_verified: bool + created_at: datetime + updated_at: datetime + + model_config = ConfigDict(from_attributes=True) + +class ProfileData(BaseModel): + """ + Pydantic model for a profile. + """ + id: str + created_at: datetime + pronouns: Optional[str] = None + job_title: Optional[str] = None + department: Optional[str] = None + social: Optional[str] = None + bio: Optional[str] = None + phone_number: Optional[str] = None + avatar_url: Optional[str] = None + recovery_email: Optional[EmailStr] + + model_config = ConfigDict(from_attributes=True) + +class OrganisationData(BaseModel): + """Base organisation schema""" + id: str + created_at: datetime + updated_at: datetime + name: str + email: Optional[EmailStr] = None + industry: Optional[str] = None + user_role: List[str] + type: Optional[str] = None + country: Optional[str] = None + state: Optional[str] = None + address: Optional[str] = None + description: Optional[str] = None + + model_config = ConfigDict(from_attributes=True) + +class AuthMeResponse(BaseModel): + """ + Auth response + """ + message: str + status_code: int + data: Dict[Literal["user", "organisations", "profile"], + Union[UserData2, List[OrganisationData], ProfileData]] + + class AllUsersResponse(BaseModel): """ Schema for all users From 6bf77eb4d7d20e77f931493ed2c3db7618d873b7 Mon Sep 17 00:00:00 2001 From: johnson-oragui Date: Thu, 22 Aug 2024 22:30:28 +0100 Subject: [PATCH 2/2] fix: moved users/me to auth/@me --- api/v1/routes/auth.py | 37 +++++++++++++++++++++++++++++++++---- api/v1/routes/user.py | 22 ---------------------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/api/v1/routes/auth.py b/api/v1/routes/auth.py index 40bf2d519..3de88aa63 100644 --- a/api/v1/routes/auth.py +++ b/api/v1/routes/auth.py @@ -1,22 +1,29 @@ from datetime import timedelta -from fastapi import BackgroundTasks, Depends, status, APIRouter, Response, Request +from fastapi import (BackgroundTasks, Depends, + status, APIRouter, + Response, Request) from fastapi.encoders import jsonable_encoder -from fastapi.responses import JSONResponse from sqlalchemy.orm import Session +from typing import Annotated from api.core.dependencies.email_sender import send_email from api.utils.success_response import auth_response, success_response from api.utils.send_mail import send_magic_link from api.v1.models import User from api.v1.schemas.user import Token -from api.v1.schemas.user import LoginRequest, UserCreate, EmailRequest +from api.v1.schemas.user import (LoginRequest, UserCreate, EmailRequest, + ProfileData, UserData2) from api.v1.schemas.token import TokenRequest -from api.v1.schemas.user import UserCreate, MagicLinkRequest, ChangePasswordSchema +from api.v1.schemas.user import (UserCreate, + MagicLinkRequest, + ChangePasswordSchema, + AuthMeResponse) from api.v1.services.organisation import organisation_service from api.v1.schemas.organisation import CreateUpdateOrganisation from api.db.database import get_db from api.v1.services.user import user_service from api.v1.services.auth import AuthService +from api.v1.services.profile import profile_service auth = APIRouter(prefix="/auth", tags=["Authentication"]) @@ -357,3 +364,25 @@ async def change_password( old_password=schema.old_password) return success_response(status_code=200, message="Password changed successfully") + + +@auth.get("/@me", + status_code=status.HTTP_200_OK, + response_model=AuthMeResponse) +def get_current_user_details( + db: Annotated[Session, Depends(get_db)], + current_user: Annotated[User, Depends(user_service.get_current_user)], +): + """Endpoint to get current user details. + """ + profile = profile_service.fetch_by_user_id(db, current_user.id) + organisation = organisation_service.retrieve_user_organizations(current_user, db) + return AuthMeResponse( + message='User details retrieved successfully', + status_code=200, + data={ + 'user': UserData2.model_validate(current_user, from_attributes=True), + 'organisations': organisation, + 'profile': ProfileData.model_validate(profile, from_attributes=True) + } + ) \ No newline at end of file diff --git a/api/v1/routes/user.py b/api/v1/routes/user.py index a54e80074..0ae2a31ff 100644 --- a/api/v1/routes/user.py +++ b/api/v1/routes/user.py @@ -16,28 +16,6 @@ user_router = APIRouter(prefix="/users", tags=["Users"]) -@user_router.get("/me", status_code=status.HTTP_200_OK, response_model=success_response) -def get_current_user_details( - db: Session = Depends(get_db), - current_user: User = Depends(user_service.get_current_user), -): - """Endpoint to get current user details""" - - return success_response( - status_code=200, - message="User details retrieved successfully", - data=jsonable_encoder( - current_user, - exclude=[ - "password", - "is_deleted", - "is_verified", - "updated_at", - ], - ), - ) - - @user_router.get('/delete', status_code=200) async def delete_account(request: Request, db: Session = Depends(get_db), current_user: User = Depends(user_service.get_current_user)): '''Endpoint to delete a user account'''