events: Add new event type 'user_settings' for updating user settings.

We send a event with type 'user_settings' on updating user's display
and notification settings.

The old event types - 'update_global_notifications' and
'update_display_settings', are still supported for backwards
compatibility.
This commit is contained in:
Sahil Batra 2021-07-26 12:05:27 +05:30 committed by Tim Abbott
parent e73d2fff97
commit 7959ae3fab
7 changed files with 169 additions and 21 deletions

View File

@ -11,6 +11,13 @@ below features are supported.
## Changes in Zulip 5.0 ## Changes in Zulip 5.0
**Feature level 89**
* [`GET /events`](/api/get-events): Introduced new event type
`user_settings`. The previous `update_display_settings` and
`update_global_notifications` event types are still supported
for backwards compatibility, but will be removed in future.
**Feature level 88** **Feature level 88**
* [`POST /register`](/api/register-queue): Added `zulip_merge_base` * [`POST /register`](/api/register-queue): Added `zulip_merge_base`

View File

@ -33,7 +33,7 @@ DESKTOP_WARNING_VERSION = "5.4.3"
# Changes should be accompanied by documentation explaining what the # Changes should be accompanied by documentation explaining what the
# new level means in templates/zerver/api/changelog.md, as well as # new level means in templates/zerver/api/changelog.md, as well as
# "**Changes**" entries in the endpoint's documentation in `zulip.yaml`. # "**Changes**" entries in the endpoint's documentation in `zulip.yaml`.
API_FEATURE_LEVEL = 88 API_FEATURE_LEVEL = 89
# Bump the minor PROVISION_VERSION to indicate that folks should provision # Bump the minor PROVISION_VERSION to indicate that folks should provision
# only when going from an old version of the code to a newer version. Bump # only when going from an old version of the code to a newer version. Bump

View File

@ -5043,10 +5043,10 @@ def do_change_notification_settings(
user_profile.save(update_fields=[name]) user_profile.save(update_fields=[name])
event = { event = {
"type": "update_global_notifications", "type": "user_settings",
"user": user_profile.email, "op": "update",
"notification_name": name, "property": name,
"setting": value, "value": value,
} }
event_time = timezone_now() event_time = timezone_now()
RealmAuditLog.objects.create( RealmAuditLog.objects.create(
@ -5066,6 +5066,16 @@ def do_change_notification_settings(
send_event(user_profile.realm, event, [user_profile.id]) send_event(user_profile.realm, event, [user_profile.id])
# This legacy event format is for backwards-compatiblity with
# clients that don't support the new user_settings event type.
legacy_event = {
"type": "update_global_notifications",
"user": user_profile.email,
"notification_name": name,
"setting": value,
}
send_event(user_profile.realm, legacy_event, [user_profile.id])
def do_set_user_display_setting( def do_set_user_display_setting(
user_profile: UserProfile, setting_name: str, setting_value: Union[bool, str, int] user_profile: UserProfile, setting_name: str, setting_value: Union[bool, str, int]
@ -5077,7 +5087,22 @@ def do_set_user_display_setting(
assert isinstance(setting_value, property_type) assert isinstance(setting_value, property_type)
setattr(user_profile, setting_name, setting_value) setattr(user_profile, setting_name, setting_value)
user_profile.save(update_fields=[setting_name]) user_profile.save(update_fields=[setting_name])
event = { event = {
"type": "user_settings",
"op": "update",
"property": setting_name,
"value": setting_value,
}
if setting_name == "default_language":
assert isinstance(setting_value, str)
event["language_name"] = get_language_name(setting_value)
send_event(user_profile.realm, event, [user_profile.id])
# This legacy event format is for backwards-compatiblity with
# clients that don't support the new user_settings event type.
legacy_event = {
"type": "update_display_settings", "type": "update_display_settings",
"user": user_profile.email, "user": user_profile.email,
"setting_name": setting_name, "setting_name": setting_name,
@ -5085,9 +5110,9 @@ def do_set_user_display_setting(
} }
if setting_name == "default_language": if setting_name == "default_language":
assert isinstance(setting_value, str) assert isinstance(setting_value, str)
event["language_name"] = get_language_name(setting_value) legacy_event["language_name"] = get_language_name(setting_value)
send_event(user_profile.realm, event, [user_profile.id]) send_event(user_profile.realm, legacy_event, [user_profile.id])
# Updates to the timezone display setting are sent to all users # Updates to the timezone display setting are sent to all users
if setting_name == "timezone": if setting_name == "timezone":

View File

@ -1398,6 +1398,21 @@ update_display_settings_event = event_dict_type(
) )
_check_update_display_settings = make_checker(update_display_settings_event) _check_update_display_settings = make_checker(update_display_settings_event)
user_settings_update_event = event_dict_type(
required_keys=[
("type", Equals("user_settings")),
("op", Equals("update")),
("property", str),
("value", value_type),
],
optional_keys=[
# force vertical
("language_name", str),
],
)
_check_user_settings_update = make_checker(user_settings_update_event)
def check_update_display_settings( def check_update_display_settings(
var_name: str, var_name: str,
@ -1425,6 +1440,30 @@ def check_update_display_settings(
assert "language_name" not in event.keys() assert "language_name" not in event.keys()
def check_user_settings_update(
var_name: str,
event: Dict[str, object],
) -> None:
_check_user_settings_update(var_name, event)
setting_name = event["property"]
value = event["value"]
assert isinstance(setting_name, str)
if setting_name == "timezone":
assert isinstance(value, str)
elif setting_name in UserProfile.property_types:
setting_type = UserProfile.property_types[setting_name]
assert isinstance(value, setting_type)
else:
setting_type = UserProfile.notification_setting_types[setting_name]
assert isinstance(value, setting_type)
if setting_name == "default_language":
assert "language_name" in event.keys()
else:
assert "language_name" not in event.keys()
update_global_notifications_event = event_dict_type( update_global_notifications_event = event_dict_type(
required_keys=[ required_keys=[
("type", Equals("update_global_notifications")), ("type", Equals("update_global_notifications")),

View File

@ -1102,6 +1102,16 @@ def apply_event(
elif event["type"] == "update_global_notifications": elif event["type"] == "update_global_notifications":
assert event["notification_name"] in UserProfile.notification_setting_types assert event["notification_name"] in UserProfile.notification_setting_types
state[event["notification_name"]] = event["setting"] state[event["notification_name"]] = event["setting"]
elif event["type"] == "user_settings":
# timezone setting is not included in property_types or
# notification_setting_types dicts, because this setting
# is not a part of UserBaseSettings class.
if event["property"] != "timezone":
assert (
event["property"] in UserProfile.property_types
or event["property"] in UserProfile.notification_setting_types
)
state[event["property"]] = event["value"]
elif event["type"] == "invites_changed": elif event["type"] == "invites_changed":
pass pass
elif event["type"] == "user_group": elif event["type"] == "user_group":

View File

@ -243,6 +243,10 @@ paths:
description: | description: |
Event sent to a user's clients when that user's display settings Event sent to a user's clients when that user's display settings
have changed. have changed.
**Changes**: Deprecated in Zulip 5.0 (feature level 89), replaced by
the `user_settings` event type.
deprecated: true
properties: properties:
id: id:
$ref: "#/components/schemas/EventIdSchema" $ref: "#/components/schemas/EventIdSchema"
@ -280,6 +284,10 @@ paths:
description: | description: |
Event sent to a user's clients when that user's [notification Event sent to a user's clients when that user's [notification
settings](/api/update-settings) have changed. settings](/api/update-settings) have changed.
**Changes**: Deprecated in Zulip 5.0 (feature level 89), replaced by
the `user_settings` event type.
deprecated: true
properties: properties:
id: id:
$ref: "#/components/schemas/EventIdSchema" $ref: "#/components/schemas/EventIdSchema"
@ -307,6 +315,52 @@ paths:
"setting": true, "setting": true,
"id": 0, "id": 0,
} }
- type: object
description: |
Event sent to a user's clients when that user's settings
have changed.
**Changes**: New in Zulip 5.0 (feature level 89), replacing the
previous `update_display_settings` and `update_global_notifications`
event types, which are still present for backwards compatibility reasons.
properties:
id:
$ref: "#/components/schemas/EventIdSchema"
type:
allOf:
- $ref: "#/components/schemas/EventTypeSchema"
- enum:
- user_settings
op:
type: string
enum:
- update
property:
type: string
description: |
Name of the changed setting.
value:
description: |
New value of the changed setting.
oneOf:
- type: boolean
- type: integer
- type: string
language_name:
description: |
Present only if the setting to be changed is
`default_language`. Contains the name of the
new default language in English.
type: string
additionalProperties: false
example:
{
"type": "user_settings",
"op": "update",
"property": "high_contrast_mode",
"value": false,
"id": 0,
}
- type: object - type: object
description: | description: |
Event sent generally to all users in an organization for changes Event sent generally to all users in an organization for changes

View File

@ -161,6 +161,7 @@ from zerver.lib.event_schema import (
check_user_group_remove, check_user_group_remove,
check_user_group_remove_members, check_user_group_remove_members,
check_user_group_update, check_user_group_update,
check_user_settings_update,
check_user_status, check_user_status,
) )
from zerver.lib.events import ( from zerver.lib.events import (
@ -1430,9 +1431,11 @@ class NormalActionsTest(BaseAction):
notification_setting, notification_setting,
setting_value, setting_value,
acting_user=self.user_profile, acting_user=self.user_profile,
) ),
num_events=2,
) )
check_update_global_notifications("events[0]", events[0], setting_value) check_user_settings_update("events[0]", events[0])
check_update_global_notifications("events[1]", events[1], setting_value)
# Also test with notification_settings_null=True # Also test with notification_settings_null=True
events = self.verify_action( events = self.verify_action(
@ -1444,8 +1447,10 @@ class NormalActionsTest(BaseAction):
), ),
notification_settings_null=True, notification_settings_null=True,
state_change_expected=False, state_change_expected=False,
num_events=2,
) )
check_update_global_notifications("events[0]", events[0], setting_value) check_user_settings_update("events[0]", events[0])
check_update_global_notifications("events[1]", events[1], setting_value)
def test_change_notification_sound(self) -> None: def test_change_notification_sound(self) -> None:
notification_setting = "notification_sound" notification_setting = "notification_sound"
@ -1453,9 +1458,11 @@ class NormalActionsTest(BaseAction):
events = self.verify_action( events = self.verify_action(
lambda: do_change_notification_settings( lambda: do_change_notification_settings(
self.user_profile, notification_setting, "ding", acting_user=self.user_profile self.user_profile, notification_setting, "ding", acting_user=self.user_profile
) ),
num_events=2,
) )
check_update_global_notifications("events[0]", events[0], "ding") check_user_settings_update("events[0]", events[0])
check_update_global_notifications("events[1]", events[1], "ding")
def test_change_desktop_icon_count_display(self) -> None: def test_change_desktop_icon_count_display(self) -> None:
notification_setting = "desktop_icon_count_display" notification_setting = "desktop_icon_count_display"
@ -1463,16 +1470,20 @@ class NormalActionsTest(BaseAction):
events = self.verify_action( events = self.verify_action(
lambda: do_change_notification_settings( lambda: do_change_notification_settings(
self.user_profile, notification_setting, 2, acting_user=self.user_profile self.user_profile, notification_setting, 2, acting_user=self.user_profile
) ),
num_events=2,
) )
check_update_global_notifications("events[0]", events[0], 2) check_user_settings_update("events[0]", events[0])
check_update_global_notifications("events[1]", events[1], 2)
events = self.verify_action( events = self.verify_action(
lambda: do_change_notification_settings( lambda: do_change_notification_settings(
self.user_profile, notification_setting, 1, acting_user=self.user_profile self.user_profile, notification_setting, 1, acting_user=self.user_profile
) ),
num_events=2,
) )
check_update_global_notifications("events[0]", events[0], 1) check_user_settings_update("events[0]", events[0])
check_update_global_notifications("events[1]", events[1], 1)
def test_realm_update_plan_type(self) -> None: def test_realm_update_plan_type(self) -> None:
realm = self.user_profile.realm realm = self.user_profile.realm
@ -2142,7 +2153,7 @@ class UserDisplayActionTest(BaseAction):
color_scheme=[2, 3, 1], color_scheme=[2, 3, 1],
) )
num_events = 1 num_events = 2
values = test_changes.get(setting_name) values = test_changes.get(setting_name)
property_type = UserProfile.property_types[setting_name] property_type = UserProfile.property_types[setting_name]
@ -2161,7 +2172,8 @@ class UserDisplayActionTest(BaseAction):
num_events=num_events, num_events=num_events,
) )
check_update_display_settings("events[0]", events[0]) check_user_settings_update("events[0]", events[0])
check_update_display_settings("events[1]", events[1])
def test_set_user_display_settings(self) -> None: def test_set_user_display_settings(self) -> None:
for prop in UserProfile.property_types: for prop in UserProfile.property_types:
@ -2169,7 +2181,7 @@ class UserDisplayActionTest(BaseAction):
def test_set_user_timezone(self) -> None: def test_set_user_timezone(self) -> None:
values = ["America/Denver", "Pacific/Pago_Pago", "Pacific/Galapagos", ""] values = ["America/Denver", "Pacific/Pago_Pago", "Pacific/Galapagos", ""]
num_events = 2 num_events = 3
for value in values: for value in values:
events = self.verify_action( events = self.verify_action(
@ -2177,8 +2189,9 @@ class UserDisplayActionTest(BaseAction):
num_events=num_events, num_events=num_events,
) )
check_update_display_settings("events[0]", events[0]) check_user_settings_update("events[0]", events[0])
check_realm_user_update("events[1]", events[1], "timezone") check_update_display_settings("events[1]", events[1])
check_realm_user_update("events[2]", events[2], "timezone")
class SubscribeActionTest(BaseAction): class SubscribeActionTest(BaseAction):