Merge branch 'master' into MM-28014_Improve_Channel_Intros

This commit is contained in:
Matthew Birtch 2024-03-12 10:24:57 -04:00
commit ce6d6673a6
89 changed files with 877 additions and 605 deletions

View File

@ -12243,6 +12243,41 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---
## stylelint
This product contains 'stylelint' by stylelint.
A mighty CSS linter that helps you avoid errors and enforce conventions.
* HOMEPAGE:
* https://stylelint.io
* LICENSE: MIT
The MIT License (MIT)
Copyright (c) 2015 - present Maxime Thirouin, David Clark & Richard Hallows
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
---
## throttled/throttled

View File

@ -147,7 +147,7 @@ TEMPLATES_DIR=templates
# Plugins Packages
PLUGIN_PACKAGES ?= $(PLUGIN_PACKAGES:)
PLUGIN_PACKAGES += mattermost-plugin-calls-v0.24.0
PLUGIN_PACKAGES += mattermost-plugin-calls-v0.25.0
PLUGIN_PACKAGES += mattermost-plugin-github-v2.2.0
PLUGIN_PACKAGES += mattermost-plugin-gitlab-v1.8.0
PLUGIN_PACKAGES += mattermost-plugin-playbooks-v1.39.1

View File

@ -485,9 +485,9 @@ func (th *TestHelper) InitBasic() *TestHelper {
}
func (th *TestHelper) DeleteBots() *TestHelper {
preexistingBots, _ := th.App.GetBots(&model.BotGetOptions{Page: 0, PerPage: 100})
preexistingBots, _ := th.App.GetBots(th.Context, &model.BotGetOptions{Page: 0, PerPage: 100})
for _, bot := range preexistingBots {
th.App.PermanentDeleteBot(bot.UserId)
th.App.PermanentDeleteBot(th.Context, bot.UserId)
}
return th
}

View File

