diff --git a/api_docs/changelog.md b/api_docs/changelog.md index ed240cdf5d..70d8c01337 100644 --- a/api_docs/changelog.md +++ b/api_docs/changelog.md @@ -20,6 +20,16 @@ format used by the Zulip server that they are interacting with. ## Changes in Zulip 8.0 +**Feature level 194** + +* [`GET /messages`](/api/get-messages), + [`GET /messages/matches_narrow`](/api/check-messages-match-narrow), + [`POST /message/flags/narrow`](/api/update-message-flags-for-narrow), + [`POST /register`](/api/register-queue): + For [search/narrow filters](/api/construct-narrow) with the `id` + operator, added support for encoding the message ID operand as either + a string or an integer. Previously, only string encoding was supported. + **Feature level 193** * [`POST /messages/{message_id}/reactions`](/api/add-reaction), diff --git a/api_docs/construct-narrow.md b/api_docs/construct-narrow.md index 60a138258c..8e322e8c07 100644 --- a/api_docs/construct-narrow.md +++ b/api_docs/construct-narrow.md @@ -65,12 +65,33 @@ filters did. ## Narrows that use IDs +### Message IDs + The `near` and `id` operators, documented in the help center, use message IDs for their operands. * `near:12345`: Search messages around the message with ID `12345`. * `id:12345`: Search for only message with ID `12345`. +The message ID operand for the `id` operator may be encoded as either a +number or a string. The message ID operand for the `near` operator must +be encoded as a string. + +**Changes**: Prior to Zulip 8.0 (feature level 194), the message ID +operand for the `id` operator needed to be encoded as a string. + + +```json +[ + { + "operator": "id", + "operand": 12345 + } +] +``` + +### Stream and user IDs + There are a few additional narrow/search options (new in Zulip 2.1) that use either stream IDs or user IDs that are not documented in the help center because they are primarily useful to API clients: diff --git a/version.py b/version.py index 7b47b9c972..60bcda16d4 100644 --- a/version.py +++ b/version.py @@ -33,7 +33,7 @@ DESKTOP_WARNING_VERSION = "5.9.3" # Changes should be accompanied by documentation explaining what the # new level means in api_docs/changelog.md, as well as "**Changes**" # entries in the endpoint's documentation in `zulip.yaml`. -API_FEATURE_LEVEL = 193 +API_FEATURE_LEVEL = 194 # Bump the minor PROVISION_VERSION to indicate that folks should provision # only when going from an old version of the code to a newer version. Bump diff --git a/web/src/message_fetch.js b/web/src/message_fetch.js index f96925a4c7..21bfd0d3fe 100644 --- a/web/src/message_fetch.js +++ b/web/src/message_fetch.js @@ -140,13 +140,20 @@ function get_messages_success(data, opts) { process_result(data, opts); } -// This function modifies the data.narrow filters to use user IDs -// instead of emails string if it is supported. We currently don't set -// or convert the emails string to user IDs directly into the Filter code -// because doing so breaks the app in various modules that expect emails string. +// This function modifies the data.narrow filters to use integer IDs +// instead of strings if it is supported. We currently don't set or +// convert user emails to user IDs directly in the Filter code +// because doing so breaks the app in various modules that expect a +// string of user emails. function handle_operators_supporting_id_based_api(data) { const operators_supporting_ids = new Set(["dm", "pm-with"]); - const operators_supporting_id = new Set(["sender", "group-pm-with", "stream", "dm-including"]); + const operators_supporting_id = new Set([ + "id", + "stream", + "sender", + "group-pm-with", + "dm-including", + ]); if (data.narrow === undefined) { return data; @@ -159,6 +166,12 @@ function handle_operators_supporting_id_based_api(data) { } if (operators_supporting_id.has(filter.operator)) { + if (filter.operator === "id") { + // The message ID may not exist locally, + // so send the filter to the server as is. + return filter; + } + if (filter.operator === "stream") { const stream_id = stream_data.get_stream_id(filter.operand); if (stream_id !== undefined) { diff --git a/zerver/lib/narrow.py b/zerver/lib/narrow.py index 64acd4a2b7..c1b74e5046 100644 --- a/zerver/lib/narrow.py +++ b/zerver/lib/narrow.py @@ -751,11 +751,17 @@ def narrow_parameter(var_name: str, json: str) -> OptionalNarrowListT: return dict(operator=elem[0], operand=elem[1]) if isinstance(elem, dict): - # Make sure to sync this list to frontend also when adding a new operator. - # that supports user IDs. Relevant code is located in web/src/message_fetch.js - # in handle_operators_supporting_id_based_api function where you will need to update - # operators_supporting_id, or operators_supporting_ids array. - operators_supporting_id = ["sender", "group-pm-with", "stream", "dm-including"] + # Make sure to sync this list to frontend also when adding a new operator that + # supports integer IDs. Relevant code is located in web/src/message_fetch.js + # in handle_operators_supporting_id_based_api function where you will need to + # update operators_supporting_id, or operators_supporting_ids array. + operators_supporting_id = [ + "id", + "stream", + "sender", + "group-pm-with", + "dm-including", + ] operators_supporting_ids = ["pm-with", "dm"] operators_non_empty_operand = {"search"} diff --git a/zerver/tests/test_message_fetch.py b/zerver/tests/test_message_fetch.py index 6194e94352..23acc64e05 100644 --- a/zerver/tests/test_message_fetch.py +++ b/zerver/tests/test_message_fetch.py @@ -417,10 +417,14 @@ class NarrowBuilderTest(ZulipTestCase): "WHERE NOT (sender_id = %(sender_id_1)s AND recipient_id = %(recipient_id_1)s OR sender_id = %(sender_id_2)s AND recipient_id = %(recipient_id_2)s OR recipient_id IN (__[POSTCOMPILE_recipient_id_3]))", ) - def test_add_term_using_id_operator(self) -> None: + def test_add_term_using_id_operator_integer(self) -> None: term = dict(operator="id", operand=555) self._do_add_term_test(term, "WHERE id = %(param_1)s") + def test_add_term_using_id_operator_string(self) -> None: + term = dict(operator="id", operand="555") + self._do_add_term_test(term, "WHERE id = %(param_1)s") + def test_add_term_using_id_operator_invalid(self) -> None: term = dict(operator="id", operand="") self.assertRaises(BadNarrowOperatorError, self._build_query, term) @@ -3412,10 +3416,11 @@ class GetOldMessagesTest(ZulipTestCase): def test_invalid_narrow_operand_in_dict(self) -> None: self.login("hamlet") - # str or int is required for "sender", "stream", "dm-including" and "group-pm-with" operators + # str or int is required for "id", "sender", "stream", "dm-including" and "group-pm-with" + # operators invalid_operands = [["1"], [2], None] error_msg = 'elem["operand"] is not a string or integer' - for operand in ["sender", "group-pm-with", "stream", "dm-including"]: + for operand in ["id", "sender", "stream", "dm-including", "group-pm-with"]: self.exercise_bad_narrow_operand_using_dict_api(operand, invalid_operands, error_msg) # str or int list is required for "dm" and "pm-with" operator @@ -3432,7 +3437,7 @@ class GetOldMessagesTest(ZulipTestCase): # For others only str is acceptable invalid_operands = [2, None, [1]] error_msg = 'elem["operand"] is not a string' - for operand in ["is", "near", "has", "id"]: + for operand in ["is", "near", "has"]: self.exercise_bad_narrow_operand_using_dict_api(operand, invalid_operands, error_msg) # Disallow empty search terms