api: Add limit of total messages available per request of 5000.

Tweaked by tabbott to use a declared constant rather than just use
5000 in multiple places; this also means we can change the count
without updating translations.

Fixes #10446.
This commit is contained in:
Callum Fraser 2018-09-09 13:54:52 +01:00 committed by Tim Abbott
parent 7956c57448
commit 383f1633e6
3 changed files with 21 additions and 3 deletions

View File

@ -14,8 +14,7 @@ this endpoint to fetch the messages matching any search query that is
supported by Zulip's powerful full-text search backend. supported by Zulip's powerful full-text search backend.
When a narrow is not specified, it can be used to fetch a user's When a narrow is not specified, it can be used to fetch a user's
entire message history (though we recommend paginating to 1000 message history (We recommend paginating to 1000 messages at a time).
messages at a time).
In either case, you specify an `anchor` message (or ask the server to In either case, you specify an `anchor` message (or ask the server to
calculate the first unread message for you and use that as the calculate the first unread message for you and use that as the
@ -26,7 +25,9 @@ whether there are more messages matching the query that were not
returned due to the `num_before` and `num_after` limits. returned due to the `num_before` and `num_after` limits.
We recommend using `num_before <= 1000` and `num_after <= 1000` to We recommend using `num_before <= 1000` and `num_after <= 1000` to
avoid generating very large HTTP responses. avoid generating very large HTTP responses. A maximum of 5000 messages
can be obtained per request; attempting to exceed this will result in an
error.
## Usage examples ## Usage examples

View File

@ -1705,6 +1705,19 @@ class GetOldMessagesTest(ZulipTestCase):
self.assert_json_error(result, self.assert_json_error(result,
"Missing '%s' argument" % (required_args[i][0],)) "Missing '%s' argument" % (required_args[i][0],))
def test_get_messages_limits(self) -> None:
"""
A call to GET /json/messages requesting more than
MAX_MESSAGES_PER_FETCH messages returns an error message.
"""
self.login(self.example_email("hamlet"))
result = self.client_get("/json/messages", dict(anchor=1, num_before=3000, num_after=3000))
self.assert_json_error(result, "Too many messages requested (maximum 5000).")
result = self.client_get("/json/messages", dict(anchor=1, num_before=6000, num_after=0))
self.assert_json_error(result, "Too many messages requested (maximum 5000).")
result = self.client_get("/json/messages", dict(anchor=1, num_before=0, num_after=6000))
self.assert_json_error(result, "Too many messages requested (maximum 5000).")
def test_bad_int_params(self) -> None: def test_bad_int_params(self) -> None:
""" """
num_before, num_after, and narrow must all be non-negative num_before, num_after, and narrow must all be non-negative

View File

@ -52,6 +52,7 @@ import ujson
import datetime import datetime
LARGER_THAN_MAX_MESSAGE_ID = 10000000000000000 LARGER_THAN_MAX_MESSAGE_ID = 10000000000000000
MAX_MESSAGES_PER_FETCH = 5000
class BadNarrowOperator(JsonableError): class BadNarrowOperator(JsonableError):
code = ErrorCode.BAD_NARROW code = ErrorCode.BAD_NARROW
@ -694,6 +695,9 @@ def get_messages_backend(request: HttpRequest, user_profile: UserProfile,
apply_markdown: bool=REQ(validator=check_bool, default=True)) -> HttpResponse: apply_markdown: bool=REQ(validator=check_bool, default=True)) -> HttpResponse:
if anchor is None and not use_first_unread_anchor: if anchor is None and not use_first_unread_anchor:
return json_error(_("Missing 'anchor' argument (or set 'use_first_unread_anchor'=True).")) return json_error(_("Missing 'anchor' argument (or set 'use_first_unread_anchor'=True)."))
if num_before + num_after > MAX_MESSAGES_PER_FETCH:
return json_error(_("Too many messages requested (maximum %s)."
% (MAX_MESSAGES_PER_FETCH,)))
include_history = ok_to_include_history(narrow, user_profile) include_history = ok_to_include_history(narrow, user_profile)
if include_history: if include_history: