events: Add option to send partial data.

Server can now send partial data to the client to help in
developement. We don't want this to be widely used right now,
hence no documentation changes have been made.

This will likely be a check on client capability later.
This commit is contained in:
Aman Agrawal
2025-05-19 12:46:38 +05:30
committed by Tim Abbott
parent 01e08f0e32
commit f4e6f2f89b
4 changed files with 47 additions and 0 deletions

View File

@@ -43,6 +43,7 @@ from zerver.models.users import (
active_user_ids,
base_bulk_get_user_queryset,
base_get_user_queryset,
get_partial_realm_user_dicts,
get_realm_user_dicts,
get_realm_user_dicts_from_ids,
get_user_by_id_in_realm_including_cross_realm,
@@ -1058,6 +1059,8 @@ def get_user_dicts_in_realm(
) -> tuple[list[RawUserDict], list[APIUserDict]]:
if user_ids is not None:
all_user_dicts = get_realm_user_dicts_from_ids(realm.id, user_ids)
elif settings.PARTIAL_USERS:
all_user_dicts = get_partial_realm_user_dicts(realm.id, user_profile)
else:
all_user_dicts = get_realm_user_dicts(realm.id)
if check_user_can_access_all_users(user_profile):

View File

@@ -1116,6 +1116,27 @@ def get_realm_user_dicts(realm_id: int) -> list[RawUserDict]:
)
def get_partial_realm_user_dicts(
realm_id: int, user_profile: UserProfile | None
) -> list[RawUserDict]:
"""Returns a subset of the users in the realm, guaranteed to
include the current user as well as all bots in the realm.
"""
# Currently, we send the minimum set of users permitted by the API.
user_selection_clause = Q(is_bot=True)
if user_profile is not None:
user_selection_clause |= Q(id=user_profile.id)
return list(
UserProfile.objects.filter(realm_id=realm_id)
.filter(
user_selection_clause,
)
.values(*realm_user_dict_fields)
)
def get_realm_user_dicts_from_ids(realm_id: int, user_ids: list[int]) -> list[RawUserDict]:
return list(
UserProfile.objects.filter(

View File

@@ -3165,6 +3165,17 @@ class GetProfileTest(ZulipTestCase):
result = self.client_get(f"/json/users/{user.id}")
self.assert_json_error(result, "Insufficient permission")
with self.settings(PARTIAL_USERS=True), self.assert_database_query_count(9):
result = self.client_get("/json/users")
self.assert_json_success(result)
result_dict = orjson.loads(result.content)
all_fetched_users = result_dict["members"]
self.assertEqual(
len(all_fetched_users),
UserProfile.objects.filter(realm=hamlet.realm, is_bot=True).count() + 1,
)
def test_get_inaccessible_user_ids(self) -> None:
polonius = self.example_user("polonius")
bot = self.example_user("default_bot")
@@ -3232,6 +3243,16 @@ class GetProfileTest(ZulipTestCase):
user_ids_to_fetch,
)
with self.settings(PARTIAL_USERS=True), self.assert_database_query_count(4):
result = self.client_get("/json/users")
self.assert_json_success(result)
result_dict = orjson.loads(result.content)
all_fetched_users = result_dict["members"]
self.assertEqual(
len(all_fetched_users),
UserProfile.objects.filter(realm=hamlet.realm, is_bot=True).count(),
)
class DeleteUserTest(ZulipTestCase):
def test_do_delete_user(self) -> None:

View File

@@ -1307,3 +1307,5 @@ SCIM_SERVICE_PROVIDER = {
# Which API key to use will be determined based on TOPIC_SUMMARIZATION_MODEL.
TOPIC_SUMMARIZATION_API_KEY = get_secret("topic_summarization_api_key", None)
PARTIAL_USERS = bool(os.environ.get("PARTIAL_USERS"))