[MM-55143] Disallow reacting with an emoji that does not exist, limit the total number of unique reactions per post (#25331)

* [MM-55143] Disallow reacting with an emoji that does not exist

* WIP for server limit on emoji reactions

* WIP

* Implement default limit of 25 unique emoji reactions

* Add modal for reaction limit

* Fix test

* PR feedback

* Fix i18n

* Update admin string

* Merge'd

* Fixing some issues, check limits correctly based on other users reactions

* Fix typos

* Fix lint/test

* Add tests, fix other tests

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
Devin Binnie
2023-11-27 09:11:04 -05:00
коммит произвёл GitHub
родитель 0a38042d58
Коммит eaa5cce3ce
24 изменённых файлов: 652 добавлений и 29 удалений

Просмотреть файл

@@ -29,18 +29,20 @@ func TestReactionsOfPost(t *testing.T) {
reactionObject := model.Reaction{
UserId: th.BasicUser.Id,
PostId: post.Id,
EmojiName: "emoji",
EmojiName: "smile",
CreateAt: model.GetMillis(),
}
reactionObjectDeleted := model.Reaction{
UserId: th.BasicUser2.Id,
PostId: post.Id,
EmojiName: "emoji",
EmojiName: "smile",
CreateAt: model.GetMillis(),
}
th.App.SaveReactionForPost(th.Context, &reactionObject)
th.App.SaveReactionForPost(th.Context, &reactionObjectDeleted)
_, err := th.App.SaveReactionForPost(th.Context, &reactionObject)
require.Nil(t, err)
_, err = th.App.SaveReactionForPost(th.Context, &reactionObjectDeleted)
require.Nil(t, err)
reactionsOfPost, err := th.App.BuildPostReactions(th.Context, post.Id)
require.Nil(t, err)

Просмотреть файл

@@ -20,6 +20,30 @@ func (a *App) SaveReactionForPost(c request.CTX, reaction *model.Reaction) (*mod
return nil, err
}
// Check whether this is a valid emoji
if _, ok := model.GetSystemEmojiId(reaction.EmojiName); !ok {
if _, emojiErr := a.GetEmojiByName(c, reaction.EmojiName); emojiErr != nil {
return nil, emojiErr
}
}
existing, dErr := a.Srv().Store().Reaction().ExistsOnPost(reaction.PostId, reaction.EmojiName)
if dErr != nil {
return nil, model.NewAppError("SaveReactionForPost", "app.reaction.save.save.app_error", nil, "", http.StatusInternalServerError).Wrap(dErr)
}
// If it exists already, we don't need to check for the limit
if !existing {
count, dErr := a.Srv().Store().Reaction().GetUniqueCountForPost(reaction.PostId)
if dErr != nil {
return nil, model.NewAppError("SaveReactionForPost", "app.reaction.save.save.app_error", nil, "", http.StatusInternalServerError).Wrap(dErr)
}
if count >= *a.Config().ServiceSettings.UniqueEmojiReactionLimitPerPost {
return nil, model.NewAppError("SaveReactionForPost", "app.reaction.save.save.too_many_reactions", nil, "", http.StatusBadRequest)
}
}
channel, err := a.GetChannel(c, post.ChannelId)
if err != nil {
return nil, err

Просмотреть файл

@@ -13,6 +13,89 @@ import (
"github.com/mattermost/mattermost/server/v8/channels/testlib"
)
func TestSaveReactionForPost(t *testing.T) {
th := Setup(t).InitBasic()
post := th.CreatePost(th.BasicChannel)
reaction1, err := th.App.SaveReactionForPost(th.Context, &model.Reaction{
UserId: th.BasicUser.Id,
PostId: post.Id,
EmojiName: "cry",
})
require.NotNil(t, reaction1)
require.Nil(t, err)
reaction2, err := th.App.SaveReactionForPost(th.Context, &model.Reaction{
UserId: th.BasicUser.Id,
PostId: post.Id,
EmojiName: "smile",
})
require.NotNil(t, reaction2)
require.Nil(t, err)
reaction3, err := th.App.SaveReactionForPost(th.Context, &model.Reaction{
UserId: th.BasicUser.Id,
PostId: post.Id,
EmojiName: "rofl",
})
require.NotNil(t, reaction3)
require.Nil(t, err)
t.Run("should not add reaction if it does not exist on the system", func(t *testing.T) {
reaction := &model.Reaction{
UserId: th.BasicUser.Id,
PostId: th.BasicPost.Id,
EmojiName: "definitely-not-a-real-emoji",
}
result, err := th.App.SaveReactionForPost(th.Context, reaction)
require.NotNil(t, err)
require.Nil(t, result)
})
t.Run("should not add reaction if we are over the limit", func(t *testing.T) {
var originalLimit *int
th.UpdateConfig(func(cfg *model.Config) {
originalLimit = cfg.ServiceSettings.UniqueEmojiReactionLimitPerPost
*cfg.ServiceSettings.UniqueEmojiReactionLimitPerPost = 3
})
defer th.UpdateConfig(func(cfg *model.Config) {
cfg.ServiceSettings.UniqueEmojiReactionLimitPerPost = originalLimit
})
reaction := &model.Reaction{
UserId: th.BasicUser.Id,
PostId: post.Id,
EmojiName: "joy",
}
result, err := th.App.SaveReactionForPost(th.Context, reaction)
require.NotNil(t, err)
require.Nil(t, result)
})
t.Run("should always add reaction if we are over the limit but the reaction is not unique", func(t *testing.T) {
user := th.CreateUser()
var originalLimit *int
th.UpdateConfig(func(cfg *model.Config) {
originalLimit = cfg.ServiceSettings.UniqueEmojiReactionLimitPerPost
*cfg.ServiceSettings.UniqueEmojiReactionLimitPerPost = 3
})
defer th.UpdateConfig(func(cfg *model.Config) {
cfg.ServiceSettings.UniqueEmojiReactionLimitPerPost = originalLimit
})
reaction := &model.Reaction{
UserId: user.Id,
PostId: post.Id,
EmojiName: "cry",
}
result, err := th.App.SaveReactionForPost(th.Context, reaction)
require.Nil(t, err)
require.NotNil(t, result)
})
}
func TestSharedChannelSyncForReactionActions(t *testing.T) {
t.Run("adding a reaction in a shared channel performs a content sync when sync service is running on that node", func(t *testing.T) {
th := Setup(t).InitBasic()
@@ -84,3 +167,15 @@ func TestSharedChannelSyncForReactionActions(t *testing.T) {
assert.Equal(t, channel.Id, sharedChannelService.channelNotifications[1])
})
}
func (th *TestHelper) UpdateConfig(f func(*model.Config)) {
if th.ConfigStore.IsReadOnly() {
return
}
old := th.ConfigStore.Get()
updated := old.Clone()
f(updated)
if _, _, err := th.ConfigStore.Set(updated); err != nil {
panic(err)
}
}