3030# Add models to support user emails in requests
3131class UserGroupCreate (BaseModel ):
3232 name : str
33- users : Optional [List [str ]] = None
33+ user_ids : Optional [List [Union [ str , int ]]] = None # Can be either user IDs or email addresses
3434
3535
3636class UserGroupUpdate (BaseModel ):
@@ -45,42 +45,45 @@ class UserEmailIdentifier(BaseModel):
4545 email : str
4646
4747
48- # Helper function to get user ID from email or ID
49- async def get_user_id (cursor : AsyncCursor , user_identifier : Union [str , int ]) -> Optional [ int ]:
48+ # Helper function to process user_ids from request (used by both POST and PUT)
49+ async def process_user_ids (cursor : AsyncCursor , user_ids : List [ Union [str , int ]] ) -> tuple [ List [ int ], List [ tuple ] ]:
5050 """
51- Get a user ID from either an email address or ID.
51+ Process a list of user identifiers (can be integers or strings).
52+ Converts all valid identifiers to integer user IDs.
5253
5354 Parameters
5455 ----------
5556 cursor : AsyncCursor
5657 Database cursor
57- user_identifier : Union[str, int]
58- Either a user email (string) or user ID (int )
58+ user_ids : List[ Union[str, int] ]
59+ List of user identifiers (integers or numeric strings )
5960
6061 Returns
6162 -------
62- Optional[int]
63- User ID if found, None otherwise
63+ tuple[List[int], List[tuple]]
64+ Tuple of (processed_user_ids, conversions)
65+ where conversions is a list of (original_value, converted_id) tuples
6466 """
65- try :
66- if isinstance (user_identifier , int ):
67- # Check if user exists
68- user = await db .read_user (cursor , user_identifier )
69- return user .id if user else None
67+ processed_user_ids = []
68+ conversions = []
69+
70+ for user_id in user_ids :
71+ if isinstance (user_id , str ):
72+ # String, try to convert to int
73+ try :
74+ numeric_id = int (user_id )
75+ processed_user_ids .append (numeric_id )
76+ conversions .append ((user_id , numeric_id ))
77+ logger .debug (f"Converted string '{ user_id } ' to integer { numeric_id } " )
78+ except (ValueError , TypeError ):
79+ logger .warning (f"Invalid user ID format: { user_id } " )
80+ raise HTTPException (status_code = 400 , detail = f"Invalid user ID format: { user_id } " )
7081 else :
71- # Try to find user by email
72- user = await db .read_user_by_email (cursor , user_identifier )
73- return user .id if user else None
74- except Exception as e :
75- logger .error (f"Error in get_user_id: { str (e )} " )
76- bugsnag .notify (
77- e ,
78- metadata = {
79- "user_identifier" : str (user_identifier ),
80- "type" : type (user_identifier ).__name__
81- }
82- )
83- return None
82+ # Already an int
83+ processed_user_ids .append (user_id )
84+ logger .debug (f"Using integer user ID { user_id } as is" )
85+
86+ return processed_user_ids , conversions
8487
8588
8689@router .get ("" , response_model = List [UserGroup ], ** user_group_defaults )
@@ -324,24 +327,21 @@ async def create_user_group(
324327 admin_ids .append (current_user .id ) # Make current user an admin
325328 logger .debug (f"Added current user (ID: { current_user .id } ) as member and admin" )
326329
327- # Handle email addresses if provided
328- user_emails_added = []
329- if group_data .users :
330- logger .debug (f"Processing { len (group_data .users )} user emails from request body" )
331- for email in group_data .users :
332- logger .debug (f"Looking up user with email: { email } " )
333- user = await db .read_user_by_email (cursor , email )
334- if user and user .id and user .id not in user_ids : # Avoid duplicates
335- user_ids .append (user .id )
336- user_emails_added .append (email )
337- logger .debug (f"Added user { user .id } ({ email } ) as member" )
330+ # Process user_ids field
331+ if group_data .user_ids :
332+ logger .debug (f"Processing { len (group_data .user_ids )} user identifiers from request body" )
333+ processed_user_ids , conversions = await process_user_ids (cursor , group_data .user_ids )
334+
335+ # Add processed user IDs, avoiding duplicates
336+ for processed_id in processed_user_ids :
337+ if processed_id not in user_ids :
338+ user_ids .append (processed_id )
339+ logger .debug (f"Added user ID { processed_id } as member" )
338340 else :
339- if not user :
340- logger .warning (f"User with email { email } not found" )
341- elif user .id in user_ids :
342- logger .debug (f"User { user .id } ({ email } ) already added to members list" )
341+ logger .debug (f"User ID { processed_id } already in members list" )
343342
344- logger .debug (f"Added { len (user_emails_added )} users as members from request body: { user_emails_added } " )
343+ if conversions :
344+ logger .debug (f"Converted { len (conversions )} string IDs to integers: { conversions } " )
345345
346346 # Handle admin emails if provided
347347 admin_emails_added = []
@@ -400,8 +400,8 @@ async def create_user_group(
400400 },
401401 "group_data" : {
402402 "name" : group_data .name ,
403- "users_count " : len (group_data .users ) if group_data .users else 0 ,
404- "users " : group_data .users if group_data .users else [],
403+ "user_ids_count " : len (group_data .user_ids ) if group_data .user_ids else 0 ,
404+ "user_ids " : group_data .user_ids if group_data .user_ids else [],
405405 "admins_count" : len (admins ) if admins else 0 ,
406406 "admins" : admins if admins else [],
407407 "current_user_id" : current_user .id if current_user else None ,
@@ -532,42 +532,13 @@ async def update_user_group(
532532 logger .warning (f"ID mismatch: path ID { group_id } != body ID { group_data .id } " )
533533 raise exceptions .id_mismatch
534534
535- # Process user_ids field in case it contains emails instead of integer IDs
536- processed_user_ids = []
537- email_conversions = []
538-
535+ # Process user_ids field
539536 logger .debug (f"Processing { len (group_data .user_ids )} user identifiers..." )
540- for user_id in group_data .user_ids :
541- if isinstance (user_id , str ):
542- # Check if it's an email (contains @ sign)
543- if '@' in user_id :
544- # This looks like an email address, try to find the user ID
545- logger .debug (f"Looking up user by email: { user_id } " )
546- user = await db .read_user_by_email (cursor , user_id )
547- if user and user .id :
548- processed_user_ids .append (user .id )
549- email_conversions .append ((user_id , user .id ))
550- logger .debug (f"Converted email { user_id } to user ID { user .id } " )
551- else :
552- logger .warning (f"User with email { user_id } not found" )
553- raise HTTPException (status_code = 404 , detail = f"User with email { user_id } not found" )
554- else :
555- # String but not an email, try to convert to int if it's a digit string
556- try :
557- numeric_id = int (user_id )
558- processed_user_ids .append (numeric_id )
559- logger .debug (f"Converted string '{ user_id } ' to integer { numeric_id } " )
560- except (ValueError , TypeError ):
561- logger .warning (f"Invalid user ID format: { user_id } " )
562- raise HTTPException (status_code = 400 , detail = f"Invalid user ID format: { user_id } " )
563- else :
564- # Already an int
565- processed_user_ids .append (user_id )
566- logger .debug (f"Using integer user ID { user_id } as is" )
567-
537+ processed_user_ids , conversions = await process_user_ids (cursor , group_data .user_ids )
538+
568539 logger .debug (f"Processed { len (processed_user_ids )} user IDs: { processed_user_ids } " )
569- if email_conversions :
570- logger .debug (f"Converted { len (email_conversions )} emails to user IDs : { email_conversions } " )
540+ if conversions :
541+ logger .debug (f"Converted { len (conversions )} string IDs to integers : { conversions } " )
571542
572543 # Log collaborator map details if present
573544 if group_data .collaborator_map :
0 commit comments