diff --git a/inbox/api/filtering.py b/inbox/api/filtering.py index 9e5899566..e15dd8d84 100644 --- a/inbox/api/filtering.py +++ b/inbox/api/filtering.py @@ -237,6 +237,7 @@ def messages_or_drafts( # type: ignore[no-untyped-def] # noqa: ANN201 in_, unread, starred, + order_by, limit, offset, view, @@ -476,7 +477,15 @@ def messages_or_drafts( # type: ignore[no-untyped-def] # noqa: ANN201 res = query.params(**param_dict).one()[0] return {"count": res} - query = query.order_by(desc(Message.received_date)) + if not order_by: + query = query.order_by(desc(Message.received_date)) + elif order_by == "received_date": + query = query.order_by(asc(Message.received_date)) + elif order_by == "-received_date": + query = query.order_by(desc(Message.received_date)) + else: + raise ValueError(f"Unknown 'order_by' value: '{order_by}'") + query = query.limit(bindparam("limit")) if offset: query = query.offset(bindparam("offset")) diff --git a/inbox/api/ns_api.py b/inbox/api/ns_api.py index f1bc8f7de..690d9a74f 100644 --- a/inbox/api/ns_api.py +++ b/inbox/api/ns_api.py @@ -554,6 +554,7 @@ def message_query_api(): # type: ignore[no-untyped-def] # noqa: ANN201 g.parser.add_argument("thread_id", type=valid_public_id, location="args") g.parser.add_argument("unread", type=strict_bool, location="args") g.parser.add_argument("starred", type=strict_bool, location="args") + g.parser.add_argument("order_by", type=bounded_str, location="args") g.parser.add_argument("view", type=view, location="args") args = strict_parse_args(g.parser, request.args) @@ -578,6 +579,7 @@ def message_query_api(): # type: ignore[no-untyped-def] # noqa: ANN201 in_=args["in"], unread=args["unread"], starred=args["starred"], + order_by=args["order_by"], limit=args["limit"], offset=args["offset"], view=args["view"], @@ -1739,6 +1741,7 @@ def draft_query_api(): # type: ignore[no-untyped-def] # noqa: ANN201 g.parser.add_argument("thread_id", type=valid_public_id, location="args") g.parser.add_argument("unread", type=strict_bool, location="args") g.parser.add_argument("starred", type=strict_bool, location="args") + g.parser.add_argument("order_by", type=bounded_str, location="args") g.parser.add_argument("view", type=view, location="args") args = strict_parse_args(g.parser, request.args) @@ -1763,6 +1766,7 @@ def draft_query_api(): # type: ignore[no-untyped-def] # noqa: ANN201 in_=args["in"], unread=args["unread"], starred=args["starred"], + order_by=args["order_by"], limit=args["limit"], offset=args["offset"], view=args["view"], diff --git a/tests/api/test_filtering.py b/tests/api/test_filtering.py index 21aaa2f46..cd0a4ed44 100644 --- a/tests/api/test_filtering.py +++ b/tests/api/test_filtering.py @@ -2,9 +2,7 @@ import datetime import json -from sqlalchemy import desc - -from inbox.models import Block, Category, Message, Namespace, Thread +from inbox.models import Block, Category, Namespace, Thread from inbox.util.misc import dt_to_timestamp from tests.util.base import add_fake_message, add_fake_thread, test_client @@ -256,27 +254,46 @@ def test_query_target(db, api_client, thread, default_namespace): def test_ordering(api_client, db, default_namespace): + thr = add_fake_thread(db.session, default_namespace.id) + messages = [] for i in range(3): - thr = add_fake_thread(db.session, default_namespace.id) received_date = datetime.datetime.utcnow() + datetime.timedelta( seconds=22 * (i + 1) ) - add_fake_message( - db.session, default_namespace.id, thr, received_date=received_date + messages.append( + add_fake_message( + db.session, + default_namespace.id, + thr, + received_date=received_date, + ) ) + ordered_results = api_client.get_data("/messages") + ordered_ids = [result["id"] for result in ordered_results] ordered_dates = [result["date"] for result in ordered_results] + assert ordered_ids == list(reversed([m.public_id for m in messages])) assert ordered_dates == sorted(ordered_dates, reverse=True) - ordered_results = api_client.get_data("/messages?limit=3") - expected_public_ids = [ - public_id - for public_id, in db.session.query(Message.public_id) - .filter(Message.namespace_id == default_namespace.id) - .order_by(desc(Message.received_date)) - .limit(3) - ] - assert expected_public_ids == [r["id"] for r in ordered_results] + ordered_results = api_client.get_data("/messages?order_by=received_date") + ordered_ids = [result["id"] for result in ordered_results] + ordered_dates = [result["date"] for result in ordered_results] + assert ordered_ids == [m.public_id for m in messages] + assert ordered_dates == sorted(ordered_dates) + + ordered_results = api_client.get_data("/messages?order_by=-received_date") + ordered_ids = [result["id"] for result in ordered_results] + ordered_dates = [result["date"] for result in ordered_results] + assert ordered_ids == list(reversed([m.public_id for m in messages])) + assert ordered_dates == sorted(ordered_dates, reverse=True) + + response = api_client.get_data("/messages?order_by=nonsense") + assert response == { + "message": ( + "An internal error occured. If this issue persists, please contact" + " support@nylas.com and include this request_uid: None" + ) + } def test_strict_argument_parsing(api_client):