Add /mark_all_as_read endpoint.

This change simplifies how we mark all messages as read.  It also
speeds up the backend by taking advantage of our partial index
for unread messages.  We also use a new statsd indicator.
This commit is contained in:
Steve Howell 2017-08-04 14:26:38 -04:00 committed by Tim Abbott
parent eafd5a731a
commit 541156792e
5 changed files with 46 additions and 33 deletions

View File

@ -10,12 +10,8 @@ exports.mark_all_as_read = function mark_all_as_read(cont) {
unread_ui.update_unread_counts(); unread_ui.update_unread_counts();
channel.post({ channel.post({
url: '/json/messages/flags', url: '/json/mark_all_as_read',
idempotent: true, idempotent: true,
data: {messages: JSON.stringify([]),
all: true,
op: 'add',
flag: 'read'},
success: cont}); success: cont});
}; };

View File

@ -2574,6 +2574,32 @@ def do_update_pointer(user_profile, pointer, update_flags=False):
event = dict(type='pointer', pointer=pointer) event = dict(type='pointer', pointer=pointer)
send_event(event, [user_profile.id]) send_event(event, [user_profile.id])
def do_mark_all_as_read(user_profile):
# type: (UserProfile) -> int
log_statsd_event('bankruptcy')
msgs = UserMessage.objects.filter(
user_profile=user_profile
).extra(
where=[UserMessage.where_unread()]
)
count = msgs.update(
flags=F('flags').bitor(UserMessage.flags.read)
)
event = dict(
type='update_message_flags',
operation='add',
flag='read',
messages=[], # we don't send messages, since the client reloads anyway
all=True
)
send_event(event, [user_profile.id])
statsd.incr("mark_all_as_read", count)
return count
def do_update_message_flags(user_profile, operation, flag, messages, all, stream_obj, topic_name): def do_update_message_flags(user_profile, operation, flag, messages, all, stream_obj, topic_name):
# type: (UserProfile, Text, Text, Optional[Sequence[int]], bool, Optional[Stream], Optional[Text]) -> int # type: (UserProfile, Text, Text, Optional[Sequence[int]], bool, Optional[Stream], Optional[Text]) -> int
flagattr = getattr(UserMessage.flags, flag) flagattr = getattr(UserMessage.flags, flag)

View File

@ -88,11 +88,9 @@ class PointerTest(ZulipTestCase):
self.login(self.example_email("hamlet")) self.login(self.example_email("hamlet"))
# Ensure the pointer is not set (-1) # Ensure the pointer is not set (-1)
self.assertEqual(self.example_user('hamlet').pointer, -1) self.assertEqual(self.example_user('hamlet').pointer, -1)
# Mark all existing messages as read # Mark all existing messages as read
result = self.client_post("/json/messages/flags", {"messages": ujson.dumps([]), result = self.client_post("/json/mark_all_as_read")
"op": "add",
"flag": "read",
"all": ujson.dumps(True)})
self.assert_json_success(result) self.assert_json_success(result)
# Send a new message (this will be unread) # Send a new message (this will be unread)
@ -211,29 +209,6 @@ class UnreadCountTests(ZulipTestCase):
elif msg['id'] == self.unread_msg_ids[1]: elif msg['id'] == self.unread_msg_ids[1]:
self.assertEqual(msg['flags'], []) self.assertEqual(msg['flags'], [])
def test_update_all_flags(self):
# type: () -> None
self.login(self.example_email("hamlet"))
message_ids = [self.send_message(self.example_email("hamlet"), self.example_email("iago"),
Recipient.PERSONAL, "test"),
self.send_message(self.example_email("hamlet"), self.example_email("cordelia"),
Recipient.PERSONAL, "test2")]
result = self.client_post("/json/messages/flags", {"messages": ujson.dumps(message_ids),
"op": "add",
"flag": "read"})
self.assert_json_success(result)
result = self.client_post("/json/messages/flags", {"messages": ujson.dumps([]),
"op": "remove",
"flag": "read",
"all": ujson.dumps(True)})
self.assert_json_success(result)
for msg in self.get_messages():
self.assertEqual(msg['flags'], [])
def test_mark_all_in_stream_read(self): def test_mark_all_in_stream_read(self):
# type: () -> None # type: () -> None
self.login(self.example_email("hamlet")) self.login(self.example_email("hamlet"))

View File

@ -19,7 +19,8 @@ from zerver.lib import bugdown
from zerver.lib.actions import recipient_for_emails, do_update_message_flags, \ from zerver.lib.actions import recipient_for_emails, do_update_message_flags, \
compute_mit_user_fullname, compute_irc_user_fullname, compute_jabber_user_fullname, \ compute_mit_user_fullname, compute_irc_user_fullname, compute_jabber_user_fullname, \
create_mirror_user_if_needed, check_send_message, do_update_message, \ create_mirror_user_if_needed, check_send_message, do_update_message, \
extract_recipients, truncate_body, render_incoming_message, do_delete_message extract_recipients, truncate_body, render_incoming_message, do_delete_message, \
do_mark_all_as_read
from zerver.lib.queue import queue_json_publish from zerver.lib.queue import queue_json_publish
from zerver.lib.cache import ( from zerver.lib.cache import (
generic_bulk_cached_fetch, generic_bulk_cached_fetch,
@ -828,6 +829,17 @@ def update_message_flags(request, user_profile,
'messages': messages, 'messages': messages,
'msg': ''}) 'msg': ''})
@has_request_variables
def mark_all_as_read(request, user_profile):
# type: (HttpRequest, UserProfile) -> HttpResponse
count = do_mark_all_as_read(user_profile)
log_data_str = "[%s updated]" % (count,)
request._log_data["extra"] = log_data_str
return json_success({'result': 'success',
'msg': ''})
def create_mirrored_message_users(request, user_profile, recipients): def create_mirrored_message_users(request, user_profile, recipients):
# type: (HttpRequest, UserProfile, Iterable[Text]) -> Tuple[bool, Optional[UserProfile]] # type: (HttpRequest, UserProfile, Iterable[Text]) -> Tuple[bool, Optional[UserProfile]]
if "sender" not in request.POST: if "sender" not in request.POST:

View File

@ -250,6 +250,10 @@ v1_api_and_json_patterns = [
url(r'^invites$', rest_dispatch, url(r'^invites$', rest_dispatch,
{'POST': 'zerver.views.invite.invite_users_backend'}), {'POST': 'zerver.views.invite.invite_users_backend'}),
# mark messages as read (in bulk)
url(r'^mark_all_as_read$', rest_dispatch,
{'POST': 'zerver.views.messages.mark_all_as_read'}),
# messages -> zerver.views.messages # messages -> zerver.views.messages
# GET returns messages, possibly filtered, POST sends a message # GET returns messages, possibly filtered, POST sends a message
url(r'^messages$', rest_dispatch, url(r'^messages$', rest_dispatch,