feat: clinic and user table change
feat: centralized db error handler fix: api responses
This commit is contained in:
+17
-6
@@ -3,19 +3,20 @@ from services.jwtService import create_jwt_token
|
||||
from services.userServices import UserServices
|
||||
from utils.password_utils import verify_password
|
||||
from schemas.CreateSchemas import UserCreate
|
||||
from schemas.BaseSchemas import AuthBase
|
||||
from exceptions.unauthorized_exception import UnauthorizedException
|
||||
|
||||
class AuthService:
|
||||
def __init__(self):
|
||||
self.user_service = UserServices()
|
||||
|
||||
async def login(self, email, password) -> str:
|
||||
def login(self, data: AuthBase) -> str:
|
||||
|
||||
# get user
|
||||
user = await self.user_service.get_user_by_email(email)
|
||||
user = self.user_service.get_user_by_email(data.email)
|
||||
|
||||
# verify password
|
||||
if not verify_password(password, user.password):
|
||||
if not verify_password(data.password, user.password):
|
||||
raise UnauthorizedException("Invalid credentials")
|
||||
|
||||
# remove password from user dict
|
||||
@@ -26,6 +27,16 @@ class AuthService:
|
||||
token = create_jwt_token(user_dict)
|
||||
return token
|
||||
|
||||
async def register(self, user_data: UserCreate) -> None:
|
||||
await self.user_service.create_user(user_data)
|
||||
return
|
||||
def register(self, user_data: UserCreate):
|
||||
response = self.user_service.create_user(user_data)
|
||||
user = {
|
||||
"id": response.id,
|
||||
"username": response.username,
|
||||
"email": response.email,
|
||||
"clinicRole": response.clinicRole,
|
||||
"userType": response.userType,
|
||||
"mobile": response.mobile,
|
||||
"clinicId": response.created_clinics[0].id
|
||||
}
|
||||
token = create_jwt_token(user)
|
||||
return token
|
||||
@@ -18,6 +18,10 @@ class ClinicServices:
|
||||
|
||||
return clinic_response
|
||||
|
||||
def get_latest_clinic_id(self) -> int:
|
||||
clinic = self.db.query(Clinics).order_by(Clinics.id.desc()).first()
|
||||
return clinic.id if clinic else 0
|
||||
|
||||
|
||||
def get_clinic_by_id(self, clinic_id: int) -> Clinic:
|
||||
clinic = self.db.query(Clinics).filter(Clinics.id == clinic_id).first()
|
||||
|
||||
+4
-14
@@ -40,37 +40,27 @@ class S3Service:
|
||||
def get_s3_service():
|
||||
return S3Service()
|
||||
|
||||
async def upload_file(
|
||||
user_id: str,
|
||||
def upload_file(
|
||||
folder: S3FolderNameEnum,
|
||||
file_name: str,
|
||||
clinic_id: Optional[str] = None
|
||||
) -> Dict[str, str]:
|
||||
"""
|
||||
Generate a pre-signed URL for uploading a file to S3.
|
||||
|
||||
Args:
|
||||
user_id: The ID of the user
|
||||
folder: The folder enum to store the file in
|
||||
file_name: The name of the file
|
||||
clinic_id: Optional design ID for assets
|
||||
|
||||
Returns:
|
||||
Dict containing the URLs and key information
|
||||
"""
|
||||
s3_service = get_s3_service()
|
||||
if folder == S3FolderNameEnum.ASSETS and not clinic_id:
|
||||
raise BusinessValidationException("Clinic id is required!")
|
||||
|
||||
if folder != S3FolderNameEnum.PROFILE and not user_id:
|
||||
raise BusinessValidationException("User id is required!")
|
||||
|
||||
|
||||
timestamp = int(datetime.now().timestamp() * 1000)
|
||||
|
||||
if folder == S3FolderNameEnum.PROFILE:
|
||||
key = f"common/{S3FolderNameEnum.PROFILE.value}/{user_id}/{timestamp}_{file_name}"
|
||||
key = f"common/{timestamp}_{file_name}"
|
||||
else:
|
||||
key = f"common/{S3FolderNameEnum.ASSETS.value}/clinic/{clinic_id}/{timestamp}_{file_name}"
|
||||
key = f"assets/{timestamp}_{file_name}"
|
||||
|
||||
try:
|
||||
put_url = s3_service.s3.generate_presigned_url(
|
||||
|
||||
+48
-23
@@ -10,16 +10,18 @@ from enums.enums import ClinicStatus, UserType
|
||||
from schemas.UpdateSchemas import UserUpdate
|
||||
from exceptions.unauthorized_exception import UnauthorizedException
|
||||
from interface.common_response import CommonResponse
|
||||
from exceptions.business_exception import BusinessValidationException
|
||||
from utils.password_utils import hash_password
|
||||
from schemas.CreateSchemas import UserCreate
|
||||
from exceptions.resource_not_found_exception import ResourceNotFoundException
|
||||
from exceptions.db_exceptions import DBExceptionHandler
|
||||
|
||||
|
||||
class UserServices:
|
||||
def __init__(self):
|
||||
self.db: Session = next(get_db())
|
||||
|
||||
async def create_user(self, user_data: UserCreate):
|
||||
def create_user(self, user_data: UserCreate):
|
||||
# Start a transaction
|
||||
try:
|
||||
user = user_data.user
|
||||
@@ -42,23 +44,27 @@ class UserServices:
|
||||
password=hash_password(user.password),
|
||||
clinicRole=user.clinicRole,
|
||||
userType=user.userType,
|
||||
mobile=user.mobile
|
||||
)
|
||||
|
||||
# Add user to database but don't commit yet
|
||||
self.db.add(new_user)
|
||||
self.db.flush() # Flush to get the user ID without committing
|
||||
|
||||
# Get clinic data
|
||||
clinic = user_data.clinic
|
||||
|
||||
# cross verify domain, in db
|
||||
# Convert to lowercase and keep only alphabetic characters, hyphens, and underscores
|
||||
domain = ''.join(char for char in clinic.name.lower() if char.isalpha() or char == '-' or char == '_')
|
||||
# Convert to lowercase and keep only alphanumeric characters, hyphens, and underscores
|
||||
domain = ''.join(char for char in clinic.name.lower() if char.isalnum() or char == '-' or char == '_')
|
||||
existing_clinic = self.db.query(Clinics).filter(Clinics.domain == domain).first()
|
||||
|
||||
if existing_clinic:
|
||||
# This will trigger rollback in the exception handler
|
||||
raise ValidationException("Clinic with same domain already exists")
|
||||
|
||||
|
||||
|
||||
# Create clinic instance
|
||||
new_clinic = Clinics(
|
||||
name=clinic.name,
|
||||
@@ -85,8 +91,9 @@ class UserServices:
|
||||
voice_model_gender=clinic.voice_model_gender,
|
||||
scenarios=clinic.scenarios,
|
||||
general_info=clinic.general_info,
|
||||
status=ClinicStatus.UNDER_REVIEW,
|
||||
status=ClinicStatus.UNDER_REVIEW, #TODO: change this to PAYMENT_DUE
|
||||
domain=domain,
|
||||
creator_id=new_user.id, # Set the creator_id to link the clinic to the user who created it
|
||||
)
|
||||
|
||||
# Add clinic to database
|
||||
@@ -95,31 +102,49 @@ class UserServices:
|
||||
# Now commit both user and clinic in a single transaction
|
||||
self.db.commit()
|
||||
|
||||
return
|
||||
return new_user
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating user: {str(e)}")
|
||||
# Rollback the transaction if any error occurs
|
||||
self.db.rollback()
|
||||
if isinstance(e, ValidationException):
|
||||
raise ValidationException(e.message)
|
||||
if isinstance(e, ResourceNotFoundException):
|
||||
raise ResourceNotFoundException(e.message)
|
||||
raise e
|
||||
|
||||
# Use the centralized exception handler
|
||||
DBExceptionHandler.handle_exception(e, context="creating user")
|
||||
|
||||
def get_user(self, user_id) -> UserResponse:
|
||||
try:
|
||||
# Query the user by ID and explicitly load the created clinics relationship
|
||||
from sqlalchemy.orm import joinedload
|
||||
user = self.db.query(Users).options(joinedload(Users.created_clinics)).filter(Users.id == user_id).first()
|
||||
|
||||
# Query the user by ID
|
||||
user = self.db.query(Users).filter(Users.id == user_id).first()
|
||||
|
||||
if not user:
|
||||
logger.error("User not found")
|
||||
raise ResourceNotFoundException("User not found")
|
||||
|
||||
user_dict = user.__dict__.copy()
|
||||
|
||||
user_response = UserResponse(**user_dict).model_dump()
|
||||
|
||||
return user_response
|
||||
if not user:
|
||||
logger.error("User not found")
|
||||
raise ResourceNotFoundException("User not found")
|
||||
|
||||
# First convert the user to a dictionary
|
||||
user_dict = {}
|
||||
for column in user.__table__.columns:
|
||||
user_dict[column.name] = getattr(user, column.name)
|
||||
|
||||
# Convert created clinics to dictionaries
|
||||
if user.created_clinics:
|
||||
clinics_list = []
|
||||
for clinic in user.created_clinics:
|
||||
clinic_dict = {}
|
||||
for column in clinic.__table__.columns:
|
||||
clinic_dict[column.name] = getattr(clinic, column.name)
|
||||
clinics_list.append(clinic_dict)
|
||||
user_dict['created_clinics'] = clinics_list
|
||||
|
||||
# Create the user response
|
||||
user_response = UserResponse.model_validate(user_dict)
|
||||
|
||||
# Return the response as a dictionary
|
||||
return user_response.model_dump()
|
||||
except Exception as e:
|
||||
# Use the centralized exception handler
|
||||
from twillio.exceptions.db_exceptions import DBExceptionHandler
|
||||
DBExceptionHandler.handle_exception(e, context="getting user")
|
||||
|
||||
def get_users(self, limit:int, offset:int, search:str):
|
||||
query = self.db.query(Users)
|
||||
@@ -141,7 +166,7 @@ class UserServices:
|
||||
|
||||
return response
|
||||
|
||||
async def get_user_by_email(self, email: str) -> UserResponse:
|
||||
def get_user_by_email(self, email: str) -> UserResponse:
|
||||
user = self.db.query(Users).filter(Users.email == email.lower()).first()
|
||||
|
||||
if not user:
|
||||
|
||||
Reference in New Issue
Block a user