from http import HTTPStatus as http_status from sqlalchemy import and_, or_ from server.db import db_session from server.db.models.core import Account from server.db.models.users import Device from server.utils.logger import logger async def register_user_device( expo_token: str, inner_id: str, os: str, user_id: str, app_slug: str, app_version: str = None ) -> int: """Handle user devices: Create new or update existing Args: expo_token: inner_id: device identification os: "android"/"ios" user_id: app_slug: app_version: user mobile application version Returns: int == http status of the operation that was done """ db_record = None created = False async with db_session() as session: account, _ = await Account.get_or_create(user_id=user_id, app_slug=app_slug, session=session) records_by_device_or_token, _ = await Device.list( query=Device.q_filter( Device.q_select_with_account(), filters=(or_(and_(Device.inner_id == inner_id, Device.os == os), Device.token == expo_token),), ), session=session, ) for record in records_by_device_or_token: token_is_equal = record.token == expo_token user_is_equal = record.account.user_id == user_id if token_is_equal: if not (record.inner_id == inner_id and record.os == os): # if not device is equal await session.delete(record) logger.error( f"Handled Expo token inconsistency error during registering device." f"\nRequest data: expo_token: {expo_token}, inner_id: {inner_id}, os: {os} user_id: {user_id}." f"\nExisted db record: expo_token: {record.token}, inner_id: {record.inner_id}, os: {record.os}," f" user_id: {record.account.user_id}." f"The record was deleted.\n" ) continue else: # Means device is equal (no sense to check) because of the query; record.token = expo_token logger.warning( f"Expo token change was detected during device registering." f"\nRequest data: expo_token: {expo_token}, inner_id: {inner_id}, os: {os} user_id: {user_id}." f"\nExisted db record: expo_token: {record.token}, inner_id: {record.inner_id}, os: {record.os}," f" user_id: {record.account.user_id}." f"\nThe record was updated with new token.\n" ) # Expo knows nothing about our users, so if token has changed for some device, # it affects all device-related records, so we need to update expo token for all of them. if user_is_equal: if db_record: await session.delete(record) continue db_record = record record.status = user_is_equal if not db_record: await Device.create( dict(os=os, token=expo_token, account_id=account.id, inner_id=inner_id, app_version=app_version), session=session, ) created = True if db_record: if app_version and (db_record.app_version is None or db_record.app_version != app_version): db_record.app_version = app_version return http_status.CREATED if created else http_status.OK