diff --git a/zerver/tests/test_reactions.py b/zerver/tests/test_reactions.py index 6463f7ad7b..2d1b8dff32 100644 --- a/zerver/tests/test_reactions.py +++ b/zerver/tests/test_reactions.py @@ -494,6 +494,12 @@ class DefaultEmojiReactionTests(EmojiReactionBase): result = self.delete_reaction(reaction_info) self.assert_json_success(result) + def test_delete_insufficient_arguments_reaction(self) -> None: + result = self.delete_reaction({}) + self.assert_json_error(result, 'At least one of the following ' + 'arguments must be present: emoji_name, ' + 'emoji_code') + def test_delete_non_existing_emoji_reaction(self) -> None: reaction_info = { 'emoji_name': 'thumbs_up', @@ -519,6 +525,29 @@ class DefaultEmojiReactionTests(EmojiReactionBase): result = self.delete_reaction(reaction_info) self.assert_json_success(result) + def test_delete_reaction_by_name(self) -> None: + hamlet = self.example_user('hamlet') + message = Message.objects.get(id=1) + Reaction.objects.create(user_profile=hamlet, + message=message, + emoji_name='+1', + emoji_code='1f44d', + reaction_type='unicode_emoji', + ) + + reaction_info = { + 'emoji_name': '+1' + } + result = self.delete_reaction(reaction_info) + self.assert_json_success(result) + self.assertFalse( + Reaction.objects.filter(user_profile=hamlet, + message=message, + emoji_name=reaction_info['emoji_name'], + emoji_code='1f44d', + reaction_type='unicode_emoji').exists() + ) + def test_react_historical(self) -> None: """ Reacting with valid emoji on a historical message succeeds. diff --git a/zerver/views/reactions.py b/zerver/views/reactions.py index 934020bdbe..59c0f74500 100644 --- a/zerver/views/reactions.py +++ b/zerver/views/reactions.py @@ -83,10 +83,24 @@ def add_reaction(request: HttpRequest, user_profile: UserProfile, message_id: in @has_request_variables def remove_reaction(request: HttpRequest, user_profile: UserProfile, message_id: int, - emoji_code: str=REQ(), + emoji_name: Optional[str]=REQ(default=None), + emoji_code: Optional[str]=REQ(default=None), reaction_type: str=REQ(default="unicode_emoji")) -> HttpResponse: message, user_message = access_message(user_profile, message_id) + if emoji_code is None: + if emoji_name is None: + raise JsonableError(_('At least one of the following arguments ' + 'must be present: emoji_name, emoji_code')) + # A correct full Zulip client implementation should always + # pass an emoji_code, because of the corner cases discussed in + # the long block comments elsewhere in this file. However, to + # make it easy for simple API clients to use the reactions API + # without needing the mapping between emoji names and codes, + # we allow instead passing the emoji_name and looking up the + # corresponding code using the current data. + emoji_code = emoji_name_to_emoji_code(message.sender.realm, emoji_name)[0] + if not Reaction.objects.filter(user_profile=user_profile, message=message, emoji_code=emoji_code,