request: Extract out methods from 'scheduled_messages' to reuse.

This is a prep commit that extracts the following two methods
from '/actions/scheduled_messages' to reuse in the next commit.
* extract_stream_id
* extract_direct_message_recipient_ids

The 'to' parameter for 'POST /typing' will follow the same pattern
in the next commit as we currently have for the 'to' parameter in
'POST /scheduled_messages', so we can reuse these functions.
This commit is contained in:
Prakhar Pratyush
2023-10-07 13:52:37 +05:30
committed by Tim Abbott
parent 39ec5b9f66
commit 8b12cc606a
5 changed files with 83 additions and 72 deletions

View File

@@ -2,7 +2,6 @@ import datetime
import logging import logging
from typing import List, Optional, Sequence, Tuple from typing import List, Optional, Sequence, Tuple
import orjson
from django.conf import settings from django.conf import settings
from django.db import transaction from django.db import transaction
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
@@ -18,6 +17,7 @@ from zerver.actions.uploads import check_attachment_reference_change, do_claim_a
from zerver.lib.addressee import Addressee from zerver.lib.addressee import Addressee
from zerver.lib.exceptions import JsonableError, RealmDeactivatedError, UserDeactivatedError from zerver.lib.exceptions import JsonableError, RealmDeactivatedError, UserDeactivatedError
from zerver.lib.message import SendMessageRequest, render_markdown, truncate_topic from zerver.lib.message import SendMessageRequest, render_markdown, truncate_topic
from zerver.lib.recipient_parsing import extract_direct_message_recipient_ids, extract_stream_id
from zerver.lib.scheduled_messages import access_scheduled_message from zerver.lib.scheduled_messages import access_scheduled_message
from zerver.lib.string_validation import check_stream_topic from zerver.lib.string_validation import check_stream_topic
from zerver.models import ( from zerver.models import (
@@ -34,31 +34,6 @@ from zerver.tornado.django_api import send_event
SCHEDULED_MESSAGE_LATE_CUTOFF_MINUTES = 10 SCHEDULED_MESSAGE_LATE_CUTOFF_MINUTES = 10
def extract_stream_id(req_to: str) -> List[int]:
# Recipient should only be a single stream ID.
try:
stream_id = int(req_to)
except ValueError:
raise JsonableError(_("Invalid data type for stream ID"))
return [stream_id]
def extract_direct_message_recipient_ids(req_to: str) -> List[int]:
try:
user_ids = orjson.loads(req_to)
except orjson.JSONDecodeError:
user_ids = req_to
if not isinstance(user_ids, list):
raise JsonableError(_("Invalid data type for recipients"))
for user_id in user_ids:
if not isinstance(user_id, int):
raise JsonableError(_("Recipient list may only contain user IDs"))
return list(set(user_ids))
def check_schedule_message( def check_schedule_message(
sender: UserProfile, sender: UserProfile,
client: Client, client: Client,
@@ -182,7 +157,8 @@ def edit_scheduled_message(
# Update message recipient if changed. # Update message recipient if changed.
if message_to is not None: if message_to is not None:
if updated_recipient_type_name == "stream": if updated_recipient_type_name == "stream":
updated_recipient = extract_stream_id(message_to) stream_id = extract_stream_id(message_to)
updated_recipient = [stream_id]
else: else:
updated_recipient = extract_direct_message_recipient_ids(message_to) updated_recipient = extract_direct_message_recipient_ids(message_to)
else: else:

View File

@@ -0,0 +1,31 @@
from typing import List
import orjson
from django.utils.translation import gettext as _
from zerver.lib.exceptions import JsonableError
def extract_stream_id(req_to: str) -> int:
# Recipient should only be a single stream ID.
try:
stream_id = int(req_to)
except ValueError:
raise JsonableError(_("Invalid data type for stream ID"))
return stream_id
def extract_direct_message_recipient_ids(req_to: str) -> List[int]:
try:
user_ids = orjson.loads(req_to)
except orjson.JSONDecodeError:
user_ids = req_to
if not isinstance(user_ids, list):
raise JsonableError(_("Invalid data type for recipients"))
for user_id in user_ids:
if not isinstance(user_id, int):
raise JsonableError(_("Recipient list may only contain user IDs"))
return list(set(user_ids))

View File

@@ -0,0 +1,46 @@
import orjson
from zerver.lib.exceptions import JsonableError
from zerver.lib.recipient_parsing import extract_direct_message_recipient_ids, extract_stream_id
from zerver.lib.test_classes import ZulipTestCase
class TestRecipientParsing(ZulipTestCase):
def test_extract_stream_id(self) -> None:
# stream message recipient = single stream ID.
stream_id = extract_stream_id("1")
self.assertEqual(stream_id, 1)
with self.assertRaisesRegex(JsonableError, "Invalid data type for stream ID"):
extract_stream_id("1,2")
with self.assertRaisesRegex(JsonableError, "Invalid data type for stream ID"):
extract_stream_id("[1]")
with self.assertRaisesRegex(JsonableError, "Invalid data type for stream ID"):
extract_stream_id("general")
def test_extract_recipient_ids(self) -> None:
# direct message recipients = user IDs.
user_ids = "[3,2,1]"
result = sorted(extract_direct_message_recipient_ids(user_ids))
self.assertEqual(result, [1, 2, 3])
# JSON list w/duplicates
user_ids = orjson.dumps([3, 3, 12]).decode()
result = sorted(extract_direct_message_recipient_ids(user_ids))
self.assertEqual(result, [3, 12])
# Invalid data
user_ids = "1, 12"
with self.assertRaisesRegex(JsonableError, "Invalid data type for recipients"):
extract_direct_message_recipient_ids(user_ids)
user_ids = orjson.dumps(dict(recipient=12)).decode()
with self.assertRaisesRegex(JsonableError, "Invalid data type for recipients"):
extract_direct_message_recipient_ids(user_ids)
# Heterogeneous lists are not supported
user_ids = orjson.dumps([3, 4, "eeshan@example.com"]).decode()
with self.assertRaisesRegex(JsonableError, "Recipient list may only contain user IDs"):
extract_direct_message_recipient_ids(user_ids)

View File

@@ -11,12 +11,9 @@ from django.utils.timezone import now as timezone_now
from zerver.actions.scheduled_messages import ( from zerver.actions.scheduled_messages import (
SCHEDULED_MESSAGE_LATE_CUTOFF_MINUTES, SCHEDULED_MESSAGE_LATE_CUTOFF_MINUTES,
extract_direct_message_recipient_ids,
extract_stream_id,
try_deliver_one_scheduled_message, try_deliver_one_scheduled_message,
) )
from zerver.actions.users import change_user_is_active from zerver.actions.users import change_user_is_active
from zerver.lib.exceptions import JsonableError
from zerver.lib.test_classes import ZulipTestCase from zerver.lib.test_classes import ZulipTestCase
from zerver.lib.test_helpers import most_recent_message from zerver.lib.test_helpers import most_recent_message
from zerver.lib.timestamp import timestamp_to_datetime from zerver.lib.timestamp import timestamp_to_datetime
@@ -717,42 +714,3 @@ class ScheduledMessageTest(ZulipTestCase):
[scheduled_message.id], [scheduled_message.id],
) )
self.assertEqual(scheduled_message.has_attachment, True) self.assertEqual(scheduled_message.has_attachment, True)
def test_extract_stream_id(self) -> None:
# Scheduled stream message recipient = single stream ID.
stream_id = extract_stream_id("1")
self.assertEqual(stream_id, [1])
with self.assertRaisesRegex(JsonableError, "Invalid data type for stream ID"):
extract_stream_id("1,2")
with self.assertRaisesRegex(JsonableError, "Invalid data type for stream ID"):
extract_stream_id("[1]")
with self.assertRaisesRegex(JsonableError, "Invalid data type for stream ID"):
extract_stream_id("general")
def test_extract_recipient_ids(self) -> None:
# Scheduled direct message recipients = user IDs.
user_ids = "[3,2,1]"
result = sorted(extract_direct_message_recipient_ids(user_ids))
self.assertEqual(result, [1, 2, 3])
# JSON list w/duplicates
user_ids = orjson.dumps([3, 3, 12]).decode()
result = sorted(extract_direct_message_recipient_ids(user_ids))
self.assertEqual(result, [3, 12])
# Invalid data
user_ids = "1, 12"
with self.assertRaisesRegex(JsonableError, "Invalid data type for recipients"):
extract_direct_message_recipient_ids(user_ids)
user_ids = orjson.dumps(dict(recipient=12)).decode()
with self.assertRaisesRegex(JsonableError, "Invalid data type for recipients"):
extract_direct_message_recipient_ids(user_ids)
# Heterogeneous lists are not supported
user_ids = orjson.dumps([3, 4, "eeshan@example.com"]).decode()
with self.assertRaisesRegex(JsonableError, "Recipient list may only contain user IDs"):
extract_direct_message_recipient_ids(user_ids)

View File

@@ -8,10 +8,9 @@ from zerver.actions.scheduled_messages import (
check_schedule_message, check_schedule_message,
delete_scheduled_message, delete_scheduled_message,
edit_scheduled_message, edit_scheduled_message,
extract_direct_message_recipient_ids,
extract_stream_id,
) )
from zerver.lib.exceptions import JsonableError from zerver.lib.exceptions import JsonableError
from zerver.lib.recipient_parsing import extract_direct_message_recipient_ids, extract_stream_id
from zerver.lib.request import REQ, RequestNotes, has_request_variables from zerver.lib.request import REQ, RequestNotes, has_request_variables
from zerver.lib.response import json_success from zerver.lib.response import json_success
from zerver.lib.scheduled_messages import get_undelivered_scheduled_messages from zerver.lib.scheduled_messages import get_undelivered_scheduled_messages
@@ -133,7 +132,8 @@ def create_scheduled_message_backend(
assert client is not None assert client is not None
if recipient_type_name == "stream": if recipient_type_name == "stream":
message_to = extract_stream_id(req_to) stream_id = extract_stream_id(req_to)
message_to = [stream_id]
else: else:
message_to = extract_direct_message_recipient_ids(req_to) message_to = extract_direct_message_recipient_ids(req_to)