Skip to content

Commit

Permalink
Only send new alias events on user creation (#2285)
Browse files Browse the repository at this point in the history
* Only send new alias events on user creation

(cherry picked from commit ab8f998)

* Trigger a sync when a new partner user is created

* Improve tests

* Move it to the partner_utils

---------

Co-authored-by: Carlos Quintana <[email protected]>
  • Loading branch information
acasajus and cquintana92 authored Oct 23, 2024
1 parent f55ab58 commit 9646f84
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 20 deletions.
1 change: 1 addition & 0 deletions app/account_linking.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ def ensure_partner_user_exists_for_user(
partner_email=link_request.email,
external_user_id=link_request.external_user_id,
)

Session.commit()
LOG.i(
f"Created new partner_user for partner:{partner.id} user:{sl_user.id} external_user_id:{link_request.external_user_id}. PartnerUser.id is {res.id}"
Expand Down
9 changes: 8 additions & 1 deletion app/partner_user_utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from typing import Optional

import arrow
from arrow import Arrow

from app.models import PartnerUser, PartnerSubscription, User
from app import config
from app.models import PartnerUser, PartnerSubscription, User, Job
from app.user_audit_log_utils import emit_user_audit_log, UserAuditLogAction


Expand All @@ -15,6 +17,11 @@ def create_partner_user(
partner_email=partner_email,
external_user_id=external_user_id,
)
Job.create(
name=config.JOB_SEND_ALIAS_CREATION_EVENTS,
payload={"user_id": user.id},
run_at=arrow.now(),
)
emit_user_audit_log(
user=user,
action=UserAuditLogAction.LinkAccount,
Expand Down
14 changes: 1 addition & 13 deletions app/proton/proton_callback_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@
from enum import Enum
from flask import url_for
from typing import Optional
import arrow

from app import config
from app.errors import LinkException
from app.models import User, Partner, Job
from app.models import User, Partner
from app.proton.proton_client import ProtonClient, ProtonUser
from app.account_linking import (
process_login_case,
Expand Down Expand Up @@ -43,21 +41,12 @@ class ProtonCallbackHandler:
def __init__(self, proton_client: ProtonClient):
self.proton_client = proton_client

def _initial_alias_sync(self, user: User):
Job.create(
name=config.JOB_SEND_ALIAS_CREATION_EVENTS,
payload={"user_id": user.id},
run_at=arrow.now(),
commit=True,
)

def handle_login(self, partner: Partner) -> ProtonCallbackResult:
try:
user = self.__get_partner_user()
if user is None:
return generate_account_not_allowed_to_log_in()
res = process_login_case(user, partner)
self._initial_alias_sync(res.user)
return ProtonCallbackResult(
redirect_to_login=False,
flash_message=None,
Expand Down Expand Up @@ -86,7 +75,6 @@ def handle_link(
if user is None:
return generate_account_not_allowed_to_log_in()
res = process_link_case(user, current_user, partner)
self._initial_alias_sync(res.user)
return ProtonCallbackResult(
redirect_to_login=False,
flash_message="Account successfully linked",
Expand Down
49 changes: 43 additions & 6 deletions tests/proton/test_proton_callback_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,17 @@ def get_user(self) -> Optional[UserInformation]:
return self.user


def check_initial_sync_job(user: User):
def check_initial_sync_job(user: User, expected: bool):
found = False
for job in Job.yield_per_query(10).filter_by(
name=config.JOB_SEND_ALIAS_CREATION_EVENTS,
state=JobState.ready.value,
):
if job.payload.get("user_id") == user.id:
found = True
Job.delete(job.id)
return
assert False
break
assert expected == found


def test_proton_callback_handler_unexistant_sl_user():
Expand Down Expand Up @@ -69,10 +71,9 @@ def test_proton_callback_handler_unexistant_sl_user():
)
assert partner_user is not None
assert partner_user.external_user_id == external_id
check_initial_sync_job(res.user)


def test_proton_callback_handler_existant_sl_user():
def test_proton_callback_handler_existing_sl_user():
email = random_email()
sl_user = User.create(email, commit=True)

Expand All @@ -98,7 +99,43 @@ def test_proton_callback_handler_existant_sl_user():
sa = PartnerUser.get_by(user_id=sl_user.id, partner_id=get_proton_partner().id)
assert sa is not None
assert sa.partner_email == user.email
check_initial_sync_job(res.user)
check_initial_sync_job(res.user, True)


def test_proton_callback_handler_linked_sl_user():
email = random_email()
external_id = random_string()
sl_user = User.create(email, commit=True)
PartnerUser.create(
user_id=sl_user.id,
partner_id=get_proton_partner().id,
external_user_id=external_id,
partner_email=email,
commit=True,
)

user = UserInformation(
email=email,
name=random_string(),
id=external_id,
plan=SLPlan(type=SLPlanType.Premium, expiration=Arrow.utcnow().shift(hours=2)),
)
handler = ProtonCallbackHandler(MockProtonClient(user=user))
res = handler.handle_login(get_proton_partner())

assert res.user is not None
assert res.user.id == sl_user.id
# Ensure the user is not marked as created from partner
assert User.FLAG_CREATED_FROM_PARTNER != (
res.user.flags & User.FLAG_CREATED_FROM_PARTNER
)
assert res.user.notification is True
assert res.user.trial_end is not None

sa = PartnerUser.get_by(user_id=sl_user.id, partner_id=get_proton_partner().id)
assert sa is not None
assert sa.partner_email == user.email
check_initial_sync_job(res.user, False)


def test_proton_callback_handler_none_user_login():
Expand Down

0 comments on commit 9646f84

Please sign in to comment.