"""TransferWise Profile Persister. Handles doing CRUD operations on the transferwise_profile table. """ from datetime import datetime from typing import Optional from sqlalchemy.orm.session import Session from collaborator.connectors import mysql from collaborator.constants import error from collaborator.constants.transferwise_profile import TransferwiseProfileStatus from collaborator.models.rds.transferwise_profile import TransferwiseProfile from collaborator.utils import logging from collaborator.utils.error import OwsError from collaborator.utils.typing import Account, User class TransferwiseProfilePersister: """Handles high level operations for TransferWise Profiles.""" @classmethod @mysql.db_session def create_profile( cls, account: Account, subaccount_id: int, profile_id: int, access_token: str, refresh_token: str, verification_status: str, session: Session, ) -> dict: """Create a TransferWise profile. Args: account (Account): Account which is creating the profile. subaccount_id (int): Optional subaccount for the profile. profile_id (int): TransferWise Profile ID. access_token (str): User's TransferWise access token. refresh_token (str): Token for obtaining a new access token. verification_status (str): Verification status of the profile. session (sqlalchemy.orm.session.Session): Database session. Returns: dict: the newly created profile """ profile = TransferwiseProfile( profile_id=profile_id, vendor_id=account.id, subaccount_id=subaccount_id, access_token=access_token, refresh_token=refresh_token, status=verification_status, ) session.add(profile) session.commit() # so a newly created entry gets an ID return profile.to_dict() @classmethod @mysql.db_session def soft_delete_profile( cls, account: Account, subaccount_id: Optional[int], user: User, session: Session, ) -> dict: """Soft delete an account's profile. Args: account (Account): Account to delete the profile for subaccount_id (int): Subaccount to delete the profile for user (User): User who is doing the deletion session (Session): Database session Returns: dict: The updated profile """ query = session.query(TransferwiseProfile).filter_by( vendor_id=int(account.id), subaccount_id=subaccount_id, active=True ) profile = query.first() if not profile: raise OwsError.not_found( code=error.ERROR_CODE_PROFILE_NOT_FOUND, message=error.ERROR_MESSAGE_PROFILE_NOT_FOUND, ) old_profile = profile.to_dict() query.update( {"active": False, "deleted_by": str(user), "deleted_date": datetime.now()} ) session.refresh(profile) updated_profile = profile.to_dict() logging.log_event( logging.LOG_EVENT_UPDATE, "transferwise_profile", old_profile["id"], old_profile, updated_profile, user, ) return updated_profile @classmethod @mysql.db_session def update_active_profile( cls, account: Account, subaccount_id: int, profile_id: int, access_token: str, refresh_token: str, verification_status: Optional[str], session: Session, ) -> dict: """Update a TransferWise profile. Args: account (Account): Account which is updating the profile. subaccount_id (int): Optional subaccount for the profile. profile_id (int): TransferWise Profile ID. access_token (str): User's TransferWise access token. refresh_token (str): Token for obtaining a new access token. session (sqlalchemy.orm.session.Session): Database session. Returns: dict: the newly created profile """ query = session.query(TransferwiseProfile).filter_by( vendor_id=int(account.id), subaccount_id=subaccount_id, active=True ) profile = query.first() if not profile: raise OwsError.not_found( code=error.ERROR_CODE_PROFILE_NOT_FOUND, message=error.ERROR_MESSAGE_PROFILE_NOT_FOUND, ) profile.profile_id = profile_id profile.access_token = access_token profile.refresh_token = refresh_token if verification_status is not None: profile.status = TransferwiseProfileStatus(verification_status) session.flush() return profile.to_dict() @classmethod @mysql.db_session def get_active_profile(cls, account, subaccount_id, session): """Get a TransferWise profile. WARNING: This retrieves the entire profile, including the potentially sensitive fields (access token and refresh token). They will need to be filtered out if the object is sent to the frontend. Args: account (Account): Account which is creating the profile. subaccount_id (int): Optional subaccount for the profile. Returns: dict: the profile """ query = session.query(TransferwiseProfile).filter_by( vendor_id=account.id, subaccount_id=subaccount_id, active=True ) profile = query.first() if not profile: raise OwsError.not_found( code=error.ERROR_CODE_PROFILE_NOT_FOUND, message=error.ERROR_MESSAGE_PROFILE_NOT_FOUND, ) return profile.to_dict() @classmethod @mysql.db_session def update_profile_status( cls, profile_id: int, profile_status: str, session: Session ) -> list: """Update verification status for TransferWise profiles. Args: profile_id (int): ID of profile to update. profile_status (str): Profile verification status session (Session): Database session. Returns: list: the updated profiles """ query = session.query(TransferwiseProfile).filter_by(profile_id=profile_id) profiles = query.all() if len(profiles) == 0: raise OwsError.not_found( code=error.ERROR_CODE_PROFILE_NOT_FOUND, message=error.ERROR_MESSAGE_PROFILE_NOT_FOUND, ) original_profiles = { item.transferwise_profile_id: item.to_dict() for item in profiles } query.update({"status": profile_status}) profiles = query.all() updated_profiles = { item.transferwise_profile_id: item.to_dict() for item in profiles } for profile_id in updated_profiles: logging.log_event( logging.LOG_EVENT_UPDATE, "transferwise_profile", profile_id, original_profiles[profile_id], updated_profiles[profile_id], None, ) return list(updated_profiles.values())