mirror of https://github.com/zulip/zulip.git
channel_subscribe: Use IDs instead of emails when processing results.
As part of our todo in the code, we want to use the unique user IDs instead of emails when processing the results of subscribing users to a channel. These changes apply those changes and streamlines the use of IDs.
This commit is contained in:
parent
62f74fcb83
commit
65893292b5
|
@ -20,6 +20,12 @@ format used by the Zulip server that they are interacting with.
|
||||||
|
|
||||||
## Changes in Zulip 10.0
|
## Changes in Zulip 10.0
|
||||||
|
|
||||||
|
**Feature level 289**
|
||||||
|
|
||||||
|
* [`POST /users/{user_id}/subscription`](/api/subscribe): In the response,
|
||||||
|
users are identified by their numeric user ID rather than by their
|
||||||
|
Zulip API email address.
|
||||||
|
|
||||||
**Feature level 288**
|
**Feature level 288**
|
||||||
|
|
||||||
* [`POST /register`](/api/register-queue):
|
* [`POST /register`](/api/register-queue):
|
||||||
|
|
|
@ -34,7 +34,7 @@ DESKTOP_WARNING_VERSION = "5.9.3"
|
||||||
# new level means in api_docs/changelog.md, as well as "**Changes**"
|
# new level means in api_docs/changelog.md, as well as "**Changes**"
|
||||||
# entries in the endpoint's documentation in `zulip.yaml`.
|
# entries in the endpoint's documentation in `zulip.yaml`.
|
||||||
|
|
||||||
API_FEATURE_LEVEL = 288 # Last bumped for presence_history_limit_days
|
API_FEATURE_LEVEL = 289 # return ID as key while subscribing to a stream.
|
||||||
|
|
||||||
# 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
|
||||||
|
|
|
@ -226,11 +226,11 @@ function subscribe_new_users({pill_user_ids}: {pill_user_ids: number[]}): void {
|
||||||
function invite_success(raw_data: unknown): void {
|
function invite_success(raw_data: unknown): void {
|
||||||
const data = add_user_ids_api_response_schema.parse(raw_data);
|
const data = add_user_ids_api_response_schema.parse(raw_data);
|
||||||
pill_widget.clear();
|
pill_widget.clear();
|
||||||
const subscribed_users = Object.keys(data.subscribed).map(
|
const subscribed_users = Object.keys(data.subscribed).map((user_id) =>
|
||||||
(email) => people.get_by_email(email)!,
|
people.get_by_user_id(Number(user_id)),
|
||||||
);
|
);
|
||||||
const already_subscribed_users = Object.keys(data.already_subscribed).map(
|
const already_subscribed_users = Object.keys(data.already_subscribed).map((user_id) =>
|
||||||
(email) => people.get_by_email(email)!,
|
people.get_by_user_id(Number(user_id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
show_stream_subscription_request_result({
|
show_stream_subscription_request_result({
|
||||||
|
|
|
@ -129,7 +129,7 @@ def add_subscriptions(client: Client) -> None:
|
||||||
# {code_example|end}
|
# {code_example|end}
|
||||||
assert_success_response(result)
|
assert_success_response(result)
|
||||||
validate_against_openapi_schema(result, "/users/me/subscriptions", "post", "200")
|
validate_against_openapi_schema(result, "/users/me/subscriptions", "post", "200")
|
||||||
assert "newbie@zulip.com" in result["subscribed"]
|
assert str(user_id) in result["subscribed"]
|
||||||
|
|
||||||
|
|
||||||
def test_add_subscriptions_already_subscribed(client: Client) -> None:
|
def test_add_subscriptions_already_subscribed(client: Client) -> None:
|
||||||
|
|
|
@ -9916,12 +9916,15 @@ paths:
|
||||||
subscribed:
|
subscribed:
|
||||||
type: object
|
type: object
|
||||||
description: |
|
description: |
|
||||||
A dictionary where the key is the Zulip API email address of the user/bot
|
A dictionary where the key is the ID of the user and the value
|
||||||
and the value is a list of the names of the channels that were subscribed
|
is a list of the names of the channels that user was subscribed
|
||||||
to as a result of the query.
|
to as a result of the request.
|
||||||
|
|
||||||
|
**Changes**: Before Zulip 10.0 (feature level 289), the user
|
||||||
|
keys were Zulip API email addresses, not user ID.
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
description: |
|
description: |
|
||||||
`{email_address}`: List of the names of the channels that were subscribed
|
`{id}`: List of the names of the channels that were subscribed
|
||||||
to as a result of the query.
|
to as a result of the query.
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
|
@ -9929,12 +9932,16 @@ paths:
|
||||||
already_subscribed:
|
already_subscribed:
|
||||||
type: object
|
type: object
|
||||||
description: |
|
description: |
|
||||||
A dictionary where the key is the Zulip API email address of the user/bot
|
A dictionary where the key is the ID of the user and the value
|
||||||
and the value is a list of the names of the channels that the user/bot is
|
is a list of the names of the channels that where the user was
|
||||||
already subscribed to.
|
not added as a subscriber in this request, because they were
|
||||||
|
already a subscriber.
|
||||||
|
|
||||||
|
**Changes**: Before Zulip 10.0 (feature level 289), the user
|
||||||
|
keys were Zulip API email addresses, not user IDs.
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
description: |
|
description: |
|
||||||
`{email_address}`: List of the names of the channels that the user is
|
`{id}`: List of the names of the channels that the user is
|
||||||
already subscribed to.
|
already subscribed to.
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
|
@ -9948,11 +9955,10 @@ paths:
|
||||||
authorized to subscribe to. Only present if `"authorization_errors_fatal": false`.
|
authorized to subscribe to. Only present if `"authorization_errors_fatal": false`.
|
||||||
example:
|
example:
|
||||||
{
|
{
|
||||||
"already_subscribed":
|
"already_subscribed": {"1": ["testing-help"]},
|
||||||
{"iago@zulip.com": ["testing-help"]},
|
|
||||||
"msg": "",
|
"msg": "",
|
||||||
"result": "success",
|
"result": "success",
|
||||||
"subscribed": {"newbie@zulip.com": ["testing-help"]},
|
"subscribed": {"2": ["testing-help"]},
|
||||||
}
|
}
|
||||||
"400":
|
"400":
|
||||||
description: Bad request.
|
description: Bad request.
|
||||||
|
|
|
@ -495,10 +495,10 @@ class TestCreateStreams(ZulipTestCase):
|
||||||
"result": "success",
|
"result": "success",
|
||||||
"msg": "",
|
"msg": "",
|
||||||
"subscribed": {
|
"subscribed": {
|
||||||
"AARON@zulip.com": ["brand new stream"],
|
"10": ["brand new stream"],
|
||||||
"cordelia@zulip.com": ["brand new stream"],
|
"11": ["brand new stream"],
|
||||||
"hamlet@zulip.com": ["brand new stream"],
|
"6": ["brand new stream"],
|
||||||
"iago@zulip.com": ["brand new stream"],
|
"8": ["brand new stream"],
|
||||||
},
|
},
|
||||||
"already_subscribed": {},
|
"already_subscribed": {},
|
||||||
}
|
}
|
||||||
|
@ -4062,6 +4062,7 @@ class SubscriptionAPITest(ZulipTestCase):
|
||||||
self.user_profile = self.example_user("hamlet")
|
self.user_profile = self.example_user("hamlet")
|
||||||
self.test_email = self.user_profile.email
|
self.test_email = self.user_profile.email
|
||||||
self.test_user = self.user_profile
|
self.test_user = self.user_profile
|
||||||
|
self.test_id = str(self.user_profile.id)
|
||||||
self.login_user(self.user_profile)
|
self.login_user(self.user_profile)
|
||||||
self.test_realm = self.user_profile.realm
|
self.test_realm = self.user_profile.realm
|
||||||
self.streams = self.get_streams(self.user_profile)
|
self.streams = self.get_streams(self.user_profile)
|
||||||
|
@ -4170,29 +4171,30 @@ class SubscriptionAPITest(ZulipTestCase):
|
||||||
email: str,
|
email: str,
|
||||||
new_subs: list[str],
|
new_subs: list[str],
|
||||||
realm: Realm,
|
realm: Realm,
|
||||||
|
id: str,
|
||||||
invite_only: bool = False,
|
invite_only: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Check result of adding subscriptions.
|
Check result of adding subscriptions.
|
||||||
|
|
||||||
You can add subscriptions for yourself or possibly many
|
You can add subscriptions for yourself or possibly many
|
||||||
principals, which is why e-mails map to subscriptions in the
|
principals, which is why user ID map to subscriptions in the
|
||||||
result.
|
result.
|
||||||
|
|
||||||
The result json is of the form
|
The result json is of the form
|
||||||
|
|
||||||
{"msg": "",
|
{"msg": "",
|
||||||
"result": "success",
|
"result": "success",
|
||||||
"already_subscribed": {self.example_email("iago"): ["Venice", "Verona"]},
|
"already_subscribed": {self.example_user("iago").id: ["Venice", "Verona"]},
|
||||||
"subscribed": {self.example_email("iago"): ["Venice8"]}}
|
"subscribed": {self.example_user("iago").id: ["Venice8"]}}
|
||||||
"""
|
"""
|
||||||
result = self.common_subscribe_to_streams(
|
result = self.common_subscribe_to_streams(
|
||||||
self.test_user, subscriptions, other_params, invite_only=invite_only
|
self.test_user, subscriptions, other_params, invite_only=invite_only
|
||||||
)
|
)
|
||||||
json = self.assert_json_success(result)
|
json = self.assert_json_success(result)
|
||||||
self.assertEqual(sorted(subscribed), sorted(json["subscribed"][email]))
|
self.assertEqual(sorted(subscribed), sorted(json["subscribed"][id]))
|
||||||
self.assertEqual(sorted(already_subscribed), sorted(json["already_subscribed"][email]))
|
self.assertEqual(sorted(already_subscribed), sorted(json["already_subscribed"][id]))
|
||||||
user = get_user(email, realm)
|
user = get_user_profile_by_id_in_realm(int(id), realm)
|
||||||
new_streams = self.get_streams(user)
|
new_streams = self.get_streams(user)
|
||||||
self.assertEqual(sorted(new_streams), sorted(new_subs))
|
self.assertEqual(sorted(new_streams), sorted(new_subs))
|
||||||
|
|
||||||
|
@ -4217,6 +4219,7 @@ class SubscriptionAPITest(ZulipTestCase):
|
||||||
self.test_email,
|
self.test_email,
|
||||||
self.streams + add_streams,
|
self.streams + add_streams,
|
||||||
self.test_realm,
|
self.test_realm,
|
||||||
|
self.test_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_successful_subscriptions_add_with_announce(self) -> None:
|
def test_successful_subscriptions_add_with_announce(self) -> None:
|
||||||
|
@ -4246,6 +4249,7 @@ class SubscriptionAPITest(ZulipTestCase):
|
||||||
self.test_email,
|
self.test_email,
|
||||||
self.streams + add_streams,
|
self.streams + add_streams,
|
||||||
self.test_realm,
|
self.test_realm,
|
||||||
|
self.test_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
expected_stream_ids = {get_stream(stream, self.test_realm).id for stream in add_streams}
|
expected_stream_ids = {get_stream(stream, self.test_realm).id for stream in add_streams}
|
||||||
|
@ -4392,6 +4396,7 @@ class SubscriptionAPITest(ZulipTestCase):
|
||||||
self.test_email,
|
self.test_email,
|
||||||
[*self.streams, "hümbüǵ"],
|
[*self.streams, "hümbüǵ"],
|
||||||
self.test_realm,
|
self.test_realm,
|
||||||
|
self.test_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_subscriptions_add_too_long(self) -> None:
|
def test_subscriptions_add_too_long(self) -> None:
|
||||||
|
@ -4703,6 +4708,7 @@ class SubscriptionAPITest(ZulipTestCase):
|
||||||
other_profile.email,
|
other_profile.email,
|
||||||
streams_to_sub,
|
streams_to_sub,
|
||||||
invitee_realm,
|
invitee_realm,
|
||||||
|
str(other_profile.id),
|
||||||
invite_only=invite_only,
|
invite_only=invite_only,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -4875,7 +4881,7 @@ class SubscriptionAPITest(ZulipTestCase):
|
||||||
do_change_stream_post_policy(stream, Stream.STREAM_POST_POLICY_ADMINS, acting_user=member)
|
do_change_stream_post_policy(stream, Stream.STREAM_POST_POLICY_ADMINS, acting_user=member)
|
||||||
result = self.common_subscribe_to_streams(member, ["stream1"])
|
result = self.common_subscribe_to_streams(member, ["stream1"])
|
||||||
json = self.assert_json_success(result)
|
json = self.assert_json_success(result)
|
||||||
self.assertEqual(json["subscribed"], {member.email: ["stream1"]})
|
self.assertEqual(json["subscribed"], {str(member.id): ["stream1"]})
|
||||||
self.assertEqual(json["already_subscribed"], {})
|
self.assertEqual(json["already_subscribed"], {})
|
||||||
|
|
||||||
def test_subscribe_to_stream_post_policy_restrict_new_members_stream(self) -> None:
|
def test_subscribe_to_stream_post_policy_restrict_new_members_stream(self) -> None:
|
||||||
|
@ -4895,7 +4901,7 @@ class SubscriptionAPITest(ZulipTestCase):
|
||||||
)
|
)
|
||||||
result = self.common_subscribe_to_streams(new_member, ["stream1"])
|
result = self.common_subscribe_to_streams(new_member, ["stream1"])
|
||||||
json = self.assert_json_success(result)
|
json = self.assert_json_success(result)
|
||||||
self.assertEqual(json["subscribed"], {new_member.email: ["stream1"]})
|
self.assertEqual(json["subscribed"], {str(new_member.id): ["stream1"]})
|
||||||
self.assertEqual(json["already_subscribed"], {})
|
self.assertEqual(json["already_subscribed"], {})
|
||||||
|
|
||||||
def test_subscribe_to_stream_post_policy_moderators_stream(self) -> None:
|
def test_subscribe_to_stream_post_policy_moderators_stream(self) -> None:
|
||||||
|
@ -4912,7 +4918,7 @@ class SubscriptionAPITest(ZulipTestCase):
|
||||||
)
|
)
|
||||||
result = self.common_subscribe_to_streams(member, ["stream1"])
|
result = self.common_subscribe_to_streams(member, ["stream1"])
|
||||||
json = self.assert_json_success(result)
|
json = self.assert_json_success(result)
|
||||||
self.assertEqual(json["subscribed"], {member.email: ["stream1"]})
|
self.assertEqual(json["subscribed"], {str(member.id): ["stream1"]})
|
||||||
self.assertEqual(json["already_subscribed"], {})
|
self.assertEqual(json["already_subscribed"], {})
|
||||||
|
|
||||||
def test_guest_user_subscribe(self) -> None:
|
def test_guest_user_subscribe(self) -> None:
|
||||||
|
@ -5897,7 +5903,7 @@ class InviteOnlyStreamTest(ZulipTestCase):
|
||||||
result = self.common_subscribe_to_streams(hamlet, [stream_name], invite_only=True)
|
result = self.common_subscribe_to_streams(hamlet, [stream_name], invite_only=True)
|
||||||
|
|
||||||
json = self.assert_json_success(result)
|
json = self.assert_json_success(result)
|
||||||
self.assertEqual(json["subscribed"], {hamlet.email: [stream_name]})
|
self.assertEqual(json["subscribed"], {str(hamlet.id): [stream_name]})
|
||||||
self.assertEqual(json["already_subscribed"], {})
|
self.assertEqual(json["already_subscribed"], {})
|
||||||
|
|
||||||
# Subscribing oneself to an invite-only stream is not allowed
|
# Subscribing oneself to an invite-only stream is not allowed
|
||||||
|
@ -5925,7 +5931,7 @@ class InviteOnlyStreamTest(ZulipTestCase):
|
||||||
extra_post_data={"principals": orjson.dumps([othello.id]).decode()},
|
extra_post_data={"principals": orjson.dumps([othello.id]).decode()},
|
||||||
)
|
)
|
||||||
json = self.assert_json_success(result)
|
json = self.assert_json_success(result)
|
||||||
self.assertEqual(json["subscribed"], {othello.email: [stream_name]})
|
self.assertEqual(json["subscribed"], {str(othello.id): [stream_name]})
|
||||||
self.assertEqual(json["already_subscribed"], {})
|
self.assertEqual(json["already_subscribed"], {})
|
||||||
|
|
||||||
# Make sure both users are subscribed to this stream
|
# Make sure both users are subscribed to this stream
|
||||||
|
|
|
@ -664,9 +664,7 @@ def add_subscriptions_backend(
|
||||||
realm, streams, subscribers, acting_user=user_profile, color_map=color_map
|
realm, streams, subscribers, acting_user=user_profile, color_map=color_map
|
||||||
)
|
)
|
||||||
|
|
||||||
# We can assume unique emails here for now, but we should eventually
|
id_to_user_profile: dict[str, UserProfile] = {}
|
||||||
# convert this function to be more id-centric.
|
|
||||||
email_to_user_profile: dict[str, UserProfile] = {}
|
|
||||||
|
|
||||||
result: dict[str, Any] = dict(
|
result: dict[str, Any] = dict(
|
||||||
subscribed=defaultdict(list), already_subscribed=defaultdict(list)
|
subscribed=defaultdict(list), already_subscribed=defaultdict(list)
|
||||||
|
@ -674,12 +672,14 @@ def add_subscriptions_backend(
|
||||||
for sub_info in subscribed:
|
for sub_info in subscribed:
|
||||||
subscriber = sub_info.user
|
subscriber = sub_info.user
|
||||||
stream = sub_info.stream
|
stream = sub_info.stream
|
||||||
result["subscribed"][subscriber.email].append(stream.name)
|
user_id = str(subscriber.id)
|
||||||
email_to_user_profile[subscriber.email] = subscriber
|
result["subscribed"][user_id].append(stream.name)
|
||||||
|
id_to_user_profile[user_id] = subscriber
|
||||||
for sub_info in already_subscribed:
|
for sub_info in already_subscribed:
|
||||||
subscriber = sub_info.user
|
subscriber = sub_info.user
|
||||||
stream = sub_info.stream
|
stream = sub_info.stream
|
||||||
result["already_subscribed"][subscriber.email].append(stream.name)
|
user_id = str(subscriber.id)
|
||||||
|
result["already_subscribed"][user_id].append(stream.name)
|
||||||
|
|
||||||
result["subscribed"] = dict(result["subscribed"])
|
result["subscribed"] = dict(result["subscribed"])
|
||||||
result["already_subscribed"] = dict(result["already_subscribed"])
|
result["already_subscribed"] = dict(result["already_subscribed"])
|
||||||
|
@ -688,7 +688,7 @@ def add_subscriptions_backend(
|
||||||
user_profile=user_profile,
|
user_profile=user_profile,
|
||||||
subscribers=subscribers,
|
subscribers=subscribers,
|
||||||
new_subscriptions=result["subscribed"],
|
new_subscriptions=result["subscribed"],
|
||||||
email_to_user_profile=email_to_user_profile,
|
id_to_user_profile=id_to_user_profile,
|
||||||
created_streams=created_streams,
|
created_streams=created_streams,
|
||||||
announce=announce,
|
announce=announce,
|
||||||
)
|
)
|
||||||
|
@ -704,7 +704,7 @@ def send_messages_for_new_subscribers(
|
||||||
user_profile: UserProfile,
|
user_profile: UserProfile,
|
||||||
subscribers: set[UserProfile],
|
subscribers: set[UserProfile],
|
||||||
new_subscriptions: dict[str, list[str]],
|
new_subscriptions: dict[str, list[str]],
|
||||||
email_to_user_profile: dict[str, UserProfile],
|
id_to_user_profile: dict[str, UserProfile],
|
||||||
created_streams: list[Stream],
|
created_streams: list[Stream],
|
||||||
announce: bool,
|
announce: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -716,7 +716,7 @@ def send_messages_for_new_subscribers(
|
||||||
excessive query counts by mocking this function so that it
|
excessive query counts by mocking this function so that it
|
||||||
doesn't drown out query counts from other code.
|
doesn't drown out query counts from other code.
|
||||||
"""
|
"""
|
||||||
bots = {subscriber.email: subscriber.is_bot for subscriber in subscribers}
|
bots = {str(subscriber.id): subscriber.is_bot for subscriber in subscribers}
|
||||||
|
|
||||||
newly_created_stream_names = {s.name for s in created_streams}
|
newly_created_stream_names = {s.name for s in created_streams}
|
||||||
|
|
||||||
|
@ -727,11 +727,11 @@ def send_messages_for_new_subscribers(
|
||||||
# or if a new stream was created with the "announce" option.
|
# or if a new stream was created with the "announce" option.
|
||||||
notifications = []
|
notifications = []
|
||||||
if new_subscriptions:
|
if new_subscriptions:
|
||||||
for email, subscribed_stream_names in new_subscriptions.items():
|
for id, subscribed_stream_names in new_subscriptions.items():
|
||||||
if email == user_profile.email:
|
if id == str(user_profile.id):
|
||||||
# Don't send a Zulip if you invited yourself.
|
# Don't send a Zulip if you invited yourself.
|
||||||
continue
|
continue
|
||||||
if bots[email]:
|
if bots[id]:
|
||||||
# Don't send invitation Zulips to bots
|
# Don't send invitation Zulips to bots
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -742,7 +742,7 @@ def send_messages_for_new_subscribers(
|
||||||
if not notify_stream_names:
|
if not notify_stream_names:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
recipient_user = email_to_user_profile[email]
|
recipient_user = id_to_user_profile[id]
|
||||||
sender = get_system_bot(settings.NOTIFICATION_BOT, recipient_user.realm_id)
|
sender = get_system_bot(settings.NOTIFICATION_BOT, recipient_user.realm_id)
|
||||||
|
|
||||||
msg = you_were_just_subscribed_message(
|
msg = you_were_just_subscribed_message(
|
||||||
|
|
Loading…
Reference in New Issue