from dataclasses import dataclass from anydi import singleton from fansifter_common.exceptions import InvalidInputError from fansifter_common.identifiers.types import ProfileToken from fansifter_common.legal_info.services import LegalInfoService from preference_center.profile.cache import ProfileCache from preference_center.profile.dispatcher import ProfileDispatcher from preference_center.profile.exceptions import ( ProfileBirthdayUpdateNotAllowedError, ProfileDateOfBirthUpdateNotAllowedError, ProfileEmailAlreadyInUseError, ) from preference_center.profile.models import Profile from preference_center.profile.repositories import ProfileRepository from preference_center.profile.requests import IPCountryRequest from preference_center.profile.services import ProfileService from preference_center.profile.types import ProfileValues @dataclass class UpdateProfileRequest(IPCountryRequest): token: ProfileToken values: ProfileValues @singleton class UpdateProfileHandler: def __init__( self, dispatcher: ProfileDispatcher, cache: ProfileCache, legal_info_service: LegalInfoService, profile_service: ProfileService, profile_repository: ProfileRepository, ) -> None: self.dispatcher = dispatcher self.cache = cache self.legal_info_service = legal_info_service self.profile_service = profile_service self.profile_repository = profile_repository def handle(self, request: UpdateProfileRequest) -> Profile: identifier = self.profile_service.decode_profile_identifier(request.token) is_date_of_birth_allowed = self.legal_info_service.is_date_of_birth_allowed( request.ip_country ) # Enforce the country's date-of-birth rules if not is_date_of_birth_allowed and "date_of_birth" in request.values: raise ProfileDateOfBirthUpdateNotAllowedError elif is_date_of_birth_allowed and "birthday" in request.values: raise ProfileBirthdayUpdateNotAllowedError profile = self.profile_service.get_profile_by_identifier_cached(identifier) profile.set_values(request.values) # Exit early when nothing changed if not profile.has_changed: return profile profile.updated_by = identifier.impersonated_by # Ensure the email remains unique self._validate_email_uniqueness(profile) # Refresh the cached profile self.cache.set_profile(identifier, profile=profile) # Dispatch the profile update event self.dispatcher.dispatch_profile_updated(profile) # Remove the date of birth if the country disallows it if not is_date_of_birth_allowed: profile.date_of_birth = None return profile def _validate_email_uniqueness(self, profile: Profile) -> None: if ( "email" in profile.changed_fields and self.profile_repository.exists_by_email( profile.email, exclude_profile_id=profile.id ) ): raise InvalidInputError( field_errors={ "email": { "message": ProfileEmailAlreadyInUseError.message, "code": ProfileEmailAlreadyInUseError.code, } } )