actions: Move part into zerver.lib.streams.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2022-04-14 14:42:50 -07:00
parent 6168c0110a
commit a29f1b39da
12 changed files with 148 additions and 150 deletions

View File

@ -379,7 +379,6 @@ python_rules = RuleList(
# This one in check_message is kinda terrible, since it's
# how most instances are written, but better to exclude something than nothing
("zerver/lib/actions.py", "stream = get_stream(stream_name, realm)"),
("zerver/lib/actions.py", 'return get_stream("signups", realm)'),
},
"description": "Please use access_stream_by_*() to fetch Stream objects",
},

View File

@ -26,7 +26,7 @@ import orjson
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import IntegrityError, connection, transaction
from django.db.models import Exists, F, OuterRef, Q
from django.db.models import F
from django.db.models.query import QuerySet
from django.utils.html import escape
from django.utils.timezone import now as timezone_now
@ -125,7 +125,6 @@ from zerver.lib.stream_subscription import (
get_active_subscriptions_for_stream_id,
get_bulk_stream_subscriber_info,
get_stream_subscriptions_for_user,
get_subscribed_stream_ids_for_user,
get_subscriptions_for_send_message,
get_used_colors_for_user_ids,
num_subscribers_for_stream_id,
@ -138,10 +137,11 @@ from zerver.lib.streams import (
access_stream_for_send_message,
can_access_stream_user_ids,
check_stream_access_based_on_stream_post_policy,
create_stream_if_needed,
ensure_stream,
get_default_value_for_history_public_to_subscribers,
get_occupied_streams,
get_signups_stream,
get_stream_permission_policy_name,
get_web_public_streams_queryset,
render_stream_description,
send_stream_creation_event,
subscribed_to_stream,
@ -279,11 +279,6 @@ def subscriber_info(user_id: int) -> Dict[str, Any]:
return {"id": user_id, "flags": ["read"]}
def get_signups_stream(realm: Realm) -> Stream:
# This one-liner helps us work around a lint rule.
return get_stream("signups", realm)
def send_message_to_signup_notification_stream(
sender: UserProfile, realm: Realm, message: str, topic_name: str = _("signups")
) -> None:
@ -2442,23 +2437,6 @@ def do_remove_reaction(
notify_reaction_update(user_profile, message, reaction, "remove")
def ensure_stream(
realm: Realm,
stream_name: str,
invite_only: bool = False,
stream_description: str = "",
*,
acting_user: Optional[UserProfile],
) -> Stream:
return create_stream_if_needed(
realm,
stream_name,
invite_only=invite_only,
stream_description=stream_description,
acting_user=acting_user,
)[0]
def already_sent_mirrored_message_id(message: Message) -> Optional[int]:
if message.recipient.type == Recipient.HUDDLE:
# For huddle messages, we use a 10-second window because the
@ -7015,107 +6993,6 @@ def do_remove_realm_domain(
transaction.on_commit(lambda: send_event(realm, event, active_user_ids(realm.id)))
def get_occupied_streams(realm: Realm) -> QuerySet:
# TODO: Make a generic stub for QuerySet
"""Get streams with subscribers"""
exists_expression = Exists(
Subscription.objects.filter(
active=True,
is_user_active=True,
user_profile__realm=realm,
recipient_id=OuterRef("recipient_id"),
),
)
occupied_streams = (
Stream.objects.filter(realm=realm, deactivated=False)
.annotate(occupied=exists_expression)
.filter(occupied=True)
)
return occupied_streams
def get_web_public_streams(realm: Realm) -> List[Dict[str, Any]]: # nocoverage
query = get_web_public_streams_queryset(realm)
streams = Stream.get_client_data(query)
return streams
def do_get_streams(
user_profile: UserProfile,
include_public: bool = True,
include_web_public: bool = False,
include_subscribed: bool = True,
include_all_active: bool = False,
include_default: bool = False,
include_owner_subscribed: bool = False,
) -> List[Dict[str, Any]]:
# This function is only used by API clients now.
if include_all_active and not user_profile.is_realm_admin:
raise JsonableError(_("User not authorized for this query"))
include_public = include_public and user_profile.can_access_public_streams()
# Start out with all active streams in the realm.
query = Stream.objects.filter(realm=user_profile.realm, deactivated=False)
if include_all_active:
streams = Stream.get_client_data(query)
else:
# We construct a query as the or (|) of the various sources
# this user requested streams from.
query_filter: Optional[Q] = None
def add_filter_option(option: Q) -> None:
nonlocal query_filter
if query_filter is None:
query_filter = option
else:
query_filter |= option
if include_subscribed:
subscribed_stream_ids = get_subscribed_stream_ids_for_user(user_profile)
recipient_check = Q(id__in=set(subscribed_stream_ids))
add_filter_option(recipient_check)
if include_public:
invite_only_check = Q(invite_only=False)
add_filter_option(invite_only_check)
if include_web_public:
# This should match get_web_public_streams_queryset
web_public_check = Q(
is_web_public=True,
invite_only=False,
history_public_to_subscribers=True,
deactivated=False,
)
add_filter_option(web_public_check)
if include_owner_subscribed and user_profile.is_bot:
bot_owner = user_profile.bot_owner
assert bot_owner is not None
owner_stream_ids = get_subscribed_stream_ids_for_user(bot_owner)
owner_subscribed_check = Q(id__in=set(owner_stream_ids))
add_filter_option(owner_subscribed_check)
if query_filter is not None:
query = query.filter(query_filter)
streams = Stream.get_client_data(query)
else:
# Don't bother going to the database with no valid sources
streams = []
streams.sort(key=lambda elt: elt["name"])
if include_default:
is_default = {}
default_streams = get_default_streams_for_realm(user_profile.realm_id)
for default_stream in default_streams:
is_default[default_stream.id] = True
for stream in streams:
stream["is_default"] = is_default.get(stream["stream_id"], False)
return streams
def notify_attachment_update(
user_profile: UserProfile, op: str, attachment_dict: Dict[str, Any]
) -> None:

View File

@ -13,12 +13,7 @@ from zerver.actions.default_streams import (
get_default_streams_for_realm,
streams_to_dicts_sorted,
)
from zerver.lib.actions import (
do_get_streams,
gather_subscriptions_helper,
get_owned_bot_dicts,
get_web_public_streams,
)
from zerver.lib.actions import gather_subscriptions_helper, get_owned_bot_dicts
from zerver.lib.alert_words import user_alert_words
from zerver.lib.avatar import avatar_url
from zerver.lib.bot_config import load_bot_config_template
@ -46,6 +41,7 @@ from zerver.lib.realm_logo import get_realm_logo_source, get_realm_logo_url
from zerver.lib.soft_deactivation import reactivate_user_if_soft_deactivated
from zerver.lib.sounds import get_available_notification_sounds
from zerver.lib.stream_subscription import handle_stream_notifications_compatibility
from zerver.lib.streams import do_get_streams, get_web_public_streams
from zerver.lib.subscription_info import get_web_public_subs
from zerver.lib.timestamp import datetime_to_timestamp
from zerver.lib.timezone import canonicalize_timezone

View File

@ -1,18 +1,23 @@
from typing import Collection, List, Optional, Set, Tuple, Union
from typing import Any, Collection, Dict, List, Optional, Set, Tuple, Union
from django.db import transaction
from django.db.models import Exists, OuterRef, Q
from django.db.models.query import QuerySet
from django.utils.timezone import now as timezone_now
from django.utils.translation import gettext as _
from typing_extensions import TypedDict
from zerver.actions.default_streams import get_default_streams_for_realm
from zerver.lib.exceptions import (
JsonableError,
OrganizationOwnerRequired,
StreamAdministratorRequired,
)
from zerver.lib.markdown import markdown_convert
from zerver.lib.stream_subscription import get_active_subscriptions_for_stream_id
from zerver.lib.stream_subscription import (
get_active_subscriptions_for_stream_id,
get_subscribed_stream_ids_for_user,
)
from zerver.lib.string_validation import check_stream_name
from zerver.models import (
DefaultStreamGroup,
@ -738,3 +743,126 @@ def get_stream_by_narrow_operand_access_unchecked(operand: Union[str, int], real
if isinstance(operand, str):
return get_stream(operand, realm)
return get_stream_by_id_in_realm(operand, realm)
def get_signups_stream(realm: Realm) -> Stream:
# This one-liner helps us work around a lint rule.
return get_stream("signups", realm)
def ensure_stream(
realm: Realm,
stream_name: str,
invite_only: bool = False,
stream_description: str = "",
*,
acting_user: Optional[UserProfile],
) -> Stream:
return create_stream_if_needed(
realm,
stream_name,
invite_only=invite_only,
stream_description=stream_description,
acting_user=acting_user,
)[0]
def get_occupied_streams(realm: Realm) -> QuerySet:
# TODO: Make a generic stub for QuerySet
"""Get streams with subscribers"""
exists_expression = Exists(
Subscription.objects.filter(
active=True,
is_user_active=True,
user_profile__realm=realm,
recipient_id=OuterRef("recipient_id"),
),
)
occupied_streams = (
Stream.objects.filter(realm=realm, deactivated=False)
.annotate(occupied=exists_expression)
.filter(occupied=True)
)
return occupied_streams
def get_web_public_streams(realm: Realm) -> List[Dict[str, Any]]: # nocoverage
query = get_web_public_streams_queryset(realm)
streams = Stream.get_client_data(query)
return streams
def do_get_streams(
user_profile: UserProfile,
include_public: bool = True,
include_web_public: bool = False,
include_subscribed: bool = True,
include_all_active: bool = False,
include_default: bool = False,
include_owner_subscribed: bool = False,
) -> List[Dict[str, Any]]:
# This function is only used by API clients now.
if include_all_active and not user_profile.is_realm_admin:
raise JsonableError(_("User not authorized for this query"))
include_public = include_public and user_profile.can_access_public_streams()
# Start out with all active streams in the realm.
query = Stream.objects.filter(realm=user_profile.realm, deactivated=False)
if include_all_active:
streams = Stream.get_client_data(query)
else:
# We construct a query as the or (|) of the various sources
# this user requested streams from.
query_filter: Optional[Q] = None
def add_filter_option(option: Q) -> None:
nonlocal query_filter
if query_filter is None:
query_filter = option
else:
query_filter |= option
if include_subscribed:
subscribed_stream_ids = get_subscribed_stream_ids_for_user(user_profile)
recipient_check = Q(id__in=set(subscribed_stream_ids))
add_filter_option(recipient_check)
if include_public:
invite_only_check = Q(invite_only=False)
add_filter_option(invite_only_check)
if include_web_public:
# This should match get_web_public_streams_queryset
web_public_check = Q(
is_web_public=True,
invite_only=False,
history_public_to_subscribers=True,
deactivated=False,
)
add_filter_option(web_public_check)
if include_owner_subscribed and user_profile.is_bot:
bot_owner = user_profile.bot_owner
assert bot_owner is not None
owner_stream_ids = get_subscribed_stream_ids_for_user(bot_owner)
owner_subscribed_check = Q(id__in=set(owner_stream_ids))
add_filter_option(owner_subscribed_check)
if query_filter is not None:
query = query.filter(query_filter)
streams = Stream.get_client_data(query)
else:
# Don't bother going to the database with no valid sources
streams = []
streams.sort(key=lambda elt: elt["name"])
if include_default:
is_default = {}
default_streams = get_default_streams_for_realm(user_profile.realm_id)
for default_stream in default_streams:
is_default[default_stream.id] = True
for stream in streams:
stream["is_default"] = is_default.get(stream["stream_id"], False)
return streams

View File

@ -2,8 +2,9 @@ from typing import Any
from django.core.management.base import CommandParser
from zerver.lib.actions import bulk_add_subscriptions, ensure_stream
from zerver.lib.actions import bulk_add_subscriptions
from zerver.lib.management import ZulipBaseCommand
from zerver.lib.streams import ensure_stream
class Command(ZulipBaseCommand):

View File

@ -1,8 +1,8 @@
from argparse import ArgumentParser
from typing import Any
from zerver.lib.actions import ensure_stream
from zerver.lib.management import ZulipBaseCommand
from zerver.lib.streams import ensure_stream
from zerver.models import DefaultStreamGroup

View File

@ -51,7 +51,6 @@ from zerver.lib.actions import (
do_reactivate_realm,
do_reactivate_user,
do_set_realm_property,
ensure_stream,
)
from zerver.lib.avatar import avatar_url
from zerver.lib.avatar_hash import user_avatar_path
@ -66,6 +65,7 @@ from zerver.lib.initial_password import initial_password
from zerver.lib.mobile_auth_otp import otp_decrypt_api_key
from zerver.lib.rate_limiter import add_ratelimit_rule, remove_ratelimit_rule
from zerver.lib.storage import static_path
from zerver.lib.streams import ensure_stream
from zerver.lib.test_classes import ZulipTestCase
from zerver.lib.test_helpers import (
create_s3_buckets,

View File

@ -11,12 +11,7 @@ import orjson
from django.conf import settings
from django.http import HttpResponse
from zerver.lib.actions import (
do_change_stream_post_policy,
do_deactivate_realm,
do_deactivate_user,
ensure_stream,
)
from zerver.lib.actions import do_change_stream_post_policy, do_deactivate_realm, do_deactivate_user
from zerver.lib.email_mirror import (
create_missed_message_address,
filter_footer,
@ -37,6 +32,7 @@ from zerver.lib.email_mirror_helpers import (
)
from zerver.lib.email_notifications import convert_html_to_markdown
from zerver.lib.send_email import FromAddress
from zerver.lib.streams import ensure_stream
from zerver.lib.test_classes import ZulipTestCase
from zerver.lib.test_helpers import mock_queue_publish, most_recent_message, most_recent_usermessage
from zerver.models import (

View File

@ -34,9 +34,7 @@ from zerver.lib.actions import (
do_create_realm,
do_deactivate_stream,
do_deactivate_user,
do_get_streams,
do_set_realm_property,
ensure_stream,
gather_subscriptions,
gather_subscriptions_helper,
get_topic_messages,
@ -63,6 +61,8 @@ from zerver.lib.streams import (
can_access_stream_user_ids,
create_stream_if_needed,
create_streams_if_needed,
do_get_streams,
ensure_stream,
filter_stream_authorization,
list_to_streams,
)

View File

@ -6,7 +6,8 @@ import orjson
from django.utils.timezone import now as timezone_now
from zerver.actions.user_groups import promote_new_full_members
from zerver.lib.actions import do_set_realm_property, ensure_stream
from zerver.lib.actions import do_set_realm_property
from zerver.lib.streams import ensure_stream
from zerver.lib.test_classes import ZulipTestCase
from zerver.lib.test_helpers import most_recent_usermessage
from zerver.lib.user_groups import (

View File

@ -38,7 +38,6 @@ from zerver.lib.actions import (
do_change_subscription_property,
do_deactivate_stream,
do_delete_messages,
do_get_streams,
do_rename_stream,
do_send_messages,
gather_subscriptions,
@ -64,6 +63,7 @@ from zerver.lib.streams import (
access_stream_for_delete_or_update,
access_web_public_stream,
check_stream_name_available,
do_get_streams,
filter_stream_authorization,
get_stream_permission_policy_name,
list_to_streams,

View File

@ -8,10 +8,10 @@ from zerver.lib.actions import (
do_change_avatar_fields,
do_create_user,
do_send_messages,
ensure_stream,
internal_prep_stream_message,
)
from zerver.lib.emoji import emoji_name_to_emoji_code
from zerver.lib.streams import ensure_stream
from zerver.lib.upload import upload_avatar_image
from zerver.models import Message, UserProfile, get_realm