mirror of https://github.com/zulip/zulip.git
search: Add server support for has:reaction search operator.
Web app support will be a follow-up commit.
This commit is contained in:
parent
436dab0e01
commit
c3408b56f0
|
@ -20,6 +20,13 @@ format used by the Zulip server that they are interacting with.
|
||||||
|
|
||||||
## Changes in Zulip 9.0
|
## Changes in Zulip 9.0
|
||||||
|
|
||||||
|
**Feature level 249**
|
||||||
|
|
||||||
|
* [`GET /messages`](/api/get-messages), [`GET
|
||||||
|
/messages/matches_narrow`](/api/check-messages-match-narrow): Added
|
||||||
|
new `has:reaction` search operator, matching messages with at least
|
||||||
|
one emoji reaction.
|
||||||
|
|
||||||
**Feature level 248**
|
**Feature level 248**
|
||||||
|
|
||||||
* [`POST /typing`](/api/set-typing-status), [`POST /messages`](/api/send-message),
|
* [`POST /typing`](/api/set-typing-status), [`POST /messages`](/api/send-message),
|
||||||
|
|
|
@ -51,7 +51,11 @@ important optimization when fetching messages in certain cases (e.g.
|
||||||
when [adding the `read` flag to a user's personal
|
when [adding the `read` flag to a user's personal
|
||||||
messages](/api/update-message-flags-for-narrow)).
|
messages](/api/update-message-flags-for-narrow)).
|
||||||
|
|
||||||
**Changes**: In Zulip 7.0 (feature level 177), support was added
|
**Changes**: In Zulip 9.0 (feature level 249), narrows gained support
|
||||||
|
for a new filter `has:reaction`. This allows clients to retrieve only
|
||||||
|
messages that have at least one reaction.
|
||||||
|
|
||||||
|
In Zulip 7.0 (feature level 177), support was added
|
||||||
for three filters related to direct messages: `is:dm`, `dm` and
|
for three filters related to direct messages: `is:dm`, `dm` and
|
||||||
`dm-including`. The `dm` operator replaced and deprecated the
|
`dm-including`. The `dm` operator replaced and deprecated the
|
||||||
`pm-with` operator. The `is:dm` filter replaced and deprecated
|
`pm-with` operator. The `is:dm` filter replaced and deprecated
|
||||||
|
|
|
@ -33,7 +33,7 @@ DESKTOP_WARNING_VERSION = "5.9.3"
|
||||||
# Changes should be accompanied by documentation explaining what the
|
# Changes should be accompanied by documentation explaining what the
|
||||||
# new level means in api_docs/changelog.md, as well as "**Changes**"
|
# new level means in api_docs/changelog.md, as well as "**Changes**"
|
||||||
# entries in the endpoint's documentation in `zulip.yaml`.
|
# entries in the endpoint's documentation in `zulip.yaml`.
|
||||||
API_FEATURE_LEVEL = 248
|
API_FEATURE_LEVEL = 249
|
||||||
|
|
||||||
# Bump the minor PROVISION_VERSION to indicate that folks should provision
|
# 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
|
# only when going from an old version of the code to a newer version. Bump
|
||||||
|
|
|
@ -333,8 +333,24 @@ class NarrowBuilder:
|
||||||
return method(query, operand, maybe_negate)
|
return method(query, operand, maybe_negate)
|
||||||
|
|
||||||
def by_has(self, query: Select, operand: str, maybe_negate: ConditionTransform) -> Select:
|
def by_has(self, query: Select, operand: str, maybe_negate: ConditionTransform) -> Select:
|
||||||
if operand not in ["attachment", "image", "link"]:
|
if operand not in ["attachment", "image", "link", "reaction"]:
|
||||||
raise BadNarrowOperatorError("unknown 'has' operand " + operand)
|
raise BadNarrowOperatorError("unknown 'has' operand " + operand)
|
||||||
|
|
||||||
|
if operand == "reaction":
|
||||||
|
if self.msg_id_column.name == "message_id":
|
||||||
|
# If the initial query uses `zerver_usermessage`
|
||||||
|
check_col = literal_column("zerver_usermessage.message_id", Integer)
|
||||||
|
else:
|
||||||
|
# If the initial query doesn't use `zerver_usermessage`
|
||||||
|
check_col = literal_column("zerver_message.id", Integer)
|
||||||
|
exists_cond = (
|
||||||
|
select([1])
|
||||||
|
.select_from(table("zerver_reaction"))
|
||||||
|
.where(check_col == literal_column("zerver_reaction.message_id", Integer))
|
||||||
|
.exists()
|
||||||
|
)
|
||||||
|
return query.where(maybe_negate(exists_cond))
|
||||||
|
|
||||||
col_name = "has_" + operand
|
col_name = "has_" + operand
|
||||||
cond = column(col_name, Boolean)
|
cond = column(col_name, Boolean)
|
||||||
return query.where(maybe_negate(cond))
|
return query.where(maybe_negate(cond))
|
||||||
|
|
|
@ -6189,7 +6189,10 @@ paths:
|
||||||
subscribed to appropriate streams or use a shared history
|
subscribed to appropriate streams or use a shared history
|
||||||
search narrow with this endpoint.
|
search narrow with this endpoint.
|
||||||
|
|
||||||
**Changes**: In Zulip 7.0 (feature level 177), narrows gained support
|
**Changes**: In Zulip 9.0 (feature level 249), added new `has:reaction`
|
||||||
|
filter, matching messages with at least one emoji reaction.
|
||||||
|
|
||||||
|
In Zulip 7.0 (feature level 177), narrows gained support
|
||||||
for three new filters related to direct messages: `is:dm`, `dm` and
|
for three new filters related to direct messages: `is:dm`, `dm` and
|
||||||
`dm-including`; replacing and deprecating `is:private`, `pm-with` and
|
`dm-including`; replacing and deprecating `is:private`, `pm-with` and
|
||||||
`group-pm-with` respectively.
|
`group-pm-with` respectively.
|
||||||
|
@ -7059,7 +7062,10 @@ paths:
|
||||||
optimization. Including that filter takes advantage of the fact that
|
optimization. Including that filter takes advantage of the fact that
|
||||||
the server has a database index for unread messages.
|
the server has a database index for unread messages.
|
||||||
|
|
||||||
**Changes**: In Zulip 7.0 (feature level 177), narrows gained support
|
**Changes**: In Zulip 9.0 (feature level 249), added new `has:reaction`
|
||||||
|
filter, matching messages with at least one emoji reaction.
|
||||||
|
|
||||||
|
In Zulip 7.0 (feature level 177), narrows gained support
|
||||||
for three new filters related to direct messages: `is:dm`, `dm` and
|
for three new filters related to direct messages: `is:dm`, `dm` and
|
||||||
`dm-including`; replacing and deprecating `is:private`, `pm-with` and
|
`dm-including`; replacing and deprecating `is:private`, `pm-with` and
|
||||||
`group-pm-with` respectively.
|
`group-pm-with` respectively.
|
||||||
|
@ -7483,7 +7489,10 @@ paths:
|
||||||
A structure defining the narrow to check against. See how to
|
A structure defining the narrow to check against. See how to
|
||||||
[construct a narrow](/api/construct-narrow).
|
[construct a narrow](/api/construct-narrow).
|
||||||
|
|
||||||
**Changes**: In Zulip 7.0 (feature level 177), narrows gained support
|
**Changes**: In Zulip 9.0 (feature level 249), added new `has:reaction`
|
||||||
|
filter, matching messages with at least one emoji reaction.
|
||||||
|
|
||||||
|
In Zulip 7.0 (feature level 177), narrows gained support
|
||||||
for three new filters related to direct messages: `is:dm`, `dm` and
|
for three new filters related to direct messages: `is:dm`, `dm` and
|
||||||
`dm-including`; replacing and deprecating `is:private`, `pm-with` and
|
`dm-including`; replacing and deprecating `is:private`, `pm-with` and
|
||||||
`group-pm-with` respectively.
|
`group-pm-with` respectively.
|
||||||
|
@ -21361,7 +21370,10 @@ components:
|
||||||
|
|
||||||
Defaults to `[]`.
|
Defaults to `[]`.
|
||||||
|
|
||||||
**Changes**: In Zulip 7.0 (feature level 177), narrows gained support
|
**Changes**: In Zulip 9.0 (feature level 249), added new `has:reaction`
|
||||||
|
filter, matching messages with at least one emoji reaction.
|
||||||
|
|
||||||
|
In Zulip 7.0 (feature level 177), narrows gained support
|
||||||
for three new filters related to direct messages: `is:dm`, `dm` and
|
for three new filters related to direct messages: `is:dm`, `dm` and
|
||||||
`dm-including`; replacing and deprecating `is:private`, `pm-with` and
|
`dm-including`; replacing and deprecating `is:private`, `pm-with` and
|
||||||
`group-pm-with` respectively.
|
`group-pm-with` respectively.
|
||||||
|
|
|
@ -13,6 +13,7 @@ from typing_extensions import override
|
||||||
from analytics.lib.counts import COUNT_STATS
|
from analytics.lib.counts import COUNT_STATS
|
||||||
from analytics.models import RealmCount
|
from analytics.models import RealmCount
|
||||||
from zerver.actions.message_edit import do_update_message
|
from zerver.actions.message_edit import do_update_message
|
||||||
|
from zerver.actions.reactions import check_add_reaction
|
||||||
from zerver.actions.realm_settings import do_set_realm_property
|
from zerver.actions.realm_settings import do_set_realm_property
|
||||||
from zerver.actions.uploads import do_claim_attachments
|
from zerver.actions.uploads import do_claim_attachments
|
||||||
from zerver.actions.user_settings import do_change_user_setting
|
from zerver.actions.user_settings import do_change_user_setting
|
||||||
|
@ -491,6 +492,20 @@ class NarrowBuilderTest(ZulipTestCase):
|
||||||
term = dict(operator="has", operand="link", negated=True)
|
term = dict(operator="has", operand="link", negated=True)
|
||||||
self._do_add_term_test(term, "WHERE NOT has_link")
|
self._do_add_term_test(term, "WHERE NOT has_link")
|
||||||
|
|
||||||
|
def test_add_term_using_has_operator_and_reaction_operand(self) -> None:
|
||||||
|
term = dict(operator="has", operand="reaction")
|
||||||
|
self._do_add_term_test(
|
||||||
|
term,
|
||||||
|
"EXISTS (SELECT 1 \nFROM zerver_reaction \nWHERE zerver_message.id = zerver_reaction.message_id)",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_add_term_using_has_operator_and_reaction_operand_and_negated(self) -> None:
|
||||||
|
term = dict(operator="has", operand="reaction", negated=True)
|
||||||
|
self._do_add_term_test(
|
||||||
|
term,
|
||||||
|
"NOT (EXISTS (SELECT 1 \nFROM zerver_reaction \nWHERE zerver_message.id = zerver_reaction.message_id))",
|
||||||
|
)
|
||||||
|
|
||||||
def test_add_term_using_has_operator_non_supported_operand_should_raise_error(self) -> None:
|
def test_add_term_using_has_operator_non_supported_operand_should_raise_error(self) -> None:
|
||||||
term = dict(operator="has", operand="non_supported")
|
term = dict(operator="has", operand="non_supported")
|
||||||
self.assertRaises(BadNarrowOperatorError, self._build_query, term)
|
self.assertRaises(BadNarrowOperatorError, self._build_query, term)
|
||||||
|
@ -4422,6 +4437,44 @@ class MessageHasKeywordsTest(ZulipTestCase):
|
||||||
self.assertFalse(m.called)
|
self.assertFalse(m.called)
|
||||||
m.reset_mock()
|
m.reset_mock()
|
||||||
|
|
||||||
|
def test_has_reaction(self) -> None:
|
||||||
|
self.login("iago")
|
||||||
|
has_reaction_narrow = orjson.dumps([dict(operator="has", operand="reaction")]).decode()
|
||||||
|
|
||||||
|
msg_id = self.send_stream_message(self.example_user("hamlet"), "Denmark", content="Hey")
|
||||||
|
result = self.client_get(
|
||||||
|
"/json/messages",
|
||||||
|
dict(narrow=has_reaction_narrow, anchor=msg_id, num_before=0, num_after=0),
|
||||||
|
)
|
||||||
|
messages = self.assert_json_success(result)["messages"]
|
||||||
|
self.assert_length(messages, 0)
|
||||||
|
check_add_reaction(
|
||||||
|
self.example_user("hamlet"), msg_id, "hamburger", "1f354", "unicode_emoji"
|
||||||
|
)
|
||||||
|
result = self.client_get(
|
||||||
|
"/json/messages",
|
||||||
|
dict(narrow=has_reaction_narrow, anchor=msg_id, num_before=0, num_after=0),
|
||||||
|
)
|
||||||
|
messages = self.assert_json_success(result)["messages"]
|
||||||
|
self.assert_length(messages, 1)
|
||||||
|
|
||||||
|
msg_id = self.send_personal_message(
|
||||||
|
self.example_user("iago"), self.example_user("cordelia"), "Hello Cordelia"
|
||||||
|
)
|
||||||
|
result = self.client_get(
|
||||||
|
"/json/messages",
|
||||||
|
dict(narrow=has_reaction_narrow, anchor=msg_id, num_before=0, num_after=0),
|
||||||
|
)
|
||||||
|
messages = self.assert_json_success(result)["messages"]
|
||||||
|
self.assert_length(messages, 0)
|
||||||
|
check_add_reaction(self.example_user("iago"), msg_id, "hamburger", "1f354", "unicode_emoji")
|
||||||
|
result = self.client_get(
|
||||||
|
"/json/messages",
|
||||||
|
dict(narrow=has_reaction_narrow, anchor=msg_id, num_before=0, num_after=0),
|
||||||
|
)
|
||||||
|
messages = self.assert_json_success(result)["messages"]
|
||||||
|
self.assert_length(messages, 1)
|
||||||
|
|
||||||
|
|
||||||
class MessageVisibilityTest(ZulipTestCase):
|
class MessageVisibilityTest(ZulipTestCase):
|
||||||
def test_update_first_visible_message_id(self) -> None:
|
def test_update_first_visible_message_id(self) -> None:
|
||||||
|
|
Loading…
Reference in New Issue