diff --git a/zerver/lib/message.py b/zerver/lib/message.py index f76bb4e80e..6a57445c46 100644 --- a/zerver/lib/message.py +++ b/zerver/lib/message.py @@ -9,7 +9,12 @@ from six import binary_type from zerver.lib.avatar import get_avatar_field import zerver.lib.bugdown as bugdown -from zerver.lib.cache import cache_with_key, to_dict_cache_key +from zerver.lib.cache import ( + cache_with_key, + generic_bulk_cached_fetch, + to_dict_cache_key, + to_dict_cache_key_id, +) from zerver.lib.request import JsonableError from zerver.lib.str_utils import force_bytes, dict_with_str_keys from zerver.lib.stream_subscription import ( @@ -50,6 +55,42 @@ UnreadMessagesResult = TypedDict('UnreadMessagesResult', { MAX_UNREAD_MESSAGES = 5000 +def messages_for_ids(message_ids: List[int], + user_message_flags: Dict[int, List[str]], + search_fields: Dict[int, Dict[str, Text]], + apply_markdown: bool, + client_gravatar: bool, + allow_edit_history: bool) -> List[Dict[str, Any]]: + + cache_transformer = MessageDict.build_dict_from_raw_db_row + id_fetcher = lambda row: row['id'] + + message_dicts = generic_bulk_cached_fetch(to_dict_cache_key_id, + MessageDict.get_raw_db_rows, + message_ids, + id_fetcher=id_fetcher, + cache_transformer=cache_transformer, + extractor=extract_message_dict, + setter=stringify_message_dict) + + message_list = [] # type: List[Dict[str, Any]] + + for message_id in message_ids: + msg_dict = message_dicts[message_id] + msg_dict.update({"flags": user_message_flags[message_id]}) + if message_id in search_fields: + msg_dict.update(search_fields[message_id]) + # Make sure that we never send message edit history to clients + # in realms with allow_edit_history disabled. + if "edit_history" in msg_dict and not allow_edit_history: + del msg_dict["edit_history"] + message_list.append(msg_dict) + + MessageDict.post_process_dicts(message_list, apply_markdown, client_gravatar) + + return message_list + + def sew_messages_and_reactions(messages, reactions): # type: (List[Dict[str, Any]], List[Dict[str, Any]]) -> List[Dict[str, Any]] """Given a iterable of messages and reactions stitch reactions diff --git a/zerver/tests/test_messages.py b/zerver/tests/test_messages.py index 705cf4a421..d761a16222 100644 --- a/zerver/tests/test_messages.py +++ b/zerver/tests/test_messages.py @@ -21,6 +21,7 @@ from zerver.lib.actions import ( from zerver.lib.message import ( MessageDict, + messages_for_ids, sew_messages_and_reactions, ) @@ -2795,3 +2796,46 @@ class MessageHydrationTest(ZulipTestCase): ], ) self.assertEqual(obj['type'], 'private') + + def test_messages_for_ids(self): + # type: () -> None + hamlet = self.example_user('hamlet') + cordelia = self.example_user('cordelia') + + stream_name = 'test stream' + self.subscribe(cordelia, stream_name) + + old_message_id = self.send_stream_message(cordelia.email, stream_name, content='foo') + + self.subscribe(hamlet, stream_name) + + content = 'hello @**King Hamlet**' + new_message_id = self.send_stream_message(cordelia.email, stream_name, content=content) + + user_message_flags = { + old_message_id: ['read', 'historical'], + new_message_id: ['mentioned'], + } + + messages = messages_for_ids( + message_ids=[old_message_id, new_message_id], + user_message_flags=user_message_flags, + search_fields={}, + apply_markdown=True, + client_gravatar=True, + allow_edit_history=False, + ) + + self.assertEqual(len(messages), 2) + + for message in messages: + if message['id'] == old_message_id: + old_message = message + elif message['id'] == new_message_id: + new_message = message + + self.assertEqual(old_message['content'], '

foo

') + self.assertEqual(old_message['flags'], ['read', 'historical']) + + self.assertIn('class="user-mention"', new_message['content']) + self.assertEqual(new_message['flags'], ['mentioned']) diff --git a/zerver/views/messages.py b/zerver/views/messages.py index ac6db54d6c..0f87b836ca 100644 --- a/zerver/views/messages.py +++ b/zerver/views/messages.py @@ -21,16 +21,10 @@ from zerver.lib.actions import recipient_for_emails, do_update_message_flags, \ extract_recipients, truncate_body, render_incoming_message, do_delete_message, \ do_mark_all_as_read, do_mark_stream_messages_as_read, get_user_info_for_message_updates from zerver.lib.queue import queue_json_publish -from zerver.lib.cache import ( - generic_bulk_cached_fetch, - to_dict_cache_key_id, -) from zerver.lib.message import ( access_message, - MessageDict, - extract_message_dict, + messages_for_ids, render_markdown, - stringify_message_dict, ) from zerver.lib.response import json_success, json_error from zerver.lib.sqlalchemy_utils import get_sqlalchemy_connection @@ -756,31 +750,14 @@ def get_messages_backend(request, user_profile, search_fields[message_id] = get_search_fields(rendered_content, subject, content_matches, subject_matches) - cache_transformer = MessageDict.build_dict_from_raw_db_row - id_fetcher = lambda row: row['id'] - - message_dicts = generic_bulk_cached_fetch(to_dict_cache_key_id, - MessageDict.get_raw_db_rows, - message_ids, - id_fetcher=id_fetcher, - cache_transformer=cache_transformer, - extractor=extract_message_dict, - setter=stringify_message_dict) - - message_list = [] - - for message_id in message_ids: - msg_dict = message_dicts[message_id] - msg_dict.update({"flags": user_message_flags[message_id]}) - if message_id in search_fields: - msg_dict.update(search_fields[message_id]) - # Make sure that we never send message edit history to clients - # in realms with allow_edit_history disabled. - if "edit_history" in msg_dict and not user_profile.realm.allow_edit_history: - del msg_dict["edit_history"] - message_list.append(msg_dict) - - MessageDict.post_process_dicts(message_list, apply_markdown, client_gravatar) + message_list = messages_for_ids( + message_ids=message_ids, + user_message_flags=user_message_flags, + search_fields=search_fields, + apply_markdown=apply_markdown, + client_gravatar=client_gravatar, + allow_edit_history=user_profile.realm.allow_edit_history, + ) statsd.incr('loaded_old_messages', len(message_list)) ret = {'messages': message_list,