diff --git a/tools/linter_lib/custom_check.py b/tools/linter_lib/custom_check.py index a4a6012a9a..7a6f82491f 100644 --- a/tools/linter_lib/custom_check.py +++ b/tools/linter_lib/custom_check.py @@ -13,6 +13,7 @@ from zulint.custom_rules import Rule, RuleList FILES_WITH_LEGACY_SUBJECT = { # This basically requires a big DB migration: "zerver/lib/topic.py", + "zerver/lib/types.py", # This is for backward compatibility. "zerver/tests/test_legacy_subject.py", # Other migration-related changes require extreme care. @@ -229,7 +230,7 @@ python_rules = RuleList( rules=[ { "pattern": "subject|SUBJECT", - "exclude_pattern": "subject to the|email|outbox", + "exclude_pattern": "subject to the|email|outbox|edit_history_event", "description": "avoid subject as a var", "good_lines": ["topic_name"], "bad_lines": ['subject="foo"', " MAX_SUBJECT_LEN"], diff --git a/zerver/lib/actions.py b/zerver/lib/actions.py index 17d3d9bb3b..05922f7b92 100644 --- a/zerver/lib/actions.py +++ b/zerver/lib/actions.py @@ -161,7 +161,6 @@ from zerver.lib.string_validation import check_stream_name, check_stream_topic from zerver.lib.timestamp import datetime_to_timestamp, timestamp_to_datetime from zerver.lib.timezone import canonicalize_timezone from zerver.lib.topic import ( - LEGACY_PREV_TOPIC, ORIG_TOPIC, RESOLVED_TOPIC_PREFIX, TOPIC_LINKS, @@ -174,7 +173,12 @@ from zerver.lib.topic import ( update_messages_for_topic_edit, ) from zerver.lib.topic_mutes import add_topic_mute, get_topic_mutes, remove_topic_mute -from zerver.lib.types import ProfileDataElementValue, ProfileFieldData, UnspecifiedValue +from zerver.lib.types import ( + EditHistoryEvent, + ProfileDataElementValue, + ProfileFieldData, + UnspecifiedValue, +) from zerver.lib.upload import ( claim_attachment, delete_avatar_image, @@ -6695,7 +6699,7 @@ def do_update_message( "rendering_only": False, } - edit_history_event: Dict[str, Any] = { + edit_history_event: EditHistoryEvent = { "user_id": user_profile.id, "timestamp": event["edit_timestamp"], } @@ -6869,7 +6873,7 @@ def do_update_message( event[ORIG_TOPIC] = orig_topic_name event[TOPIC_NAME] = topic_name event[TOPIC_LINKS] = topic_links(target_message.sender.realm_id, topic_name) - edit_history_event[LEGACY_PREV_TOPIC] = orig_topic_name + edit_history_event["prev_subject"] = orig_topic_name update_edit_history(target_message, timestamp, edit_history_event) @@ -6879,16 +6883,14 @@ def do_update_message( assert stream_being_edited is not None # Other messages should only get topic/stream fields in their edit history. - topic_only_edit_history_event = { - k: v - for (k, v) in edit_history_event.items() - if k - not in [ - "prev_content", - "prev_rendered_content", - "prev_rendered_content_version", - ] + topic_only_edit_history_event: EditHistoryEvent = { + "user_id": edit_history_event["user_id"], + "timestamp": edit_history_event["timestamp"], } + if topic_name is not None: + topic_only_edit_history_event["prev_subject"] = edit_history_event["prev_subject"] + if new_stream is not None: + topic_only_edit_history_event["prev_stream"] = edit_history_event["prev_stream"] messages_list = update_messages_for_topic_edit( acting_user=user_profile, diff --git a/zerver/lib/message.py b/zerver/lib/message.py index ce6c36bdd7..728f8e215a 100644 --- a/zerver/lib/message.py +++ b/zerver/lib/message.py @@ -38,7 +38,7 @@ from zerver.lib.streams import get_web_public_streams_queryset from zerver.lib.timestamp import datetime_to_timestamp from zerver.lib.topic import DB_TOPIC_NAME, MESSAGE__TOPIC, TOPIC_LINKS, TOPIC_NAME from zerver.lib.topic_mutes import build_topic_mute_checker, topic_is_muted -from zerver.lib.types import DisplayRecipientT, UserDisplayRecipient +from zerver.lib.types import DisplayRecipientT, EditHistoryEvent, UserDisplayRecipient from zerver.models import ( MAX_TOPIC_NAME_LENGTH, Message, @@ -502,7 +502,8 @@ class MessageDict: if last_edit_time is not None: obj["last_edit_timestamp"] = datetime_to_timestamp(last_edit_time) assert edit_history_json is not None - edit_history = orjson.loads(edit_history_json) + # Here we assume EditHistoryEvent == APIEditHistoryEvent + edit_history: List[EditHistoryEvent] = orjson.loads(edit_history_json) obj["edit_history"] = edit_history if Message.need_to_render_content( diff --git a/zerver/lib/topic.py b/zerver/lib/topic.py index 6dadc741c9..0f8e21053a 100644 --- a/zerver/lib/topic.py +++ b/zerver/lib/topic.py @@ -8,6 +8,7 @@ from sqlalchemy.sql import ColumnElement, column, func, literal from sqlalchemy.types import Boolean, Text from zerver.lib.request import REQ +from zerver.lib.types import EditHistoryEvent from zerver.models import Message, Stream, UserMessage, UserProfile # Only use these constants for events. @@ -135,11 +136,11 @@ def user_message_exists_for_topic( def update_edit_history( - message: Message, last_edit_time: datetime, edit_history_event: Dict[str, Any] + message: Message, last_edit_time: datetime, edit_history_event: EditHistoryEvent ) -> None: message.last_edit_time = last_edit_time if message.edit_history is not None: - edit_history = orjson.loads(message.edit_history) + edit_history: List[EditHistoryEvent] = orjson.loads(message.edit_history) edit_history.insert(0, edit_history_event) else: edit_history = [edit_history_event] @@ -154,7 +155,7 @@ def update_messages_for_topic_edit( topic_name: Optional[str], new_stream: Optional[Stream], old_stream: Stream, - edit_history_event: Dict[str, Any], + edit_history_event: EditHistoryEvent, last_edit_time: datetime, ) -> List[Message]: propagate_query = Q(recipient_id=old_stream.recipient_id, subject__iexact=orig_topic_name) diff --git a/zerver/lib/types.py b/zerver/lib/types.py index 15072fd7e9..16eae69fb7 100644 --- a/zerver/lib/types.py +++ b/zerver/lib/types.py @@ -89,3 +89,40 @@ class UnspecifiedValue: """ pass + + +class APIEditHistoryEvent(TypedDict, total=False): + """Format of legacy edit history events in the API. Contains legacy + fields like LEGACY_PREV_TOPIC that we intend to remove from the + API eventually. + """ + + # Commented fields are fields we plan to add. + user_id: Optional[int] + timestamp: int + prev_stream: int + # stream: int + prev_subject: str + # prev_topic: str + # topic: str + prev_content: str + prev_rendered_content: Optional[str] + prev_rendered_content_version: Optional[int] + + +class EditHistoryEvent(TypedDict, total=False): + """ + Database format for edit history events. + """ + + # Commented fields are fields we plan to add. + user_id: Optional[int] + timestamp: int + prev_stream: int + # stream: int + prev_subject: str + # prev_topic: str + # topic: str + prev_content: str + prev_rendered_content: Optional[str] + prev_rendered_content_version: Optional[int]