mirror of
https://github.com/zulip/zulip.git
synced 2025-11-02 13:03:29 +00:00
streams: Fix events send when archiving and unarchiving streams.
This commit is contained in:
@@ -159,7 +159,17 @@ def do_deactivate_stream(stream: Stream, *, acting_user: UserProfile | None) ->
|
||||
for group in default_stream_groups_for_stream:
|
||||
do_remove_streams_from_default_stream_group(stream.realm, group, [stream])
|
||||
|
||||
send_stream_deletion_event(stream.realm, affected_user_ids, [stream])
|
||||
event = dict(
|
||||
type="stream",
|
||||
op="update",
|
||||
stream_id=stream.id,
|
||||
name=stream.name,
|
||||
property="is_archived",
|
||||
value=True,
|
||||
)
|
||||
send_event_on_commit(stream.realm, event, affected_user_ids)
|
||||
|
||||
send_stream_deletion_event(stream.realm, affected_user_ids, [stream], for_archiving=True)
|
||||
|
||||
event_time = timezone_now()
|
||||
RealmAuditLog.objects.create(
|
||||
@@ -271,9 +281,25 @@ def do_unarchive_stream(stream: Stream, new_name: str, *, acting_user: UserProfi
|
||||
recent_traffic = get_streams_traffic({stream.id}, realm)
|
||||
|
||||
notify_user_ids = list(can_access_stream_metadata_user_ids(stream))
|
||||
|
||||
event = dict(
|
||||
type="stream",
|
||||
op="update",
|
||||
stream_id=stream.id,
|
||||
name=stream.name,
|
||||
property="is_archived",
|
||||
value=False,
|
||||
)
|
||||
send_event_on_commit(stream.realm, event, notify_user_ids)
|
||||
|
||||
anonymous_group_membership = get_anonymous_group_membership_dict_for_streams([stream])
|
||||
send_stream_creation_event(
|
||||
realm, stream, notify_user_ids, recent_traffic, anonymous_group_membership
|
||||
realm,
|
||||
stream,
|
||||
notify_user_ids,
|
||||
recent_traffic,
|
||||
anonymous_group_membership,
|
||||
for_unarchiving=True,
|
||||
)
|
||||
|
||||
sender = get_system_bot(settings.NOTIFICATION_BOT, stream.realm_id)
|
||||
|
||||
@@ -529,6 +529,7 @@ def check_stream_update(
|
||||
"name",
|
||||
"stream_id",
|
||||
"first_message_id",
|
||||
"is_archived",
|
||||
}
|
||||
|
||||
if prop == "description":
|
||||
@@ -565,6 +566,9 @@ def check_stream_update(
|
||||
elif prop == "is_announcement_only":
|
||||
assert extra_keys == set()
|
||||
assert isinstance(value, bool)
|
||||
elif prop == "is_archived":
|
||||
assert extra_keys == set()
|
||||
assert isinstance(value, bool)
|
||||
else:
|
||||
raise AssertionError(f"Unknown property: {prop}")
|
||||
|
||||
|
||||
@@ -1309,71 +1309,46 @@ def apply_event(
|
||||
if event["op"] == "delete":
|
||||
deleted_stream_ids = {stream["stream_id"] for stream in event["streams"]}
|
||||
|
||||
updated_first_message_ids: dict[int, int] = dict()
|
||||
if archived_channels:
|
||||
for stream in state["subscriptions"]:
|
||||
if stream["stream_id"] in deleted_stream_ids:
|
||||
stream["is_archived"] = True
|
||||
state["subscriptions"] = [
|
||||
stream
|
||||
for stream in state["subscriptions"]
|
||||
if stream["stream_id"] not in deleted_stream_ids
|
||||
]
|
||||
|
||||
for stream in state["unsubscribed"]:
|
||||
if stream["stream_id"] in deleted_stream_ids:
|
||||
stream["is_archived"] = True
|
||||
if stream["first_message_id"] is None:
|
||||
new_first_message_id = Stream.objects.get(
|
||||
id=stream["stream_id"]
|
||||
).first_message_id
|
||||
assert new_first_message_id is not None
|
||||
stream["first_message_id"] = new_first_message_id
|
||||
updated_first_message_ids[stream["stream_id"]] = new_first_message_id
|
||||
state["unsubscribed"] = [
|
||||
stream
|
||||
for stream in state["unsubscribed"]
|
||||
if stream["stream_id"] not in deleted_stream_ids
|
||||
]
|
||||
|
||||
for stream in state["never_subscribed"]:
|
||||
if stream["stream_id"] in deleted_stream_ids:
|
||||
stream["is_archived"] = True
|
||||
if stream["first_message_id"] is None:
|
||||
new_first_message_id = Stream.objects.get(
|
||||
id=stream["stream_id"]
|
||||
).first_message_id
|
||||
assert new_first_message_id is not None
|
||||
stream["first_message_id"] = new_first_message_id
|
||||
updated_first_message_ids[stream["stream_id"]] = new_first_message_id
|
||||
state["never_subscribed"] = [
|
||||
stream
|
||||
for stream in state["never_subscribed"]
|
||||
if stream["stream_id"] not in deleted_stream_ids
|
||||
]
|
||||
|
||||
if "streams" in state:
|
||||
for stream in state["streams"]:
|
||||
if stream["stream_id"] in deleted_stream_ids:
|
||||
stream["is_archived"] = True
|
||||
if stream["stream_id"] in updated_first_message_ids:
|
||||
stream["first_message_id"] = updated_first_message_ids[
|
||||
stream["stream_id"]
|
||||
]
|
||||
else:
|
||||
state["subscriptions"] = [
|
||||
stream
|
||||
for stream in state["subscriptions"]
|
||||
if stream["stream_id"] not in deleted_stream_ids
|
||||
if "streams" in state:
|
||||
state["streams"] = [
|
||||
s for s in state["streams"] if s["stream_id"] not in deleted_stream_ids
|
||||
]
|
||||
|
||||
state["unsubscribed"] = [
|
||||
stream
|
||||
for stream in state["unsubscribed"]
|
||||
if stream["stream_id"] not in deleted_stream_ids
|
||||
]
|
||||
|
||||
state["never_subscribed"] = [
|
||||
stream
|
||||
for stream in state["never_subscribed"]
|
||||
if stream["stream_id"] not in deleted_stream_ids
|
||||
]
|
||||
|
||||
if "streams" in state:
|
||||
state["streams"] = [
|
||||
s for s in state["streams"] if s["stream_id"] not in deleted_stream_ids
|
||||
]
|
||||
|
||||
if event["op"] == "update":
|
||||
# For legacy reasons, we call stream data 'subscriptions' in
|
||||
# the state var here, for the benefit of the JS code.
|
||||
for obj in state["subscriptions"]:
|
||||
if obj["name"].lower() == event["name"].lower():
|
||||
obj[event["property"]] = event["value"]
|
||||
if event["property"] == "description":
|
||||
obj["rendered_description"] = event["rendered_description"]
|
||||
if event.get("history_public_to_subscribers") is not None:
|
||||
obj["history_public_to_subscribers"] = event[
|
||||
"history_public_to_subscribers"
|
||||
]
|
||||
if event.get("is_web_public") is not None:
|
||||
obj["is_web_public"] = event["is_web_public"]
|
||||
|
||||
updated_first_message_ids = dict()
|
||||
for sub_list in [
|
||||
state["subscriptions"],
|
||||
state["unsubscribed"],
|
||||
state["never_subscribed"],
|
||||
]:
|
||||
@@ -1388,6 +1363,17 @@ def apply_event(
|
||||
]
|
||||
if event.get("is_web_public") is not None:
|
||||
obj["is_web_public"] = event["is_web_public"]
|
||||
if (
|
||||
event["property"] == "is_archived"
|
||||
and event["value"]
|
||||
and obj["first_message_id"] is None
|
||||
):
|
||||
new_first_message_id = Stream.objects.get(
|
||||
id=obj["stream_id"]
|
||||
).first_message_id
|
||||
assert new_first_message_id is not None
|
||||
obj["first_message_id"] = new_first_message_id
|
||||
updated_first_message_ids[obj["stream_id"]] = new_first_message_id
|
||||
# Also update the pure streams data
|
||||
if "streams" in state:
|
||||
for stream in state["streams"]:
|
||||
@@ -1403,6 +1389,13 @@ def apply_event(
|
||||
]
|
||||
if event.get("is_web_public") is not None:
|
||||
stream["is_web_public"] = event["is_web_public"]
|
||||
if (
|
||||
event["property"] == "is_archived"
|
||||
and stream["stream_id"] in updated_first_message_ids
|
||||
):
|
||||
stream["first_message_id"] = updated_first_message_ids[
|
||||
stream["stream_id"]
|
||||
]
|
||||
|
||||
elif event["type"] == "default_streams":
|
||||
state["realm_default_streams"] = event["default_streams"]
|
||||
|
||||
@@ -148,11 +148,13 @@ def send_stream_creation_event(
|
||||
user_ids: list[int],
|
||||
recent_traffic: dict[int, int] | None = None,
|
||||
anonymous_group_membership: dict[int, UserGroupMembersData] | None = None,
|
||||
for_unarchiving: bool = False,
|
||||
) -> None:
|
||||
event = dict(
|
||||
type="stream",
|
||||
op="create",
|
||||
streams=[stream_to_dict(stream, recent_traffic, anonymous_group_membership)],
|
||||
for_unarchiving=for_unarchiving,
|
||||
)
|
||||
send_event_on_commit(realm, event, user_ids)
|
||||
|
||||
@@ -1758,7 +1760,7 @@ def check_update_all_streams_active_status(
|
||||
|
||||
|
||||
def send_stream_deletion_event(
|
||||
realm: Realm, user_ids: Iterable[int], streams: list[Stream]
|
||||
realm: Realm, user_ids: Iterable[int], streams: list[Stream], for_archiving: bool = False
|
||||
) -> None:
|
||||
stream_deletion_event = dict(
|
||||
type="stream",
|
||||
@@ -1766,6 +1768,7 @@ def send_stream_deletion_event(
|
||||
# "streams" is deprecated, kept only for compatibility.
|
||||
streams=[dict(stream_id=stream.id) for stream in streams],
|
||||
stream_ids=[stream.id for stream in streams],
|
||||
for_archiving=for_archiving,
|
||||
)
|
||||
send_event_on_commit(realm, stream_deletion_event, user_ids)
|
||||
|
||||
|
||||
@@ -1296,11 +1296,19 @@ paths:
|
||||
private channel is made public, or a guest user is subscribed
|
||||
to a public (or private) channel.
|
||||
|
||||
This event is also sent when a channel is unarchived but only
|
||||
to clients that did not declare the `archived_channels` [client
|
||||
capability][client-capabilities].
|
||||
|
||||
Note that organization administrators who are not subscribed will
|
||||
not be able to see content on the channel; just that it exists.
|
||||
|
||||
**Changes**: Prior to Zulip 8.0 (feature level 220), this event was
|
||||
incorrectly not sent to guest users a web-public channel was created.
|
||||
**Changes**: Prior to Zulip 11.0 (feature level ZF-960986), this
|
||||
event was sent to all the users who could see the channel when it
|
||||
was unarchived.
|
||||
|
||||
Prior to Zulip 8.0 (feature level 220), this event was incorrectly
|
||||
not sent to guest users a web-public channel was created.
|
||||
|
||||
Prior to Zulip 8.0 (feature level 205), this event was not sent
|
||||
when a user gained access to a channel due to their role changing.
|
||||
@@ -1363,16 +1371,23 @@ paths:
|
||||
}
|
||||
- type: object
|
||||
description: |
|
||||
Event sent to all users who can see a channel when it is deactivated.
|
||||
|
||||
This event is also sent when a user loses access to a channel they
|
||||
previously [could access](/help/channel-permissions) because they
|
||||
are unsubscribed from a private channel or their [role](/help/user-roles)
|
||||
Event sent when a user loses access to a channel they previously
|
||||
[could access](/help/channel-permissions) because they are
|
||||
unsubscribed from a private channel or their [role](/help/user-roles)
|
||||
has changed.
|
||||
|
||||
**Changes**: Prior to Zulip 8.0 (feature level 205), this event was
|
||||
not sent when a user lost access to a channel due to their role
|
||||
changing.
|
||||
This event is also sent when a channel is archived but only
|
||||
to clients that did not declare the `archived_channels` [client
|
||||
capability][client-capabilities].
|
||||
|
||||
**Changes**: Prior to Zulip 11.0 (feature level ZF-960986), this
|
||||
event was sent to all the users who could see the channel when it
|
||||
was archived.
|
||||
|
||||
Prior to Zulip 8.0 (feature level 205), this event was not sent
|
||||
when a user lost access to a channel due to their role changing.
|
||||
|
||||
[client-capabilities]: /api/register-queue#parameter-client_capabilities
|
||||
properties:
|
||||
id:
|
||||
$ref: "#/components/schemas/EventIdSchema"
|
||||
@@ -1429,10 +1444,21 @@ paths:
|
||||
[GET /streams](/api/get-streams#response) response
|
||||
for details on the various properties of a channel.
|
||||
|
||||
**Changes**: Before Zulip 9.0 (feature level 256), this event
|
||||
was never sent when the `first_message_id` property of a channel
|
||||
was updated because the oldest message that had been sent to it
|
||||
This event is also sent when archiving or unarchiving a
|
||||
channel to all the users who can see that channel exists
|
||||
but only to the clients that declared the `archived_channels`
|
||||
[client capability][client-capabilities].
|
||||
|
||||
**Changes**: Prior to Zulip 11.0 (feature level ZF-960986),
|
||||
this event was never sent when archiving or unarchiving
|
||||
a channel.
|
||||
|
||||
Before Zulip 9.0 (feature level 256), this event was never
|
||||
sent when the `first_message_id` property of a channel was
|
||||
updated because the oldest message that had been sent to it
|
||||
changed.
|
||||
|
||||
[client-capabilities]: /api/register-queue#parameter-client_capabilities
|
||||
properties:
|
||||
id:
|
||||
$ref: "#/components/schemas/EventIdSchema"
|
||||
|
||||
@@ -112,6 +112,7 @@ from zerver.actions.streams import (
|
||||
do_change_subscription_property,
|
||||
do_deactivate_stream,
|
||||
do_rename_stream,
|
||||
do_unarchive_stream,
|
||||
)
|
||||
from zerver.actions.submessage import do_add_submessage
|
||||
from zerver.actions.typing import (
|
||||
@@ -363,6 +364,7 @@ class BaseAction(ZulipTestCase):
|
||||
linkifier_url_template=linkifier_url_template,
|
||||
user_list_incomplete=user_list_incomplete,
|
||||
include_deactivated_groups=include_deactivated_groups,
|
||||
archived_channels=archived_channels,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -3771,7 +3773,19 @@ class NormalActionsTest(BaseAction):
|
||||
include_streams=include_streams, archived_channels=True
|
||||
) as events:
|
||||
do_deactivate_stream(stream, acting_user=None)
|
||||
check_stream_update("events[0]", events[0])
|
||||
self.assertEqual(events[0]["stream_id"], stream.id)
|
||||
self.assertEqual(events[0]["property"], "is_archived")
|
||||
self.assertEqual(events[0]["value"], True)
|
||||
|
||||
do_unarchive_stream(stream, stream.name, acting_user=None)
|
||||
|
||||
with self.verify_action(
|
||||
include_streams=include_streams, archived_channels=False
|
||||
) as events:
|
||||
do_deactivate_stream(stream, acting_user=None)
|
||||
check_stream_delete("events[0]", events[0])
|
||||
self.assertEqual(events[0]["stream_ids"], [stream.id])
|
||||
|
||||
def test_admin_deactivate_unsubscribed_stream(self) -> None:
|
||||
self.set_up_db_for_testing_user_access()
|
||||
@@ -3788,7 +3802,37 @@ class NormalActionsTest(BaseAction):
|
||||
|
||||
with self.verify_action(num_events=1, archived_channels=True) as events:
|
||||
do_deactivate_stream(stream, acting_user=iago)
|
||||
check_stream_update("events[0]", events[0])
|
||||
self.assertEqual(events[0]["stream_id"], stream.id)
|
||||
self.assertEqual(events[0]["property"], "is_archived")
|
||||
self.assertEqual(events[0]["value"], True)
|
||||
|
||||
do_unarchive_stream(stream, stream.name, acting_user=iago)
|
||||
|
||||
with self.verify_action(num_events=1, archived_channels=False) as events:
|
||||
do_deactivate_stream(stream, acting_user=iago)
|
||||
check_stream_delete("events[0]", events[0])
|
||||
self.assertEqual(events[0]["stream_ids"], [stream.id])
|
||||
|
||||
def test_unarchiving_stream(self) -> None:
|
||||
iago = self.example_user("iago")
|
||||
stream = self.make_stream("test_stream")
|
||||
do_deactivate_stream(stream, acting_user=iago)
|
||||
|
||||
with self.verify_action(num_events=1, archived_channels=False) as events:
|
||||
do_unarchive_stream(stream, stream.name, acting_user=iago)
|
||||
check_stream_create("events[0]", events[0])
|
||||
self.assert_length(events[0]["streams"], 1)
|
||||
self.assertEqual(events[0]["streams"][0]["stream_id"], stream.id)
|
||||
|
||||
do_deactivate_stream(stream, acting_user=iago)
|
||||
|
||||
with self.verify_action(num_events=1, archived_channels=True) as events:
|
||||
do_unarchive_stream(stream, stream.name, acting_user=iago)
|
||||
check_stream_update("events[0]", events[0])
|
||||
self.assertEqual(events[0]["stream_id"], stream.id)
|
||||
self.assertEqual(events[0]["property"], "is_archived")
|
||||
self.assertEqual(events[0]["value"], False)
|
||||
|
||||
def test_user_losing_access_on_deactivating_stream(self) -> None:
|
||||
self.set_up_db_for_testing_user_access()
|
||||
@@ -3804,7 +3848,7 @@ class NormalActionsTest(BaseAction):
|
||||
|
||||
with self.verify_action(num_events=2, archived_channels=True) as events:
|
||||
do_deactivate_stream(stream, acting_user=None)
|
||||
check_stream_delete("events[0]", events[0])
|
||||
check_stream_update("events[0]", events[0])
|
||||
|
||||
# Test that if the subscribers of deactivated stream are involved in
|
||||
# DMs with guest, then the guest does not get "remove" event for them.
|
||||
@@ -3818,7 +3862,7 @@ class NormalActionsTest(BaseAction):
|
||||
|
||||
with self.verify_action(num_events=2, archived_channels=True) as events:
|
||||
do_deactivate_stream(stream, acting_user=None)
|
||||
check_stream_delete("events[0]", events[0])
|
||||
check_stream_update("events[0]", events[0])
|
||||
|
||||
def test_subscribe_other_user_never_subscribed(self) -> None:
|
||||
for i, include_streams in enumerate([True, False]):
|
||||
@@ -4939,6 +4983,38 @@ class SubscribeActionTest(BaseAction):
|
||||
acting_user=iago,
|
||||
)
|
||||
|
||||
# Check updating description, stream permission for an unsubscribed streams.
|
||||
self.user_profile = self.example_user("hamlet")
|
||||
self.unsubscribe(self.example_user("hamlet"), stream.name)
|
||||
with self.verify_action(include_subscribers=include_subscribers, num_events=1) as events:
|
||||
do_change_stream_description(
|
||||
stream, "description", acting_user=self.example_user("hamlet")
|
||||
)
|
||||
check_stream_update("events[0]", events[0])
|
||||
|
||||
with self.verify_action(include_subscribers=include_subscribers, num_events=1) as events:
|
||||
do_change_stream_permission(
|
||||
stream,
|
||||
invite_only=False,
|
||||
history_public_to_subscribers=True,
|
||||
is_web_public=True,
|
||||
acting_user=iago,
|
||||
)
|
||||
check_stream_update("events[0]", events[0])
|
||||
|
||||
with self.verify_action(include_subscribers=include_subscribers, num_events=1) as events:
|
||||
do_change_stream_permission(
|
||||
stream,
|
||||
invite_only=True,
|
||||
history_public_to_subscribers=False,
|
||||
is_web_public=False,
|
||||
acting_user=iago,
|
||||
)
|
||||
check_stream_update("events[0]", events[0])
|
||||
|
||||
# Subscribe the user again for further tests.
|
||||
self.subscribe(self.example_user("hamlet"), stream.name)
|
||||
|
||||
self.user_profile = self.example_user("hamlet")
|
||||
with self.verify_action(include_subscribers=include_subscribers, num_events=2) as events:
|
||||
do_change_stream_message_retention_days(stream, self.example_user("hamlet"), -1)
|
||||
|
||||
@@ -1925,34 +1925,47 @@ class StreamAdminTest(ZulipTestCase):
|
||||
acting_user=zoe,
|
||||
)
|
||||
self.subscribe(self.example_user("cordelia"), "stream_private_name1")
|
||||
with self.capture_send_event_calls(expected_num_events=2) as events:
|
||||
with self.capture_send_event_calls(expected_num_events=3) as events:
|
||||
do_unarchive_stream(stream, new_name="private", acting_user=None)
|
||||
|
||||
stream = Stream.objects.get(id=stream.id)
|
||||
self.assertFalse(stream.is_web_public)
|
||||
|
||||
# Clients will get this event only if they support
|
||||
# archived_channels client capability.
|
||||
self.assertEqual(events[0]["event"]["op"], "update")
|
||||
self.assertEqual(events[0]["event"]["stream_id"], stream.id)
|
||||
self.assertEqual(events[0]["event"]["property"], "is_archived")
|
||||
self.assertEqual(events[0]["event"]["value"], False)
|
||||
|
||||
# Tell all users with metadata access that the stream exists.
|
||||
self.assertEqual(events[0]["event"]["op"], "create")
|
||||
self.assertEqual(events[0]["event"]["streams"][0]["name"], "private")
|
||||
self.assertEqual(events[0]["event"]["streams"][0]["stream_id"], stream.id)
|
||||
notified_user_ids = set(events[0]["users"])
|
||||
self.assertEqual(
|
||||
notified_user_ids,
|
||||
can_access_stream_metadata_user_ids(stream),
|
||||
)
|
||||
self.assertIn(self.example_user("cordelia").id, notified_user_ids)
|
||||
# An important corner case is that all organization admins are notified.
|
||||
self.assertIn(self.example_user("iago").id, notified_user_ids)
|
||||
# The current user, Hamlet was made an admin and thus should be notified too.
|
||||
self.assertIn(aaron.id, notified_user_ids)
|
||||
# Channel admin should be notified.
|
||||
self.assertIn(self.example_user("aaron").id, notified_user_ids)
|
||||
# User belonging to `can_add_subscribers_group` should be notified.
|
||||
self.assertIn(prospero.id, notified_user_ids)
|
||||
# User belonging to `can_subscribe_group` should be notified.
|
||||
self.assertIn(zoe.id, notified_user_ids)
|
||||
# Guest user should not be notified.
|
||||
self.assertNotIn(self.example_user("polonius").id, notified_user_ids)
|
||||
# This event will only be sent to clients that do not support
|
||||
# archived_channels client capability, as clients supporting
|
||||
# archived_channels client capability will already know that
|
||||
# the stream exists.
|
||||
self.assertEqual(events[1]["event"]["op"], "create")
|
||||
self.assertEqual(events[1]["event"]["streams"][0]["name"], "private")
|
||||
self.assertEqual(events[1]["event"]["streams"][0]["stream_id"], stream.id)
|
||||
|
||||
for event in [events[0], events[1]]:
|
||||
notified_user_ids = set(event["users"])
|
||||
self.assertEqual(
|
||||
notified_user_ids,
|
||||
can_access_stream_metadata_user_ids(stream),
|
||||
)
|
||||
self.assertIn(self.example_user("cordelia").id, notified_user_ids)
|
||||
# An important corner case is that all organization admins are notified.
|
||||
self.assertIn(self.example_user("iago").id, notified_user_ids)
|
||||
# The current user, Hamlet was made an admin and thus should be notified too.
|
||||
self.assertIn(aaron.id, notified_user_ids)
|
||||
# Channel admin should be notified.
|
||||
self.assertIn(self.example_user("aaron").id, notified_user_ids)
|
||||
# User belonging to `can_add_subscribers_group` should be notified.
|
||||
self.assertIn(prospero.id, notified_user_ids)
|
||||
# User belonging to `can_subscribe_group` should be notified.
|
||||
self.assertIn(zoe.id, notified_user_ids)
|
||||
# Guest user should not be notified.
|
||||
self.assertNotIn(self.example_user("polonius").id, notified_user_ids)
|
||||
|
||||
def test_unarchive_stream(self) -> None:
|
||||
hamlet = self.example_user("hamlet")
|
||||
@@ -1966,20 +1979,33 @@ class StreamAdminTest(ZulipTestCase):
|
||||
self.subscribe(hamlet, stream.name)
|
||||
self.subscribe(cordelia, stream.name)
|
||||
do_deactivate_stream(stream, acting_user=None)
|
||||
with self.capture_send_event_calls(expected_num_events=2) as events:
|
||||
with self.capture_send_event_calls(expected_num_events=3) as events:
|
||||
do_unarchive_stream(stream, new_name="new_stream", acting_user=None)
|
||||
|
||||
# Clients will get this event only if they support
|
||||
# archived_channels client capability.
|
||||
self.assertEqual(events[0]["event"]["op"], "update")
|
||||
self.assertEqual(events[0]["event"]["stream_id"], stream.id)
|
||||
self.assertEqual(events[0]["event"]["property"], "is_archived")
|
||||
self.assertEqual(events[0]["event"]["value"], False)
|
||||
|
||||
# Tell all users with metadata access that the stream exists.
|
||||
self.assertEqual(events[0]["event"]["op"], "create")
|
||||
self.assertEqual(events[0]["event"]["streams"][0]["name"], "new_stream")
|
||||
self.assertEqual(events[0]["event"]["streams"][0]["stream_id"], stream.id)
|
||||
notified_user_ids = set(events[0]["users"])
|
||||
self.assertCountEqual(
|
||||
notified_user_ids,
|
||||
set(active_non_guest_user_ids(stream.realm_id)),
|
||||
)
|
||||
# Guest user should not be notified.
|
||||
self.assertNotIn(self.example_user("polonius").id, notified_user_ids)
|
||||
# This event will only be sent to clients that do not support
|
||||
# archived_channels client capability, as clients supporting
|
||||
# archived_channels client capability will already know that
|
||||
# the stream exists.
|
||||
self.assertEqual(events[1]["event"]["op"], "create")
|
||||
self.assertEqual(events[1]["event"]["streams"][0]["name"], "new_stream")
|
||||
self.assertEqual(events[1]["event"]["streams"][0]["stream_id"], stream.id)
|
||||
|
||||
for event in [events[0], events[1]]:
|
||||
notified_user_ids = set(event["users"])
|
||||
self.assertCountEqual(
|
||||
notified_user_ids,
|
||||
set(active_non_guest_user_ids(stream.realm_id)),
|
||||
)
|
||||
# Guest user should not be notified.
|
||||
self.assertNotIn(self.example_user("polonius").id, notified_user_ids)
|
||||
|
||||
stream = Stream.objects.get(id=stream.id)
|
||||
self.assertFalse(stream.deactivated)
|
||||
@@ -3206,7 +3232,7 @@ class StreamAdminTest(ZulipTestCase):
|
||||
realm = stream.realm
|
||||
stream_id = stream.id
|
||||
|
||||
with self.capture_send_event_calls(expected_num_events=2) as events:
|
||||
with self.capture_send_event_calls(expected_num_events=3) as events:
|
||||
result = self.client_delete("/json/streams/" + str(stream_id))
|
||||
self.assert_json_success(result)
|
||||
|
||||
@@ -3217,10 +3243,21 @@ class StreamAdminTest(ZulipTestCase):
|
||||
self.assertEqual(sub_events, [])
|
||||
|
||||
stream_events = [e for e in events if e["event"]["type"] == "stream"]
|
||||
self.assert_length(stream_events, 1)
|
||||
event = stream_events[0]["event"]
|
||||
self.assertEqual(event["op"], "delete")
|
||||
self.assertEqual(event["streams"][0]["stream_id"], stream.id)
|
||||
self.assert_length(stream_events, 2)
|
||||
|
||||
# Clients will get this event only if they support
|
||||
# archived_channels client capability.
|
||||
update_event = stream_events[0]["event"]
|
||||
self.assertEqual(update_event["op"], "update")
|
||||
self.assertEqual(update_event["stream_id"], stream.id)
|
||||
self.assertEqual(update_event["property"], "is_archived")
|
||||
self.assertEqual(update_event["value"], True)
|
||||
|
||||
# This event will only be sent to clients that do not support
|
||||
# archived_channels client capability.
|
||||
delete_event = stream_events[1]["event"]
|
||||
self.assertEqual(delete_event["op"], "delete")
|
||||
self.assertEqual(delete_event["streams"][0]["stream_id"], stream.id)
|
||||
|
||||
hashed_stream_id = hashlib.sha512(str(stream_id).encode()).hexdigest()[0:7]
|
||||
old_deactivated_stream_name = hashed_stream_id + "!DEACTIVATED:" + active_name
|
||||
|
||||
@@ -259,6 +259,15 @@ class ClientDescriptor:
|
||||
# groups by themselves. Other clients receive 'remove' and
|
||||
# 'add' event.
|
||||
return self.include_deactivated_groups
|
||||
if (
|
||||
event["type"] == "stream"
|
||||
and event["op"] == "update"
|
||||
and event["property"] == "is_archived"
|
||||
):
|
||||
# 'update' events for archiving and unarchiving streams are
|
||||
# only sent to clients that can process archived channels.
|
||||
# Other clients receive "create" and "delete" events.
|
||||
return self.archived_channels
|
||||
return True
|
||||
|
||||
# TODO: Refactor so we don't need this function
|
||||
@@ -1575,6 +1584,28 @@ def process_user_group_name_update_event(event: Mapping[str, Any], users: Iterab
|
||||
client.add_event(user_group_event)
|
||||
|
||||
|
||||
def process_stream_creation_event(event: Mapping[str, Any], users: Iterable[int]) -> None:
|
||||
stream_create_event = dict(event)
|
||||
event_for_unarchiving_stream = stream_create_event.pop("for_unarchiving", False)
|
||||
for user_profile_id in users:
|
||||
for client in get_client_descriptors_for_user(user_profile_id):
|
||||
if client.accepts_event(stream_create_event):
|
||||
if event_for_unarchiving_stream and client.archived_channels:
|
||||
continue
|
||||
client.add_event(stream_create_event)
|
||||
|
||||
|
||||
def process_stream_deletion_event(event: Mapping[str, Any], users: Iterable[int]) -> None:
|
||||
stream_delete_event = dict(event)
|
||||
event_for_archiving_stream = stream_delete_event.pop("for_archiving", False)
|
||||
for user_profile_id in users:
|
||||
for client in get_client_descriptors_for_user(user_profile_id):
|
||||
if client.accepts_event(stream_delete_event):
|
||||
if event_for_archiving_stream and client.archived_channels:
|
||||
continue
|
||||
client.add_event(stream_delete_event)
|
||||
|
||||
|
||||
def process_user_topic_event(event: Mapping[str, Any], users: Iterable[int]) -> None:
|
||||
empty_topic_name_fallback_event: Mapping[str, Any] | dict[str, Any]
|
||||
if event.get("topic_name") == "":
|
||||
@@ -1668,6 +1699,10 @@ def process_notification(notice: Mapping[str, Any]) -> None:
|
||||
and event["flag"] == "read"
|
||||
):
|
||||
process_mark_message_unread_event(event, cast(list[int], users))
|
||||
elif event["type"] == "stream" and event["op"] == "create":
|
||||
process_stream_creation_event(event, cast(list[int], users))
|
||||
elif event["type"] == "stream" and event["op"] == "delete":
|
||||
process_stream_deletion_event(event, cast(list[int], users))
|
||||
elif event["type"] == "cleanup_queue":
|
||||
# cleanup_event_queue may generate this event to forward cleanup
|
||||
# requests to the right shard.
|
||||
|
||||
Reference in New Issue
Block a user