mirror of https://github.com/zulip/zulip.git
scheduled_messages: Send event on commit in edit_scheduled_message.
Earlier, we were using 'send_event' in 'edit_scheduled_messages' which can lead to a situation, if any db operation is added after the 'send_event' in future, where we enqueue events but the action function fails at a later stage. Events should not be sent until we know we're not rolling back. Fixes part of #30489.
This commit is contained in:
parent
b0390ce1ee
commit
500fb3d804
|
@ -127,6 +127,7 @@ def notify_update_scheduled_message(
|
||||||
send_event_on_commit(user_profile.realm, event, [user_profile.id])
|
send_event_on_commit(user_profile.realm, event, [user_profile.id])
|
||||||
|
|
||||||
|
|
||||||
|
@transaction.atomic(durable=True)
|
||||||
def edit_scheduled_message(
|
def edit_scheduled_message(
|
||||||
sender: UserProfile,
|
sender: UserProfile,
|
||||||
client: Client,
|
client: Client,
|
||||||
|
@ -138,110 +139,109 @@ def edit_scheduled_message(
|
||||||
deliver_at: datetime | None,
|
deliver_at: datetime | None,
|
||||||
realm: Realm,
|
realm: Realm,
|
||||||
) -> None:
|
) -> None:
|
||||||
with transaction.atomic():
|
scheduled_message_object = access_scheduled_message(sender, scheduled_message_id)
|
||||||
scheduled_message_object = access_scheduled_message(sender, scheduled_message_id)
|
|
||||||
|
|
||||||
# Handles the race between us initiating this transaction and user sending us the edit request.
|
# Handles the race between us initiating this transaction and user sending us the edit request.
|
||||||
if scheduled_message_object.delivered is True:
|
if scheduled_message_object.delivered is True:
|
||||||
raise JsonableError(_("Scheduled message was already sent"))
|
raise JsonableError(_("Scheduled message was already sent"))
|
||||||
|
|
||||||
# If the server failed to send the scheduled message, a new scheduled
|
# If the server failed to send the scheduled message, a new scheduled
|
||||||
# delivery timestamp (`deliver_at`) is required.
|
# delivery timestamp (`deliver_at`) is required.
|
||||||
if scheduled_message_object.failed and deliver_at is None:
|
if scheduled_message_object.failed and deliver_at is None:
|
||||||
raise JsonableError(_("Scheduled delivery time must be in the future."))
|
raise JsonableError(_("Scheduled delivery time must be in the future."))
|
||||||
|
|
||||||
# Get existing scheduled message's recipient IDs and recipient_type_name.
|
# Get existing scheduled message's recipient IDs and recipient_type_name.
|
||||||
existing_recipient, existing_recipient_type_name = get_recipient_ids(
|
existing_recipient, existing_recipient_type_name = get_recipient_ids(
|
||||||
scheduled_message_object.recipient, sender.id
|
scheduled_message_object.recipient, sender.id
|
||||||
|
)
|
||||||
|
|
||||||
|
# If any recipient information or message content has been updated,
|
||||||
|
# we check the message again.
|
||||||
|
if recipient_type_name is not None or message_to is not None or message_content is not None:
|
||||||
|
# Update message type if changed.
|
||||||
|
if recipient_type_name is not None:
|
||||||
|
updated_recipient_type_name = recipient_type_name
|
||||||
|
else:
|
||||||
|
updated_recipient_type_name = existing_recipient_type_name
|
||||||
|
|
||||||
|
# Update message recipient if changed.
|
||||||
|
if message_to is not None:
|
||||||
|
if updated_recipient_type_name == "stream":
|
||||||
|
stream_id = extract_stream_id(message_to)
|
||||||
|
updated_recipient = [stream_id]
|
||||||
|
else:
|
||||||
|
updated_recipient = extract_direct_message_recipient_ids(message_to)
|
||||||
|
else:
|
||||||
|
updated_recipient = existing_recipient
|
||||||
|
|
||||||
|
# Update topic name if changed.
|
||||||
|
if topic_name is not None:
|
||||||
|
updated_topic_name = topic_name
|
||||||
|
else:
|
||||||
|
# This will be ignored in Addressee.legacy_build if type
|
||||||
|
# is being changed from stream to direct.
|
||||||
|
updated_topic_name = scheduled_message_object.topic_name()
|
||||||
|
|
||||||
|
# Update message content if changed.
|
||||||
|
if message_content is not None:
|
||||||
|
updated_content = message_content
|
||||||
|
else:
|
||||||
|
updated_content = scheduled_message_object.content
|
||||||
|
|
||||||
|
# Check message again.
|
||||||
|
addressee = Addressee.legacy_build(
|
||||||
|
sender, updated_recipient_type_name, updated_recipient, updated_topic_name
|
||||||
|
)
|
||||||
|
send_request = check_message(
|
||||||
|
sender,
|
||||||
|
client,
|
||||||
|
addressee,
|
||||||
|
updated_content,
|
||||||
|
realm=realm,
|
||||||
|
forwarder_user_profile=sender,
|
||||||
)
|
)
|
||||||
|
|
||||||
# If any recipient information or message content has been updated,
|
if recipient_type_name is not None or message_to is not None:
|
||||||
# we check the message again.
|
# User has updated the scheduled message's recipient.
|
||||||
if recipient_type_name is not None or message_to is not None or message_content is not None:
|
scheduled_message_object.recipient = send_request.message.recipient
|
||||||
# Update message type if changed.
|
scheduled_message_object.stream = send_request.stream
|
||||||
if recipient_type_name is not None:
|
# Update the topic based on the new recipient information.
|
||||||
updated_recipient_type_name = recipient_type_name
|
new_topic_name = send_request.message.topic_name()
|
||||||
else:
|
scheduled_message_object.set_topic_name(topic_name=new_topic_name)
|
||||||
updated_recipient_type_name = existing_recipient_type_name
|
elif topic_name is not None and existing_recipient_type_name == "stream":
|
||||||
|
# User has updated the scheduled message's topic, but not
|
||||||
|
# the existing recipient information. We ignore topics sent
|
||||||
|
# for scheduled direct messages.
|
||||||
|
check_stream_topic(topic_name)
|
||||||
|
new_topic_name = truncate_topic(topic_name)
|
||||||
|
scheduled_message_object.set_topic_name(topic_name=new_topic_name)
|
||||||
|
|
||||||
# Update message recipient if changed.
|
if message_content is not None:
|
||||||
if message_to is not None:
|
# User has updated the scheduled messages's content.
|
||||||
if updated_recipient_type_name == "stream":
|
rendering_result = render_message_markdown(
|
||||||
stream_id = extract_stream_id(message_to)
|
send_request.message, send_request.message.content, send_request.realm
|
||||||
updated_recipient = [stream_id]
|
)
|
||||||
else:
|
scheduled_message_object.content = send_request.message.content
|
||||||
updated_recipient = extract_direct_message_recipient_ids(message_to)
|
scheduled_message_object.rendered_content = rendering_result.rendered_content
|
||||||
else:
|
scheduled_message_object.has_attachment = check_attachment_reference_change(
|
||||||
updated_recipient = existing_recipient
|
scheduled_message_object, rendering_result
|
||||||
|
)
|
||||||
|
|
||||||
# Update topic name if changed.
|
if deliver_at is not None:
|
||||||
if topic_name is not None:
|
# User has updated the scheduled message's send timestamp.
|
||||||
updated_topic_name = topic_name
|
scheduled_message_object.scheduled_timestamp = deliver_at
|
||||||
else:
|
|
||||||
# This will be ignored in Addressee.legacy_build if type
|
|
||||||
# is being changed from stream to direct.
|
|
||||||
updated_topic_name = scheduled_message_object.topic_name()
|
|
||||||
|
|
||||||
# Update message content if changed.
|
# Update for most recent Client information.
|
||||||
if message_content is not None:
|
scheduled_message_object.sending_client = client
|
||||||
updated_content = message_content
|
|
||||||
else:
|
|
||||||
updated_content = scheduled_message_object.content
|
|
||||||
|
|
||||||
# Check message again.
|
# If the user is editing a scheduled message that the server tried
|
||||||
addressee = Addressee.legacy_build(
|
# and failed to send, we need to update the `failed` boolean field
|
||||||
sender, updated_recipient_type_name, updated_recipient, updated_topic_name
|
# as well as the associated `failure_message` field.
|
||||||
)
|
if scheduled_message_object.failed:
|
||||||
send_request = check_message(
|
scheduled_message_object.failed = False
|
||||||
sender,
|
scheduled_message_object.failure_message = None
|
||||||
client,
|
|
||||||
addressee,
|
|
||||||
updated_content,
|
|
||||||
realm=realm,
|
|
||||||
forwarder_user_profile=sender,
|
|
||||||
)
|
|
||||||
|
|
||||||
if recipient_type_name is not None or message_to is not None:
|
scheduled_message_object.save()
|
||||||
# User has updated the scheduled message's recipient.
|
|
||||||
scheduled_message_object.recipient = send_request.message.recipient
|
|
||||||
scheduled_message_object.stream = send_request.stream
|
|
||||||
# Update the topic based on the new recipient information.
|
|
||||||
new_topic_name = send_request.message.topic_name()
|
|
||||||
scheduled_message_object.set_topic_name(topic_name=new_topic_name)
|
|
||||||
elif topic_name is not None and existing_recipient_type_name == "stream":
|
|
||||||
# User has updated the scheduled message's topic, but not
|
|
||||||
# the existing recipient information. We ignore topics sent
|
|
||||||
# for scheduled direct messages.
|
|
||||||
check_stream_topic(topic_name)
|
|
||||||
new_topic_name = truncate_topic(topic_name)
|
|
||||||
scheduled_message_object.set_topic_name(topic_name=new_topic_name)
|
|
||||||
|
|
||||||
if message_content is not None:
|
|
||||||
# User has updated the scheduled messages's content.
|
|
||||||
rendering_result = render_message_markdown(
|
|
||||||
send_request.message, send_request.message.content, send_request.realm
|
|
||||||
)
|
|
||||||
scheduled_message_object.content = send_request.message.content
|
|
||||||
scheduled_message_object.rendered_content = rendering_result.rendered_content
|
|
||||||
scheduled_message_object.has_attachment = check_attachment_reference_change(
|
|
||||||
scheduled_message_object, rendering_result
|
|
||||||
)
|
|
||||||
|
|
||||||
if deliver_at is not None:
|
|
||||||
# User has updated the scheduled message's send timestamp.
|
|
||||||
scheduled_message_object.scheduled_timestamp = deliver_at
|
|
||||||
|
|
||||||
# Update for most recent Client information.
|
|
||||||
scheduled_message_object.sending_client = client
|
|
||||||
|
|
||||||
# If the user is editing a scheduled message that the server tried
|
|
||||||
# and failed to send, we need to update the `failed` boolean field
|
|
||||||
# as well as the associated `failure_message` field.
|
|
||||||
if scheduled_message_object.failed:
|
|
||||||
scheduled_message_object.failed = False
|
|
||||||
scheduled_message_object.failure_message = None
|
|
||||||
|
|
||||||
scheduled_message_object.save()
|
|
||||||
|
|
||||||
notify_update_scheduled_message(sender, scheduled_message_object)
|
notify_update_scheduled_message(sender, scheduled_message_object)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue