mirror of https://github.com/zulip/zulip.git
zilencer: Avoid creating duplicate remote push registrations.
Servers that had upgraded from a Zulip server version that did not yet support the user_uuid field to one that did could end up with some mobile devices having two push notifications registrations, one with a user_id and the other with a user_uuid. Fix this issue by sending both user_id and user_uuid, and clearing
This commit is contained in:
parent
d888bb3df2
commit
ade2225f08
|
@ -547,6 +547,9 @@ def add_push_device_token(
|
||||||
post_data = {
|
post_data = {
|
||||||
"server_uuid": settings.ZULIP_ORG_ID,
|
"server_uuid": settings.ZULIP_ORG_ID,
|
||||||
"user_uuid": str(user_profile.uuid),
|
"user_uuid": str(user_profile.uuid),
|
||||||
|
# user_id is sent so that the bouncer can delete any pre-existing registrations
|
||||||
|
# for this user+device to avoid duplication upon adding the uuid registration.
|
||||||
|
"user_id": str(user_profile.id),
|
||||||
"token": token_str,
|
"token": token_str,
|
||||||
"token_kind": kind,
|
"token_kind": kind,
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,12 +192,6 @@ class PushBouncerNotificationTest(BouncerTestCase):
|
||||||
self.server_uuid, endpoint, {"token": token, "token_kind": token_kind}
|
self.server_uuid, endpoint, {"token": token, "token_kind": token_kind}
|
||||||
)
|
)
|
||||||
self.assert_json_error(result, "Missing user_id or user_uuid")
|
self.assert_json_error(result, "Missing user_id or user_uuid")
|
||||||
result = self.uuid_post(
|
|
||||||
self.server_uuid,
|
|
||||||
endpoint,
|
|
||||||
{"user_id": user_id, "user_uuid": "xxx", "token": token, "token_kind": token_kind},
|
|
||||||
)
|
|
||||||
self.assert_json_error(result, "Specify only one of user_id or user_uuid")
|
|
||||||
result = self.uuid_post(
|
result = self.uuid_post(
|
||||||
self.server_uuid, endpoint, {"user_id": user_id, "token": token, "token_kind": 17}
|
self.server_uuid, endpoint, {"user_id": user_id, "token": token, "token_kind": 17}
|
||||||
)
|
)
|
||||||
|
@ -280,6 +274,40 @@ class PushBouncerNotificationTest(BouncerTestCase):
|
||||||
401,
|
401,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_register_device_deduplication(self) -> None:
|
||||||
|
hamlet = self.example_user("hamlet")
|
||||||
|
token = "111222"
|
||||||
|
user_id = hamlet.id
|
||||||
|
user_uuid = str(hamlet.uuid)
|
||||||
|
token_kind = PushDeviceToken.GCM
|
||||||
|
|
||||||
|
endpoint = "/api/v1/remotes/push/register"
|
||||||
|
|
||||||
|
# First we create a legacy user_id registration.
|
||||||
|
result = self.uuid_post(
|
||||||
|
self.server_uuid,
|
||||||
|
endpoint,
|
||||||
|
{"user_id": user_id, "token_kind": token_kind, "token": token},
|
||||||
|
)
|
||||||
|
self.assert_json_success(result)
|
||||||
|
|
||||||
|
registrations = list(RemotePushDeviceToken.objects.filter(token=token))
|
||||||
|
self.assert_length(registrations, 1)
|
||||||
|
self.assertEqual(registrations[0].user_id, user_id)
|
||||||
|
self.assertEqual(registrations[0].user_uuid, None)
|
||||||
|
|
||||||
|
# Register same user+device with uuid now. The old registration should be deleted
|
||||||
|
# to avoid duplication.
|
||||||
|
result = self.uuid_post(
|
||||||
|
self.server_uuid,
|
||||||
|
endpoint,
|
||||||
|
{"user_id": user_id, "user_uuid": user_uuid, "token_kind": token_kind, "token": token},
|
||||||
|
)
|
||||||
|
registrations = list(RemotePushDeviceToken.objects.filter(token=token))
|
||||||
|
self.assert_length(registrations, 1)
|
||||||
|
self.assertEqual(registrations[0].user_id, None)
|
||||||
|
self.assertEqual(str(registrations[0].user_uuid), user_uuid)
|
||||||
|
|
||||||
def test_remote_push_user_endpoints(self) -> None:
|
def test_remote_push_user_endpoints(self) -> None:
|
||||||
endpoints = [
|
endpoints = [
|
||||||
("/api/v1/remotes/push/register", "register"),
|
("/api/v1/remotes/push/register", "register"),
|
||||||
|
|
|
@ -157,23 +157,26 @@ def register_remote_push_device(
|
||||||
if user_id is None and user_uuid is None:
|
if user_id is None and user_uuid is None:
|
||||||
raise JsonableError(_("Missing user_id or user_uuid"))
|
raise JsonableError(_("Missing user_id or user_uuid"))
|
||||||
if user_id is not None and user_uuid is not None:
|
if user_id is not None and user_uuid is not None:
|
||||||
# We don't want "hybrid" registrations with both.
|
kwargs: Dict[str, object] = {"user_uuid": user_uuid, "user_id": None}
|
||||||
# Our RemotePushDeviceToken should be either in the new uuid format
|
# Delete pre-existing user_id registration for this user+device to avoid
|
||||||
# or the legacy id one.
|
# duplication. Further down, uuid registration will be created.
|
||||||
raise JsonableError(_("Specify only one of user_id or user_uuid"))
|
RemotePushDeviceToken.objects.filter(
|
||||||
|
server=server, token=token, kind=token_kind, user_id=user_id
|
||||||
|
).delete()
|
||||||
|
else:
|
||||||
|
# One of these is None, so these will kwargs will leads to a proper registration
|
||||||
|
# of either user_id or user_uuid type
|
||||||
|
kwargs = {"user_id": user_id, "user_uuid": user_uuid}
|
||||||
try:
|
try:
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
RemotePushDeviceToken.objects.create(
|
RemotePushDeviceToken.objects.create(
|
||||||
# Exactly one of these two user identity fields will be None.
|
|
||||||
user_id=user_id,
|
|
||||||
user_uuid=user_uuid,
|
|
||||||
server=server,
|
server=server,
|
||||||
kind=token_kind,
|
kind=token_kind,
|
||||||
token=token,
|
token=token,
|
||||||
ios_app_id=ios_app_id,
|
ios_app_id=ios_app_id,
|
||||||
# last_updated is to be renamed to date_created.
|
# last_updated is to be renamed to date_created.
|
||||||
last_updated=timezone.now(),
|
last_updated=timezone.now(),
|
||||||
|
**kwargs,
|
||||||
)
|
)
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
pass
|
pass
|
||||||
|
|
Loading…
Reference in New Issue