From 1dd98b57fcbf619e5a6b57a9342c8395b44583cc Mon Sep 17 00:00:00 2001 From: Alex TYRODE Date: Sat, 3 May 2025 09:29:13 +0000 Subject: [PATCH] feat: update pad creation logic to utilize user session for failsafe user creation - Modified create_pad method in PadService to accept user_session instead of token_data, enhancing user creation logic when the owner does not exist. - Improved error handling during user creation to manage potential race conditions and provide clearer logging. - Updated pad_router to pass user_session during pad creation, ensuring synchronization with user authentication data. --- src/backend/database/service/pad_service.py | 41 ++++++++++++++++----- src/backend/routers/pad_router.py | 4 +- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/backend/database/service/pad_service.py b/src/backend/database/service/pad_service.py index 52ebfbc..1d36f8b 100644 --- a/src/backend/database/service/pad_service.py +++ b/src/backend/database/service/pad_service.py @@ -2,7 +2,7 @@ Pad service for business logic related to pads. """ -from typing import List, Optional, Dict, Any +from typing import List, Optional, Dict, Any, TYPE_CHECKING from uuid import UUID from sqlalchemy.ext.asyncio import AsyncSession @@ -10,6 +10,10 @@ from ..repository import PadRepository, UserRepository from .user_service import UserService +# Use TYPE_CHECKING to avoid circular imports +if TYPE_CHECKING: + from dependencies import UserSession + class PadService: """Service for pad-related business logic""" @@ -19,7 +23,7 @@ def __init__(self, session: AsyncSession): self.repository = PadRepository(session) self.user_repository = UserRepository(session) - async def create_pad(self, owner_id: UUID, display_name: str, data: Dict[str, Any], token_data: Dict[str, Any]) -> Dict[str, Any]: + async def create_pad(self, owner_id: UUID, display_name: str, data: Dict[str, Any], user_session: "UserSession" = None) -> Dict[str, Any]: """Create a new pad""" # Validate input if not display_name: @@ -28,20 +32,37 @@ async def create_pad(self, owner_id: UUID, display_name: str, data: Dict[str, An if not data: raise ValueError("Pad data is required") - if not token_data: - raise ValueError("Token data is required for user creation failsafe") - # Check if owner exists owner = await self.user_repository.get_by_id(owner_id) - if not owner: - # User doesn't exist, create a user record from token data - print(f"WARNING: User with ID '{owner_id}' does not exist but is trying to save a pad. Creating user as failsafe.") + if not owner and user_session: + # User doesn't exist, create a user record from user session + print(f"ANOMALY DETECTED: User with ID '{owner_id}' does not exist but has valid authentication. Creating user as failsafe.") # Create a UserService instance user_service = UserService(self.session) - # Use token data to create a complete user record - await user_service.sync_user_with_token_data(owner_id, token_data) + # Create user with data from UserSession + try: + await user_service.create_user( + user_id=user_session.id, + username=user_session.username, + email=user_session.email, + email_verified=user_session.email_verified, + name=user_session.name, + given_name=user_session.given_name, + family_name=user_session.family_name, + roles=user_session.roles + ) + print(f"Successfully created user with ID '{owner_id}' as failsafe.") + except ValueError as e: + print(f"Error creating user as failsafe: {str(e)}") + # If user creation fails due to race condition, try to get the user again + if "already exists" in str(e): + owner = await self.user_repository.get_by_id(owner_id) + if not owner: + raise ValueError(f"Failed to create user with ID '{owner_id}'") + else: + raise # Check if pad with same name already exists for this owner existing_pad = await self.repository.get_by_name(owner_id, display_name) diff --git a/src/backend/routers/pad_router.py b/src/backend/routers/pad_router.py index 5ae89a3..f1235a1 100644 --- a/src/backend/routers/pad_router.py +++ b/src/backend/routers/pad_router.py @@ -28,7 +28,7 @@ async def save_pad( owner_id=user.id, display_name=DEFAULT_PAD_NAME, data=data, - token_data=user.token_data + user_session=user ) else: # Update existing pad @@ -101,7 +101,7 @@ async def create_pad_from_template( owner_id=user.id, display_name=display_name, data=template["data"], - token_data=user.token_data + user_session=user ) # Create an initial backup for the new pad