@ -93,7 +93,7 @@ func patchBot(c *Context, w http.ResponseWriter, r *http.Request) {
audit.AddEventParameter(auditRec, "id", botUserId)
audit.AddEventParameterAuditable(auditRec, "bot", botPatch)
if err := c.App.SessionHasPermissionToManageBot(*c.AppContext.Session(), botUserId); err != nil {
if err := c.App.SessionHasPermissionToManageBot(c.AppContext, *c.AppContext.Session(), botUserId); err != nil {
c.Err = err
return
}
@ -122,7 +122,7 @@ func getBot(c *Context, w http.ResponseWriter, r *http.Request) {
includeDeleted, _ := strconv.ParseBool(r.URL.Query().Get("include_deleted"))
bot, appErr := c.App.GetBot(botUserId, includeDeleted)
bot, appErr := c.App.GetBot(c.AppContext, botUserId, includeDeleted)
if appErr != nil {
c.Err = appErr
return
@ -170,7 +170,7 @@ func getBots(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
bots, appErr := c.App.GetBots(&model.BotGetOptions{
bots, appErr := c.App.GetBots(c.AppContext, &model.BotGetOptions{
Page: c.Params.Page,
PerPage: c.Params.PerPage,
OwnerId: OwnerId,
@ -211,7 +211,7 @@ func updateBotActive(c *Context, w http.ResponseWriter, active bool) {
audit.AddEventParameter(auditRec, "id", botUserId)
audit.AddEventParameter(auditRec, "enable", active)
if err := c.App.SessionHasPermissionToManageBot(*c.AppContext.Session(), botUserId); err != nil {
if err := c.App.SessionHasPermissionToManageBot(c.AppContext, *c.AppContext.Session(), botUserId); err != nil {
c.Err = err
return
}
@ -245,7 +245,7 @@ func assignBot(c *Context, w http.ResponseWriter, _ *http.Request) {
audit.AddEventParameter(auditRec, "id", botUserId)
audit.AddEventParameter(auditRec, "user_id", userId)
if err := c.App.SessionHasPermissionToManageBot(*c.AppContext.Session(), botUserId); err != nil {
if err := c.App.SessionHasPermissionToManageBot(c.AppContext, *c.AppContext.Session(), botUserId); err != nil {
c.Err = err
return
}
@ -257,7 +257,7 @@ func assignBot(c *Context, w http.ResponseWriter, _ *http.Request) {
}
}
bot, err := c.App.UpdateBotOwner(botUserId, userId)
bot, err := c.App.UpdateBotOwner(c.AppContext, botUserId, userId)
if err != nil {
c.Err = err
return
@ -278,7 +278,7 @@ func convertBotToUser(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
bot, err := c.App.GetBot(c.Params.BotUserId, false)
bot, err := c.App.GetBot(c.AppContext, c.Params.BotUserId, false)
if err != nil {
c.Err = err
return

View File

@ -70,7 +70,7 @@ func TestCreateBot(t *testing.T) {
createdBot, resp, err := th.Client.CreateBot(context.Background(), bot)
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
require.Equal(t, bot.Username, createdBot.Username)
require.Equal(t, bot.DisplayName, createdBot.DisplayName)
require.Equal(t, bot.Description, createdBot.Description)
@ -117,7 +117,7 @@ func TestCreateBot(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(bot.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot.UserId)
th.App.UpdateUserRoles(th.Context, bot.UserId, model.TeamUserRoleId+" "+model.SystemUserAccessTokenRoleId, false)
rtoken, _, err := th.Client.CreateUserAccessToken(context.Background(), bot.UserId, "test token")
@ -183,7 +183,7 @@ func TestPatchBot(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
botPatch := &model.BotPatch{
@ -207,7 +207,7 @@ func TestPatchBot(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBotSystemAdmin.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBotSystemAdmin.UserId)
th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
botPatch := &model.BotPatch{
@ -241,7 +241,7 @@ func TestPatchBot(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
_, _, err = th.Client.PatchBot(context.Background(), createdBot.UserId, &model.BotPatch{})
CheckErrorID(t, err, "store.sql_bot.get.missing.app_error")
@ -265,7 +265,7 @@ func TestPatchBot(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
_, _, err = th.Client.PatchBot(context.Background(), createdBot.UserId, &model.BotPatch{})
CheckErrorID(t, err, "api.context.permissions.app_error")
@ -289,7 +289,7 @@ func TestPatchBot(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
botPatch := &model.BotPatch{
Username: sToP(GenerateTestUsername()),
@ -340,7 +340,7 @@ func TestPatchBot(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
botPatch := &model.BotPatch{
Username: sToP(GenerateTestUsername()),
@ -371,7 +371,7 @@ func TestPatchBot(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
botPatch := &model.BotPatch{
Username: sToP(GenerateTestUsername()),
@ -402,7 +402,7 @@ func TestPatchBot(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
botPatch := &model.BotPatch{
Username: sToP(GenerateTestUsername()),
@ -440,7 +440,7 @@ func TestPatchBot(t *testing.T) {
createdBot, resp, err := th.Client.CreateBot(context.Background(), bot)
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
botPatch := &model.BotPatch{
Username: sToP(GenerateTestUsername()),
@ -474,7 +474,7 @@ func TestPatchBot(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
r, err := th.Client.DoAPIPut(context.Background(), "/bots/"+createdBot.UserId, `{"creator_id":"`+th.BasicUser2.Id+`"}`)
require.NoError(t, err)
@ -511,7 +511,7 @@ func TestPatchBot(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
var botPatch *model.BotPatch
@ -536,7 +536,7 @@ func TestGetBot(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(bot1.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot1.UserId)
bot2, resp, err := th.SystemAdminClient.CreateBot(context.Background(), &model.Bot{
Username: GenerateTestUsername(),
@ -545,7 +545,7 @@ func TestGetBot(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(bot2.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot2.UserId)
deletedBot, resp, err := th.SystemAdminClient.CreateBot(context.Background(), &model.Bot{
Username: GenerateTestUsername(),
@ -553,7 +553,7 @@ func TestGetBot(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(deletedBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, deletedBot.UserId)
deletedBot, resp, err = th.SystemAdminClient.DisableBot(context.Background(), deletedBot.UserId)
require.NoError(t, err)
CheckOKStatus(t, resp)
@ -571,7 +571,7 @@ func TestGetBot(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(myBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, myBot.UserId)
th.RemovePermissionFromRole(model.PermissionCreateBot.Id, model.TeamUserRoleId)
t.Run("get unknown bot", func(t *testing.T) {
@ -690,7 +690,7 @@ func TestGetBots(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(bot1.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot1.UserId)
deletedBot1, resp, err := th.SystemAdminClient.CreateBot(context.Background(), &model.Bot{
Username: GenerateTestUsername(),
@ -698,7 +698,7 @@ func TestGetBots(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(deletedBot1.UserId)
defer th.App.PermanentDeleteBot(th.Context, deletedBot1.UserId)
deletedBot1, resp, err = th.SystemAdminClient.DisableBot(context.Background(), deletedBot1.UserId)
require.NoError(t, err)
CheckOKStatus(t, resp)
@ -710,7 +710,7 @@ func TestGetBots(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(bot2.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot2.UserId)
bot3, resp, err := th.SystemAdminClient.CreateBot(context.Background(), &model.Bot{
Username: GenerateTestUsername(),
@ -719,7 +719,7 @@ func TestGetBots(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(bot3.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot3.UserId)
deletedBot2, resp, err := th.SystemAdminClient.CreateBot(context.Background(), &model.Bot{
Username: GenerateTestUsername(),
@ -727,7 +727,7 @@ func TestGetBots(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(deletedBot2.UserId)
defer th.App.PermanentDeleteBot(th.Context, deletedBot2.UserId)
deletedBot2, resp, err = th.SystemAdminClient.DisableBot(context.Background(), deletedBot2.UserId)
require.NoError(t, err)
CheckOKStatus(t, resp)
@ -742,7 +742,7 @@ func TestGetBots(t *testing.T) {
require.NoError(t, err)
CheckCreatedStatus(t, resp)
th.LoginBasic()
defer th.App.PermanentDeleteBot(orphanedBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, orphanedBot.UserId)
// Automatic deactivation disabled
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.DisableBotsWhenOwnerIsDeactivated = false
@ -975,7 +975,7 @@ func TestDisableBot(t *testing.T) {
createdBot, resp, err := th.Client.CreateBot(context.Background(), bot)
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
_, _, err = th.Client.DisableBot(context.Background(), createdBot.UserId)
CheckErrorID(t, err, "store.sql_bot.get.missing.app_error")
@ -1001,7 +1001,7 @@ func TestDisableBot(t *testing.T) {
createdBot, resp, err := th.Client.CreateBot(context.Background(), bot)
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
_, _, err = th.Client.DisableBot(context.Background(), createdBot.UserId)
CheckErrorID(t, err, "api.context.permissions.app_error")
@ -1026,7 +1026,7 @@ func TestDisableBot(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(bot.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot.UserId)
disabledBot, resp, err := client.DisableBot(context.Background(), bot.UserId)
require.NoError(t, err)
@ -1080,7 +1080,7 @@ func TestEnableBot(t *testing.T) {
createdBot, resp, err := th.Client.CreateBot(context.Background(), bot)
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
_, resp, err = th.SystemAdminClient.DisableBot(context.Background(), createdBot.UserId)
require.NoError(t, err)
@ -1110,7 +1110,7 @@ func TestEnableBot(t *testing.T) {
createdBot, resp, err := th.Client.CreateBot(context.Background(), bot)
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
_, resp, err = th.SystemAdminClient.DisableBot(context.Background(), createdBot.UserId)
require.NoError(t, err)
@ -1139,7 +1139,7 @@ func TestEnableBot(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(bot.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot.UserId)
_, resp, err = th.SystemAdminClient.DisableBot(context.Background(), bot.UserId)
require.NoError(t, err)
@ -1195,7 +1195,7 @@ func TestAssignBot(t *testing.T) {
bot, resp, err := th.Client.CreateBot(context.Background(), bot)
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(bot.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot.UserId)
before, resp, err := th.Client.GetBot(context.Background(), bot.UserId, "")
require.NoError(t, err)
@ -1244,7 +1244,7 @@ func TestAssignBot(t *testing.T) {
createdBot, resp, err := th.Client.CreateBot(context.Background(), bot)
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
th.LoginBasic2()
@ -1276,7 +1276,7 @@ func TestAssignBot(t *testing.T) {
bot, resp, err := th.Client.CreateBot(context.Background(), bot)
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(bot.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot.UserId)
// Simulate custom role by just changing the system user role
th.AddPermissionToRole(model.PermissionCreateBot.Id, model.SystemUserRoleId)
@ -1312,7 +1312,7 @@ func TestAssignBot(t *testing.T) {
bot, resp, err := th.Client.CreateBot(context.Background(), bot)
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(bot.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot.UserId)
bot2, resp, err := th.Client.CreateBot(context.Background(), &model.Bot{
Username: GenerateTestUsername(),
@ -1321,7 +1321,7 @@ func TestAssignBot(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(bot2.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot2.UserId)
_, _, err = th.Client.AssignBot(context.Background(), bot.UserId, bot2.UserId)
CheckErrorID(t, err, "api.context.permissions.app_error")
@ -1345,7 +1345,7 @@ func TestConvertBotToUser(t *testing.T) {
bot, resp, err := th.Client.CreateBot(context.Background(), bot)
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(bot.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot.UserId)
_, resp, err = th.Client.ConvertBotToUser(context.Background(), bot.UserId, &model.UserPatch{}, false)
require.Error(t, err)

View File

@ -463,7 +463,7 @@ func getAnalytics(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
rows, appErr := c.App.GetAnalytics(name, teamId)
rows, appErr := c.App.GetAnalytics(c.AppContext, name, teamId)
if appErr != nil {
c.Err = appErr
return

View File

@ -430,7 +430,7 @@ func setProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if !c.App.SessionHasPermissionToUserOrBot(*c.AppContext.Session(), c.Params.UserId) {
if !c.App.SessionHasPermissionToUserOrBot(c.AppContext, *c.AppContext.Session(), c.Params.UserId) {
c.SetPermissionError(model.PermissionEditOtherUsers)
return
}
@ -501,7 +501,7 @@ func setDefaultProfileImage(c *Context, w http.ResponseWriter, r *http.Request)
return
}
if !c.App.SessionHasPermissionToUserOrBot(*c.AppContext.Session(), c.Params.UserId) {
if !c.App.SessionHasPermissionToUserOrBot(c.AppContext, *c.AppContext.Session(), c.Params.UserId) {
c.SetPermissionError(model.PermissionEditOtherUsers)
return
}
@ -851,9 +851,9 @@ func getUsers(c *Context, w http.ResponseWriter, r *http.Request) {
}
if sort == "last_activity_at" {
profiles, appErr = c.App.GetRecentlyActiveUsersForTeamPage(inTeamId, c.Params.Page, c.Params.PerPage, c.IsSystemAdmin(), restrictions)
profiles, appErr = c.App.GetRecentlyActiveUsersForTeamPage(c.AppContext, inTeamId, c.Params.Page, c.Params.PerPage, c.IsSystemAdmin(), restrictions)
} else if sort == "create_at" {
profiles, appErr = c.App.GetNewUsersForTeamPage(inTeamId, c.Params.Page, c.Params.PerPage, c.IsSystemAdmin(), restrictions)
profiles, appErr = c.App.GetNewUsersForTeamPage(c.AppContext, inTeamId, c.Params.Page, c.Params.PerPage, c.IsSystemAdmin(), restrictions)
} else {
etag = c.App.GetUsersInTeamEtag(inTeamId, restrictions.Hash())
if c.HandleEtag(etag, "Get Users in Team", w, r) {
@ -1260,7 +1260,7 @@ func updateUser(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if !c.App.SessionHasPermissionToUserOrBot(*c.AppContext.Session(), user.Id) {
if !c.App.SessionHasPermissionToUserOrBot(c.AppContext, *c.AppContext.Session(), user.Id) {
c.SetPermissionError(model.PermissionEditOtherUsers)
return
}
@ -1337,7 +1337,7 @@ func patchUser(c *Context, w http.ResponseWriter, r *http.Request) {
audit.AddEventParameterAuditable(auditRec, "user_patch", &patch)
defer c.LogAuditRec(auditRec)
if !c.App.SessionHasPermissionToUserOrBot(*c.AppContext.Session(), c.Params.UserId) {
if !c.App.SessionHasPermissionToUserOrBot(c.AppContext, *c.AppContext.Session(), c.Params.UserId) {
c.SetPermissionError(model.PermissionEditOtherUsers)
return
}
@ -1417,7 +1417,7 @@ func deleteUser(c *Context, w http.ResponseWriter, r *http.Request) {
audit.AddEventParameter(auditRec, "permanent", permanent)
defer c.LogAuditRec(auditRec)
if !c.App.SessionHasPermissionToUserOrBot(*c.AppContext.Session(), userId) {
if !c.App.SessionHasPermissionToUserOrBot(c.AppContext, *c.AppContext.Session(), userId) {
c.SetPermissionError(model.PermissionEditOtherUsers)
return
}
@ -2458,7 +2458,7 @@ func createUserAccessToken(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if !c.App.SessionHasPermissionToUserOrBot(*c.AppContext.Session(), c.Params.UserId) {
if !c.App.SessionHasPermissionToUserOrBot(c.AppContext, *c.AppContext.Session(), c.Params.UserId) {
c.SetPermissionError(model.PermissionEditOtherUsers)
return
}
@ -2550,7 +2550,7 @@ func getUserAccessTokensForUser(c *Context, w http.ResponseWriter, r *http.Reque
return
}
if !c.App.SessionHasPermissionToUserOrBot(*c.AppContext.Session(), c.Params.UserId) {
if !c.App.SessionHasPermissionToUserOrBot(c.AppContext, *c.AppContext.Session(), c.Params.UserId) {
c.SetPermissionError(model.PermissionEditOtherUsers)
return
}
@ -2587,7 +2587,7 @@ func getUserAccessToken(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if !c.App.SessionHasPermissionToUserOrBot(*c.AppContext.Session(), accessToken.UserId) {
if !c.App.SessionHasPermissionToUserOrBot(c.AppContext, *c.AppContext.Session(), accessToken.UserId) {
c.SetPermissionError(model.PermissionEditOtherUsers)
return
}
@ -2625,7 +2625,7 @@ func revokeUserAccessToken(c *Context, w http.ResponseWriter, r *http.Request) {
audit.AddEventParameterAuditable(auditRec, "user", user)
}
if !c.App.SessionHasPermissionToUserOrBot(*c.AppContext.Session(), accessToken.UserId) {
if !c.App.SessionHasPermissionToUserOrBot(c.AppContext, *c.AppContext.Session(), accessToken.UserId) {
c.SetPermissionError(model.PermissionEditOtherUsers)
return
}
@ -2670,7 +2670,7 @@ func disableUserAccessToken(c *Context, w http.ResponseWriter, r *http.Request)
audit.AddEventParameterAuditable(auditRec, "user", user)
}
if !c.App.SessionHasPermissionToUserOrBot(*c.AppContext.Session(), accessToken.UserId) {
if !c.App.SessionHasPermissionToUserOrBot(c.AppContext, *c.AppContext.Session(), accessToken.UserId) {
c.SetPermissionError(model.PermissionEditOtherUsers)
return
}
@ -2715,7 +2715,7 @@ func enableUserAccessToken(c *Context, w http.ResponseWriter, r *http.Request) {
audit.AddEventParameterAuditable(auditRec, "user", user)
}
if !c.App.SessionHasPermissionToUserOrBot(*c.AppContext.Session(), accessToken.UserId) {
if !c.App.SessionHasPermissionToUserOrBot(c.AppContext, *c.AppContext.Session(), accessToken.UserId) {
c.SetPermissionError(model.PermissionEditOtherUsers)
return
}
@ -2965,7 +2965,7 @@ func convertUserToBot(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
bot, appErr := c.App.ConvertUserToBot(user)
bot, appErr := c.App.ConvertUserToBot(c.AppContext, user)
if appErr != nil {
c.Err = appErr
return

View File

@ -194,9 +194,9 @@ func localGetUsers(c *Context, w http.ResponseWriter, r *http.Request) {
profiles, appErr = c.App.GetUsersNotInTeamPage(notInTeamId, groupConstrainedBool, c.Params.Page, c.Params.PerPage, c.IsSystemAdmin(), nil)
} else if inTeamId != "" {
if sort == "last_activity_at" {
profiles, appErr = c.App.GetRecentlyActiveUsersForTeamPage(inTeamId, c.Params.Page, c.Params.PerPage, c.IsSystemAdmin(), nil)
profiles, appErr = c.App.GetRecentlyActiveUsersForTeamPage(c.AppContext, c.Params.TeamId, c.Params.Page, c.Params.PerPage, c.IsSystemAdmin(), nil)
} else if sort == "create_at" {
profiles, appErr = c.App.GetNewUsersForTeamPage(inTeamId, c.Params.Page, c.Params.PerPage, c.IsSystemAdmin(), nil)
profiles, appErr = c.App.GetNewUsersForTeamPage(c.AppContext, c.Params.TeamId, c.Params.Page, c.Params.PerPage, c.IsSystemAdmin(), nil)
} else {
etag = c.App.GetUsersInTeamEtag(inTeamId, "")
if c.HandleEtag(etag, "Get Users in Team", w, r) {

View File

@ -916,7 +916,7 @@ func TestGetBotUser(t *testing.T) {
createdBot, resp, err := th.Client.CreateBot(context.Background(), bot)
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
botUser, _, err := th.Client.GetUser(context.Background(), createdBot.UserId, "")
require.NoError(t, err)
@ -4556,7 +4556,7 @@ func TestCreateUserAccessToken(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
t.Run("without MANAGE_BOT permission", func(t *testing.T) {
th.RemovePermissionFromRole(model.PermissionManageBots.Id, model.TeamUserRoleId)
@ -4598,7 +4598,7 @@ func TestCreateUserAccessToken(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
t.Run("only having MANAGE_BOTS permission", func(t *testing.T) {
_, resp, err = th.Client.CreateUserAccessToken(context.Background(), createdBot.UserId, "test token")
@ -4703,7 +4703,7 @@ func TestGetUserAccessToken(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
token, _, err := th.Client.CreateUserAccessToken(context.Background(), createdBot.UserId, "test token")
require.NoError(t, err)
@ -4751,7 +4751,7 @@ func TestGetUserAccessToken(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
token, _, err := th.SystemAdminClient.CreateUserAccessToken(context.Background(), createdBot.UserId, "test token")
require.NoError(t, err)
@ -4976,7 +4976,7 @@ func TestRevokeUserAccessToken(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
token, _, err := th.Client.CreateUserAccessToken(context.Background(), createdBot.UserId, "test token")
require.NoError(t, err)
@ -5020,7 +5020,7 @@ func TestRevokeUserAccessToken(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
token, _, err := th.SystemAdminClient.CreateUserAccessToken(context.Background(), createdBot.UserId, "test token")
require.NoError(t, err)
@ -5095,7 +5095,7 @@ func TestDisableUserAccessToken(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
token, _, err := th.Client.CreateUserAccessToken(context.Background(), createdBot.UserId, "test token")
require.NoError(t, err)
@ -5139,7 +5139,7 @@ func TestDisableUserAccessToken(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
token, _, err := th.SystemAdminClient.CreateUserAccessToken(context.Background(), createdBot.UserId, "test token")
require.NoError(t, err)
@ -5222,7 +5222,7 @@ func TestEnableUserAccessToken(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
token, _, err := th.Client.CreateUserAccessToken(context.Background(), createdBot.UserId, "test token")
require.NoError(t, err)
@ -5269,7 +5269,7 @@ func TestEnableUserAccessToken(t *testing.T) {
})
require.NoError(t, err)
CheckCreatedStatus(t, resp)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
token, _, err := th.SystemAdminClient.CreateUserAccessToken(context.Background(), createdBot.UserId, "test token")
require.NoError(t, err)

View File

@ -10,6 +10,7 @@ import (
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/mlog"
"github.com/mattermost/mattermost/server/public/shared/request"
)
const (
@ -17,7 +18,7 @@ const (
MonthMilliseconds = 31 * DayMilliseconds
)
func (a *App) GetAnalytics(name string, teamID string) (model.AnalyticsRows, *model.AppError) {
func (a *App) GetAnalytics(rctx request.CTX, name string, teamID string) (model.AnalyticsRows, *model.AppError) {
skipIntensiveQueries := false
var systemUserCount int64
systemUserCount, err := a.Srv().Store().User().Count(model.UserCountOptions{})
@ -26,7 +27,7 @@ func (a *App) GetAnalytics(name string, teamID string) (model.AnalyticsRows, *mo
}
if systemUserCount > int64(*a.Config().AnalyticsSettings.MaxUsersForStatistics) {
mlog.Debug("More than limit users are on the system, intensive queries skipped", mlog.Int("limit", *a.Config().AnalyticsSettings.MaxUsersForStatistics))
rctx.Logger().Debug("More than limit users are on the system, intensive queries skipped", mlog.Int("limit", *a.Config().AnalyticsSettings.MaxUsersForStatistics))
skipIntensiveQueries = true
}
@ -306,7 +307,7 @@ func (a *App) GetAnalytics(name string, teamID string) (model.AnalyticsRows, *mo
return nil, nil
}
func (a *App) GetRecentlyActiveUsersForTeam(teamID string) (map[string]*model.User, *model.AppError) {
func (a *App) GetRecentlyActiveUsersForTeam(rctx request.CTX, teamID string) (map[string]*model.User, *model.AppError) {
users, err := a.Srv().Store().User().GetRecentlyActiveUsersForTeam(teamID, 0, 100, nil)
if err != nil {
return nil, model.NewAppError("GetRecentlyActiveUsersForTeam", "app.user.get_recently_active_users.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
@ -321,7 +322,7 @@ func (a *App) GetRecentlyActiveUsersForTeam(teamID string) (map[string]*model.Us
return userMap, nil
}
func (a *App) GetRecentlyActiveUsersForTeamPage(teamID string, page, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError) {
func (a *App) GetRecentlyActiveUsersForTeamPage(rctx request.CTX, teamID string, page, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError) {
users, err := a.Srv().Store().User().GetRecentlyActiveUsersForTeam(teamID, page*perPage, perPage, viewRestrictions)
if err != nil {
return nil, model.NewAppError("GetRecentlyActiveUsersForTeamPage", "app.user.get_recently_active_users.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
@ -330,7 +331,7 @@ func (a *App) GetRecentlyActiveUsersForTeamPage(teamID string, page, perPage int
return a.sanitizeProfiles(users, asAdmin), nil
}
func (a *App) GetNewUsersForTeamPage(teamID string, page, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError) {
func (a *App) GetNewUsersForTeamPage(rctx request.CTX, teamID string, page, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError) {
users, err := a.Srv().Store().User().GetNewUsersForTeam(teamID, page*perPage, perPage, viewRestrictions)
if err != nil {
return nil, model.NewAppError("GetNewUsersForTeamPage", "app.user.get_new_users.app_error", nil, "", http.StatusInternalServerError).Wrap(err)

View File

@ -86,11 +86,11 @@ type AppIface interface {
// ConvertBotToUser converts a bot to user.
ConvertBotToUser(c request.CTX, bot *model.Bot, userPatch *model.UserPatch, sysadmin bool) (*model.User, *model.AppError)
// ConvertUserToBot converts a user to bot.
ConvertUserToBot(user *model.User) (*model.Bot, *model.AppError)
ConvertUserToBot(rctx request.CTX, user *model.User) (*model.Bot, *model.AppError)
// Create/ Update a subscription history event
SendSubscriptionHistoryEvent(userID string) (*model.SubscriptionHistory, error)
// CreateBot creates the given bot and corresponding user.
CreateBot(c request.CTX, bot *model.Bot) (*model.Bot, *model.AppError)
CreateBot(rctx request.CTX, bot *model.Bot) (*model.Bot, *model.AppError)
// CreateChannelScheme creates a new Scheme of scope channel and assigns it to the channel.
CreateChannelScheme(c request.CTX, channel *model.Channel) (*model.Scheme, *model.AppError)
// CreateDefaultMemberships adds users to teams and channels based on their group memberships and how those groups
@ -165,9 +165,9 @@ type AppIface interface {
// filter.
GetAllLdapGroupsPage(rctx request.CTX, page int, perPage int, opts model.LdapGroupSearchOpts) ([]*model.Group, int, *model.AppError)
// GetBot returns the given bot.
GetBot(botUserId string, includeDeleted bool) (*model.Bot, *model.AppError)
GetBot(rctx request.CTX, botUserId string, includeDeleted bool) (*model.Bot, *model.AppError)
// GetBots returns the requested page of bots.
GetBots(options *model.BotGetOptions) (model.BotList, *model.AppError)
GetBots(rctx request.CTX, options *model.BotGetOptions) (model.BotList, *model.AppError)
// GetChannelGroupUsers returns the users who are associated to the channel via GroupChannels and GroupMembers.
GetChannelGroupUsers(channelID string) ([]*model.User, *model.AppError)
// GetChannelModerationsForChannel Gets a channels ChannelModerations from either the higherScoped roles or from the channel scheme roles.
@ -293,7 +293,7 @@ type AppIface interface {
// For internal requests, requests are routed directly to a plugin ServerHTTP hook
DoActionRequest(c request.CTX, rawURL string, body []byte) (*http.Response, *model.AppError)
// PermanentDeleteBot permanently deletes a bot and its corresponding user.
PermanentDeleteBot(botUserId string) *model.AppError
PermanentDeleteBot(rctx request.CTX, botUserId string) *model.AppError
// PopulateWebConnConfig checks if the connection id already exists in the hub,
// and if so, accordingly populates the other fields of the webconn.
PopulateWebConnConfig(s *model.Session, cfg *platform.WebConnConfig, seqVal string) (*platform.WebConnConfig, error)
@ -325,7 +325,7 @@ type AppIface interface {
// SessionHasPermissionToManageBot returns nil if the session has access to manage the given bot.
// This function deviates from other authorization checks in returning an error instead of just
// a boolean, allowing the permission failure to be exposed with more granularity.
SessionHasPermissionToManageBot(session model.Session, botUserId string) *model.AppError
SessionHasPermissionToManageBot(rctx request.CTX, session model.Session, botUserId string) *model.AppError
// SessionHasPermissionToTeams returns true only if user has access to all teams.
SessionHasPermissionToTeams(c request.CTX, session model.Session, teamIDs []string, permission *model.Permission) bool
// SessionIsRegistered determines if a specific session has been registered
@ -378,9 +378,9 @@ type AppIface interface {
// This to be used for places we check the users password when they are already logged in
DoubleCheckPassword(rctx request.CTX, user *model.User, password string) *model.AppError
// UpdateBotActive marks a bot as active or inactive, along with its corresponding user.
UpdateBotActive(c request.CTX, botUserId string, active bool) (*model.Bot, *model.AppError)
UpdateBotActive(rctx request.CTX, botUserId string, active bool) (*model.Bot, *model.AppError)
// UpdateBotOwner changes a bot's owner to the given value.
UpdateBotOwner(botUserId, newOwnerId string) (*model.Bot, *model.AppError)
UpdateBotOwner(rctx request.CTX, botUserId, newOwnerId string) (*model.Bot, *model.AppError)
// UpdateChannel updates a given channel by its Id. It also publishes the CHANNEL_UPDATED event.
UpdateChannel(c request.CTX, channel *model.Channel) (*model.Channel, *model.AppError)
// UpdateChannelScheme saves the new SchemeId of the channel passed.
@ -621,7 +621,7 @@ type AppIface interface {
GetAllTeams() ([]*model.Team, *model.AppError)
GetAllTeamsPage(offset int, limit int, opts *model.TeamSearch) ([]*model.Team, *model.AppError)
GetAllTeamsPageWithCount(offset int, limit int, opts *model.TeamSearch) (*model.TeamsWithCount, *model.AppError)
GetAnalytics(name string, teamID string) (model.AnalyticsRows, *model.AppError)
GetAnalytics(rctx request.CTX, name string, teamID string) (model.AnalyticsRows, *model.AppError)
GetAppliedSchemaMigrations() ([]model.AppliedMigration, *model.AppError)
GetAudits(rctx request.CTX, userID string, limit int) (model.Audits, *model.AppError)
GetAuditsPage(rctx request.CTX, userID string, page int, perPage int) (model.Audits, *model.AppError)
@ -717,7 +717,7 @@ type AppIface interface {
GetMemberCountsByGroup(rctx request.CTX, channelID string, includeTimezones bool) ([]*model.ChannelMemberCountByGroup, *model.AppError)
GetMessageForNotification(post *model.Post, teamName, siteUrl string, translateFunc i18n.TranslateFunc) string
GetMultipleEmojiByName(c request.CTX, names []string) ([]*model.Emoji, *model.AppError)
GetNewUsersForTeamPage(teamID string, page, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError)
GetNewUsersForTeamPage(rctx request.CTX, teamID string, page, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError)
GetNextPostIdFromPostList(postList *model.PostList, collapsedThreads bool) string
GetNotificationNameFormat(user *model.User) string
GetNumberOfChannelsOnTeam(c request.CTX, teamID string) (int, *model.AppError)
@ -771,8 +771,8 @@ type AppIface interface {
GetPublicChannelsByIdsForTeam(c request.CTX, teamID string, channelIDs []string) (model.ChannelList, *model.AppError)
GetPublicChannelsForTeam(c request.CTX, teamID string, offset int, limit int) (model.ChannelList, *model.AppError)
GetReactionsForPost(postID string) ([]*model.Reaction, *model.AppError)
GetRecentlyActiveUsersForTeam(teamID string) (map[string]*model.User, *model.AppError)
GetRecentlyActiveUsersForTeamPage(teamID string, page, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError)
GetRecentlyActiveUsersForTeam(rctx request.CTX, teamID string) (map[string]*model.User, *model.AppError)
GetRecentlyActiveUsersForTeamPage(rctx request.CTX, teamID string, page, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError)
GetRemoteCluster(remoteClusterId string) (*model.RemoteCluster, *model.AppError)
GetRemoteClusterForUser(remoteID string, userID string) (*model.RemoteCluster, *model.AppError)
GetRemoteClusterService() (remotecluster.RemoteClusterServiceIFace, *model.AppError)
@ -1092,7 +1092,7 @@ type AppIface interface {
SessionHasPermissionToReadJob(session model.Session, jobType string) (bool, *model.Permission)
SessionHasPermissionToTeam(session model.Session, teamID string, permission *model.Permission) bool
SessionHasPermissionToUser(session model.Session, userID string) bool
SessionHasPermissionToUserOrBot(session model.Session, userID string) bool
SessionHasPermissionToUserOrBot(rctx request.CTX, session model.Session, userID string) bool
SetActiveChannel(c request.CTX, userID string, channelID string) *model.AppError
SetAutoResponderStatus(rctx request.CTX, user *model.User, oldNotifyProps model.StringMap)
SetChannels(ch *Channels)

View File

@ -217,12 +217,12 @@ func (a *App) SessionHasPermissionToUser(session model.Session, userID string) b
return false
}
func (a *App) SessionHasPermissionToUserOrBot(session model.Session, userID string) bool {
func (a *App) SessionHasPermissionToUserOrBot(rctx request.CTX, session model.Session, userID string) bool {
if session.IsUnrestricted() {
return true
}
err := a.SessionHasPermissionToManageBot(session, userID)
err := a.SessionHasPermissionToManageBot(rctx, session, userID)
if err == nil {
return true
}
@ -339,8 +339,8 @@ func (a *App) RolesGrantPermission(roleNames []string, permissionId string) bool
// SessionHasPermissionToManageBot returns nil if the session has access to manage the given bot.
// This function deviates from other authorization checks in returning an error instead of just
// a boolean, allowing the permission failure to be exposed with more granularity.
func (a *App) SessionHasPermissionToManageBot(session model.Session, botUserId string) *model.AppError {
existingBot, err := a.GetBot(botUserId, true)
func (a *App) SessionHasPermissionToManageBot(rctx request.CTX, session model.Session, botUserId string) *model.AppError {
existingBot, err := a.GetBot(rctx, botUserId, true)
if err != nil {
return err
}

View File

@ -256,7 +256,7 @@ func TestSessionHasPermissionToManageBot(t *testing.T) {
OwnerId: th.BasicUser.Id,
})
require.Nil(t, err)
defer th.App.PermanentDeleteBot(bot.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot.UserId)
assert.NotNil(t, bot)
t.Run("test my bot", func(t *testing.T) {
@ -264,19 +264,19 @@ func TestSessionHasPermissionToManageBot(t *testing.T) {
UserId: th.BasicUser.Id,
Roles: model.SystemUserRoleId,
}
err = th.App.SessionHasPermissionToManageBot(session, bot.UserId)
err = th.App.SessionHasPermissionToManageBot(th.Context, session, bot.UserId)
assert.NotNil(t, err)
assert.Equal(t, "store.sql_bot.get.missing.app_error", err.Id)
assert.NoError(t, err.Unwrap())
th.AddPermissionToRole(model.PermissionReadBots.Id, model.SystemUserRoleId)
err = th.App.SessionHasPermissionToManageBot(session, bot.UserId)
err = th.App.SessionHasPermissionToManageBot(th.Context, session, bot.UserId)
assert.NotNil(t, err)
assert.Equal(t, "api.context.permissions.app_error", err.Id)
assert.NoError(t, err.Unwrap())
th.AddPermissionToRole(model.PermissionManageBots.Id, model.SystemUserRoleId)
err = th.App.SessionHasPermissionToManageBot(session, bot.UserId)
err = th.App.SessionHasPermissionToManageBot(th.Context, session, bot.UserId)
assert.Nil(t, err)
th.RemovePermissionFromRole(model.PermissionReadBots.Id, model.SystemUserRoleId)
@ -288,19 +288,19 @@ func TestSessionHasPermissionToManageBot(t *testing.T) {
UserId: th.BasicUser2.Id,
Roles: model.SystemUserRoleId,
}
err = th.App.SessionHasPermissionToManageBot(session, bot.UserId)
err = th.App.SessionHasPermissionToManageBot(th.Context, session, bot.UserId)
assert.NotNil(t, err)
assert.Equal(t, "store.sql_bot.get.missing.app_error", err.Id)
assert.NoError(t, err.Unwrap())
th.AddPermissionToRole(model.PermissionReadOthersBots.Id, model.SystemUserRoleId)
err = th.App.SessionHasPermissionToManageBot(session, bot.UserId)
err = th.App.SessionHasPermissionToManageBot(th.Context, session, bot.UserId)
assert.NotNil(t, err)
assert.Equal(t, "api.context.permissions.app_error", err.Id)
assert.NoError(t, err.Unwrap())
th.AddPermissionToRole(model.PermissionManageOthersBots.Id, model.SystemUserRoleId)
err = th.App.SessionHasPermissionToManageBot(session, bot.UserId)
err = th.App.SessionHasPermissionToManageBot(th.Context, session, bot.UserId)
assert.Nil(t, err)
th.RemovePermissionFromRole(model.PermissionReadOthersBots.Id, model.SystemUserRoleId)
@ -314,20 +314,20 @@ func TestSessionHasPermissionToManageBot(t *testing.T) {
}
// test non bot, contains wrapped error
err = th.App.SessionHasPermissionToManageBot(session, "12345")
err = th.App.SessionHasPermissionToManageBot(th.Context, session, "12345")
assert.NotNil(t, err)
assert.Equal(t, "store.sql_bot.get.missing.app_error", err.Id)
assert.Error(t, err.Unwrap())
// test existing bot, without PermissionManageOthersBots - no wrapped error
err = th.App.SessionHasPermissionToManageBot(session, bot.UserId)
err = th.App.SessionHasPermissionToManageBot(th.Context, session, bot.UserId)
assert.NotNil(t, err)
assert.Equal(t, "store.sql_bot.get.missing.app_error", err.Id)
assert.NoError(t, err.Unwrap())
// test with correct permissions
th.AddPermissionToRole(model.PermissionManageOthersBots.Id, model.SystemUserManagerRoleId)
err = th.App.SessionHasPermissionToManageBot(session, bot.UserId)
err = th.App.SessionHasPermissionToManageBot(th.Context, session, bot.UserId)
assert.Nil(t, err)
th.RemovePermissionFromRole(model.PermissionManageOthersBots.Id, model.SystemUserManagerRoleId)
@ -338,7 +338,7 @@ func TestSessionHasPermissionToManageBot(t *testing.T) {
UserId: th.SystemAdminUser.Id,
Roles: model.SystemAdminRoleId,
}
err = th.App.SessionHasPermissionToManageBot(session, bot.UserId)
err = th.App.SessionHasPermissionToManageBot(th.Context, session, bot.UserId)
assert.Nil(t, err)
})
@ -347,7 +347,7 @@ func TestSessionHasPermissionToManageBot(t *testing.T) {
UserId: th.SystemAdminUser.Id,
Roles: model.SystemUserRoleId,
}
err = th.App.SessionHasPermissionToManageBot(session, "12345")
err = th.App.SessionHasPermissionToManageBot(th.Context, session, "12345")
assert.NotNil(t, err)
assert.Equal(t, "store.sql_bot.get.missing.app_error", err.Id)
assert.Error(t, err.Unwrap())
@ -385,7 +385,7 @@ func TestSessionHasPermissionToUser(t *testing.T) {
})
require.Nil(t, err)
assert.NotNil(t, bot)
defer th.App.PermanentDeleteBot(bot.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot.UserId)
assert.False(t, th.App.SessionHasPermissionToUser(session, bot.UserId))
})
@ -410,16 +410,16 @@ func TestSessionHasPermissionToManageUserOrBot(t *testing.T) {
OwnerId: th.BasicUser.Id,
})
require.Nil(t, err)
defer th.App.PermanentDeleteBot(bot.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot.UserId)
t.Run("test basic user access", func(t *testing.T) {
session := model.Session{
UserId: th.BasicUser.Id,
Roles: model.SystemUserRoleId,
}
assert.True(t, th.App.SessionHasPermissionToUserOrBot(session, th.BasicUser.Id))
assert.False(t, th.App.SessionHasPermissionToUserOrBot(session, bot.UserId))
assert.False(t, th.App.SessionHasPermissionToUserOrBot(session, th.BasicUser2.Id))
assert.True(t, th.App.SessionHasPermissionToUserOrBot(th.Context, session, th.BasicUser.Id))
assert.False(t, th.App.SessionHasPermissionToUserOrBot(th.Context, session, bot.UserId))
assert.False(t, th.App.SessionHasPermissionToUserOrBot(th.Context, session, th.BasicUser2.Id))
})
t.Run("test user manager access", func(t *testing.T) {
@ -427,18 +427,18 @@ func TestSessionHasPermissionToManageUserOrBot(t *testing.T) {
UserId: th.BasicUser2.Id,
Roles: model.SystemUserManagerRoleId,
}
assert.False(t, th.App.SessionHasPermissionToUserOrBot(session, th.BasicUser.Id))
assert.True(t, th.App.SessionHasPermissionToUserOrBot(session, th.BasicUser2.Id))
assert.False(t, th.App.SessionHasPermissionToUserOrBot(session, bot.UserId))
assert.False(t, th.App.SessionHasPermissionToUserOrBot(th.Context, session, th.BasicUser.Id))
assert.True(t, th.App.SessionHasPermissionToUserOrBot(th.Context, session, th.BasicUser2.Id))
assert.False(t, th.App.SessionHasPermissionToUserOrBot(th.Context, session, bot.UserId))
th.AddPermissionToRole(model.PermissionEditOtherUsers.Id, model.SystemUserManagerRoleId)
assert.True(t, th.App.SessionHasPermissionToUserOrBot(session, th.BasicUser.Id))
assert.False(t, th.App.SessionHasPermissionToUserOrBot(session, bot.UserId))
assert.True(t, th.App.SessionHasPermissionToUserOrBot(th.Context, session, th.BasicUser.Id))
assert.False(t, th.App.SessionHasPermissionToUserOrBot(th.Context, session, bot.UserId))
th.RemovePermissionFromRole(model.PermissionEditOtherUsers.Id, model.SystemUserManagerRoleId)
th.AddPermissionToRole(model.PermissionManageOthersBots.Id, model.SystemUserManagerRoleId)
assert.False(t, th.App.SessionHasPermissionToUserOrBot(session, th.BasicUser.Id))
assert.True(t, th.App.SessionHasPermissionToUserOrBot(session, bot.UserId))
assert.False(t, th.App.SessionHasPermissionToUserOrBot(th.Context, session, th.BasicUser.Id))
assert.True(t, th.App.SessionHasPermissionToUserOrBot(th.Context, session, bot.UserId))
th.RemovePermissionFromRole(model.PermissionManageOthersBots.Id, model.SystemUserManagerRoleId)
})
@ -447,8 +447,8 @@ func TestSessionHasPermissionToManageUserOrBot(t *testing.T) {
UserId: th.SystemAdminUser.Id,
Roles: model.SystemAdminRoleId,
}
assert.True(t, th.App.SessionHasPermissionToUserOrBot(session, bot.UserId))
assert.True(t, th.App.SessionHasPermissionToUserOrBot(session, th.BasicUser.Id))
assert.True(t, th.App.SessionHasPermissionToUserOrBot(th.Context, session, bot.UserId))
assert.True(t, th.App.SessionHasPermissionToUserOrBot(th.Context, session, th.BasicUser.Id))
})
}

View File

@ -89,13 +89,13 @@ func (a *App) EnsureBot(rctx request.CTX, pluginID string, bot *model.Bot) (stri
}
// CreateBot creates the given bot and corresponding user.
func (a *App) CreateBot(c request.CTX, bot *model.Bot) (*model.Bot, *model.AppError) {
func (a *App) CreateBot(rctx request.CTX, bot *model.Bot) (*model.Bot, *model.AppError) {
vErr := bot.IsValidCreate()
if vErr != nil {
return nil, vErr
}
user, nErr := a.Srv().Store().User().Save(c, model.UserFromBot(bot))
user, nErr := a.Srv().Store().User().Save(rctx, model.UserFromBot(bot))
if nErr != nil {
var appErr *model.AppError
var invErr *store.ErrInvalidInput
@ -139,7 +139,7 @@ func (a *App) CreateBot(c request.CTX, bot *model.Bot) (*model.Bot, *model.AppEr
} else if ownerUser != nil {
// Send a message to the bot's creator to inform them that the bot needs to be added
// to a team and channel after it's created
channel, err := a.getOrCreateDirectChannelWithUser(c, user, ownerUser)
channel, err := a.getOrCreateDirectChannelWithUser(rctx, user, ownerUser)
if err != nil {
return nil, err
}
@ -152,7 +152,7 @@ func (a *App) CreateBot(c request.CTX, bot *model.Bot) (*model.Bot, *model.AppEr
Message: T("api.bot.teams_channels.add_message_mobile"),
}
if _, err := a.CreatePostAsUser(c, botAddPost, c.Session().Id, true); err != nil {
if _, err := a.CreatePostAsUser(rctx, botAddPost, rctx.Session().Id, true); err != nil {
return nil, err
}
}
@ -241,7 +241,7 @@ func (a *App) getOrCreateBot(rctx request.CTX, botDef *model.Bot) (*model.Bot, *
}
//return the bot for this user
savedBot, appErr := a.GetBot(botUser.Id, false)
savedBot, appErr := a.GetBot(rctx, botUser.Id, false)
if appErr != nil {
return nil, appErr
}
@ -251,7 +251,7 @@ func (a *App) getOrCreateBot(rctx request.CTX, botDef *model.Bot) (*model.Bot, *
// PatchBot applies the given patch to the bot and corresponding user.
func (a *App) PatchBot(rctx request.CTX, botUserId string, botPatch *model.BotPatch) (*model.Bot, *model.AppError) {
bot, err := a.GetBot(botUserId, true)
bot, err := a.GetBot(rctx, botUserId, true)
if err != nil {
return nil, err
}
@ -320,7 +320,7 @@ func (a *App) PatchBot(rctx request.CTX, botUserId string, botPatch *model.BotPa
}
// GetBot returns the given bot.
func (a *App) GetBot(botUserId string, includeDeleted bool) (*model.Bot, *model.AppError) {
func (a *App) GetBot(rctx request.CTX, botUserId string, includeDeleted bool) (*model.Bot, *model.AppError) {
bot, err := a.Srv().Store().Bot().Get(botUserId, includeDeleted)
if err != nil {
var nfErr *store.ErrNotFound
@ -335,7 +335,7 @@ func (a *App) GetBot(botUserId string, includeDeleted bool) (*model.Bot, *model.
}
// GetBots returns the requested page of bots.
func (a *App) GetBots(options *model.BotGetOptions) (model.BotList, *model.AppError) {
func (a *App) GetBots(rctx request.CTX, options *model.BotGetOptions) (model.BotList, *model.AppError) {
bots, err := a.Srv().Store().Bot().GetAll(options)
if err != nil {
return nil, model.NewAppError("GetBots", "app.bot.getbots.internal_error", nil, "", http.StatusInternalServerError).Wrap(err)
@ -344,7 +344,7 @@ func (a *App) GetBots(options *model.BotGetOptions) (model.BotList, *model.AppEr
}
// UpdateBotActive marks a bot as active or inactive, along with its corresponding user.
func (a *App) UpdateBotActive(c request.CTX, botUserId string, active bool) (*model.Bot, *model.AppError) {
func (a *App) UpdateBotActive(rctx request.CTX, botUserId string, active bool) (*model.Bot, *model.AppError) {
user, nErr := a.Srv().Store().User().Get(context.Background(), botUserId)
if nErr != nil {
var nfErr *store.ErrNotFound
@ -356,7 +356,7 @@ func (a *App) UpdateBotActive(c request.CTX, botUserId string, active bool) (*mo
}
}
if _, err := a.UpdateActive(c, user, active); err != nil {
if _, err := a.UpdateActive(rctx, user, active); err != nil {
return nil, err
}
@ -400,7 +400,7 @@ func (a *App) UpdateBotActive(c request.CTX, botUserId string, active bool) (*mo
}
// PermanentDeleteBot permanently deletes a bot and its corresponding user.
func (a *App) PermanentDeleteBot(botUserId string) *model.AppError {
func (a *App) PermanentDeleteBot(rctx request.CTX, botUserId string) *model.AppError {
if err := a.Srv().Store().Bot().PermanentDelete(botUserId); err != nil {
var invErr *store.ErrInvalidInput
switch {
@ -419,7 +419,7 @@ func (a *App) PermanentDeleteBot(botUserId string) *model.AppError {
}
// UpdateBotOwner changes a bot's owner to the given value.
func (a *App) UpdateBotOwner(botUserId, newOwnerId string) (*model.Bot, *model.AppError) {
func (a *App) UpdateBotOwner(rctx request.CTX, botUserId, newOwnerId string) (*model.Bot, *model.AppError) {
bot, err := a.Srv().Store().Bot().Get(botUserId, true)
if err != nil {
var nfErr *store.ErrNotFound
@ -451,7 +451,7 @@ func (a *App) UpdateBotOwner(botUserId, newOwnerId string) (*model.Bot, *model.A
}
// disableUserBots disables all bots owned by the given user.
func (a *App) disableUserBots(c request.CTX, userID string) *model.AppError {
func (a *App) disableUserBots(rctx request.CTX, userID string) *model.AppError {
perPage := 20
for {
options := &model.BotGetOptions{
@ -461,15 +461,15 @@ func (a *App) disableUserBots(c request.CTX, userID string) *model.AppError {
Page: 0,
PerPage: perPage,
}
userBots, err := a.GetBots(options)
userBots, err := a.GetBots(rctx, options)
if err != nil {
return err
}
for _, bot := range userBots {
_, err := a.UpdateBotActive(c, bot.UserId, false)
_, err := a.UpdateBotActive(rctx, bot.UserId, false)
if err != nil {
c.Logger().Warn("Unable to deactivate bot.", mlog.String("bot_user_id", bot.UserId), mlog.Err(err))
rctx.Logger().Warn("Unable to deactivate bot.", mlog.String("bot_user_id", bot.UserId), mlog.Err(err))
}
}
@ -484,7 +484,7 @@ func (a *App) disableUserBots(c request.CTX, userID string) *model.AppError {
return nil
}
func (a *App) notifySysadminsBotOwnerDeactivated(c request.CTX, userID string) *model.AppError {
func (a *App) notifySysadminsBotOwnerDeactivated(rctx request.CTX, userID string) *model.AppError {
perPage := 25
botOptions := &model.BotGetOptions{
OwnerId: userID,
@ -496,7 +496,7 @@ func (a *App) notifySysadminsBotOwnerDeactivated(c request.CTX, userID string) *
// get owner bots
var userBots []*model.Bot
for {
bots, err := a.GetBots(botOptions)
bots, err := a.GetBots(rctx, botOptions)
if err != nil {
return err
}
@ -546,7 +546,7 @@ func (a *App) notifySysadminsBotOwnerDeactivated(c request.CTX, userID string) *
// for each sysadmin, notify user that owns bots was disabled
for _, sysAdmin := range sysAdmins {
channel, appErr := a.GetOrCreateDirectChannel(c, sysAdmin.Id, sysAdmin.Id)
channel, appErr := a.GetOrCreateDirectChannel(rctx, sysAdmin.Id, sysAdmin.Id)
if appErr != nil {
return appErr
}
@ -558,7 +558,7 @@ func (a *App) notifySysadminsBotOwnerDeactivated(c request.CTX, userID string) *
Type: model.PostTypeSystemGeneric,
}
_, appErr = a.CreatePost(c, post, channel, false, true)
_, appErr = a.CreatePost(rctx, post, channel, false, true)
if appErr != nil {
return appErr
}
@ -596,7 +596,7 @@ func (a *App) getDisableBotSysadminMessage(user *model.User, userBots model.BotL
}
// ConvertUserToBot converts a user to bot.
func (a *App) ConvertUserToBot(user *model.User) (*model.Bot, *model.AppError) {
func (a *App) ConvertUserToBot(rctx request.CTX, user *model.User) (*model.Bot, *model.AppError) {
bot, err := a.Srv().Store().Bot().Save(model.BotFromUser(user))
if err != nil {
var appErr *model.AppError

View File

@ -79,7 +79,7 @@ func TestCreateBot(t *testing.T) {
OwnerId: th.BasicUser.Id,
})
require.Nil(t, err)
defer th.App.PermanentDeleteBot(bot.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot.UserId)
assert.Equal(t, "username", bot.Username)
assert.Equal(t, "a bot", bot.Description)
assert.Equal(t, th.BasicUser.Id, bot.OwnerId)
@ -123,7 +123,7 @@ func TestPatchBot(t *testing.T) {
OwnerId: th.BasicUser.Id,
})
require.Nil(t, err)
defer th.App.PermanentDeleteBot(bot.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot.UserId)
botPatch := &model.BotPatch{
Username: sToP("invalid username"),
@ -146,7 +146,7 @@ func TestPatchBot(t *testing.T) {
OwnerId: th.BasicUser.Id,
})
require.Nil(t, err)
defer th.App.PermanentDeleteBot(bot.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot.UserId)
botPatch := &model.BotPatch{
Username: sToP("username"),
@ -172,7 +172,7 @@ func TestPatchBot(t *testing.T) {
createdBot, err := th.App.CreateBot(th.Context, bot)
require.Nil(t, err)
defer th.App.PermanentDeleteBot(createdBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, createdBot.UserId)
botPatch := &model.BotPatch{
Username: sToP("username2"),
@ -204,7 +204,7 @@ func TestPatchBot(t *testing.T) {
OwnerId: th.BasicUser.Id,
})
require.Nil(t, err)
defer th.App.PermanentDeleteBot(bot.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot.UserId)
botPatch := &model.BotPatch{
Username: sToP(th.BasicUser2.Username),
@ -226,7 +226,7 @@ func TestGetBot(t *testing.T) {
OwnerId: th.BasicUser.Id,
})
require.Nil(t, err)
defer th.App.PermanentDeleteBot(bot1.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot1.UserId)
bot2, err := th.App.CreateBot(th.Context, &model.Bot{
Username: "username2",
@ -234,7 +234,7 @@ func TestGetBot(t *testing.T) {
OwnerId: th.BasicUser.Id,
})
require.Nil(t, err)
defer th.App.PermanentDeleteBot(bot2.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot2.UserId)
deletedBot, err := th.App.CreateBot(th.Context, &model.Bot{
Username: "username3",
@ -244,34 +244,34 @@ func TestGetBot(t *testing.T) {
require.Nil(t, err)
deletedBot, err = th.App.UpdateBotActive(th.Context, deletedBot.UserId, false)
require.Nil(t, err)
defer th.App.PermanentDeleteBot(deletedBot.UserId)
defer th.App.PermanentDeleteBot(th.Context, deletedBot.UserId)
t.Run("get unknown bot", func(t *testing.T) {
_, err := th.App.GetBot(model.NewId(), false)
_, err := th.App.GetBot(th.Context, model.NewId(), false)
require.NotNil(t, err)
require.Equal(t, "store.sql_bot.get.missing.app_error", err.Id)
})
t.Run("get bot1", func(t *testing.T) {
bot, err := th.App.GetBot(bot1.UserId, false)
bot, err := th.App.GetBot(th.Context, bot1.UserId, false)
require.Nil(t, err)
assert.Equal(t, bot1, bot)
})
t.Run("get bot2", func(t *testing.T) {
bot, err := th.App.GetBot(bot2.UserId, false)
bot, err := th.App.GetBot(th.Context, bot2.UserId, false)
require.Nil(t, err)
assert.Equal(t, bot2, bot)
})
t.Run("get deleted bot", func(t *testing.T) {
_, err := th.App.GetBot(deletedBot.UserId, false)
_, err := th.App.GetBot(th.Context, deletedBot.UserId, false)
require.NotNil(t, err)
require.Equal(t, "store.sql_bot.get.missing.app_error", err.Id)
})
t.Run("get deleted bot, include deleted", func(t *testing.T) {
bot, err := th.App.GetBot(deletedBot.UserId, true)
bot, err := th.App.GetBot(th.Context, deletedBot.UserId, true)
require.Nil(t, err)
assert.Equal(t, deletedBot, bot)
})
@ -290,7 +290,7 @@ func TestGetBots(t *testing.T) {
OwnerId: OwnerId1,
})
require.Nil(t, err)
defer th.App.PermanentDeleteBot(bot1.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot1.UserId)
deletedBot1, err := th.App.CreateBot(th.Context, &model.Bot{
Username: "username4",
@ -300,7 +300,7 @@ func TestGetBots(t *testing.T) {
require.Nil(t, err)
deletedBot1, err = th.App.UpdateBotActive(th.Context, deletedBot1.UserId, false)
require.Nil(t, err)
defer th.App.PermanentDeleteBot(deletedBot1.UserId)
defer th.App.PermanentDeleteBot(th.Context, deletedBot1.UserId)
bot2, err := th.App.CreateBot(th.Context, &model.Bot{
Username: "username2",
@ -308,7 +308,7 @@ func TestGetBots(t *testing.T) {
OwnerId: OwnerId1,
})
require.Nil(t, err)
defer th.App.PermanentDeleteBot(bot2.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot2.UserId)
bot3, err := th.App.CreateBot(th.Context, &model.Bot{
Username: "username3",
@ -316,7 +316,7 @@ func TestGetBots(t *testing.T) {
OwnerId: OwnerId1,
})
require.Nil(t, err)
defer th.App.PermanentDeleteBot(bot3.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot3.UserId)
bot4, err := th.App.CreateBot(th.Context, &model.Bot{
Username: "username5",
@ -324,7 +324,7 @@ func TestGetBots(t *testing.T) {
OwnerId: OwnerId2,
})
require.Nil(t, err)
defer th.App.PermanentDeleteBot(bot4.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot4.UserId)
deletedBot2, err := th.App.CreateBot(th.Context, &model.Bot{
Username: "username6",
@ -334,10 +334,10 @@ func TestGetBots(t *testing.T) {
require.Nil(t, err)
deletedBot2, err = th.App.UpdateBotActive(th.Context, deletedBot2.UserId, false)
require.Nil(t, err)
defer th.App.PermanentDeleteBot(deletedBot2.UserId)
defer th.App.PermanentDeleteBot(th.Context, deletedBot2.UserId)
t.Run("get bots, page=0, perPage=10", func(t *testing.T) {
bots, err := th.App.GetBots(&model.BotGetOptions{
bots, err := th.App.GetBots(th.Context, &model.BotGetOptions{
Page: 0,
PerPage: 10,
OwnerId: "",
@ -348,7 +348,7 @@ func TestGetBots(t *testing.T) {
})
t.Run("get bots, page=0, perPage=1", func(t *testing.T) {
bots, err := th.App.GetBots(&model.BotGetOptions{
bots, err := th.App.GetBots(th.Context, &model.BotGetOptions{
Page: 0,
PerPage: 1,
OwnerId: "",
@ -359,7 +359,7 @@ func TestGetBots(t *testing.T) {
})
t.Run("get bots, page=1, perPage=2", func(t *testing.T) {
bots, err := th.App.GetBots(&model.BotGetOptions{
bots, err := th.App.GetBots(th.Context, &model.BotGetOptions{
Page: 1,
PerPage: 2,
OwnerId: "",
@ -370,7 +370,7 @@ func TestGetBots(t *testing.T) {
})
t.Run("get bots, page=2, perPage=2", func(t *testing.T) {
bots, err := th.App.GetBots(&model.BotGetOptions{
bots, err := th.App.GetBots(th.Context, &model.BotGetOptions{
Page: 2,
PerPage: 2,
OwnerId: "",
@ -381,7 +381,7 @@ func TestGetBots(t *testing.T) {
})
t.Run("get bots, page=0, perPage=10, include deleted", func(t *testing.T) {
bots, err := th.App.GetBots(&model.BotGetOptions{
bots, err := th.App.GetBots(th.Context, &model.BotGetOptions{
Page: 0,
PerPage: 10,
OwnerId: "",
@ -392,7 +392,7 @@ func TestGetBots(t *testing.T) {
})
t.Run("get bots, page=0, perPage=1, include deleted", func(t *testing.T) {
bots, err := th.App.GetBots(&model.BotGetOptions{
bots, err := th.App.GetBots(th.Context, &model.BotGetOptions{
Page: 0,
PerPage: 1,
OwnerId: "",
@ -403,7 +403,7 @@ func TestGetBots(t *testing.T) {
})
t.Run("get bots, page=1, perPage=2, include deleted", func(t *testing.T) {
bots, err := th.App.GetBots(&model.BotGetOptions{
bots, err := th.App.GetBots(th.Context, &model.BotGetOptions{
Page: 1,
PerPage: 2,
OwnerId: "",
@ -414,7 +414,7 @@ func TestGetBots(t *testing.T) {
})
t.Run("get bots, page=2, perPage=2, include deleted", func(t *testing.T) {
bots, err := th.App.GetBots(&model.BotGetOptions{
bots, err := th.App.GetBots(th.Context, &model.BotGetOptions{
Page: 2,
PerPage: 2,
OwnerId: "",
@ -425,7 +425,7 @@ func TestGetBots(t *testing.T) {
})
t.Run("get offset=0, limit=10, creator id 1", func(t *testing.T) {
bots, err := th.App.GetBots(&model.BotGetOptions{
bots, err := th.App.GetBots(th.Context, &model.BotGetOptions{
Page: 0,
PerPage: 10,
OwnerId: OwnerId1,
@ -436,7 +436,7 @@ func TestGetBots(t *testing.T) {
})
t.Run("get offset=0, limit=10, creator id 2", func(t *testing.T) {
bots, err := th.App.GetBots(&model.BotGetOptions{
bots, err := th.App.GetBots(th.Context, &model.BotGetOptions{
Page: 0,
PerPage: 10,
OwnerId: OwnerId2,
@ -447,7 +447,7 @@ func TestGetBots(t *testing.T) {
})
t.Run("get offset=0, limit=10, include deleted, creator id 1", func(t *testing.T) {
bots, err := th.App.GetBots(&model.BotGetOptions{
bots, err := th.App.GetBots(th.Context, &model.BotGetOptions{
Page: 0,
PerPage: 10,
OwnerId: OwnerId1,
@ -458,7 +458,7 @@ func TestGetBots(t *testing.T) {
})
t.Run("get offset=0, limit=10, include deleted, creator id 2", func(t *testing.T) {
bots, err := th.App.GetBots(&model.BotGetOptions{
bots, err := th.App.GetBots(th.Context, &model.BotGetOptions{
Page: 0,
PerPage: 10,
OwnerId: OwnerId2,
@ -489,7 +489,7 @@ func TestUpdateBotActive(t *testing.T) {
OwnerId: th.BasicUser.Id,
})
require.Nil(t, err)
defer th.App.PermanentDeleteBot(bot.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot.UserId)
disabledBot, err := th.App.UpdateBotActive(th.Context, bot.UserId, false)
require.Nil(t, err)
@ -522,9 +522,9 @@ func TestPermanentDeleteBot(t *testing.T) {
})
require.Nil(t, err)
require.Nil(t, th.App.PermanentDeleteBot(bot.UserId))
require.Nil(t, th.App.PermanentDeleteBot(th.Context, bot.UserId))
_, err = th.App.GetBot(bot.UserId, false)
_, err = th.App.GetBot(th.Context, bot.UserId, false)
require.NotNil(t, err)
require.Equal(t, "store.sql_bot.get.missing.app_error", err.Id)
}
@ -539,7 +539,7 @@ func TestDisableUserBots(t *testing.T) {
bots := []*model.Bot{}
defer func() {
for _, bot := range bots {
th.App.PermanentDeleteBot(bot.UserId)
th.App.PermanentDeleteBot(th.Context, bot.UserId)
}
}()
@ -560,20 +560,20 @@ func TestDisableUserBots(t *testing.T) {
OwnerId: ownerId2,
})
require.Nil(t, err)
defer th.App.PermanentDeleteBot(u2bot1.UserId)
defer th.App.PermanentDeleteBot(th.Context, u2bot1.UserId)
err = th.App.disableUserBots(th.Context, ownerId1)
require.Nil(t, err)
// Check all bots and corresponding users are disabled for creator 1
for _, bot := range bots {
retbot, err2 := th.App.GetBot(bot.UserId, true)
retbot, err2 := th.App.GetBot(th.Context, bot.UserId, true)
require.Nil(t, err2)
require.NotZero(t, retbot.DeleteAt, bot.Username)
}
// Check bots and corresponding user not disabled for creator 2
bot, err := th.App.GetBot(u2bot1.UserId, true)
bot, err := th.App.GetBot(th.Context, u2bot1.UserId, true)
require.Nil(t, err)
require.Zero(t, bot.DeleteAt)
@ -593,7 +593,7 @@ func TestNotifySysadminsBotOwnerDisabled(t *testing.T) {
userBots := []*model.Bot{}
defer func() {
for _, bot := range userBots {
th.App.PermanentDeleteBot(bot.UserId)
th.App.PermanentDeleteBot(th.Context, bot.UserId)
}
}()
@ -725,7 +725,7 @@ func TestConvertUserToBot(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
_, err := th.App.ConvertUserToBot(&model.User{
_, err := th.App.ConvertUserToBot(th.Context, &model.User{
Username: "username",
Id: "",
})
@ -737,7 +737,7 @@ func TestConvertUserToBot(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
_, err := th.App.ConvertUserToBot(&model.User{
_, err := th.App.ConvertUserToBot(th.Context, &model.User{
Username: "invalid username",
Id: th.BasicUser.Id,
})
@ -750,12 +750,12 @@ func TestConvertUserToBot(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
bot, err := th.App.ConvertUserToBot(&model.User{
bot, err := th.App.ConvertUserToBot(th.Context, &model.User{
Username: "username",
Id: th.BasicUser.Id,
})
require.Nil(t, err)
defer th.App.PermanentDeleteBot(bot.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot.UserId)
assert.Equal(t, "username", bot.Username)
assert.Equal(t, th.BasicUser.Id, bot.OwnerId)
})

View File

@ -1580,7 +1580,7 @@ func TestAddUserToChannel(t *testing.T) {
defer th.App.PermanentDeleteUser(th.Context, &user1)
bot := th.CreateBot()
botUser, _ := th.App.GetUser(bot.UserId)
defer th.App.PermanentDeleteBot(botUser.Id)
defer th.App.PermanentDeleteBot(th.Context, botUser.Id)
th.App.AddTeamMember(th.Context, th.BasicTeam.Id, ruser1.Id)
th.App.AddTeamMember(th.Context, th.BasicTeam.Id, bot.UserId)
@ -1662,7 +1662,7 @@ func TestRemoveUserFromChannel(t *testing.T) {
bot := th.CreateBot()
botUser, _ := th.App.GetUser(bot.UserId)
defer th.App.PermanentDeleteBot(botUser.Id)
defer th.App.PermanentDeleteBot(th.Context, botUser.Id)
th.App.AddTeamMember(th.Context, th.BasicTeam.Id, ruser.Id)
th.App.AddTeamMember(th.Context, th.BasicTeam.Id, bot.UserId)

View File

@ -278,9 +278,9 @@ func (th *TestHelper) InitBasic() *TestHelper {
}
func (th *TestHelper) DeleteBots() *TestHelper {
preexistingBots, _ := th.App.GetBots(&model.BotGetOptions{Page: 0, PerPage: 100})
preexistingBots, _ := th.App.GetBots(th.Context, &model.BotGetOptions{Page: 0, PerPage: 100})
for _, bot := range preexistingBots {
th.App.PermanentDeleteBot(bot.UserId)
th.App.PermanentDeleteBot(th.Context, bot.UserId)
}
return th
}

View File

@ -114,7 +114,7 @@ func TestGetUserLimits(t *testing.T) {
require.Equal(t, int64(3), userLimits.ActiveUserCount)
// now we'll delete the bot
_ = th.App.PermanentDeleteBot(newBot.UserId)
_ = th.App.PermanentDeleteBot(th.Context, newBot.UserId)
userLimits, appErr = th.App.GetUserLimits()
require.Nil(t, appErr)
require.Equal(t, int64(3), userLimits.ActiveUserCount)

View File

@ -2463,7 +2463,7 @@ func TestUserAllowsEmail(t *testing.T) {
t.Run("should return false in the case user is a bot", func(t *testing.T) {
user := th.CreateUser()
th.App.ConvertUserToBot(user)
th.App.ConvertUserToBot(th.Context, user)
channelMemberNotifcationProps := model.StringMap{
model.EmailNotifyProp: model.ChannelNotifyDefault,

View File

@ -1884,7 +1884,7 @@ func (a *OpenTracingAppLayer) ConvertGroupMessageToChannel(c request.CTX, conver
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) ConvertUserToBot(user *model.User) (*model.Bot, *model.AppError) {
func (a *OpenTracingAppLayer) ConvertUserToBot(rctx request.CTX, user *model.User) (*model.Bot, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.ConvertUserToBot")
@ -1896,7 +1896,7 @@ func (a *OpenTracingAppLayer) ConvertUserToBot(user *model.User) (*model.Bot, *m
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.ConvertUserToBot(user)
resultVar0, resultVar1 := a.app.ConvertUserToBot(rctx, user)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
@ -1950,7 +1950,7 @@ func (a *OpenTracingAppLayer) CopyWranglerPostlist(c request.CTX, wpl *model.Wra
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) CreateBot(c request.CTX, bot *model.Bot) (*model.Bot, *model.AppError) {
func (a *OpenTracingAppLayer) CreateBot(rctx request.CTX, bot *model.Bot) (*model.Bot, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateBot")
@ -1962,7 +1962,7 @@ func (a *OpenTracingAppLayer) CreateBot(c request.CTX, bot *model.Bot) (*model.B
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.CreateBot(c, bot)
resultVar0, resultVar1 := a.app.CreateBot(rctx, bot)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
@ -5006,7 +5006,7 @@ func (a *OpenTracingAppLayer) GetAllTeamsPageWithCount(offset int, limit int, op
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) GetAnalytics(name string, teamID string) (model.AnalyticsRows, *model.AppError) {
func (a *OpenTracingAppLayer) GetAnalytics(rctx request.CTX, name string, teamID string) (model.AnalyticsRows, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetAnalytics")
@ -5018,7 +5018,7 @@ func (a *OpenTracingAppLayer) GetAnalytics(name string, teamID string) (model.An
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.GetAnalytics(name, teamID)
resultVar0, resultVar1 := a.app.GetAnalytics(rctx, name, teamID)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
@ -5138,7 +5138,7 @@ func (a *OpenTracingAppLayer) GetAuthorizedAppsForUser(userID string, page int,
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) GetBot(botUserId string, includeDeleted bool) (*model.Bot, *model.AppError) {
func (a *OpenTracingAppLayer) GetBot(rctx request.CTX, botUserId string, includeDeleted bool) (*model.Bot, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetBot")
@ -5150,7 +5150,7 @@ func (a *OpenTracingAppLayer) GetBot(botUserId string, includeDeleted bool) (*mo
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.GetBot(botUserId, includeDeleted)
resultVar0, resultVar1 := a.app.GetBot(rctx, botUserId, includeDeleted)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
@ -5160,7 +5160,7 @@ func (a *OpenTracingAppLayer) GetBot(botUserId string, includeDeleted bool) (*mo
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) GetBots(options *model.BotGetOptions) (model.BotList, *model.AppError) {
func (a *OpenTracingAppLayer) GetBots(rctx request.CTX, options *model.BotGetOptions) (model.BotList, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetBots")
@ -5172,7 +5172,7 @@ func (a *OpenTracingAppLayer) GetBots(options *model.BotGetOptions) (model.BotLi
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.GetBots(options)
resultVar0, resultVar1 := a.app.GetBots(rctx, options)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
@ -7440,7 +7440,7 @@ func (a *OpenTracingAppLayer) GetMultipleEmojiByName(c request.CTX, names []stri
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) GetNewUsersForTeamPage(teamID string, page int, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError) {
func (a *OpenTracingAppLayer) GetNewUsersForTeamPage(rctx request.CTX, teamID string, page int, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetNewUsersForTeamPage")
@ -7452,7 +7452,7 @@ func (a *OpenTracingAppLayer) GetNewUsersForTeamPage(teamID string, page int, pe
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.GetNewUsersForTeamPage(teamID, page, perPage, asAdmin, viewRestrictions)
resultVar0, resultVar1 := a.app.GetNewUsersForTeamPage(rctx, teamID, page, perPage, asAdmin, viewRestrictions)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
@ -8779,7 +8779,7 @@ func (a *OpenTracingAppLayer) GetReactionsForPost(postID string) ([]*model.React
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) GetRecentlyActiveUsersForTeam(teamID string) (map[string]*model.User, *model.AppError) {
func (a *OpenTracingAppLayer) GetRecentlyActiveUsersForTeam(rctx request.CTX, teamID string) (map[string]*model.User, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetRecentlyActiveUsersForTeam")
@ -8791,7 +8791,7 @@ func (a *OpenTracingAppLayer) GetRecentlyActiveUsersForTeam(teamID string) (map[
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.GetRecentlyActiveUsersForTeam(teamID)
resultVar0, resultVar1 := a.app.GetRecentlyActiveUsersForTeam(rctx, teamID)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
@ -8801,7 +8801,7 @@ func (a *OpenTracingAppLayer) GetRecentlyActiveUsersForTeam(teamID string) (map[
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) GetRecentlyActiveUsersForTeamPage(teamID string, page int, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError) {
func (a *OpenTracingAppLayer) GetRecentlyActiveUsersForTeamPage(rctx request.CTX, teamID string, page int, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetRecentlyActiveUsersForTeamPage")
@ -8813,7 +8813,7 @@ func (a *OpenTracingAppLayer) GetRecentlyActiveUsersForTeamPage(teamID string, p
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.GetRecentlyActiveUsersForTeamPage(teamID, page, perPage, asAdmin, viewRestrictions)
resultVar0, resultVar1 := a.app.GetRecentlyActiveUsersForTeamPage(rctx, teamID, page, perPage, asAdmin, viewRestrictions)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
@ -13276,7 +13276,7 @@ func (a *OpenTracingAppLayer) PermanentDeleteAllUsers(c request.CTX) *model.AppE
return resultVar0
}
func (a *OpenTracingAppLayer) PermanentDeleteBot(botUserId string) *model.AppError {
func (a *OpenTracingAppLayer) PermanentDeleteBot(rctx request.CTX, botUserId string) *model.AppError {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.PermanentDeleteBot")
@ -13288,7 +13288,7 @@ func (a *OpenTracingAppLayer) PermanentDeleteBot(botUserId string) *model.AppErr
}()
defer span.Finish()
resultVar0 := a.app.PermanentDeleteBot(botUserId)
resultVar0 := a.app.PermanentDeleteBot(rctx, botUserId)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
@ -16150,7 +16150,7 @@ func (a *OpenTracingAppLayer) SessionHasPermissionToGroup(session model.Session,
return resultVar0
}
func (a *OpenTracingAppLayer) SessionHasPermissionToManageBot(session model.Session, botUserId string) *model.AppError {
func (a *OpenTracingAppLayer) SessionHasPermissionToManageBot(rctx request.CTX, session model.Session, botUserId string) *model.AppError {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SessionHasPermissionToManageBot")
@ -16162,7 +16162,7 @@ func (a *OpenTracingAppLayer) SessionHasPermissionToManageBot(session model.Sess
}()
defer span.Finish()
resultVar0 := a.app.SessionHasPermissionToManageBot(session, botUserId)
resultVar0 := a.app.SessionHasPermissionToManageBot(rctx, session, botUserId)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
@ -16240,7 +16240,7 @@ func (a *OpenTracingAppLayer) SessionHasPermissionToUser(session model.Session,
return resultVar0
}
func (a *OpenTracingAppLayer) SessionHasPermissionToUserOrBot(session model.Session, userID string) bool {
func (a *OpenTracingAppLayer) SessionHasPermissionToUserOrBot(rctx request.CTX, session model.Session, userID string) bool {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SessionHasPermissionToUserOrBot")
@ -16252,7 +16252,7 @@ func (a *OpenTracingAppLayer) SessionHasPermissionToUserOrBot(session model.Sess
}()
defer span.Finish()
resultVar0 := a.app.SessionHasPermissionToUserOrBot(session, userID)
resultVar0 := a.app.SessionHasPermissionToUserOrBot(rctx, session, userID)
return resultVar0
}
@ -17496,7 +17496,7 @@ func (a *OpenTracingAppLayer) UpdateActive(c request.CTX, user *model.User, acti
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) UpdateBotActive(c request.CTX, botUserId string, active bool) (*model.Bot, *model.AppError) {
func (a *OpenTracingAppLayer) UpdateBotActive(rctx request.CTX, botUserId string, active bool) (*model.Bot, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UpdateBotActive")
@ -17508,7 +17508,7 @@ func (a *OpenTracingAppLayer) UpdateBotActive(c request.CTX, botUserId string, a
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.UpdateBotActive(c, botUserId, active)
resultVar0, resultVar1 := a.app.UpdateBotActive(rctx, botUserId, active)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
@ -17518,7 +17518,7 @@ func (a *OpenTracingAppLayer) UpdateBotActive(c request.CTX, botUserId string, a
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) UpdateBotOwner(botUserId string, newOwnerId string) (*model.Bot, *model.AppError) {
func (a *OpenTracingAppLayer) UpdateBotOwner(rctx request.CTX, botUserId string, newOwnerId string) (*model.Bot, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UpdateBotOwner")
@ -17530,7 +17530,7 @@ func (a *OpenTracingAppLayer) UpdateBotOwner(botUserId string, newOwnerId string
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.UpdateBotOwner(botUserId, newOwnerId)
resultVar0, resultVar1 := a.app.UpdateBotOwner(rctx, botUserId, newOwnerId)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))

View File

@ -1027,11 +1027,11 @@ func (api *PluginAPI) PatchBot(userID string, botPatch *model.BotPatch) (*model.
}
func (api *PluginAPI) GetBot(userID string, includeDeleted bool) (*model.Bot, *model.AppError) {
return api.app.GetBot(userID, includeDeleted)
return api.app.GetBot(api.ctx, userID, includeDeleted)
}
func (api *PluginAPI) GetBots(options *model.BotGetOptions) ([]*model.Bot, *model.AppError) {
bots, err := api.app.GetBots(options)
bots, err := api.app.GetBots(api.ctx, options)
return []*model.Bot(bots), err
}
@ -1041,7 +1041,7 @@ func (api *PluginAPI) UpdateBotActive(userID string, active bool) (*model.Bot, *
}
func (api *PluginAPI) PermanentDeleteBot(userID string) *model.AppError {
return api.app.PermanentDeleteBot(userID)
return api.app.PermanentDeleteBot(api.ctx, userID)
}
func (api *PluginAPI) EnsureBotUser(bot *model.Bot) (string, error) {

View File

@ -203,7 +203,7 @@ func (a *App) generateSupportPacketYaml(c request.CTX) (*model.FileData, error)
/* Server stats */
analytics, appErr := a.GetAnalytics("standard", "")
analytics, appErr := a.GetAnalytics(c, "standard", "")
if appErr != nil {
rErr = multierror.Append(errors.Wrap(appErr, "error while getting analytics"))
}

View File

@ -263,9 +263,15 @@ func (a *App) createUserOrGuest(c request.CTX, user *model.User, guest bool) (*m
}
}
if user.EmailVerified {
a.InvalidateCacheForUser(ruser.Id)
// We always invalidate the user because we actually need to invalidate
// in case the user's EmailVerified is true, but we also always need to invalidate
// the GetAllProfiles cache.
// To have a proper fix would mean duplicating the invalidation of GetAllProfiles
// everywhere else. Therefore, to keep things simple we always invalidate both caches here.
// The performance penalty for invalidating the UserById cache is nil because the user was just created.
a.InvalidateCacheForUser(ruser.Id)
if user.EmailVerified {
nUser, err := a.ch.srv.userService.GetUser(ruser.Id)
if err != nil {
var nfErr *store.ErrNotFound

View File

@ -353,7 +353,7 @@ func TestUpdateActiveBotsSideEffect(t *testing.T) {
OwnerId: th.BasicUser.Id,
})
require.Nil(t, err)
defer th.App.PermanentDeleteBot(bot.UserId)
defer th.App.PermanentDeleteBot(th.Context, bot.UserId)
// Automatic deactivation disabled
th.App.UpdateConfig(func(cfg *model.Config) {
@ -362,7 +362,7 @@ func TestUpdateActiveBotsSideEffect(t *testing.T) {
th.App.UpdateActive(th.Context, th.BasicUser, false)
retbot1, err := th.App.GetBot(bot.UserId, true)
retbot1, err := th.App.GetBot(th.Context, bot.UserId, true)
require.Nil(t, err)
require.Zero(t, retbot1.DeleteAt)
user1, err := th.App.GetUser(bot.UserId)
@ -378,7 +378,7 @@ func TestUpdateActiveBotsSideEffect(t *testing.T) {
th.App.UpdateActive(th.Context, th.BasicUser, false)
retbot2, err := th.App.GetBot(bot.UserId, true)
retbot2, err := th.App.GetBot(th.Context, bot.UserId, true)
require.Nil(t, err)
require.NotZero(t, retbot2.DeleteAt)
user2, err := th.App.GetUser(bot.UserId)

View File

@ -442,7 +442,7 @@ func TestRestrictedViewMembers(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
results, err := th.App.GetNewUsersForTeamPage(tc.TeamId, 0, 2, false, tc.Restrictions)
results, err := th.App.GetNewUsersForTeamPage(th.Context, tc.TeamId, 0, 2, false, tc.Restrictions)
require.Nil(t, err)
ids := []string{}
for _, result := range results {
@ -517,7 +517,7 @@ func TestRestrictedViewMembers(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
results, err := th.App.GetRecentlyActiveUsersForTeamPage(tc.TeamId, 0, 3, false, tc.Restrictions)
results, err := th.App.GetRecentlyActiveUsersForTeamPage(th.Context, tc.TeamId, 0, 3, false, tc.Restrictions)
require.Nil(t, err)
ids := []string{}
for _, result := range results {
@ -525,7 +525,7 @@ func TestRestrictedViewMembers(t *testing.T) {
}
assert.ElementsMatch(t, tc.ExpectedResults, ids)
results, err = th.App.GetRecentlyActiveUsersForTeamPage(tc.TeamId, 0, 1, false, tc.Restrictions)
results, err = th.App.GetRecentlyActiveUsersForTeamPage(th.Context, tc.TeamId, 0, 1, false, tc.Restrictions)
require.Nil(t, err)
if len(tc.ExpectedResults) > 1 {
assert.Len(t, results, 1)

View File

@ -112,6 +112,7 @@ type LocalCacheStore struct {
postsUsageCache cache.Cache
user *LocalCacheUserStore
allUserCache cache.Cache
userProfileByIdsCache cache.Cache
profilesInChannelCache cache.Cache
@ -315,6 +316,14 @@ func NewLocalCacheLayer(baseStore store.Store, metrics einterfaces.MetricsInterf
localCacheStore.termsOfService = LocalCacheTermsOfServiceStore{TermsOfServiceStore: baseStore.TermsOfService(), rootStore: &localCacheStore}
// Users
if localCacheStore.allUserCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: 1,
Name: "AllUserProfiles",
DefaultExpiry: UserProfileByIDSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForAllProfiles,
}); err != nil {
return
}
if localCacheStore.userProfileByIdsCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: UserProfileByIDCacheSize,
Name: "UserProfileByIds",
@ -372,6 +381,7 @@ func NewLocalCacheLayer(baseStore store.Store, metrics einterfaces.MetricsInterf
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForTermsOfService, localCacheStore.termsOfService.handleClusterInvalidateTermsOfService)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForProfileByIds, localCacheStore.user.handleClusterInvalidateScheme)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForProfileInChannel, localCacheStore.user.handleClusterInvalidateProfilesInChannel)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForAllProfiles, localCacheStore.user.handleClusterInvalidateAllProfiles)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForTeams, localCacheStore.team.handleClusterInvalidateTeam)
}
return
@ -497,6 +507,7 @@ func (s *LocalCacheStore) Invalidate() {
s.doClearCacheCluster(s.termsOfServiceCache)
s.doClearCacheCluster(s.lastPostTimeCache)
s.doClearCacheCluster(s.userProfileByIdsCache)
s.doClearCacheCluster(s.allUserCache)
s.doClearCacheCluster(s.profilesInChannelCache)
s.doClearCacheCluster(s.teamAllTeamIdsForUserCache)
s.doClearCacheCluster(s.rolePermissionsCache)

View File

@ -157,6 +157,7 @@ func getMockStore(t *testing.T) *mocks.Store {
}
mockUserStore.On("GetAllProfilesInChannel", mock.Anything, "123", true).Return(fakeProfilesInChannelMap, nil)
mockUserStore.On("GetAllProfilesInChannel", mock.Anything, "123", false).Return(fakeProfilesInChannelMap, nil)
mockUserStore.On("GetAllProfiles", mock.AnythingOfType("*model.UserGetOptions")).Return(fakeUser, nil)
mockUserStore.On("Get", mock.Anything, "123").Return(fakeUser[0], nil)
users := []*model.User{

View File

@ -21,6 +21,8 @@ type LocalCacheUserStore struct {
userProfileByIdsInvalidations map[string]bool
}
const allUserKey = "ALL"
func (s *LocalCacheUserStore) handleClusterInvalidateScheme(msg *model.ClusterMessage) {
if bytes.Equal(msg.Data, clearCacheMessageData) {
s.rootStore.userProfileByIdsCache.Purge()
@ -40,8 +42,17 @@ func (s *LocalCacheUserStore) handleClusterInvalidateProfilesInChannel(msg *mode
}
}
func (s *LocalCacheUserStore) handleClusterInvalidateAllProfiles(msg *model.ClusterMessage) {
if bytes.Equal(msg.Data, clearCacheMessageData) {
s.rootStore.allUserCache.Purge()
} else {
s.rootStore.allUserCache.Remove(string(msg.Data))
}
}
func (s *LocalCacheUserStore) ClearCaches() {
s.rootStore.userProfileByIdsCache.Purge()
s.rootStore.allUserCache.Purge()
s.rootStore.profilesInChannelCache.Purge()
if s.rootStore.metrics != nil {
@ -55,6 +66,7 @@ func (s *LocalCacheUserStore) InvalidateProfileCacheForUser(userId string) {
s.userProfileByIdsInvalidations[userId] = true
s.userProfileByIdsMut.Unlock()
s.rootStore.doInvalidateCacheCluster(s.rootStore.userProfileByIdsCache, userId, nil)
s.rootStore.doInvalidateCacheCluster(s.rootStore.allUserCache, allUserKey, nil)
if s.rootStore.metrics != nil {
s.rootStore.metrics.IncrementMemCacheInvalidationCounter("Profile By Ids - Remove")
@ -85,6 +97,30 @@ func (s *LocalCacheUserStore) InvalidateProfilesInChannelCache(channelID string)
}
}
func (s *LocalCacheUserStore) GetAllProfiles(options *model.UserGetOptions) ([]*model.User, error) {
if isEmptyOptions(options) &&
options.Page == 0 && options.PerPage == 100 { // This is hardcoded to the webapp call.
// read from cache
var users []*model.User
if err := s.rootStore.doStandardReadCache(s.rootStore.allUserCache, allUserKey, &users); err == nil {
return users, nil
}
users, err := s.UserStore.GetAllProfiles(options)
if err != nil {
return nil, err
}
// populate the cache only for those options.
s.rootStore.doStandardAddToCache(s.rootStore.allUserCache, allUserKey, users)
return users, nil
}
// For any other case, simply use the store
return s.UserStore.GetAllProfiles(options)
}
func (s *LocalCacheUserStore) GetAllProfilesInChannel(ctx context.Context, channelId string, allowFromCache bool) (map[string]*model.User, error) {
if allowFromCache {
var cachedMap map[string]*model.User
@ -261,3 +297,26 @@ func dedup(elements []string) []string {
return elements[:j+1]
}
func isEmptyOptions(options *model.UserGetOptions) bool {
// We check to see if any of the options are set or not, and then
// use the cache only if none are set, which is the most common case.
// options.WithoutTeam, Sort is unused
if options.InTeamId == "" &&
options.NotInTeamId == "" &&
options.InChannelId == "" &&
options.NotInChannelId == "" &&
options.InGroupId == "" &&
options.NotInGroupId == "" &&
!options.GroupConstrained &&
!options.Inactive &&
!options.Active &&
options.Role == "" &&
len(options.Roles) == 0 &&
len(options.ChannelRoles) == 0 &&
len(options.TeamRoles) == 0 &&
options.ViewRestrictions == nil {
return true
}
return false
}

View File

@ -118,6 +118,43 @@ func TestUserStoreCache(t *testing.T) {
})
}
func TestUserStoreGetAllProfiles(t *testing.T) {
t.Run("first call not cached, second cached and returning same data", func(t *testing.T) {
mockStore := getMockStore(t)
mockCacheProvider := getMockCacheProvider()
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider)
require.NoError(t, err)
users, err := cachedStore.User().GetAllProfiles(&model.UserGetOptions{Page: 0, PerPage: 100})
require.NoError(t, err)
mockStore.User().(*mocks.UserStore).AssertNumberOfCalls(t, "GetAllProfiles", 1)
users2, _ := cachedStore.User().GetAllProfiles(&model.UserGetOptions{Page: 0, PerPage: 100})
mockStore.User().(*mocks.UserStore).AssertNumberOfCalls(t, "GetAllProfiles", 1)
assert.Equal(t, users, users2)
_, _ = cachedStore.User().GetAllProfiles(&model.UserGetOptions{Page: 2, PerPage: 1})
mockStore.User().(*mocks.UserStore).AssertNumberOfCalls(t, "GetAllProfiles", 2)
assert.Equal(t, users, users2)
})
t.Run("different page sizes aren't cached", func(t *testing.T) {
mockStore := getMockStore(t)
mockCacheProvider := getMockCacheProvider()
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider)
require.NoError(t, err)
_, _ = cachedStore.User().GetAllProfiles(&model.UserGetOptions{Page: 0, PerPage: 100})
mockStore.User().(*mocks.UserStore).AssertNumberOfCalls(t, "GetAllProfiles", 1)
_, _ = cachedStore.User().GetAllProfiles(&model.UserGetOptions{Page: 2, PerPage: 1})
mockStore.User().(*mocks.UserStore).AssertNumberOfCalls(t, "GetAllProfiles", 2)
_, _ = cachedStore.User().GetAllProfiles(&model.UserGetOptions{Page: 1, PerPage: 2})
mockStore.User().(*mocks.UserStore).AssertNumberOfCalls(t, "GetAllProfiles", 3)
})
}
func TestUserStoreProfilesInChannelCache(t *testing.T) {
fakeChannelId := "123"
fakeUserId := "456"

View File

@ -12,6 +12,8 @@ import (
"strconv"
"sync"
sqlUtils "github.com/mattermost/mattermost/server/public/utils/sql"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/mlog"
"github.com/mattermost/mattermost/server/v8/channels/db"
@ -119,16 +121,16 @@ func (ss *SqlStore) initMorph(dryRun bool) (*morph.Morph, error) {
var driver drivers.Driver
switch ss.DriverName() {
case model.DatabaseDriverMysql:
dataSource, rErr := ResetReadTimeout(*ss.settings.DataSource)
dataSource, rErr := sqlUtils.ResetReadTimeout(*ss.settings.DataSource)
if rErr != nil {
mlog.Fatal("Failed to reset read timeout from datasource.", mlog.Err(rErr), mlog.String("src", *ss.settings.DataSource))
return nil, rErr
}
dataSource, err = AppendMultipleStatementsFlag(dataSource)
dataSource, err = sqlUtils.AppendMultipleStatementsFlag(dataSource)
if err != nil {
return nil, err
}
db, err2 := SetupConnection(ss.Logger(), "master", dataSource, ss.settings, DBPingAttempts)
db, err2 := sqlUtils.SetupConnection(ss.Logger(), "master", dataSource, ss.settings, DBPingAttempts)
if err2 != nil {
return nil, err2
}

View File

@ -15,6 +15,8 @@ import (
"sync/atomic"
"time"
sqlUtils "github.com/mattermost/mattermost/server/public/utils/sql"
sq "github.com/mattermost/squirrel"
"github.com/go-sql-driver/mysql"
@ -44,7 +46,6 @@ const (
PGDuplicateObjectErrorCode = "42710"
MySQLDuplicateObjectErrorCode = 1022
DBPingAttempts = 5
DBPingTimeoutSecs = 10
// This is a numerical version string by postgres. The format is
// 2 characters for major, minor, and patch version prior to 10.
// After 10, it's major and minor only.
@ -241,58 +242,6 @@ func New(settings model.SqlSettings, logger mlog.LoggerIFace, metrics einterface
return store, nil
}
// SetupConnection sets up the connection to the database and pings it to make sure it's alive.
// It also applies any database configuration settings that are required.
func SetupConnection(logger mlog.LoggerIFace, connType string, dataSource string, settings *model.SqlSettings, attempts int) (*dbsql.DB, error) {
db, err := dbsql.Open(*settings.DriverName, dataSource)
if err != nil {
return nil, errors.Wrap(err, "failed to open SQL connection")
}
// At this point, we have passed sql.Open, so we deliberately ignore any errors.
sanitized, _ := SanitizeDataSource(*settings.DriverName, dataSource)
logger = logger.With(
mlog.String("database", connType),
mlog.String("dataSource", sanitized),
)
for i := 0; i < attempts; i++ {
logger.Info("Pinging SQL")
ctx, cancel := context.WithTimeout(context.Background(), DBPingTimeoutSecs*time.Second)
defer cancel()
err = db.PingContext(ctx)
if err != nil {
if i == attempts-1 {
return nil, err
}
logger.Error("Failed to ping DB", mlog.Int("retrying in seconds", DBPingTimeoutSecs), mlog.Err(err))
time.Sleep(DBPingTimeoutSecs * time.Second)
continue
}
break
}
if strings.HasPrefix(connType, replicaLagPrefix) {
// If this is a replica lag connection, we just open one connection.
//
// Arguably, if the query doesn't require a special credential, it does take up
// one extra connection from the replica DB. But falling back to the replica
// data source when the replica lag data source is null implies an ordering constraint
// which makes things brittle and is not a good design.
// If connections are an overhead, it is advised to use a connection pool.
db.SetMaxOpenConns(1)
db.SetMaxIdleConns(1)
} else {
db.SetMaxIdleConns(*settings.MaxIdleConns)
db.SetMaxOpenConns(*settings.MaxOpenConns)
}
db.SetConnMaxLifetime(time.Duration(*settings.ConnMaxLifetimeMilliseconds) * time.Millisecond)
db.SetConnMaxIdleTime(time.Duration(*settings.ConnMaxIdleTimeMilliseconds) * time.Millisecond)
return db, nil
}
func (ss *SqlStore) SetContext(context context.Context) {
ss.context = context
}
@ -314,13 +263,13 @@ func (ss *SqlStore) initConnection() error {
// covers that already. Ideally we'd like to do this only for the upgrade
// step. To be reviewed in MM-35789.
var err error
dataSource, err = ResetReadTimeout(dataSource)
dataSource, err = sqlUtils.ResetReadTimeout(dataSource)
if err != nil {
return errors.Wrap(err, "failed to reset read timeout from datasource")
}
}
handle, err := SetupConnection(ss.Logger(), "master", dataSource, ss.settings, DBPingAttempts)
handle, err := sqlUtils.SetupConnection(ss.Logger(), "master", dataSource, ss.settings, DBPingAttempts)
if err != nil {
return err
}
@ -338,7 +287,7 @@ func (ss *SqlStore) initConnection() error {
ss.ReplicaXs = make([]*atomic.Pointer[sqlxDBWrapper], len(ss.settings.DataSourceReplicas))
for i, replica := range ss.settings.DataSourceReplicas {
ss.ReplicaXs[i] = &atomic.Pointer[sqlxDBWrapper]{}
handle, err = SetupConnection(ss.Logger(), fmt.Sprintf("replica-%v", i), replica, ss.settings, DBPingAttempts)
handle, err = sqlUtils.SetupConnection(ss.Logger(), fmt.Sprintf("replica-%v", i), replica, ss.settings, DBPingAttempts)
if err != nil {
// Initializing to be offline
ss.ReplicaXs[i].Store(&sqlxDBWrapper{isOnline: &atomic.Bool{}})
@ -353,7 +302,7 @@ func (ss *SqlStore) initConnection() error {
ss.searchReplicaXs = make([]*atomic.Pointer[sqlxDBWrapper], len(ss.settings.DataSourceSearchReplicas))
for i, replica := range ss.settings.DataSourceSearchReplicas {
ss.searchReplicaXs[i] = &atomic.Pointer[sqlxDBWrapper]{}
handle, err = SetupConnection(ss.Logger(), fmt.Sprintf("search-replica-%v", i), replica, ss.settings, DBPingAttempts)
handle, err = sqlUtils.SetupConnection(ss.Logger(), fmt.Sprintf("search-replica-%v", i), replica, ss.settings, DBPingAttempts)
if err != nil {
// Initializing to be offline
ss.searchReplicaXs[i].Store(&sqlxDBWrapper{isOnline: &atomic.Bool{}})
@ -370,7 +319,7 @@ func (ss *SqlStore) initConnection() error {
if src.DataSource == nil {
continue
}
ss.replicaLagHandles[i], err = SetupConnection(ss.Logger(), fmt.Sprintf(replicaLagPrefix+"-%d", i), *src.DataSource, ss.settings, DBPingAttempts)
ss.replicaLagHandles[i], err = sqlUtils.SetupConnection(ss.Logger(), fmt.Sprintf(replicaLagPrefix+"-%d", i), *src.DataSource, ss.settings, DBPingAttempts)
if err != nil {
mlog.Warn("Failed to setup replica lag handle. Skipping..", mlog.String("db", fmt.Sprintf(replicaLagPrefix+"-%d", i)), mlog.Err(err))
continue
@ -525,7 +474,7 @@ func (ss *SqlStore) monitorReplicas() {
return
}
handle, err := SetupConnection(ss.Logger(), name, dsn, ss.settings, 1)
handle, err := sqlUtils.SetupConnection(ss.Logger(), name, dsn, ss.settings, 1)
if err != nil {
mlog.Warn("Failed to setup connection. Skipping..", mlog.String("db", name), mlog.Err(err))
return

View File

@ -5,7 +5,6 @@ package sqlstore
import (
"database/sql"
"errors"
"io"
"net/url"
"strconv"
@ -16,8 +15,6 @@ import (
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/mlog"
"github.com/go-sql-driver/mysql"
)
var escapeLikeSearchChar = []string{
@ -183,57 +180,6 @@ func AppendBinaryFlag(buf []byte) []byte {
return append([]byte{0x01}, buf...)
}
// AppendMultipleStatementsFlag attached dsn parameters to MySQL dsn in order to make migrations work.
func AppendMultipleStatementsFlag(dataSource string) (string, error) {
config, err := mysql.ParseDSN(dataSource)
if err != nil {
return "", err
}
if config.Params == nil {
config.Params = map[string]string{}
}
config.Params["multiStatements"] = "true"
return config.FormatDSN(), nil
}
// ResetReadTimeout removes the timeout constraint from the MySQL dsn.
func ResetReadTimeout(dataSource string) (string, error) {
config, err := mysql.ParseDSN(dataSource)
if err != nil {
return "", err
}
config.ReadTimeout = 0
return config.FormatDSN(), nil
}
func SanitizeDataSource(driverName, dataSource string) (string, error) {
switch driverName {
case model.DatabaseDriverPostgres:
u, err := url.Parse(dataSource)
if err != nil {
return "", err
}
u.User = url.UserPassword("****", "****")
params := u.Query()
params.Del("user")
params.Del("password")
u.RawQuery = params.Encode()
return u.String(), nil
case model.DatabaseDriverMysql:
cfg, err := mysql.ParseDSN(dataSource)
if err != nil {
return "", err
}
cfg.User = "****"
cfg.Passwd = "****"
return cfg.FormatDSN(), nil
default:
return "", errors.New("invalid drivername. Not postgres or mysql.")
}
}
const maxTokenSize = 50
// trimInput limits the string to a max size to prevent clogging up disk space

View File

@ -6,7 +6,6 @@ package sqlstore
import (
"testing"
"github.com/mattermost/mattermost/server/public/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -134,72 +133,3 @@ func TestMySQLJSONArgs(t *testing.T) {
assert.Equal(t, test.argString, argString)
}
}
func TestAppendMultipleStatementsFlag(t *testing.T) {
testCases := []struct {
Scenario string
DSN string
ExpectedDSN string
}{
{
"Should append multiStatements param to the DSN path with existing params",
"user:rand?&ompasswith@character@unix(/var/run/mysqld/mysqld.sock)/mattermost?writeTimeout=30s",
"user:rand?&ompasswith@character@unix(/var/run/mysqld/mysqld.sock)/mattermost?writeTimeout=30s&multiStatements=true",
},
{
"Should append multiStatements param to the DSN path with no existing params",
"user:rand?&ompasswith@character@unix(/var/run/mysqld/mysqld.sock)/mattermost",
"user:rand?&ompasswith@character@unix(/var/run/mysqld/mysqld.sock)/mattermost?multiStatements=true",
},
}
for _, tc := range testCases {
t.Run(tc.Scenario, func(t *testing.T) {
res, err := AppendMultipleStatementsFlag(tc.DSN)
require.NoError(t, err)
assert.Equal(t, tc.ExpectedDSN, res)
})
}
}
func TestSanitizeDataSource(t *testing.T) {
t.Run(model.DatabaseDriverPostgres, func(t *testing.T) {
testCases := []struct {
Original string
Sanitized string
}{
{
"postgres://mmuser:mostest@localhost/dummy?sslmode=disable",
"postgres://%2A%2A%2A%2A:%2A%2A%2A%2A@localhost/dummy?sslmode=disable",
},
{
"postgres://localhost/dummy?sslmode=disable&user=mmuser&password=mostest",
"postgres://%2A%2A%2A%2A:%2A%2A%2A%2A@localhost/dummy?sslmode=disable",
},
}
driver := model.DatabaseDriverPostgres
for _, tc := range testCases {
out, err := SanitizeDataSource(driver, tc.Original)
require.NoError(t, err)
assert.Equal(t, tc.Sanitized, out)
}
})
t.Run(model.DatabaseDriverMysql, func(t *testing.T) {
testCases := []struct {
Original string
Sanitized string
}{
{
"mmuser:mostest@tcp(localhost:3306)/mattermost_test?charset=utf8mb4,utf8&readTimeout=30s&writeTimeout=30s",
"****:****@tcp(localhost:3306)/mattermost_test?readTimeout=30s&writeTimeout=30s&charset=utf8mb4%2Cutf8",
},
}
driver := model.DatabaseDriverMysql
for _, tc := range testCases {
out, err := SanitizeDataSource(driver, tc.Original)
require.NoError(t, err)
assert.Equal(t, tc.Sanitized, out)
}
})
}

View File

@ -22,14 +22,14 @@ func (s *MmctlE2ETestSuite) TestListBotCmdF() {
bot, appErr := s.th.App.CreateBot(s.th.Context, &model.Bot{Username: model.NewId(), OwnerId: s.th.BasicUser.Id})
s.Require().Nil(appErr)
defer func() {
err := s.th.App.PermanentDeleteBot(bot.UserId)
err := s.th.App.PermanentDeleteBot(s.th.Context, bot.UserId)
s.Require().Nil(err)
}()
deletedBot, appErr := s.th.App.CreateBot(s.th.Context, &model.Bot{Username: model.NewId(), OwnerId: s.th.BasicUser.Id})
s.Require().Nil(appErr)
defer func() {
err := s.th.App.PermanentDeleteBot(deletedBot.UserId)
err := s.th.App.PermanentDeleteBot(s.th.Context, deletedBot.UserId)
s.Require().Nil(err)
}()
@ -59,14 +59,14 @@ func (s *MmctlE2ETestSuite) TestListBotCmdF() {
bot, appErr := s.th.App.CreateBot(s.th.Context, &model.Bot{Username: model.NewId(), OwnerId: s.th.BasicUser.Id})
s.Require().Nil(appErr)
defer func() {
err := s.th.App.PermanentDeleteBot(bot.UserId)
err := s.th.App.PermanentDeleteBot(s.th.Context, bot.UserId)
s.Require().Nil(err)
}()
deletedBot, appErr := s.th.App.CreateBot(s.th.Context, &model.Bot{Username: model.NewId(), OwnerId: user.Id})
s.Require().Nil(appErr)
defer func() {
err := s.th.App.PermanentDeleteBot(deletedBot.UserId)
err := s.th.App.PermanentDeleteBot(s.th.Context, deletedBot.UserId)
s.Require().Nil(err)
}()
@ -76,7 +76,7 @@ func (s *MmctlE2ETestSuite) TestListBotCmdF() {
orphanBot, appErr := s.th.App.CreateBot(s.th.Context, &model.Bot{Username: model.NewId(), OwnerId: user.Id})
s.Require().Nil(appErr)
defer func() {
err := s.th.App.PermanentDeleteBot(orphanBot.UserId)
err := s.th.App.PermanentDeleteBot(s.th.Context, orphanBot.UserId)
s.Require().Nil(err)
}()
@ -106,21 +106,21 @@ func (s *MmctlE2ETestSuite) TestListBotCmdF() {
bot, appErr := s.th.App.CreateBot(s.th.Context, &model.Bot{Username: model.NewId(), OwnerId: s.th.BasicUser2.Id})
s.Require().Nil(appErr)
defer func() {
err := s.th.App.PermanentDeleteBot(bot.UserId)
err := s.th.App.PermanentDeleteBot(s.th.Context, bot.UserId)
s.Require().Nil(err)
}()
orphanBot, appErr := s.th.App.CreateBot(s.th.Context, &model.Bot{Username: model.NewId(), OwnerId: user.Id})
s.Require().Nil(appErr)
defer func() {
err := s.th.App.PermanentDeleteBot(orphanBot.UserId)
err := s.th.App.PermanentDeleteBot(s.th.Context, orphanBot.UserId)
s.Require().Nil(err)
}()
deletedBot, appErr := s.th.App.CreateBot(s.th.Context, &model.Bot{Username: model.NewId(), OwnerId: s.th.BasicUser2.Id})
s.Require().Nil(appErr)
defer func() {
err := s.th.App.PermanentDeleteBot(deletedBot.UserId)
err := s.th.App.PermanentDeleteBot(s.th.Context, deletedBot.UserId)
s.Require().Nil(err)
}()
@ -181,7 +181,7 @@ func (s *MmctlE2ETestSuite) TestBotEnableCmd() {
s.Require().Equal(newBot.Username, printedBot.Username)
s.Require().Equal(newBot.OwnerId, printedBot.OwnerId)
bot, appErr := s.th.App.GetBot(newBot.UserId, false)
bot, appErr := s.th.App.GetBot(s.th.Context, newBot.UserId, false)
s.Require().Nil(appErr)
s.Require().Equal(newBot.UserId, bot.UserId)
s.Require().Equal(newBot.Username, bot.Username)
@ -235,7 +235,7 @@ func (s *MmctlE2ETestSuite) TestBotEnableCmd() {
s.Require().Equal(newBot.Username, printedBot.Username)
s.Require().Equal(newBot.OwnerId, printedBot.OwnerId)
bot, appErr := s.th.App.GetBot(newBot.UserId, false)
bot, appErr := s.th.App.GetBot(s.th.Context, newBot.UserId, false)
s.Require().Nil(appErr)
s.Require().Equal(newBot.UserId, bot.UserId)
s.Require().Equal(newBot.Username, bot.Username)
@ -269,7 +269,7 @@ func (s *MmctlE2ETestSuite) TestBotDisableCmd() {
s.Require().Equal(newBot.Username, printedBot.Username)
s.Require().Equal(newBot.OwnerId, printedBot.OwnerId)
_, appErr = s.th.App.GetBot(newBot.UserId, false)
_, appErr = s.th.App.GetBot(s.th.Context, newBot.UserId, false)
s.Require().NotNil(appErr)
s.Require().Equal("store.sql_bot.get.missing.app_error", appErr.Id)
})
@ -321,7 +321,7 @@ func (s *MmctlE2ETestSuite) TestBotDisableCmd() {
s.Require().Equal(newBot.Username, printedBot.Username)
s.Require().Equal(newBot.OwnerId, printedBot.OwnerId)
_, appErr = s.th.App.GetBot(newBot.UserId, false)
_, appErr = s.th.App.GetBot(s.th.Context, newBot.UserId, false)
s.Require().NotNil(appErr)
s.Require().Equal("store.sql_bot.get.missing.app_error", appErr.Id)
})
@ -351,7 +351,7 @@ func (s *MmctlE2ETestSuite) TestBotAssignCmdF() {
s.Require().Nil(appErr)
s.Require().Equal(bot.OwnerId, botOwner.Id)
defer func() {
err := s.th.App.PermanentDeleteBot(bot.UserId)
err := s.th.App.PermanentDeleteBot(s.th.Context, bot.UserId)
s.Require().Nil(err)
}()
@ -384,7 +384,7 @@ func (s *MmctlE2ETestSuite) TestBotAssignCmdF() {
s.Require().Nil(appErr)
s.Require().Equal(bot.OwnerId, botOwner.Id)
defer func() {
err := s.th.App.PermanentDeleteBot(bot.UserId)
err := s.th.App.PermanentDeleteBot(s.th.Context, bot.UserId)
s.Require().Nil(err)
}()
@ -420,7 +420,7 @@ func (s *MmctlE2ETestSuite) TestBotCreateCmdF() {
bot, ok := printer.GetLines()[0].(*model.Bot)
s.Require().True(ok)
defer func() {
err := s.th.App.PermanentDeleteBot(bot.UserId)
err := s.th.App.PermanentDeleteBot(s.th.Context, bot.UserId)
s.Require().Nil(err)
}()
token, ok := printer.GetLines()[1].(*model.UserAccessToken)

View File

@ -15,6 +15,8 @@ import (
"path/filepath"
"strings"
sqlUtils "github.com/mattermost/mattermost/server/public/utils/sql"
"github.com/jmoiron/sqlx"
"github.com/pkg/errors"
@ -27,8 +29,6 @@ import (
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/mlog"
"github.com/mattermost/mattermost/server/v8/channels/store/sqlstore"
"github.com/mattermost/morph/drivers"
ms "github.com/mattermost/morph/drivers/mysql"
ps "github.com/mattermost/morph/drivers/postgres"
@ -121,12 +121,12 @@ func (ds *DatabaseStore) initializeConfigurationsTable() error {
var driver drivers.Driver
switch ds.driverName {
case model.DatabaseDriverMysql:
dataSource, rErr := sqlstore.ResetReadTimeout(ds.dataSourceName)
dataSource, rErr := sqlUtils.ResetReadTimeout(ds.dataSourceName)
if rErr != nil {
return fmt.Errorf("failed to reset read timeout from datasource: %w", rErr)
}
dataSource, err = sqlstore.AppendMultipleStatementsFlag(dataSource)
dataSource, err = sqlUtils.AppendMultipleStatementsFlag(dataSource)
if err != nil {
return err
}
@ -409,7 +409,7 @@ func (ds *DatabaseStore) RemoveFile(name string) error {
func (ds *DatabaseStore) String() string {
// This is called during the running of MM, so we expect the parsing of DSN
// to be successful.
sanitized, _ := sqlstore.SanitizeDataSource(ds.driverName, ds.originalDsn)
sanitized, _ := sqlUtils.SanitizeDataSource(ds.driverName, ds.originalDsn)
return sanitized
}

View File

@ -7932,7 +7932,7 @@
},
{
"id": "ent.ldap.syncronize.search_failure_size_exceeded.app_error",
"translation": "Size Limit Exceeded. Try checking your [max page size](https://docs.mattermost.com/deployment/sso-ldap.html#i-see-the-log-error-ldap-result-code-4-size-limit-exceeded)."
"translation": "Size Limit Exceeded. Try increasing your Maximum page size setting. Check out https://docs.mattermost.com/onboard/ad-ldap.html#i-see-the-log-error-ldap-result-code-4-size-limit-exceeded for more details."
},
{
"id": "ent.ldap.validate_admin_filter.app_error",

View File

@ -20,6 +20,7 @@ const (
ClusterEventInvalidateCacheForRoles ClusterEvent = "inv_roles"
ClusterEventInvalidateCacheForRolePermissions ClusterEvent = "inv_role_permissions"
ClusterEventInvalidateCacheForProfileByIds ClusterEvent = "inv_profile_ids"
ClusterEventInvalidateCacheForAllProfiles ClusterEvent = "inv_all_profiles"
ClusterEventInvalidateCacheForProfileInChannel ClusterEvent = "inv_profile_in_channel"
ClusterEventInvalidateCacheForSchemes ClusterEvent = "inv_schemes"
ClusterEventInvalidateCacheForFileInfos ClusterEvent = "inv_file_infos"

View File

@ -0,0 +1,126 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package sql
import (
"context"
dbsql "database/sql"
"net/url"
"strings"
"time"
"github.com/go-sql-driver/mysql"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/mlog"
"github.com/pkg/errors"
)
const (
DBPingTimeoutSecs = 10
replicaLagPrefix = "replica-lag"
)
// ResetReadTimeout removes the timeout constraint from the MySQL dsn.
func ResetReadTimeout(dataSource string) (string, error) {
config, err := mysql.ParseDSN(dataSource)
if err != nil {
return "", err
}
config.ReadTimeout = 0
return config.FormatDSN(), nil
}
// AppendMultipleStatementsFlag attached dsn parameters to MySQL dsn in order to make migrations work.
func AppendMultipleStatementsFlag(dataSource string) (string, error) {
config, err := mysql.ParseDSN(dataSource)
if err != nil {
return "", err
}
if config.Params == nil {
config.Params = map[string]string{}
}
config.Params["multiStatements"] = "true"
return config.FormatDSN(), nil
}
// SetupConnection sets up the connection to the database and pings it to make sure it's alive.
// It also applies any database configuration settings that are required.
func SetupConnection(logger mlog.LoggerIFace, connType string, dataSource string, settings *model.SqlSettings, attempts int) (*dbsql.DB, error) {
db, err := dbsql.Open(*settings.DriverName, dataSource)
if err != nil {
return nil, errors.Wrap(err, "failed to open SQL connection")
}
// At this point, we have passed sql.Open, so we deliberately ignore any errors.
sanitized, _ := SanitizeDataSource(*settings.DriverName, dataSource)
logger = logger.With(
mlog.String("database", connType),
mlog.String("dataSource", sanitized),
)
for i := 0; i < attempts; i++ {
logger.Info("Pinging SQL")
ctx, cancel := context.WithTimeout(context.Background(), DBPingTimeoutSecs*time.Second)
defer cancel()
err = db.PingContext(ctx)
if err != nil {
if i == attempts-1 {
return nil, err
}
logger.Error("Failed to ping DB", mlog.Int("retrying in seconds", DBPingTimeoutSecs), mlog.Err(err))
time.Sleep(DBPingTimeoutSecs * time.Second)
continue
}
break
}
if strings.HasPrefix(connType, replicaLagPrefix) {
// If this is a replica lag connection, we just open one connection.
//
// Arguably, if the query doesn't require a special credential, it does take up
// one extra connection from the replica DB. But falling back to the replica
// data source when the replica lag data source is null implies an ordering constraint
// which makes things brittle and is not a good design.
// If connections are an overhead, it is advised to use a connection pool.
db.SetMaxOpenConns(1)
db.SetMaxIdleConns(1)
} else {
db.SetMaxIdleConns(*settings.MaxIdleConns)
db.SetMaxOpenConns(*settings.MaxOpenConns)
}
db.SetConnMaxLifetime(time.Duration(*settings.ConnMaxLifetimeMilliseconds) * time.Millisecond)
db.SetConnMaxIdleTime(time.Duration(*settings.ConnMaxIdleTimeMilliseconds) * time.Millisecond)
return db, nil
}
func SanitizeDataSource(driverName, dataSource string) (string, error) {
switch driverName {
case model.DatabaseDriverPostgres:
u, err := url.Parse(dataSource)
if err != nil {
return "", err
}
u.User = url.UserPassword("****", "****")
params := u.Query()
params.Del("user")
params.Del("password")
u.RawQuery = params.Encode()
return u.String(), nil
case model.DatabaseDriverMysql:
cfg, err := mysql.ParseDSN(dataSource)
if err != nil {
return "", err
}
cfg.User = "****"
cfg.Passwd = "****"
return cfg.FormatDSN(), nil
default:
return "", errors.New("invalid drivername. Not postgres or mysql.")
}
}

View File

@ -0,0 +1,109 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package sql
import (
"testing"
"github.com/mattermost/mattermost/server/public/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAppendMultipleStatementsFlag(t *testing.T) {
testCases := []struct {
Scenario string
DSN string
ExpectedDSN string
}{
{
"Should append multiStatements param to the DSN path with existing params",
"user:rand?&ompasswith@character@unix(/var/run/mysqld/mysqld.sock)/mattermost?writeTimeout=30s",
"user:rand?&ompasswith@character@unix(/var/run/mysqld/mysqld.sock)/mattermost?writeTimeout=30s&multiStatements=true",
},
{
"Should append multiStatements param to the DSN path with no existing params",
"user:rand?&ompasswith@character@unix(/var/run/mysqld/mysqld.sock)/mattermost",
"user:rand?&ompasswith@character@unix(/var/run/mysqld/mysqld.sock)/mattermost?multiStatements=true",
},
}
for _, tc := range testCases {
t.Run(tc.Scenario, func(t *testing.T) {
res, err := AppendMultipleStatementsFlag(tc.DSN)
require.NoError(t, err)
assert.Equal(t, tc.ExpectedDSN, res)
})
}
}
func TestResetReadTimeout(t *testing.T) {
testCases := []struct {
Scenario string
DSN string
ExpectedDSN string
}{
{
"Should re move read timeout param from the DSN",
"user:rand?&ompasswith@character@unix(/var/run/mysqld/mysqld.sock)/mattermost?readTimeout=30s",
"user:rand?&ompasswith@character@unix(/var/run/mysqld/mysqld.sock)/mattermost",
},
{
"Should change nothing as there is no read timeout param specified",
"user:rand?&ompasswith@character@unix(/var/run/mysqld/mysqld.sock)/mattermost",
"user:rand?&ompasswith@character@unix(/var/run/mysqld/mysqld.sock)/mattermost",
},
}
for _, tc := range testCases {
t.Run(tc.Scenario, func(t *testing.T) {
res, err := ResetReadTimeout(tc.DSN)
require.NoError(t, err)
assert.Equal(t, tc.ExpectedDSN, res)
})
}
}
func TestSanitizeDataSource(t *testing.T) {
t.Run(model.DatabaseDriverPostgres, func(t *testing.T) {
testCases := []struct {
Original string
Sanitized string
}{
{
"postgres://mmuser:mostest@localhost/dummy?sslmode=disable",
"postgres://%2A%2A%2A%2A:%2A%2A%2A%2A@localhost/dummy?sslmode=disable",
},
{
"postgres://localhost/dummy?sslmode=disable&user=mmuser&password=mostest",
"postgres://%2A%2A%2A%2A:%2A%2A%2A%2A@localhost/dummy?sslmode=disable",
},
}
driver := model.DatabaseDriverPostgres
for _, tc := range testCases {
out, err := SanitizeDataSource(driver, tc.Original)
require.NoError(t, err)
assert.Equal(t, tc.Sanitized, out)
}
})
t.Run(model.DatabaseDriverMysql, func(t *testing.T) {
testCases := []struct {
Original string
Sanitized string
}{
{
"mmuser:mostest@tcp(localhost:3306)/mattermost_test?charset=utf8mb4,utf8&readTimeout=30s&writeTimeout=30s",
"****:****@tcp(localhost:3306)/mattermost_test?readTimeout=30s&writeTimeout=30s&charset=utf8mb4%2Cutf8",
},
}
driver := model.DatabaseDriverMysql
for _, tc := range testCases {
out, err := SanitizeDataSource(driver, tc.Original)
require.NoError(t, err)
assert.Equal(t, tc.Sanitized, out)
}
})
}

View File

@ -77,6 +77,7 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with
isCurrentUserSystemAdmin={false}
isDisabled={false}
license={Object {}}
patchConfig={[MockFunction]}
roles={Object {}}
schema={
Object {
@ -84,7 +85,6 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with
}
}
setNavigationBlocked={[MockFunction]}
updateConfig={[MockFunction]}
/>
`;

View File

@ -8,6 +8,7 @@ import type {RouteComponentProps} from 'react-router-dom';
import type {CloudState} from '@mattermost/types/cloud';
import type {AdminConfig, ClientLicense, EnvironmentConfig} from '@mattermost/types/config';
import type {Role} from '@mattermost/types/roles';
import type {DeepPartial} from '@mattermost/types/utilities';
import type {ActionResult} from 'mattermost-redux/types/actions';
@ -43,7 +44,7 @@ type ExtraProps = {
setNavigationBlocked: (blocked: boolean) => void;
roles: Record<string, Role>;
editRole: (role: Role) => void;
updateConfig: (config: AdminConfig) => Promise<ActionResult>;
patchConfig: (config: DeepPartial<AdminConfig>) => Promise<ActionResult>;
cloud: CloudState;
isCurrentUserSystemAdmin: boolean;
}
@ -173,7 +174,7 @@ class AdminConsole extends React.PureComponent<Props, State> {
showNavigationPrompt,
roles,
} = this.props;
const {setNavigationBlocked, cancelNavigation, confirmNavigation, editRole, updateConfig} = this.props.actions;
const {setNavigationBlocked, cancelNavigation, confirmNavigation, editRole, patchConfig} = this.props.actions;
if (!this.props.currentUserHasAnAdminRole) {
return (
@ -203,7 +204,7 @@ class AdminConsole extends React.PureComponent<Props, State> {
setNavigationBlocked,
roles,
editRole,
updateConfig,
patchConfig,
cloud: this.props.cloud,
isCurrentUserSystemAdmin: this.props.isCurrentUserSystemAdmin,
};

View File

@ -19,7 +19,7 @@ export type BaseProps = {
environmentConfig?: EnvironmentConfig;
setNavigationBlocked?: (blocked: boolean) => void;
isDisabled?: boolean;
updateConfig?: (config: AdminConfig) => {data: AdminConfig; error: ClientErrorPlaceholder};
patchConfig?: (config: DeepPartial<AdminConfig>) => {data: AdminConfig; error: ClientErrorPlaceholder};
}
export type BaseState = {
@ -31,7 +31,7 @@ export type BaseState = {
}
// Placeholder type until ClientError is exported from redux.
// TODO: remove ClientErrorPlaceholder and change the return type of updateConfig
// TODO: remove ClientErrorPlaceholder and change the return type of patchConfig
type ClientErrorPlaceholder = {
message: string;
server_error_id: string;
@ -107,8 +107,8 @@ export default abstract class AdminSettings <Props extends BaseProps, State exte
let config = JSON.parse(JSON.stringify(this.props.config));
config = this.getConfigFromState(config);
if (this.props.updateConfig) {
const {data, error} = await this.props.updateConfig(config);
if (this.props.patchConfig) {
const {data, error} = await this.props.patchConfig(config);
if (data) {
this.setState(this.getStateFromConfig(data) as State);

View File

@ -137,7 +137,7 @@ describe('components/admin_console/CustomPluginSettings', () => {
{...baseProps}
config={config}
schema={{...plugin.settings_schema, id: plugin.id, name: plugin.name, settings}}
updateConfig={jest.fn()}
patchConfig={jest.fn()}
/>,
);
expect(wrapper).toMatchSnapshot();
@ -152,7 +152,7 @@ describe('components/admin_console/CustomPluginSettings', () => {
id: 'testplugin',
name: 'testplugin',
}}
updateConfig={jest.fn()}
patchConfig={jest.fn()}
/>,
);
expect(wrapper).toMatchSnapshot();
@ -171,7 +171,7 @@ describe('components/admin_console/CustomPluginSettings', () => {
} as PluginSettings,
}}
schema={{...plugin.settings_schema, id: plugin.id, name: plugin.name, settings}}
updateConfig={jest.fn()}
patchConfig={jest.fn()}
/>,
);
expect(wrapper).toMatchSnapshot();

View File

@ -25,7 +25,7 @@ describe('components/admin_console/CustomTermsOfServiceSettings', () => {
CustomTermsOfService: 'true',
},
setNavigationBlocked: jest.fn(),
updateConfig: jest.fn(),
patchConfig: jest.fn(),
};
test('should match snapshot', () => {

View File

@ -31,7 +31,7 @@ type Props = BaseProps & {
/*
* Action to save config file
*/
updateConfig: () => void;
patchConfig: () => void;
};
type State = BaseState & {
@ -120,7 +120,7 @@ export default class CustomTermsOfServiceSettings extends AdminSettings<Props, S
let config = JSON.parse(JSON.stringify(this.props.config));
config = this.getConfigFromState(config);
const {data, error} = await this.props.updateConfig(config);
const {data, error} = await this.props.patchConfig(config);
if (data) {
this.setState(this.getStateFromConfig(data));

View File

@ -29,7 +29,7 @@ describe('components/admin_console/data_retention_settings/data_retention_settin
createJob: jest.fn(),
getJobsByType: jest.fn().mockResolvedValue([]),
deleteDataRetentionCustomPolicy: jest.fn(),
updateConfig: jest.fn(),
patchConfig: jest.fn(),
},
};

View File

@ -44,7 +44,7 @@ type Props = {
createJob: (job: JobTypeBase) => Promise<ActionResult>;
getJobsByType: (job: JobType) => Promise<ActionResult>;
deleteDataRetentionCustomPolicy: (id: string) => Promise<ActionResult>;
updateConfig: (config: AdminConfig) => Promise<ActionResult>;
patchConfig: (config: DeepPartial<AdminConfig>) => Promise<ActionResult>;
};
} & WrappedComponentProps;
@ -437,7 +437,7 @@ class DataRetentionSettings extends React.PureComponent<Props, State> {
const newConfig = JSON.parse(JSON.stringify(this.props.config));
newConfig.DataRetentionSettings.DeletionJobStartTime = value;
await this.props.actions.updateConfig(newConfig);
await this.props.actions.patchConfig(newConfig);
this.inputRef.current?.blur();
};

View File

@ -21,7 +21,7 @@ describe('components/PluginManagement', () => {
fileRetentionHours: '2400',
environmentConfig: {},
actions: {
updateConfig: jest.fn(),
patchConfig: jest.fn(),
setNavigationBlocked: jest.fn(),
},
};

View File

@ -32,7 +32,7 @@ type Props = {
fileRetentionHours: string | undefined;
environmentConfig: Partial<EnvironmentConfig>;
actions: {
updateConfig: (config: AdminConfig) => Promise<ActionResult>;
patchConfig: (config: DeepPartial<AdminConfig>) => Promise<ActionResult>;
setNavigationBlocked: (blocked: boolean) => void;
};
};
@ -121,7 +121,7 @@ export default class GlobalPolicyForm extends React.PureComponent<Props, State>
newConfig.DataRetentionSettings.FileRetentionHours = this.setRetentionHours(fileRetentionDropdownValue.value, fileRetentionInputValue);
}
const {error} = await this.props.actions.updateConfig(newConfig);
const {error} = await this.props.actions.patchConfig(newConfig);
if (error) {
this.setState({serverError: error.message, saving: false});

View File

@ -6,7 +6,7 @@ import {bindActionCreators} from 'redux';
import type {Dispatch} from 'redux';
import {
updateConfig,
patchConfig,
} from 'mattermost-redux/actions/admin';
import {getEnvironmentConfig} from 'mattermost-redux/selectors/entities/admin';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
@ -31,7 +31,7 @@ function mapStateToProps(state: GlobalState) {
function mapDispatchToProps(dispatch: Dispatch) {
return {
actions: bindActionCreators({
updateConfig,
patchConfig,
setNavigationBlocked,
}, dispatch),
};

View File

@ -5,7 +5,7 @@ import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import type {Dispatch} from 'redux';
import {getDataRetentionCustomPolicies as fetchDataRetentionCustomPolicies, deleteDataRetentionCustomPolicy, updateConfig} from 'mattermost-redux/actions/admin';
import {getDataRetentionCustomPolicies as fetchDataRetentionCustomPolicies, deleteDataRetentionCustomPolicy, patchConfig} from 'mattermost-redux/actions/admin';
import {createJob, getJobsByType} from 'mattermost-redux/actions/jobs';
import {getDataRetentionCustomPolicies, getDataRetentionCustomPoliciesCount} from 'mattermost-redux/selectors/entities/admin';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
@ -35,7 +35,7 @@ function mapDispatchToProps(dispatch: Dispatch) {
createJob,
getJobsByType,
deleteDataRetentionCustomPolicy,
updateConfig,
patchConfig,
}, dispatch),
};
}

View File

@ -6,7 +6,7 @@ import type {ConnectedProps} from 'react-redux';
import {bindActionCreators} from 'redux';
import type {Dispatch} from 'redux';
import {getConfig, getEnvironmentConfig, updateConfig} from 'mattermost-redux/actions/admin';
import {getConfig, getEnvironmentConfig, patchConfig} from 'mattermost-redux/actions/admin';
import {loadRolesIfNeeded, editRole} from 'mattermost-redux/actions/roles';
import {selectTeam} from 'mattermost-redux/actions/teams';
import {General} from 'mattermost-redux/constants';
@ -60,7 +60,7 @@ function mapDispatchToProps(dispatch: Dispatch) {
actions: bindActionCreators({
getConfig,
getEnvironmentConfig,
updateConfig,
patchConfig,
setNavigationBlocked,
deferNavigation,
cancelNavigation,

View File

@ -5,14 +5,14 @@ import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import type {Dispatch} from 'redux';
import {updateConfig} from 'mattermost-redux/actions/admin';
import {patchConfig} from 'mattermost-redux/actions/admin';
import OpenIdConvert from './openid_convert';
function mapDispatchToProps(dispatch: Dispatch) {
return {
actions: bindActionCreators({
updateConfig,
patchConfig,
}, dispatch),
};
}

View File

@ -9,7 +9,7 @@ import OpenIdConvert from 'components/admin_console/openid_convert/openid_conver
describe('components/OpenIdConvert', () => {
const baseProps = {
actions: {
updateConfig: jest.fn(),
patchConfig: jest.fn(),
},
};

View File

@ -5,6 +5,7 @@ import React from 'react';
import {FormattedMessage} from 'react-intl';
import type {AdminConfig} from '@mattermost/types/config';
import type {DeepPartial} from '@mattermost/types/utilities';
import type {ActionResult} from 'mattermost-redux/types/actions';
@ -21,7 +22,7 @@ import './openid_convert.scss';
type Props = BaseProps & {
disabled?: boolean;
actions: {
updateConfig: (config: AdminConfig) => Promise<ActionResult>;
patchConfig: (config: DeepPartial<AdminConfig>) => Promise<ActionResult>;
};
};
type State = {
@ -59,7 +60,7 @@ export default class OpenIdConvert extends React.PureComponent<Props, State> {
newConfig[setting].TokenEndpoint = '';
});
const {error: err} = await this.props.actions.updateConfig(newConfig);
const {error: err} = await this.props.actions.patchConfig(newConfig);
if (err) {
this.setState({serverError: err.message});
} else {

View File

@ -6,6 +6,7 @@ import {Modal} from 'react-bootstrap';
import {FormattedMessage} from 'react-intl';
import type {AdminConfig} from '@mattermost/types/config';
import type {DeepPartial} from '@mattermost/types/utilities';
import type {ActionResult} from 'mattermost-redux/types/actions';
@ -22,7 +23,7 @@ type Props ={
show: boolean;
onClose: () => void;
actions: {
updateConfig: (config: AdminConfig) => Promise<ActionResult>;
patchConfig: (config: DeepPartial<AdminConfig>) => Promise<ActionResult>;
};
}
@ -48,7 +49,7 @@ export default function EditPostTimeLimitModal(props: Props) {
const newConfig = JSON.parse(JSON.stringify(props.config));
newConfig.ServiceSettings.PostEditTimeLimit = alwaysAllowPostEditing ? Constants.UNSET_POST_EDIT_TIME_LIMIT : postEditTimeLimit;
const {error} = await props.actions.updateConfig(newConfig);
const {error} = await props.actions.patchConfig(newConfig);
if (error) {
setErrorMessage(error.message);
setSaving(false);

View File

@ -5,7 +5,7 @@ import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import type {Dispatch} from 'redux';
import {updateConfig} from 'mattermost-redux/actions/admin';
import {patchConfig} from 'mattermost-redux/actions/admin';
import {getConfig} from 'mattermost-redux/selectors/entities/admin';
import type {GlobalState} from 'types/store';
@ -20,7 +20,7 @@ function mapStateToProps(state: GlobalState) {
function mapDispatchToProps(dispatch: Dispatch) {
return {
actions: bindActionCreators({updateConfig}, dispatch),
actions: bindActionCreators({patchConfig}, dispatch),
};
}

View File

@ -256,7 +256,7 @@ describe('components/admin_console/SchemaAdminSettings', () => {
config={config}
environmentConfig={environmentConfig}
schema={{...schema} as AdminDefinitionSubSectionSchema}
updateConfig={jest.fn()}
patchConfig={jest.fn()}
/>,
);
expect(wrapper).toMatchSnapshot();
@ -269,7 +269,7 @@ describe('components/admin_console/SchemaAdminSettings', () => {
config={config}
environmentConfig={environmentConfig}
schema={{component: () => <p>{'Test'}</p>} as AdminDefinitionSubSectionSchema}
updateConfig={jest.fn()}
patchConfig={jest.fn()}
/>,
);
expect(wrapper).toMatchSnapshot();
@ -285,7 +285,7 @@ describe('components/admin_console/SchemaAdminSettings', () => {
...schema,
header: headerText,
} as AdminDefinitionSubSectionSchema,
updateConfig: jest.fn(),
patchConfig: jest.fn(),
};
const wrapper = shallowWithIntl(<SchemaAdminSettings {...props}/>);
@ -308,7 +308,7 @@ describe('components/admin_console/SchemaAdminSettings', () => {
...schema,
footer: footerText,
} as AdminDefinitionSubSectionSchema,
updateConfig: jest.fn(),
patchConfig: jest.fn(),
};
const wrapper = shallowWithIntl(<SchemaAdminSettings {...props}/>);
@ -327,7 +327,7 @@ describe('components/admin_console/SchemaAdminSettings', () => {
config,
environmentConfig,
schema: null,
updateConfig: jest.fn(),
patchConfig: jest.fn(),
};
const wrapper = shallowWithIntl(<SchemaAdminSettings {...props}/>);
@ -360,7 +360,7 @@ describe('components/admin_console/SchemaAdminSettings', () => {
id: '',
environmentConfig,
schema: localSchema,
updateConfig: jest.fn(),
patchConfig: jest.fn(),
};
const wrapper = shallowWithIntl(<SchemaAdminSettings {...props}/>);
@ -389,7 +389,7 @@ describe('components/admin_console/SchemaAdminSettings', () => {
config,
environmentConfig,
schema: localSchema,
updateConfig: jest.fn(),
patchConfig: jest.fn(),
};
const wrapper = shallowWithIntl(<SchemaAdminSettings {...props}/>);

View File

@ -10,6 +10,7 @@ import {Link} from 'react-router-dom';
import type {CloudState} from '@mattermost/types/cloud';
import type {AdminConfig, ClientLicense, EnvironmentConfig} from '@mattermost/types/config';
import type {Role} from '@mattermost/types/roles';
import type {DeepPartial} from '@mattermost/types/utilities';
import type {ActionResult} from 'mattermost-redux/types/actions';
@ -53,7 +54,7 @@ type Props = {
roles: Record<string, Role>;
license: ClientLicense;
editRole: (role: Role) => void;
updateConfig: (config: AdminConfig) => Promise<ActionResult>;
patchConfig: (config: DeepPartial<AdminConfig>) => Promise<ActionResult>;
isDisabled: boolean;
consoleAccess: ConsoleAccess;
cloud: CloudState;
@ -1145,7 +1146,7 @@ export class SchemaAdminSettings extends React.PureComponent<Props, State> {
let config = JSON.parse(JSON.stringify(this.props.config));
config = this.getConfigFromState(config);
const {error} = await this.props.updateConfig(config);
const {error} = await this.props.patchConfig(config);
if (error) {
this.setState({
serverError: error.message,

View File

@ -292,7 +292,7 @@ callbackUrl2.com
values={
Object {
"link": <ExternalLink
href="https://mattermost.com/pl/configure-session-lengths"
href="https://mattermost.com/pl/default-allow-untrusted-internal-connections"
location="abstract_outgoing_webhook"
>
<Memo(MemoizedFormattedMessage)

View File

@ -504,7 +504,7 @@ export default class AbstractOutgoingWebhook extends React.PureComponent<Props,
values={{
link: (
<ExternalLink
href={DocLinks.SESSION_LENGTHS}
href={DocLinks.TRUSTED_CONNECTION}
location='abstract_outgoing_webhook'
>
<FormattedMessage

View File

@ -13,7 +13,6 @@ describe('components/post_view/new_message_separator', () => {
<NewMessageSeparator
separatorId='1234'
newMessagesSeparatorActions={[]}
lastViewedAt={0}
/>,
);

View File

@ -4,6 +4,8 @@
import React, {memo} from 'react';
import {FormattedMessage} from 'react-intl';
import * as PostList from 'mattermost-redux/utils/post_list';
import NotificationSeparator from 'components/widgets/separator/notification-separator';
import type {PluginComponent} from 'types/store/plugins';
@ -12,19 +14,19 @@ type Props = {
separatorId: string;
wrapperRef?: React.RefObject<HTMLDivElement>;
newMessagesSeparatorActions: PluginComponent[];
lastViewedAt: number;
channelId?: string;
threadId?: string;
}
const NewMessageSeparator = ({
newMessagesSeparatorActions,
lastViewedAt,
channelId,
threadId,
wrapperRef,
separatorId,
}: Props) => {
const lastViewedAt = PostList.getTimestampForStartOfNewMessages(separatorId);
const pluginItems = newMessagesSeparatorActions?.
map((item) => {
if (!item.component) {
@ -47,7 +49,7 @@ const NewMessageSeparator = ({
ref={wrapperRef}
className='new-separator'
>
<NotificationSeparator id={separatorId}>
<NotificationSeparator>
<FormattedMessage
id='posts_view.newMsg'
defaultMessage='New Messages'

View File

@ -95,9 +95,8 @@ exports[`components/post_view/post_list_row should render more messages loading
exports[`components/post_view/post_list_row should render new messages line 1`] = `
<Memo(NewMessageSeparator)
channelId="channel_id_1"
lastViewedAt={0}
newMessagesSeparatorActions={Array []}
separatorId="start-of-new-messages"
separatorId="start-of-new-messages-1553106600000"
/>
`;

View File

@ -41,7 +41,6 @@ describe('components/post_view/post_list_row', () => {
post: TestHelper.getPostMock({id: 'post_id_1'}),
currentUserId: 'user_id_1',
newMessagesSeparatorActions: [],
lastViewedAt: 0,
channelId: 'channel_id_1',
};
@ -107,7 +106,7 @@ describe('components/post_view/post_list_row', () => {
});
test('should render new messages line', () => {
const listId = PostListRowListIds.START_OF_NEW_MESSAGES;
const listId = PostListRowListIds.START_OF_NEW_MESSAGES + '1553106600000';
const props = {
...defaultProps,
listId,

View File

@ -57,7 +57,6 @@ export type PostListRowProps = {
limitsLoaded: boolean;
exceededLimitChannelId?: string;
firstInaccessiblePostTime?: number;
lastViewedAt: number;
channelId: string;
newMessagesSeparatorActions: PluginComponent[];
@ -120,7 +119,6 @@ export default class PostListRow extends React.PureComponent<PostListRowProps> {
separatorId={listId}
newMessagesSeparatorActions={this.props.newMessagesSeparatorActions}
channelId={this.props.channelId}
lastViewedAt={this.props.lastViewedAt}
/>
);
}

View File

@ -51,7 +51,7 @@ describe('PostList', () => {
'post3',
DATE_LINE + 1551711600000,
'post4',
PostListRowListIds.START_OF_NEW_MESSAGES,
PostListRowListIds.START_OF_NEW_MESSAGES + 1551711601000,
'post5',
];
@ -459,7 +459,7 @@ describe('PostList', () => {
for (let i = 0; i < 120; i++) {
postListIds.push(`post${i}`);
}
postListIds[65] = PostListRowListIds.START_OF_NEW_MESSAGES;
postListIds[65] = PostListRowListIds.START_OF_NEW_MESSAGES + 1551711601000;
const props = {
...baseProps,
@ -506,7 +506,7 @@ describe('PostList', () => {
'post3',
DATE_LINE + 1551711600000,
'post4',
PostListRowListIds.START_OF_NEW_MESSAGES,
PostListRowListIds.START_OF_NEW_MESSAGES + 1551711601000,
'post5',
],
};
@ -531,7 +531,7 @@ describe('PostList', () => {
'post3',
DATE_LINE + 1551711600000,
'post4',
PostListRowListIds.START_OF_NEW_MESSAGES,
PostListRowListIds.START_OF_NEW_MESSAGES + 1551711601000,
'post5',
],
};
@ -614,7 +614,7 @@ describe('PostList', () => {
'post2',
'post3',
'post4',
PostListRowListIds.START_OF_NEW_MESSAGES,
PostListRowListIds.START_OF_NEW_MESSAGES + 1551711599000,
DATE_LINE + 1551711600000,
'post5',
];
@ -637,7 +637,7 @@ describe('PostList', () => {
'post2',
'post3',
'post4',
PostListRowListIds.START_OF_NEW_MESSAGES,
PostListRowListIds.START_OF_NEW_MESSAGES + 1551711601000,
'post5',
];

View File

@ -9,7 +9,7 @@ import React from 'react';
import AutoSizer from 'react-virtualized-auto-sizer';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
import {isDateLine, isStartOfNewMessages} from 'mattermost-redux/utils/post_list';
import {getNewMessagesIndex, isDateLine, isStartOfNewMessages} from 'mattermost-redux/utils/post_list';
import type {updateNewMessagesAtInChannel} from 'actions/global_actions';
import type {CanLoadMorePosts} from 'actions/views/channel';
@ -22,7 +22,7 @@ import ToastWrapper from 'components/toast_wrapper';
import Pluggable from 'plugins/pluggable';
import Constants, {PostListRowListIds, EventTypes, PostRequestTypes} from 'utils/constants';
import DelayedAction from 'utils/delayed_action';
import {getPreviousPostId, getLatestPostId, getNewMessageIndex} from 'utils/post_utils';
import {getPreviousPostId, getLatestPostId} from 'utils/post_utils';
import * as Utils from 'utils/utils';
import LatestPostReader from './latest_post_reader';
@ -193,9 +193,9 @@ export default class PostList extends React.PureComponent<Props, State> {
if (props.focusedPostId) {
postIndex = (this.props.postListIds || []).findIndex((postId) => postId === this.props.focusedPostId);
} else {
postIndex = this.getNewMessagesSeparatorIndex(props.postListIds || []);
postIndex = getNewMessagesIndex(props.postListIds || []);
}
this.newMessageLineIndex = this.getNewMessagesSeparatorIndex(props.postListIds || []);
this.newMessageLineIndex = getNewMessagesIndex(props.postListIds || []);
const maxPostsForSlicing = props.focusedPostId ? MAXIMUM_POSTS_FOR_SLICING.permalink : MAXIMUM_POSTS_FOR_SLICING.channel;
this.initRangeToRender = [
@ -241,7 +241,7 @@ export default class PostList extends React.PureComponent<Props, State> {
const prevPostsCount = (prevProps.postListIds || []).length;
const presentPostsCount = (this.props.postListIds || []).length;
this.newMessageLineIndex = this.getNewMessagesSeparatorIndex(this.props.postListIds || []);
this.newMessageLineIndex = getNewMessagesIndex(this.props.postListIds || []);
if (snapshot) {
const postlistScrollHeight = this.postListRef.current.scrollHeight;
@ -309,12 +309,6 @@ export default class PostList extends React.PureComponent<Props, State> {
return nextState;
}
getNewMessagesSeparatorIndex = (postListIds: string[]) => {
return postListIds.findIndex(
(item) => item.indexOf(PostListRowListIds.START_OF_NEW_MESSAGES) === 0,
);
};
handleWindowResize = () => {
this.showSearchHintThreshold = this.getShowSearchHintThreshold();
};
@ -377,7 +371,6 @@ export default class PostList extends React.PureComponent<Props, State> {
isLastPost={isLastPost}
loadingNewerPosts={this.props.loadingNewerPosts}
loadingOlderPosts={this.props.loadingOlderPosts}
lastViewedAt={this.props.lastViewedAt}
channelId={this.props.channelId}
/>
</div>
@ -599,7 +592,7 @@ export default class PostList extends React.PureComponent<Props, State> {
};
}
const newMessagesSeparatorIndex = getNewMessageIndex(this.state.postListIds);
const newMessagesSeparatorIndex = getNewMessagesIndex(this.state.postListIds);
if (newMessagesSeparatorIndex > 0) {
// if there is a dateLine above START_OF_NEW_MESSAGES then scroll to date line
@ -641,7 +634,7 @@ export default class PostList extends React.PureComponent<Props, State> {
};
scrollToNewMessage = () => {
this.listRef.current?.scrollToItem(getNewMessageIndex(this.state.postListIds), 'start', OFFSET_TO_SHOW_TOAST);
this.listRef.current?.scrollToItem(getNewMessagesIndex(this.state.postListIds), 'start', OFFSET_TO_SHOW_TOAST);
};
updateNewMessagesAtInChannel = (lastViewedAt = Date.now()) => {

View File

@ -60,6 +60,7 @@ function ThreadFooter({
channel_id: channelId,
},
} = thread;
const participantIds = useMemo(() => (participants || []).map(({id}) => id).reverse(), [participants]);
const handleReply = useCallback((e) => {

View File

@ -32,6 +32,7 @@ describe('components/threading/ThreadViewer', () => {
user_id: post.user_id,
channel_id: post.channel_id,
message: post.message,
reply_count: 3,
};
const channel: Channel = TestHelper.getChannelMock({

View File

@ -54,7 +54,6 @@ function makeMapStateToProps() {
isMobileView: getIsMobileView(state),
lastPost,
replyListIds,
lastViewedAt,
newMessagesSeparatorActions,
};
};

View File

@ -22,13 +22,13 @@ import Reply from './reply';
type Props = {
a11yIndex: number;
currentUserId: string;
replyCount: number;
isRootPost: boolean;
isLastPost: boolean;
listId: string;
onCardClick: (post: Post) => void;
previousPostId: string;
timestampProps?: Partial<TimestampProps>;
lastViewedAt: number;
threadId: string;
newMessagesSeparatorActions: PluginComponent[];
};
@ -40,10 +40,10 @@ function ThreadViewerRow({
isRootPost,
isLastPost,
listId,
replyCount,
onCardClick,
previousPostId,
timestampProps,
lastViewedAt,
threadId,
newMessagesSeparatorActions,
}: Props) {
@ -62,7 +62,6 @@ function ThreadViewerRow({
return (
<NewMessageSeparator
separatorId={listId}
lastViewedAt={lastViewedAt}
threadId={threadId}
newMessagesSeparatorActions={newMessagesSeparatorActions}
/>
@ -70,13 +69,20 @@ function ThreadViewerRow({
case isRootPost:
return (
<PostComponent
postId={listId}
isLastPost={isLastPost}
handleCardClick={onCardClick}
timestampProps={timestampProps}
location={Locations.RHS_ROOT}
/>
<>
<PostComponent
postId={listId}
isLastPost={isLastPost}
handleCardClick={onCardClick}
timestampProps={timestampProps}
location={Locations.RHS_ROOT}
/>
{replyCount > 0 && (
<div className='root-post__divider'>
<div>{`${replyCount} Replies`}</div>
</div>
)}
</>
);
case PostListUtils.isCombinedUserActivityPost(listId): {
return (

View File

@ -26,6 +26,7 @@ function getBasePropsAndState(): [Props, DeepPartial<GlobalState>] {
const currentUser = TestHelper.getUserMock({roles: 'role'});
const post = TestHelper.getPostMock({
channel_id: channel.id,
reply_count: 0,
});
const directTeammate: UserProfile = TestHelper.getUserMock();
@ -40,7 +41,6 @@ function getBasePropsAndState(): [Props, DeepPartial<GlobalState>] {
useRelativeTimestamp: true,
isMobileView: false,
isThreadView: false,
lastViewedAt: 0,
newMessagesSeparatorActions: [],
fromSuppressed: false,
};

View File

@ -3,15 +3,18 @@
import {DynamicSizeList} from 'dynamic-virtualized-list';
import type {OnScrollArgs, OnItemsRenderedArgs} from 'dynamic-virtualized-list';
import React, {PureComponent} from 'react';
import React, {PureComponent, useMemo} from 'react';
import type {RefObject} from 'react';
import {useSelector} from 'react-redux';
import AutoSizer from 'react-virtualized-auto-sizer';
import type {Channel} from '@mattermost/types/channels';
import type {Post} from '@mattermost/types/posts';
import type {UserProfile} from '@mattermost/types/users';
import {isDateLine, isStartOfNewMessages, isCreateComment} from 'mattermost-redux/utils/post_list';
import {getPost} from 'mattermost-redux/selectors/entities/posts';
import {makeGetThreadOrSynthetic} from 'mattermost-redux/selectors/entities/threads';
import {getNewMessagesIndex, isDateLine, isStartOfNewMessages, isCreateComment} from 'mattermost-redux/utils/post_list';
import NewRepliesBanner from 'components/new_replies_banner';
import FloatingTimestamp from 'components/post_view/floating_timestamp';
@ -19,9 +22,10 @@ import {THREADING_TIME as BASE_THREADING_TIME} from 'components/threading/common
import Constants from 'utils/constants';
import DelayedAction from 'utils/delayed_action';
import {getNewMessageIndex, getPreviousPostId, getLatestPostId} from 'utils/post_utils';
import {getPreviousPostId, getLatestPostId} from 'utils/post_utils';
import * as Utils from 'utils/utils';
import type {GlobalState} from 'types/store';
import type {PluginComponent} from 'types/store/plugins';
import type {FakePost} from 'types/store/rhs';
@ -41,7 +45,6 @@ type Props = {
useRelativeTimestamp: boolean;
isMobileView: boolean;
isThreadView: boolean;
lastViewedAt: number;
newMessagesSeparatorActions: PluginComponent[];
inputPlaceholder?: string;
fromSuppressed?: boolean;
@ -168,7 +171,7 @@ class ThreadViewerVirtualized extends PureComponent<Props, State> {
};
}
const newMessagesSeparatorIndex = getNewMessageIndex(replyListIds);
const newMessagesSeparatorIndex = getNewMessagesIndex(replyListIds);
if (newMessagesSeparatorIndex > 0) {
return {
index: newMessagesSeparatorIndex,
@ -250,7 +253,7 @@ class ThreadViewerVirtualized extends PureComponent<Props, State> {
if (this.props.highlightedPostId) {
postIndex = this.props.replyListIds.findIndex((postId) => postId === this.props.highlightedPostId);
} else {
postIndex = getNewMessageIndex(this.props.replyListIds);
postIndex = getNewMessagesIndex(this.props.replyListIds);
}
return postIndex === -1 ? 0 : postIndex;
@ -303,7 +306,7 @@ class ThreadViewerVirtualized extends PureComponent<Props, State> {
};
handleToastClick = () => {
const index = getNewMessageIndex(this.props.replyListIds);
const index = getNewMessagesIndex(this.props.replyListIds);
if (index >= 0) {
this.scrollToItem(index, 'start', OFFSET_TO_SHOW_TOAST);
} else {
@ -350,6 +353,14 @@ class ThreadViewerVirtualized extends PureComponent<Props, State> {
const isLastPost = itemId === this.props.lastPost.id;
const isRootPost = itemId === this.props.selected.id;
const post = useSelector((state: GlobalState) => getPost(state, this.props.selected.id));
const getThreadOrSynthetic = useMemo(makeGetThreadOrSynthetic, []);
const totalReplies = useSelector((state: GlobalState) => {
const thread = getThreadOrSynthetic(state, post);
return thread.reply_count || 0;
});
if (!isDateLine(itemId) && !isStartOfNewMessages(itemId) && !isCreateComment(itemId) && !isRootPost) {
a11yIndex++;
}
@ -379,10 +390,10 @@ class ThreadViewerVirtualized extends PureComponent<Props, State> {
isRootPost={isRootPost}
isLastPost={isLastPost}
listId={itemId}
replyCount={totalReplies}
onCardClick={this.props.onCardClick}
previousPostId={getPreviousPostId(data, index)}
timestampProps={this.props.useRelativeTimestamp ? THREADING_TIME : undefined}
lastViewedAt={this.props.lastViewedAt}
threadId={this.props.selected.id}
newMessagesSeparatorActions={this.props.newMessagesSeparatorActions}
/>
@ -400,7 +411,7 @@ class ThreadViewerVirtualized extends PureComponent<Props, State> {
isNewMessagesVisible = (): boolean => {
const {visibleStopIndex} = this.state;
const newMessagesSeparatorIndex = getNewMessageIndex(this.props.replyListIds);
const newMessagesSeparatorIndex = getNewMessagesIndex(this.props.replyListIds);
if (visibleStopIndex != null) {
return visibleStopIndex < newMessagesSeparatorIndex;
}

View File

@ -85,7 +85,7 @@ describe('components/ToastWrapper', () => {
'post1',
'post2',
'post3',
PostListRowListIds.START_OF_NEW_MESSAGES,
PostListRowListIds.START_OF_NEW_MESSAGES + 1551711599000,
DATE_LINE + 1551711600000,
'post4',
'post5',
@ -104,7 +104,7 @@ describe('components/ToastWrapper', () => {
'post1',
'post2',
'post3',
PostListRowListIds.START_OF_NEW_MESSAGES,
PostListRowListIds.START_OF_NEW_MESSAGES + 1551711599000,
DATE_LINE + 1551711600000,
'post4',
'post5',
@ -162,7 +162,7 @@ describe('components/ToastWrapper', () => {
'post1',
'post2',
'post3',
PostListRowListIds.START_OF_NEW_MESSAGES,
PostListRowListIds.START_OF_NEW_MESSAGES + 1551711599000,
DATE_LINE + 1551711600000,
'post4',
'post5',
@ -191,7 +191,7 @@ describe('components/ToastWrapper', () => {
'post1',
'post2',
'post3',
PostListRowListIds.START_OF_NEW_MESSAGES,
PostListRowListIds.START_OF_NEW_MESSAGES + 1551711599000,
DATE_LINE + 1551711600000,
'post4',
'post5',
@ -247,7 +247,7 @@ describe('components/ToastWrapper', () => {
'post1',
'post2',
'post3',
PostListRowListIds.START_OF_NEW_MESSAGES,
PostListRowListIds.START_OF_NEW_MESSAGES + 1551711599000,
DATE_LINE + 1551711600000,
'post4',
'post5',
@ -277,7 +277,7 @@ describe('components/ToastWrapper', () => {
'post1',
'post2',
'post3',
PostListRowListIds.START_OF_NEW_MESSAGES,
PostListRowListIds.START_OF_NEW_MESSAGES + 1551711599000,
DATE_LINE + 1551711600000,
'post4',
'post5',
@ -312,7 +312,7 @@ describe('components/ToastWrapper', () => {
'post1',
'post2',
'post3',
PostListRowListIds.START_OF_NEW_MESSAGES,
PostListRowListIds.START_OF_NEW_MESSAGES + 1551711599000,
DATE_LINE + 1551711600000,
'post4',
'post5',
@ -334,7 +334,7 @@ describe('components/ToastWrapper', () => {
'post1',
'post2',
'post3',
PostListRowListIds.START_OF_NEW_MESSAGES,
PostListRowListIds.START_OF_NEW_MESSAGES + 1551711599000,
DATE_LINE + 1551711600000,
'post4',
'post5',
@ -356,7 +356,7 @@ describe('components/ToastWrapper', () => {
'post1',
'post2',
'post3',
PostListRowListIds.START_OF_NEW_MESSAGES,
PostListRowListIds.START_OF_NEW_MESSAGES + 1551711599000,
DATE_LINE + 1551711600000,
'post4',
'post5',
@ -381,7 +381,7 @@ describe('components/ToastWrapper', () => {
'post1',
'post2',
'post3',
PostListRowListIds.START_OF_NEW_MESSAGES,
PostListRowListIds.START_OF_NEW_MESSAGES + 1551711599000,
DATE_LINE + 1551711600000,
'post4',
'post5',
@ -403,7 +403,7 @@ describe('components/ToastWrapper', () => {
'post1',
'post2',
'post3',
PostListRowListIds.START_OF_NEW_MESSAGES,
PostListRowListIds.START_OF_NEW_MESSAGES + 1551711599000,
DATE_LINE + 1551711600000,
'post4',
'post5',
@ -426,7 +426,7 @@ describe('components/ToastWrapper', () => {
'post1',
'post2',
'post3',
PostListRowListIds.START_OF_NEW_MESSAGES,
PostListRowListIds.START_OF_NEW_MESSAGES + 1551711599000,
DATE_LINE + 1551711600000,
'post4',
'post5',
@ -464,7 +464,7 @@ describe('components/ToastWrapper', () => {
postListIds: [
'post2',
'post3',
PostListRowListIds.START_OF_NEW_MESSAGES,
PostListRowListIds.START_OF_NEW_MESSAGES + 1551711599000,
DATE_LINE + 1551711600000,
'post4',
'post5',
@ -480,7 +480,7 @@ describe('components/ToastWrapper', () => {
'post1',
'post2',
'post3',
PostListRowListIds.START_OF_NEW_MESSAGES,
PostListRowListIds.START_OF_NEW_MESSAGES + 1551711599000,
DATE_LINE + 1551711600000,
'post4',
'post5',
@ -497,7 +497,7 @@ describe('components/ToastWrapper', () => {
'post1',
'post2',
'post3',
PostListRowListIds.START_OF_NEW_MESSAGES,
PostListRowListIds.START_OF_NEW_MESSAGES + 1551711599000,
DATE_LINE + 1551711600000,
'post4',
'post5',

View File

@ -7,6 +7,7 @@ import type {IntlShape, WrappedComponentProps} from 'react-intl';
import type {RouteComponentProps} from 'react-router-dom';
import {Preferences} from 'mattermost-redux/constants';
import {getNewMessagesIndex} from 'mattermost-redux/utils/post_list';
import {HintToast} from 'components/hint-toast/hint_toast';
import ScrollToBottomToast from 'components/scroll_to_bottom_toast';
@ -18,7 +19,7 @@ import {getHistory} from 'utils/browser_history';
import Constants from 'utils/constants';
import {isToday} from 'utils/datetime';
import {isKeyPressed} from 'utils/keyboard';
import {isIdNotPost, getNewMessageIndex} from 'utils/post_utils';
import {isIdNotPost} from 'utils/post_utils';
import {localizeMessage} from 'utils/utils';
import './toast__wrapper.scss';
@ -91,7 +92,7 @@ export class ToastWrapperClass extends React.PureComponent<Props, State> {
}
static countNewMessages = (rootPosts: Record<string, boolean>, isCollapsedThreadsEnabled: boolean, postListIds: string[] = []) => {
const mark = getNewMessageIndex(postListIds);
const mark = getNewMessagesIndex(postListIds);
if (mark <= 0) {
return 0;
}

View File

@ -5,7 +5,9 @@ import React from 'react';
import './separator.scss';
import './notification-separator.scss';
type Props = React.PropsWithChildren<any>;
type Props = {
children?: React.ReactNode;
};
const NotificationSeparator = ({children}: Props) => {
return (

View File

@ -112,26 +112,35 @@ describe('Actions.Admin', () => {
expect(config.TeamSettings.SiteName === 'Mattermost').toBeTruthy();
});
it('updateConfig', async () => {
it('patchConfig', async () => {
nock(Client4.getBaseRoute()).
get('/config').
reply(200, {
TeamSettings: {
SiteName: 'Mattermost',
TeammateNameDisplay: 'username',
},
});
const {data} = await store.dispatch(Actions.getConfig());
const updated = JSON.parse(JSON.stringify(data));
// Creating a copy.
const reply = JSON.parse(JSON.stringify(data));
const oldSiteName = updated.TeamSettings.SiteName;
const oldNameDisplay = updated.TeamSettings.TeammateNameDisplay;
const testSiteName = 'MattermostReduxTest';
updated.TeamSettings.SiteName = testSiteName;
reply.TeamSettings.SiteName = testSiteName;
// Testing partial config patch.
updated.TeamSettings.TeammateNameDisplay = null;
nock(Client4.getBaseRoute()).
put('/config').
reply(200, updated);
put('/config/patch').
reply(200, reply);
await store.dispatch(Actions.updateConfig(updated));
await store.dispatch(Actions.patchConfig(updated));
let state = store.getState();
@ -139,14 +148,15 @@ describe('Actions.Admin', () => {
expect(config).toBeTruthy();
expect(config.TeamSettings).toBeTruthy();
expect(config.TeamSettings.SiteName === testSiteName).toBeTruthy();
expect(config.TeamSettings.TeammateNameDisplay === oldNameDisplay).toBeTruthy();
updated.TeamSettings.SiteName = oldSiteName;
nock(Client4.getBaseRoute()).
put('/config').
put('/config/patch').
reply(200, updated);
await store.dispatch(Actions.updateConfig(updated));
await store.dispatch(Actions.patchConfig(updated));
state = store.getState();

View File

@ -23,6 +23,7 @@ import type {
Team,
TeamSearchOpts,
} from '@mattermost/types/teams';
import type {DeepPartial} from '@mattermost/types/utilities';
import {AdminTypes} from 'mattermost-redux/action_types';
import {getUsersLimits} from 'mattermost-redux/actions/limits';
@ -79,9 +80,9 @@ export function getConfig() {
});
}
export function updateConfig(config: AdminConfig) {
export function patchConfig(config: DeepPartial<AdminConfig>) {
return bindClientFunc({
clientFunc: Client4.updateConfig,
clientFunc: Client4.patchConfig,
onSuccess: [AdminTypes.RECEIVED_CONFIG],
params: [
config,

View File

@ -8,7 +8,7 @@ export default {
PAGE_SIZE_MAXIMUM: 200,
LOGS_PAGE_SIZE_DEFAULT: 10000,
AUDITS_CHUNK_SIZE: 100,
PROFILE_CHUNK_SIZE: 100,
PROFILE_CHUNK_SIZE: 100, // WARN: Do not change this without changing the cache key on server side as well. See https://github.com/mattermost/mattermost/pull/26391.
CHANNELS_CHUNK_SIZE: 50,
TEAMS_CHUNK_SIZE: 50,
JOBS_CHUNK_SIZE: 50,

View File

@ -208,7 +208,7 @@ describe('makeFilterPostsAndAddSeparators', () => {
'1010',
'1005',
'1000',
START_OF_NEW_MESSAGES,
START_OF_NEW_MESSAGES + (time + 999),
'date-' + (today.getTime() + 1000),
]);
@ -217,7 +217,7 @@ describe('makeFilterPostsAndAddSeparators', () => {
expect(now).toEqual([
'1010',
'1005',
START_OF_NEW_MESSAGES,
START_OF_NEW_MESSAGES + (time + 1003),
'1000',
'date-' + (today.getTime() + 1000),
]);
@ -225,7 +225,7 @@ describe('makeFilterPostsAndAddSeparators', () => {
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt: time + 1006, indicateNewMessages: true});
expect(now).toEqual([
'1010',
START_OF_NEW_MESSAGES,
START_OF_NEW_MESSAGES + (time + 1006),
'1005',
'1000',
'date-' + (today.getTime() + 1000),
@ -296,7 +296,7 @@ describe('makeFilterPostsAndAddSeparators', () => {
'1004',
'date-' + tomorrow.getTime(),
'1003',
START_OF_NEW_MESSAGES,
START_OF_NEW_MESSAGES + lastViewedAt,
'1001',
'date-' + today.getTime(),
]);
@ -304,13 +304,13 @@ describe('makeFilterPostsAndAddSeparators', () => {
// No changes
let prev = now;
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages: true});
expect(now).toEqual(prev);
expect(now).toBe(prev);
expect(now).toEqual([
'1006',
'1004',
'date-' + tomorrow.getTime(),
'1003',
START_OF_NEW_MESSAGES,
START_OF_NEW_MESSAGES + lastViewedAt,
'1001',
'date-' + today.getTime(),
]);
@ -320,13 +320,13 @@ describe('makeFilterPostsAndAddSeparators', () => {
prev = now;
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages: true});
expect(now).toEqual(prev);
expect(now).not.toBe(prev);
expect(now).toEqual([
'1006',
'1004',
'date-' + tomorrow.getTime(),
'1003',
START_OF_NEW_MESSAGES,
START_OF_NEW_MESSAGES + lastViewedAt,
'1001',
'date-' + today.getTime(),
]);
@ -336,11 +336,11 @@ describe('makeFilterPostsAndAddSeparators', () => {
prev = now;
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages: true});
expect(now).not.toEqual(prev);
expect(now).not.toBe(prev);
expect(now).toEqual([
'1006',
'1004',
START_OF_NEW_MESSAGES,
START_OF_NEW_MESSAGES + lastViewedAt,
'date-' + tomorrow.getTime(),
'1003',
'1001',
@ -349,11 +349,11 @@ describe('makeFilterPostsAndAddSeparators', () => {
prev = now;
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages: true});
expect(now).toEqual(prev);
expect(now).toBe(prev);
expect(now).toEqual([
'1006',
'1004',
START_OF_NEW_MESSAGES,
START_OF_NEW_MESSAGES + lastViewedAt,
'date-' + tomorrow.getTime(),
'1003',
'1001',
@ -365,11 +365,11 @@ describe('makeFilterPostsAndAddSeparators', () => {
prev = now;
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages: true});
expect(now).toEqual(prev);
expect(now).toBe(prev);
expect(now).toEqual([
'1006',
'1004',
START_OF_NEW_MESSAGES,
START_OF_NEW_MESSAGES + lastViewedAt,
'date-' + tomorrow.getTime(),
'1003',
'1001',
@ -393,11 +393,11 @@ describe('makeFilterPostsAndAddSeparators', () => {
prev = now;
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages: true});
expect(now).toEqual(prev);
expect(now).toBe(prev);
expect(now).toEqual([
'1006',
'1004',
START_OF_NEW_MESSAGES,
START_OF_NEW_MESSAGES + lastViewedAt,
'date-' + tomorrow.getTime(),
'1003',
'1001',
@ -421,11 +421,11 @@ describe('makeFilterPostsAndAddSeparators', () => {
prev = now;
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages: true});
expect(now).toEqual(prev);
expect(now).toBe(prev);
expect(now).toEqual([
'1006',
'1004',
START_OF_NEW_MESSAGES,
START_OF_NEW_MESSAGES + lastViewedAt,
'date-' + tomorrow.getTime(),
'1003',
'1001',
@ -453,10 +453,10 @@ describe('makeFilterPostsAndAddSeparators', () => {
prev = now;
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages: true});
expect(now).not.toEqual(prev);
expect(now).not.toBe(prev);
expect(now).toEqual([
'1004',
START_OF_NEW_MESSAGES,
START_OF_NEW_MESSAGES + lastViewedAt,
'date-' + tomorrow.getTime(),
'1003',
'1001',
@ -465,10 +465,10 @@ describe('makeFilterPostsAndAddSeparators', () => {
prev = now;
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages: true});
expect(now).toEqual(prev);
expect(now).toBe(prev);
expect(now).toEqual([
'1004',
START_OF_NEW_MESSAGES,
START_OF_NEW_MESSAGES + lastViewedAt,
'date-' + tomorrow.getTime(),
'1003',
'1001',
@ -908,7 +908,7 @@ describe('getFirstPostId', () => {
});
test('should skip the new message line', () => {
expect(getFirstPostId([START_OF_NEW_MESSAGES, 'post2', 'post3', 'post4'])).toBe('post2');
expect(getFirstPostId([START_OF_NEW_MESSAGES + '1234', 'post2', 'post3', 'post4'])).toBe('post2');
});
});
@ -926,7 +926,7 @@ describe('getLastPostId', () => {
});
test('should skip the new message line', () => {
expect(getLastPostId(['post2', 'post3', 'post4', START_OF_NEW_MESSAGES])).toBe('post4');
expect(getLastPostId(['post2', 'post3', 'post4', START_OF_NEW_MESSAGES + '1234'])).toBe('post4');
});
});
@ -944,7 +944,7 @@ describe('getLastPostIndex', () => {
});
test('should skip the new message line and return index of last post', () => {
expect(getLastPostIndex(['post2', 'post3', 'post4', START_OF_NEW_MESSAGES])).toBe(2);
expect(getLastPostIndex(['post2', 'post3', 'post4', START_OF_NEW_MESSAGES + '1234'])).toBe(2);
});
});

View File

@ -20,7 +20,7 @@ import {getUserCurrentTimezone} from 'mattermost-redux/utils/timezone_utils';
export const COMBINED_USER_ACTIVITY = 'user-activity-';
export const CREATE_COMMENT = 'create-comment';
export const DATE_LINE = 'date-';
export const START_OF_NEW_MESSAGES = 'start-of-new-messages';
export const START_OF_NEW_MESSAGES = 'start-of-new-messages-';
export const MAX_COMBINED_SYSTEM_POSTS = 100;
export function shouldShowJoinLeaveMessages(state: GlobalState) {
@ -111,7 +111,7 @@ export function makeFilterPostsAndAddSeparators() {
!addedNewMessagesIndicator &&
indicateNewMessages
) {
out.push(START_OF_NEW_MESSAGES);
out.push(START_OF_NEW_MESSAGES + lastViewedAt);
addedNewMessagesIndicator = true;
}
@ -140,7 +140,7 @@ export function makeCombineUserActivityPosts() {
for (let i = 0; i < postIds.length; i++) {
const postId = postIds[i];
if (postId === START_OF_NEW_MESSAGES || postId.startsWith(DATE_LINE) || isCreateComment(postId)) {
if (isStartOfNewMessages(postId) || isDateLine(postId) || isCreateComment(postId)) {
// Not a post, so it won't be combined
out.push(postId);
@ -187,7 +187,15 @@ export function makeCombineUserActivityPosts() {
}
export function isStartOfNewMessages(item: string) {
return item === START_OF_NEW_MESSAGES;
return item.startsWith(START_OF_NEW_MESSAGES);
}
export function getTimestampForStartOfNewMessages(item: string) {
return parseInt(item.substring(START_OF_NEW_MESSAGES.length), 10);
}
export function getNewMessagesIndex(postListIds: string[]): number {
return postListIds.findIndex(isStartOfNewMessages);
}
export function isCreateComment(item: string) {

View File

@ -38,9 +38,37 @@
display: none;
}
.root-post__divider {
position: relative;
display: flex;
height: 28px;
align-items: center;
margin: 0 0 4px 30px;
div {
z-index: 1;
padding: 0 12px;
margin-left: 34px;
background: rgba(v(center-channel-bg-rgb), 1);
color: rgba(v(center-channel-color-rgb), 0.72);
font-size: 12px;
font-weight: 600;
}
&::before {
position: absolute;
top: calc(50% - 1px);
left: 0;
display: block;
width: 100%;
border-top: 1px solid rgba(var(--center-channel-color-rgb), 0.12);
content: "";
}
}
.post {
&.post--root {
padding-top: 2rem;
padding-top: 16px;
.post__body {
background: transparent !important;
@ -52,9 +80,9 @@
}
.post-pre-header__icons-container {
width: 60px; // If the width of post__img changes, this needs to be adjusted accordingly
padding-right: 12px; // If the padding of post__img changes, this needs to be adjusted accordingly
margin-left: 0; // if left margin of post__content changes, this needs to be adjusted accordingly
width: 54px; // If the width of post__img changes, this needs to be adjusted accordingly;
padding-right: 12px; // If the padding of post__img changes, this needs to be adjusted accordingly;
margin-left: 0; // if left margin of post__content changes, this needs to be adjusted accordingly;
}
.post__header {
@ -119,8 +147,8 @@
}
.post__img {
width: 60px; // if this changes, the width of post-pre-header__icons-container needs to be adjusted accordingly
padding: 2px 12px 0 0; // if the right padding changes, the padding of post-pre-header__icons-container needs to be adjusted accordingly
width: 54px; // if this changes, the width of post-pre-header__icons-container needs to be adjusted accordingly;
padding: 2px 12px 0 0; // if the right padding changes, the padding of post-pre-header__icons-container needs to be adjusted accordingly;
}
.post-body {

View File

@ -440,7 +440,7 @@
.post {
&.post--thread {
.post-pre-header__icons-container {
width: 60px; // If the width of post__img changes, this needs to be adjusted accordingly
width: 44px; // If the width of post__img changes, this needs to be adjusted accordingly
padding-right: 12px; // If the padding of post__img changes, this needs to be adjusted accordingly
margin-left: 0; // if left margin of post__content changes, this needs to be adjusted accordingly
}

View File

@ -94,6 +94,7 @@ export const getSelectedPost = createSelector(
message: localizeMessage('rhs_thread.rootPostDeletedMessage.body', 'Part of this thread has been deleted due to a data retention policy. You can no longer reply to this thread.'),
channel_id: selectedPostChannelId,
user_id: currentUserId,
reply_count: 0,
};
},
);

View File

@ -16,6 +16,7 @@ export type FakePost = {
exists: boolean;
type: PostType;
message: string;
reply_count: number;
channel_id: Channel['id'];
user_id: UserProfile['id'];
};

View File

@ -1086,6 +1086,7 @@ export const DocLinks = {
SITE_URL: 'https://mattermost.com/pl/configure-site-url',
SSL_CERTIFICATE: 'https://mattermost.com/pl/setup-ssl-client-certificate',
TRUE_UP_REVIEW: 'https://mattermost.com/pl/true-up-documentation',
TRUSTED_CONNECTION: 'https://mattermost.com/pl/default-allow-untrusted-internal-connections',
UPGRADE_SERVER: 'https://mattermost.com/pl/upgrade-mattermost',
};

View File

@ -801,7 +801,7 @@ describe('PostUtils.getOldestPostId', () => {
});
test('Should not return START_OF_NEW_MESSAGES', () => {
const postId = PostUtils.getOldestPostId(['postId1', 'postId2', PostListRowListIds.START_OF_NEW_MESSAGES]);
const postId = PostUtils.getOldestPostId(['postId1', 'postId2', PostListRowListIds.START_OF_NEW_MESSAGES + 1551711599000]);
expect(postId).toEqual('postId2');
});
});
@ -813,7 +813,7 @@ describe('PostUtils.getPreviousPostId', () => {
});
test('Should skip START_OF_NEW_MESSAGES', () => {
const postId = PostUtils.getPreviousPostId(['postId1', 'postId2', PostListRowListIds.START_OF_NEW_MESSAGES, 'postId3'], 1);
const postId = PostUtils.getPreviousPostId(['postId1', 'postId2', PostListRowListIds.START_OF_NEW_MESSAGES + 1551711599000, 'postId3'], 1);
expect(postId).toEqual('postId3');
});
@ -845,7 +845,7 @@ describe('PostUtils.getLatestPostId', () => {
});
test('Should not return START_OF_NEW_MESSAGES', () => {
const postId = PostUtils.getLatestPostId([PostListRowListIds.START_OF_NEW_MESSAGES, 'postId1', 'postId2']);
const postId = PostUtils.getLatestPostId([PostListRowListIds.START_OF_NEW_MESSAGES + 1551711599000, 'postId1', 'postId2']);
expect(postId).toEqual('postId1');
});

View File

@ -626,12 +626,6 @@ export function splitMessageBasedOnTextSelection(selectionStart: number, selecti
return {firstPiece, lastPiece};
}
export function getNewMessageIndex(postListIds: string[]): number {
return postListIds.findIndex(
(item) => item.indexOf(PostListRowListIds.START_OF_NEW_MESSAGES) === 0,
);
}
export function areConsecutivePostsBySameUser(post: Post, previousPost: Post): boolean {
if (!(post && previousPost)) {
return false;