events: Fix buggy realm_user/update events during user creation.

We've been seeing an exception in server_event_dispatch.js in
production where in large organizations, sometimes when a new user
joined, every other browser in the organization would throw an
exception processing some sort of realm_user/update event.

It turns out the cause was that when a user copies their profile from
an existing user account with a user-uploaded avatar, the code path we
reused to set the avatar properly send a realm_user/update event about
the avatar change -- for a user that hadn't been fully created and
certainly hadn't have the realm_user/add event sent for.

We fix this and add tests and comments to prevent it recurring.

(Removed an incorrect docstring while working on this).
This commit is contained in:
Tim Abbott
2020-06-26 10:35:42 -07:00
parent bc2ed25d2d
commit a5be2a30fa
3 changed files with 28 additions and 5 deletions

View File

@@ -19,7 +19,12 @@ from zerver.models import (
def copy_user_settings(source_profile: UserProfile, target_profile: UserProfile) -> None:
"""Warning: Does not save, to avoid extra database queries"""
# Important note: Code run from here to configure the user's
# settings should not call send_event, as that would cause clients
# to throw an exception (we haven't sent the realm_user/add event
# yet, so that event will include the updated details of target_profile).
#
# Note that this function will do at least one save() on target_profile.
for settings_name in UserProfile.property_types:
value = getattr(source_profile, settings_name)
setattr(target_profile, settings_name, value)
@@ -34,7 +39,8 @@ def copy_user_settings(source_profile: UserProfile, target_profile: UserProfile)
if source_profile.avatar_source == UserProfile.AVATAR_FROM_USER:
from zerver.lib.actions import do_change_avatar_fields
do_change_avatar_fields(target_profile, UserProfile.AVATAR_FROM_USER)
do_change_avatar_fields(target_profile, UserProfile.AVATAR_FROM_USER,
skip_notify=True)
copy_avatar(source_profile, target_profile)
copy_hotpots(source_profile, target_profile)