mirror of
https://github.com/mattermost/mattermost.git
synced 2026-02-18 18:18:23 -05:00
MM-56082 Add PreferencesHaveChanged plugin hook (#25659)
* Add interface for PreferencesHaveChanged hook * Add context to preference-related methods of App * Implement PreferencesHaveChanged * Re-add missing "fmt" import * Update minimum server version for the new hook * Remove pointers to be consistent with other preference APIs
This commit is contained in:
parent
18f0d8d88f
commit
502cd6ef7d
19 changed files with 226 additions and 64 deletions
|
|
@ -31,7 +31,7 @@ func getPreferences(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
preferences, err := c.App.GetPreferencesForUser(c.Params.UserId)
|
||||
preferences, err := c.App.GetPreferencesForUser(c.AppContext, c.Params.UserId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
|
|
@ -53,7 +53,7 @@ func getPreferencesByCategory(c *Context, w http.ResponseWriter, r *http.Request
|
|||
return
|
||||
}
|
||||
|
||||
preferences, err := c.App.GetPreferenceByCategoryForUser(c.Params.UserId, c.Params.Category)
|
||||
preferences, err := c.App.GetPreferenceByCategoryForUser(c.AppContext, c.Params.UserId, c.Params.Category)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
|
|
@ -75,7 +75,7 @@ func getPreferenceByCategoryAndName(c *Context, w http.ResponseWriter, r *http.R
|
|||
return
|
||||
}
|
||||
|
||||
preferences, err := c.App.GetPreferenceByCategoryAndNameForUser(c.Params.UserId, c.Params.Category, c.Params.PreferenceName)
|
||||
preferences, err := c.App.GetPreferenceByCategoryAndNameForUser(c.AppContext, c.Params.UserId, c.Params.Category, c.Params.PreferenceName)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
|
|
@ -125,7 +125,7 @@ func updatePreferences(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||
sanitizedPreferences = append(sanitizedPreferences, pref)
|
||||
}
|
||||
|
||||
if err := c.App.UpdatePreferences(c.Params.UserId, sanitizedPreferences); err != nil {
|
||||
if err := c.App.UpdatePreferences(c.AppContext, c.Params.UserId, sanitizedPreferences); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
|
@ -154,7 +154,7 @@ func deletePreferences(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if err := c.App.DeletePreferences(c.Params.UserId, preferences); err != nil {
|
||||
if err := c.App.DeletePreferences(c.AppContext, c.Params.UserId, preferences); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ func removeUserRecentCustomStatus(c *Context, w http.ResponseWriter, r *http.Req
|
|||
return
|
||||
}
|
||||
|
||||
if err := c.App.RemoveRecentCustomStatus(c.Params.UserId, &recentCustomStatus); err != nil {
|
||||
if err := c.App.RemoveRecentCustomStatus(c.AppContext, c.Params.UserId, &recentCustomStatus); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3089,7 +3089,7 @@ func TestGetUsersInGroupByDisplayName(t *testing.T) {
|
|||
Value: model.ShowUsername,
|
||||
}
|
||||
|
||||
err = th.App.UpdatePreferences(th.SystemAdminUser.Id, model.Preferences{preference})
|
||||
err = th.App.UpdatePreferences(th.Context, th.SystemAdminUser.Id, model.Preferences{preference})
|
||||
assert.Nil(t, err)
|
||||
|
||||
t.Run("Returns users in group in right order for username", func(t *testing.T) {
|
||||
|
|
@ -3099,7 +3099,7 @@ func TestGetUsersInGroupByDisplayName(t *testing.T) {
|
|||
})
|
||||
|
||||
preference.Value = model.ShowNicknameFullName
|
||||
err = th.App.UpdatePreferences(th.SystemAdminUser.Id, model.Preferences{preference})
|
||||
err = th.App.UpdatePreferences(th.Context, th.SystemAdminUser.Id, model.Preferences{preference})
|
||||
assert.Nil(t, err)
|
||||
|
||||
t.Run("Returns users in group in right order for nickname", func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -555,7 +555,7 @@ type AppIface interface {
|
|||
DeleteOutgoingWebhook(hookID string) *model.AppError
|
||||
DeletePluginKey(pluginID string, key string) *model.AppError
|
||||
DeletePost(c request.CTX, postID, deleteByID string) (*model.Post, *model.AppError)
|
||||
DeletePreferences(userID string, preferences model.Preferences) *model.AppError
|
||||
DeletePreferences(c request.CTX, userID string, preferences model.Preferences) *model.AppError
|
||||
DeleteReactionForPost(c request.CTX, reaction *model.Reaction) *model.AppError
|
||||
DeleteRemoteCluster(remoteClusterId string) (bool, *model.AppError)
|
||||
DeleteRetentionPolicy(policyID string) *model.AppError
|
||||
|
|
@ -750,9 +750,9 @@ type AppIface interface {
|
|||
GetPostsForChannelAroundLastUnread(c request.CTX, channelID, userID string, limitBefore, limitAfter int, skipFetchThreads bool, collapsedThreads, collapsedThreadsExtended bool) (*model.PostList, *model.AppError)
|
||||
GetPostsPage(options model.GetPostsOptions) (*model.PostList, *model.AppError)
|
||||
GetPostsSince(options model.GetPostsSinceOptions) (*model.PostList, *model.AppError)
|
||||
GetPreferenceByCategoryAndNameForUser(userID string, category string, preferenceName string) (*model.Preference, *model.AppError)
|
||||
GetPreferenceByCategoryForUser(userID string, category string) (model.Preferences, *model.AppError)
|
||||
GetPreferencesForUser(userID string) (model.Preferences, *model.AppError)
|
||||
GetPreferenceByCategoryAndNameForUser(c request.CTX, userID string, category string, preferenceName string) (*model.Preference, *model.AppError)
|
||||
GetPreferenceByCategoryForUser(c request.CTX, userID string, category string) (model.Preferences, *model.AppError)
|
||||
GetPreferencesForUser(c request.CTX, userID string) (model.Preferences, *model.AppError)
|
||||
GetPrevPostIdFromPostList(postList *model.PostList, collapsedThreads bool) string
|
||||
GetPriorityForPost(postId string) (*model.PostPriority, *model.AppError)
|
||||
GetPriorityForPostList(list *model.PostList) (map[string]*model.PostPriority, *model.AppError)
|
||||
|
|
@ -994,7 +994,7 @@ type AppIface interface {
|
|||
RemoveLdapPrivateCertificate() *model.AppError
|
||||
RemoveLdapPublicCertificate() *model.AppError
|
||||
RemoveNotifications(c request.CTX, post *model.Post, channel *model.Channel) error
|
||||
RemoveRecentCustomStatus(userID string, status *model.CustomStatus) *model.AppError
|
||||
RemoveRecentCustomStatus(c request.CTX, userID string, status *model.CustomStatus) *model.AppError
|
||||
RemoveSamlIdpCertificate() *model.AppError
|
||||
RemoveSamlPrivateCertificate() *model.AppError
|
||||
RemoveSamlPublicCertificate() *model.AppError
|
||||
|
|
@ -1157,7 +1157,7 @@ type AppIface interface {
|
|||
UpdatePasswordByUserIdSendEmail(c request.CTX, userID, newPassword, method string) *model.AppError
|
||||
UpdatePasswordSendEmail(c request.CTX, user *model.User, newPassword, method string) *model.AppError
|
||||
UpdatePost(c request.CTX, receivedUpdatedPost *model.Post, safeUpdate bool) (*model.Post, *model.AppError)
|
||||
UpdatePreferences(userID string, preferences model.Preferences) *model.AppError
|
||||
UpdatePreferences(c request.CTX, userID string, preferences model.Preferences) *model.AppError
|
||||
UpdateRemoteCluster(rc *model.RemoteCluster) (*model.RemoteCluster, *model.AppError)
|
||||
UpdateRemoteClusterTopics(remoteClusterId string, topics string) (*model.RemoteCluster, *model.AppError)
|
||||
UpdateRole(role *model.Role) (*model.Role, *model.AppError)
|
||||
|
|
|
|||
|
|
@ -278,7 +278,7 @@ func (a *App) exportAllUsers(ctx request.CTX, job *model.Job, writer io.Writer,
|
|||
|
||||
// Gathering here the exportable preferences to pass them on to ImportLineFromUser
|
||||
exportedPrefs := make(map[string]*string)
|
||||
allPrefs, err := a.GetPreferencesForUser(user.Id)
|
||||
allPrefs, err := a.GetPreferencesForUser(ctx, user.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -319,7 +319,7 @@ func (a *App) exportAllUsers(ctx request.CTX, job *model.Job, writer io.Writer,
|
|||
userLine.User.NotifyProps = a.buildUserNotifyProps(user.NotifyProps)
|
||||
|
||||
// Do the Team Memberships.
|
||||
members, err := a.buildUserTeamAndChannelMemberships(user.Id, includeArchivedChannels)
|
||||
members, err := a.buildUserTeamAndChannelMemberships(ctx, user.Id, includeArchivedChannels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -335,7 +335,7 @@ func (a *App) exportAllUsers(ctx request.CTX, job *model.Job, writer io.Writer,
|
|||
return nil
|
||||
}
|
||||
|
||||
func (a *App) buildUserTeamAndChannelMemberships(userID string, includeArchivedChannels bool) (*[]imports.UserTeamImportData, *model.AppError) {
|
||||
func (a *App) buildUserTeamAndChannelMemberships(c request.CTX, userID string, includeArchivedChannels bool) (*[]imports.UserTeamImportData, *model.AppError) {
|
||||
var memberships []imports.UserTeamImportData
|
||||
|
||||
members, err := a.Srv().Store().Team().GetTeamMembersForExport(userID)
|
||||
|
|
@ -353,7 +353,7 @@ func (a *App) buildUserTeamAndChannelMemberships(userID string, includeArchivedC
|
|||
memberData := ImportUserTeamDataFromTeamMember(member)
|
||||
|
||||
// Do the Channel Memberships.
|
||||
channelMembers, err := a.buildUserChannelMemberships(userID, member.TeamId, includeArchivedChannels)
|
||||
channelMembers, err := a.buildUserChannelMemberships(c, userID, member.TeamId, includeArchivedChannels)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -372,14 +372,14 @@ func (a *App) buildUserTeamAndChannelMemberships(userID string, includeArchivedC
|
|||
return &memberships, nil
|
||||
}
|
||||
|
||||
func (a *App) buildUserChannelMemberships(userID string, teamID string, includeArchivedChannels bool) (*[]imports.UserChannelImportData, *model.AppError) {
|
||||
func (a *App) buildUserChannelMemberships(c request.CTX, userID string, teamID string, includeArchivedChannels bool) (*[]imports.UserChannelImportData, *model.AppError) {
|
||||
members, nErr := a.Srv().Store().Channel().GetChannelMembersForExport(userID, teamID, includeArchivedChannels)
|
||||
if nErr != nil {
|
||||
return nil, model.NewAppError("buildUserChannelMemberships", "app.channel.get_members.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr)
|
||||
}
|
||||
|
||||
category := model.PreferenceCategoryFavoriteChannel
|
||||
preferences, err := a.GetPreferenceByCategoryForUser(userID, category)
|
||||
preferences, err := a.GetPreferenceByCategoryForUser(c, userID, category)
|
||||
if err != nil && err.StatusCode != http.StatusNotFound {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ func TestExportUserChannels(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
th.App.UpdateChannelMemberNotifyProps(th.Context, notifyProps, channel.Id, user.Id)
|
||||
exportData, appErr := th.App.buildUserChannelMemberships(user.Id, team.Id, false)
|
||||
exportData, appErr := th.App.buildUserChannelMemberships(th.Context, user.Id, team.Id, false)
|
||||
require.Nil(t, appErr)
|
||||
assert.Equal(t, len(*exportData), 3)
|
||||
for _, data := range *exportData {
|
||||
|
|
|
|||
|
|
@ -3394,7 +3394,7 @@ func (a *OpenTracingAppLayer) DeletePost(c request.CTX, postID string, deleteByI
|
|||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) DeletePreferences(userID string, preferences model.Preferences) *model.AppError {
|
||||
func (a *OpenTracingAppLayer) DeletePreferences(c request.CTX, userID string, preferences model.Preferences) *model.AppError {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.DeletePreferences")
|
||||
|
||||
|
|
@ -3406,7 +3406,7 @@ func (a *OpenTracingAppLayer) DeletePreferences(userID string, preferences model
|
|||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0 := a.app.DeletePreferences(userID, preferences)
|
||||
resultVar0 := a.app.DeletePreferences(c, userID, preferences)
|
||||
|
||||
if resultVar0 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar0))
|
||||
|
|
@ -8395,7 +8395,7 @@ func (a *OpenTracingAppLayer) GetPostsUsage() (int64, *model.AppError) {
|
|||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetPreferenceByCategoryAndNameForUser(userID string, category string, preferenceName string) (*model.Preference, *model.AppError) {
|
||||
func (a *OpenTracingAppLayer) GetPreferenceByCategoryAndNameForUser(c request.CTX, userID string, category string, preferenceName string) (*model.Preference, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetPreferenceByCategoryAndNameForUser")
|
||||
|
||||
|
|
@ -8407,7 +8407,7 @@ func (a *OpenTracingAppLayer) GetPreferenceByCategoryAndNameForUser(userID strin
|
|||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.GetPreferenceByCategoryAndNameForUser(userID, category, preferenceName)
|
||||
resultVar0, resultVar1 := a.app.GetPreferenceByCategoryAndNameForUser(c, userID, category, preferenceName)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
|
|
@ -8417,7 +8417,7 @@ func (a *OpenTracingAppLayer) GetPreferenceByCategoryAndNameForUser(userID strin
|
|||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetPreferenceByCategoryForUser(userID string, category string) (model.Preferences, *model.AppError) {
|
||||
func (a *OpenTracingAppLayer) GetPreferenceByCategoryForUser(c request.CTX, userID string, category string) (model.Preferences, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetPreferenceByCategoryForUser")
|
||||
|
||||
|
|
@ -8429,7 +8429,7 @@ func (a *OpenTracingAppLayer) GetPreferenceByCategoryForUser(userID string, cate
|
|||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.GetPreferenceByCategoryForUser(userID, category)
|
||||
resultVar0, resultVar1 := a.app.GetPreferenceByCategoryForUser(c, userID, category)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
|
|
@ -8439,7 +8439,7 @@ func (a *OpenTracingAppLayer) GetPreferenceByCategoryForUser(userID string, cate
|
|||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetPreferencesForUser(userID string) (model.Preferences, *model.AppError) {
|
||||
func (a *OpenTracingAppLayer) GetPreferencesForUser(c request.CTX, userID string) (model.Preferences, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetPreferencesForUser")
|
||||
|
||||
|
|
@ -8451,7 +8451,7 @@ func (a *OpenTracingAppLayer) GetPreferencesForUser(userID string) (model.Prefer
|
|||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.GetPreferencesForUser(userID)
|
||||
resultVar0, resultVar1 := a.app.GetPreferencesForUser(c, userID)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
|
|
@ -14031,7 +14031,7 @@ func (a *OpenTracingAppLayer) RemoveNotifications(c request.CTX, post *model.Pos
|
|||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) RemoveRecentCustomStatus(userID string, status *model.CustomStatus) *model.AppError {
|
||||
func (a *OpenTracingAppLayer) RemoveRecentCustomStatus(c request.CTX, userID string, status *model.CustomStatus) *model.AppError {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.RemoveRecentCustomStatus")
|
||||
|
||||
|
|
@ -14043,7 +14043,7 @@ func (a *OpenTracingAppLayer) RemoveRecentCustomStatus(userID string, status *mo
|
|||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0 := a.app.RemoveRecentCustomStatus(userID, status)
|
||||
resultVar0 := a.app.RemoveRecentCustomStatus(c, userID, status)
|
||||
|
||||
if resultVar0 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar0))
|
||||
|
|
@ -17878,7 +17878,7 @@ func (a *OpenTracingAppLayer) UpdatePost(c request.CTX, receivedUpdatedPost *mod
|
|||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) UpdatePreferences(userID string, preferences model.Preferences) *model.AppError {
|
||||
func (a *OpenTracingAppLayer) UpdatePreferences(c request.CTX, userID string, preferences model.Preferences) *model.AppError {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UpdatePreferences")
|
||||
|
||||
|
|
@ -17890,7 +17890,7 @@ func (a *OpenTracingAppLayer) UpdatePreferences(userID string, preferences model
|
|||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0 := a.app.UpdatePreferences(userID, preferences)
|
||||
resultVar0 := a.app.UpdatePreferences(c, userID, preferences)
|
||||
|
||||
if resultVar0 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar0))
|
||||
|
|
|
|||
|
|
@ -271,15 +271,15 @@ func (api *PluginAPI) GetUsersInTeam(teamID string, page int, perPage int) ([]*m
|
|||
}
|
||||
|
||||
func (api *PluginAPI) GetPreferencesForUser(userID string) ([]model.Preference, *model.AppError) {
|
||||
return api.app.GetPreferencesForUser(userID)
|
||||
return api.app.GetPreferencesForUser(api.ctx, userID)
|
||||
}
|
||||
|
||||
func (api *PluginAPI) UpdatePreferencesForUser(userID string, preferences []model.Preference) *model.AppError {
|
||||
return api.app.UpdatePreferences(userID, preferences)
|
||||
return api.app.UpdatePreferences(api.ctx, userID, preferences)
|
||||
}
|
||||
|
||||
func (api *PluginAPI) DeletePreferencesForUser(userID string, preferences []model.Preference) *model.AppError {
|
||||
return api.app.DeletePreferences(userID, preferences)
|
||||
return api.app.DeletePreferences(api.ctx, userID, preferences)
|
||||
}
|
||||
|
||||
func (api *PluginAPI) GetSession(sessionID string) (*model.Session, *model.AppError) {
|
||||
|
|
|
|||
|
|
@ -1294,6 +1294,10 @@ func TestHookReactionHasBeenRemoved(t *testing.T) {
|
|||
err := th.App.DeleteReactionForPost(th.Context, reaction)
|
||||
|
||||
require.Nil(t, err)
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
mockAPI.AssertCalled(t, "LogDebug", "star")
|
||||
}
|
||||
|
||||
func TestHookRunDataRetention(t *testing.T) {
|
||||
|
|
@ -1632,3 +1636,76 @@ func TestHookMessagesWillBeConsumed(t *testing.T) {
|
|||
assert.Equal(t, "mwbc_plugin:message", post.Message)
|
||||
})
|
||||
}
|
||||
|
||||
func TestHookPreferencesHaveChanged(t *testing.T) {
|
||||
t.Run("should be called when preferences are changed by non-plugin code", func(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
// Setup plugin
|
||||
var mockAPI plugintest.API
|
||||
|
||||
tearDown, pluginIDs, _ := SetAppEnvironmentWithPlugins(t, []string{`
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/plugin"
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
)
|
||||
|
||||
type MyPlugin struct {
|
||||
plugin.MattermostPlugin
|
||||
}
|
||||
|
||||
func (p *MyPlugin) PreferencesHaveChanged(c *plugin.Context, preferences []model.Preference) {
|
||||
for _, preference := range preferences {
|
||||
p.API.LogDebug(fmt.Sprintf("category=%s name=%s value=%s", preference.Category, preference.Name, preference.Value))
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
plugin.ClientMain(&MyPlugin{})
|
||||
}
|
||||
`}, th.App, func(*model.Manifest) plugin.API { return &mockAPI })
|
||||
defer tearDown()
|
||||
|
||||
// Confirm plugin is actually running
|
||||
require.Len(t, pluginIDs, 1)
|
||||
pluginID := pluginIDs[0]
|
||||
|
||||
require.True(t, th.App.GetPluginsEnvironment().IsActive(pluginID))
|
||||
|
||||
// Setup test
|
||||
preferences := model.Preferences{
|
||||
{
|
||||
UserId: th.BasicUser.Id,
|
||||
Category: "test_category",
|
||||
Name: "test_name_1",
|
||||
Value: "test_value_1",
|
||||
},
|
||||
{
|
||||
UserId: th.BasicUser.Id,
|
||||
Category: "test_category",
|
||||
Name: "test_name_2",
|
||||
Value: "test_value_2",
|
||||
},
|
||||
}
|
||||
|
||||
mockAPI.On("LogDebug", "category=test_category name=test_name_1 value=test_value_1")
|
||||
mockAPI.On("LogDebug", "category=test_category name=test_name_2 value=test_value_2")
|
||||
defer mockAPI.AssertExpectations(t)
|
||||
|
||||
// Run test
|
||||
err := th.App.UpdatePreferences(th.Context, th.BasicUser.Id, preferences)
|
||||
|
||||
require.Nil(t, err)
|
||||
|
||||
// Hooks are run in a goroutine, so wait for those to complete
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
mockAPI.AssertCalled(t, "LogDebug", "category=test_category name=test_name_1 value=test_value_1")
|
||||
mockAPI.AssertCalled(t, "LogDebug", "category=test_category name=test_name_2 value=test_value_2")
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/public/plugin"
|
||||
"github.com/mattermost/mattermost/server/public/shared/request"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/product"
|
||||
)
|
||||
|
||||
|
|
@ -20,19 +22,19 @@ type preferencesServiceWrapper struct {
|
|||
app AppIface
|
||||
}
|
||||
|
||||
func (w *preferencesServiceWrapper) GetPreferencesForUser(userID string) (model.Preferences, *model.AppError) {
|
||||
return w.app.GetPreferencesForUser(userID)
|
||||
func (w *preferencesServiceWrapper) GetPreferencesForUser(c request.CTX, userID string) (model.Preferences, *model.AppError) {
|
||||
return w.app.GetPreferencesForUser(c, userID)
|
||||
}
|
||||
|
||||
func (w *preferencesServiceWrapper) UpdatePreferencesForUser(userID string, preferences model.Preferences) *model.AppError {
|
||||
return w.app.UpdatePreferences(userID, preferences)
|
||||
func (w *preferencesServiceWrapper) UpdatePreferencesForUser(c request.CTX, userID string, preferences model.Preferences) *model.AppError {
|
||||
return w.app.UpdatePreferences(c, userID, preferences)
|
||||
}
|
||||
|
||||
func (w *preferencesServiceWrapper) DeletePreferencesForUser(userID string, preferences model.Preferences) *model.AppError {
|
||||
return w.app.DeletePreferences(userID, preferences)
|
||||
func (w *preferencesServiceWrapper) DeletePreferencesForUser(c request.CTX, userID string, preferences model.Preferences) *model.AppError {
|
||||
return w.app.DeletePreferences(c, userID, preferences)
|
||||
}
|
||||
|
||||
func (a *App) GetPreferencesForUser(userID string) (model.Preferences, *model.AppError) {
|
||||
func (a *App) GetPreferencesForUser(c request.CTX, userID string) (model.Preferences, *model.AppError) {
|
||||
preferences, err := a.Srv().Store().Preference().GetAll(userID)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetPreferencesForUser", "app.preference.get_all.app_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
|
|
@ -40,7 +42,7 @@ func (a *App) GetPreferencesForUser(userID string) (model.Preferences, *model.Ap
|
|||
return preferences, nil
|
||||
}
|
||||
|
||||
func (a *App) GetPreferenceByCategoryForUser(userID string, category string) (model.Preferences, *model.AppError) {
|
||||
func (a *App) GetPreferenceByCategoryForUser(c request.CTX, userID string, category string) (model.Preferences, *model.AppError) {
|
||||
preferences, err := a.Srv().Store().Preference().GetCategory(userID, category)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetPreferenceByCategoryForUser", "app.preference.get_category.app_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
|
|
@ -52,7 +54,7 @@ func (a *App) GetPreferenceByCategoryForUser(userID string, category string) (mo
|
|||
return preferences, nil
|
||||
}
|
||||
|
||||
func (a *App) GetPreferenceByCategoryAndNameForUser(userID string, category string, preferenceName string) (*model.Preference, *model.AppError) {
|
||||
func (a *App) GetPreferenceByCategoryAndNameForUser(c request.CTX, userID string, category string, preferenceName string) (*model.Preference, *model.AppError) {
|
||||
res, err := a.Srv().Store().Preference().Get(userID, category, preferenceName)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetPreferenceByCategoryAndNameForUser", "app.preference.get.app_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
|
|
@ -60,7 +62,7 @@ func (a *App) GetPreferenceByCategoryAndNameForUser(userID string, category stri
|
|||
return res, nil
|
||||
}
|
||||
|
||||
func (a *App) UpdatePreferences(userID string, preferences model.Preferences) *model.AppError {
|
||||
func (a *App) UpdatePreferences(c request.CTX, userID string, preferences model.Preferences) *model.AppError {
|
||||
for _, preference := range preferences {
|
||||
if userID != preference.UserId {
|
||||
return model.NewAppError("savePreferences", "api.preference.update_preferences.set.app_error", nil,
|
||||
|
|
@ -94,10 +96,18 @@ func (a *App) UpdatePreferences(userID string, preferences model.Preferences) *m
|
|||
message.Add("preferences", string(prefsJSON))
|
||||
a.Publish(message)
|
||||
|
||||
pluginContext := pluginContext(c)
|
||||
a.Srv().Go(func() {
|
||||
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.PreferencesHaveChanged(pluginContext, preferences)
|
||||
return true
|
||||
}, plugin.PreferencesHaveChangedID)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) DeletePreferences(userID string, preferences model.Preferences) *model.AppError {
|
||||
func (a *App) DeletePreferences(c request.CTX, userID string, preferences model.Preferences) *model.AppError {
|
||||
for _, preference := range preferences {
|
||||
if userID != preference.UserId {
|
||||
err := model.NewAppError("DeletePreferences", "api.preference.delete_preferences.delete.app_error", nil,
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ func (a *App) SetCustomStatus(c request.CTX, userID string, cs *model.CustomStat
|
|||
return updateErr
|
||||
}
|
||||
|
||||
if err := a.addRecentCustomStatus(userID, cs); err != nil {
|
||||
if err := a.addRecentCustomStatus(c, userID, cs); err != nil {
|
||||
c.Logger().Error("Can't add recent custom status for", mlog.String("userID", userID), mlog.Err(err))
|
||||
}
|
||||
|
||||
|
|
@ -126,10 +126,10 @@ func (a *App) GetCustomStatus(userID string) (*model.CustomStatus, *model.AppErr
|
|||
return user.GetCustomStatus(), nil
|
||||
}
|
||||
|
||||
func (a *App) addRecentCustomStatus(userID string, status *model.CustomStatus) *model.AppError {
|
||||
func (a *App) addRecentCustomStatus(c request.CTX, userID string, status *model.CustomStatus) *model.AppError {
|
||||
var newRCS model.RecentCustomStatuses
|
||||
|
||||
pref, appErr := a.GetPreferenceByCategoryAndNameForUser(userID, model.PreferenceCategoryCustomStatus, model.PreferenceNameRecentCustomStatuses)
|
||||
pref, appErr := a.GetPreferenceByCategoryAndNameForUser(c, userID, model.PreferenceCategoryCustomStatus, model.PreferenceNameRecentCustomStatuses)
|
||||
if appErr != nil || pref.Value == "" {
|
||||
newRCS = model.RecentCustomStatuses{*status}
|
||||
} else {
|
||||
|
|
@ -150,15 +150,15 @@ func (a *App) addRecentCustomStatus(userID string, status *model.CustomStatus) *
|
|||
Name: model.PreferenceNameRecentCustomStatuses,
|
||||
Value: string(newRCSJSON),
|
||||
}
|
||||
if appErr := a.UpdatePreferences(userID, model.Preferences{*pref}); appErr != nil {
|
||||
if appErr := a.UpdatePreferences(c, userID, model.Preferences{*pref}); appErr != nil {
|
||||
return appErr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) RemoveRecentCustomStatus(userID string, status *model.CustomStatus) *model.AppError {
|
||||
pref, appErr := a.GetPreferenceByCategoryAndNameForUser(userID, model.PreferenceCategoryCustomStatus, model.PreferenceNameRecentCustomStatuses)
|
||||
func (a *App) RemoveRecentCustomStatus(c request.CTX, userID string, status *model.CustomStatus) *model.AppError {
|
||||
pref, appErr := a.GetPreferenceByCategoryAndNameForUser(c, userID, model.PreferenceCategoryCustomStatus, model.PreferenceNameRecentCustomStatuses)
|
||||
if appErr != nil {
|
||||
return appErr
|
||||
}
|
||||
|
|
@ -186,7 +186,7 @@ func (a *App) RemoveRecentCustomStatus(userID string, status *model.CustomStatus
|
|||
return model.NewAppError("RemoveRecentCustomStatus", "api.marshal_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
pref.Value = string(newRCSJSON)
|
||||
if appErr := a.UpdatePreferences(userID, model.Preferences{*pref}); appErr != nil {
|
||||
if appErr := a.UpdatePreferences(c, userID, model.Preferences{*pref}); appErr != nil {
|
||||
return appErr
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1813,17 +1813,17 @@ func TestCreateUserWithInitialPreferences(t *testing.T) {
|
|||
testUser := th.CreateUser()
|
||||
defer th.App.PermanentDeleteUser(th.Context, testUser)
|
||||
|
||||
tutorialStepPref, appErr := th.App.GetPreferenceByCategoryAndNameForUser(testUser.Id, model.PreferenceCategoryTutorialSteps, testUser.Id)
|
||||
tutorialStepPref, appErr := th.App.GetPreferenceByCategoryAndNameForUser(th.Context, testUser.Id, model.PreferenceCategoryTutorialSteps, testUser.Id)
|
||||
require.Nil(t, appErr)
|
||||
assert.Equal(t, testUser.Id, tutorialStepPref.Name)
|
||||
|
||||
recommendedNextStepsPref, appErr := th.App.GetPreferenceByCategoryForUser(testUser.Id, model.PreferenceRecommendedNextSteps)
|
||||
recommendedNextStepsPref, appErr := th.App.GetPreferenceByCategoryForUser(th.Context, testUser.Id, model.PreferenceRecommendedNextSteps)
|
||||
require.Nil(t, appErr)
|
||||
assert.Equal(t, model.PreferenceRecommendedNextSteps, recommendedNextStepsPref[0].Category)
|
||||
assert.Equal(t, "hide", recommendedNextStepsPref[0].Name)
|
||||
assert.Equal(t, "false", recommendedNextStepsPref[0].Value)
|
||||
|
||||
gmASdmNoticeViewedPref, appErr := th.App.GetPreferenceByCategoryAndNameForUser(testUser.Id, model.PreferenceCategorySystemNotice, "GMasDM")
|
||||
gmASdmNoticeViewedPref, appErr := th.App.GetPreferenceByCategoryAndNameForUser(th.Context, testUser.Id, model.PreferenceCategorySystemNotice, "GMasDM")
|
||||
require.Nil(t, appErr)
|
||||
assert.Equal(t, "GMasDM", gmASdmNoticeViewedPref.Name)
|
||||
assert.Equal(t, "true", gmASdmNoticeViewedPref.Value)
|
||||
|
|
@ -1835,17 +1835,17 @@ func TestCreateUserWithInitialPreferences(t *testing.T) {
|
|||
testUser := th.CreateGuest()
|
||||
defer th.App.PermanentDeleteUser(th.Context, testUser)
|
||||
|
||||
tutorialStepPref, appErr := th.App.GetPreferenceByCategoryAndNameForUser(testUser.Id, model.PreferenceCategoryTutorialSteps, testUser.Id)
|
||||
tutorialStepPref, appErr := th.App.GetPreferenceByCategoryAndNameForUser(th.Context, testUser.Id, model.PreferenceCategoryTutorialSteps, testUser.Id)
|
||||
require.Nil(t, appErr)
|
||||
assert.Equal(t, testUser.Id, tutorialStepPref.Name)
|
||||
|
||||
recommendedNextStepsPref, appErr := th.App.GetPreferenceByCategoryForUser(testUser.Id, model.PreferenceRecommendedNextSteps)
|
||||
recommendedNextStepsPref, appErr := th.App.GetPreferenceByCategoryForUser(th.Context, testUser.Id, model.PreferenceRecommendedNextSteps)
|
||||
require.Nil(t, appErr)
|
||||
assert.Equal(t, model.PreferenceRecommendedNextSteps, recommendedNextStepsPref[0].Category)
|
||||
assert.Equal(t, "hide", recommendedNextStepsPref[0].Name)
|
||||
assert.Equal(t, "false", recommendedNextStepsPref[0].Value)
|
||||
|
||||
gmASdmNoticeViewedPref, appErr := th.App.GetPreferenceByCategoryAndNameForUser(testUser.Id, model.PreferenceCategorySystemNotice, "GMasDM")
|
||||
gmASdmNoticeViewedPref, appErr := th.App.GetPreferenceByCategoryAndNameForUser(th.Context, testUser.Id, model.PreferenceCategorySystemNotice, "GMasDM")
|
||||
require.Nil(t, appErr)
|
||||
assert.Equal(t, "GMasDM", gmASdmNoticeViewedPref.Name)
|
||||
assert.Equal(t, "true", gmASdmNoticeViewedPref.Value)
|
||||
|
|
|
|||
|
|
@ -207,9 +207,9 @@ type SystemService interface {
|
|||
//
|
||||
// The service shall be registered via app.PreferencesKey service key.
|
||||
type PreferencesService interface {
|
||||
GetPreferencesForUser(userID string) (model.Preferences, *model.AppError)
|
||||
UpdatePreferencesForUser(userID string, preferences model.Preferences) *model.AppError
|
||||
DeletePreferencesForUser(userID string, preferences model.Preferences) *model.AppError
|
||||
GetPreferencesForUser(c request.CTX, userID string) (model.Preferences, *model.AppError)
|
||||
UpdatePreferencesForUser(c request.CTX, userID string, preferences model.Preferences) *model.AppError
|
||||
DeletePreferencesForUser(c request.CTX, userID string, preferences model.Preferences) *model.AppError
|
||||
}
|
||||
|
||||
// SessionService is the API for accessing the session.
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ func authorizeOAuthPage(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
isAuthorized := false
|
||||
|
||||
if _, err := c.App.GetPreferenceByCategoryAndNameForUser(c.AppContext.Session().UserId, model.PreferenceCategoryAuthorizedOAuthApp, authRequest.ClientId); err == nil {
|
||||
if _, err := c.App.GetPreferenceByCategoryAndNameForUser(c.AppContext, c.AppContext.Session().UserId, model.PreferenceCategoryAuthorizedOAuthApp, authRequest.ClientId); err == nil {
|
||||
// when we support scopes we should check if the scopes match
|
||||
isAuthorized = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1018,6 +1018,40 @@ func (s *hooksRPCServer) OnSharedChannelsPing(args *Z_OnSharedChannelsPingArgs,
|
|||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
hookNameToId["PreferencesHaveChanged"] = PreferencesHaveChangedID
|
||||
}
|
||||
|
||||
type Z_PreferencesHaveChangedArgs struct {
|
||||
A *Context
|
||||
B []model.Preference
|
||||
}
|
||||
|
||||
type Z_PreferencesHaveChangedReturns struct {
|
||||
}
|
||||
|
||||
func (g *hooksRPCClient) PreferencesHaveChanged(c *Context, preferences []model.Preference) {
|
||||
_args := &Z_PreferencesHaveChangedArgs{c, preferences}
|
||||
_returns := &Z_PreferencesHaveChangedReturns{}
|
||||
if g.implemented[PreferencesHaveChangedID] {
|
||||
if err := g.client.Call("Plugin.PreferencesHaveChanged", _args, _returns); err != nil {
|
||||
g.log.Error("RPC call PreferencesHaveChanged to plugin failed.", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *hooksRPCServer) PreferencesHaveChanged(args *Z_PreferencesHaveChangedArgs, returns *Z_PreferencesHaveChangedReturns) error {
|
||||
if hook, ok := s.impl.(interface {
|
||||
PreferencesHaveChanged(c *Context, preferences []model.Preference)
|
||||
}); ok {
|
||||
hook.PreferencesHaveChanged(args.A, args.B)
|
||||
} else {
|
||||
return encodableError(fmt.Errorf("Hook PreferencesHaveChanged called but not implemented."))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Z_RegisterCommandArgs struct {
|
||||
A *model.Command
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ const (
|
|||
ServeMetricsID = 39
|
||||
OnSharedChannelsSyncMsgID = 40
|
||||
OnSharedChannelsPingID = 41
|
||||
PreferencesHaveChangedID = 42
|
||||
TotalHooksID = iota
|
||||
)
|
||||
|
||||
|
|
@ -354,4 +355,11 @@ type Hooks interface {
|
|||
//
|
||||
// Minimum server version: 9.5
|
||||
OnSharedChannelsPing(rc *model.RemoteCluster) bool
|
||||
|
||||
// PreferencesHaveChanged is invoked after one or more of a user's preferences have changed.
|
||||
// Note that this method will be called for preferences changed by plugins, including the plugin that changed
|
||||
// the preferences.
|
||||
//
|
||||
// Minimum server version: 9.5
|
||||
PreferencesHaveChanged(c *Context, preferences []model.Preference)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -264,3 +264,9 @@ func (hooks *hooksTimerLayer) OnSharedChannelsPing(rc *model.RemoteCluster) bool
|
|||
hooks.recordTime(startTime, "OnSharedChannelsPing", true)
|
||||
return _returnsA
|
||||
}
|
||||
|
||||
func (hooks *hooksTimerLayer) PreferencesHaveChanged(c *Context, preferences []model.Preference) {
|
||||
startTime := timePkg.Now()
|
||||
hooks.hooksImpl.PreferencesHaveChanged(c, preferences)
|
||||
hooks.recordTime(startTime, "PreferencesHaveChanged", true)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -359,6 +359,11 @@ func (_m *Hooks) OnWebSocketDisconnect(webConnID string, userID string) {
|
|||
_m.Called(webConnID, userID)
|
||||
}
|
||||
|
||||
// PreferencesHaveChanged provides a mock function with given fields: c, preferences
|
||||
func (_m *Hooks) PreferencesHaveChanged(c *plugin.Context, preferences []model.Preference) {
|
||||
_m.Called(c, preferences)
|
||||
}
|
||||
|
||||
// ReactionHasBeenAdded provides a mock function with given fields: c, reaction
|
||||
func (_m *Hooks) ReactionHasBeenAdded(c *plugin.Context, reaction *model.Reaction) {
|
||||
_m.Called(c, reaction)
|
||||
|
|
|
|||
|
|
@ -147,6 +147,10 @@ type OnSharedChannelsPingIFace interface {
|
|||
OnSharedChannelsPing(rc *model.RemoteCluster) bool
|
||||
}
|
||||
|
||||
type PreferencesHaveChangedIFace interface {
|
||||
PreferencesHaveChanged(c *Context, preferences []model.Preference)
|
||||
}
|
||||
|
||||
type HooksAdapter struct {
|
||||
implemented map[int]struct{}
|
||||
productHooks any
|
||||
|
|
@ -457,6 +461,15 @@ func NewAdapter(productHooks any) (*HooksAdapter, error) {
|
|||
return nil, errors.New("hook has OnSharedChannelsPing method but does not implement plugin.OnSharedChannelsPing interface")
|
||||
}
|
||||
|
||||
// Assessing the type of the productHooks if it individually implements PreferencesHaveChanged interface.
|
||||
tt = reflect.TypeOf((*PreferencesHaveChangedIFace)(nil)).Elem()
|
||||
|
||||
if ft.Implements(tt) {
|
||||
a.implemented[PreferencesHaveChangedID] = struct{}{}
|
||||
} else if _, ok := ft.MethodByName("PreferencesHaveChanged"); ok {
|
||||
return nil, errors.New("hook has PreferencesHaveChanged method but does not implement plugin.PreferencesHaveChanged interface")
|
||||
}
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
|
|
@ -756,3 +769,12 @@ func (a *HooksAdapter) OnSharedChannelsPing(rc *model.RemoteCluster) bool {
|
|||
return a.productHooks.(OnSharedChannelsPingIFace).OnSharedChannelsPing(rc)
|
||||
|
||||
}
|
||||
|
||||
func (a *HooksAdapter) PreferencesHaveChanged(c *Context, preferences []model.Preference) {
|
||||
if _, ok := a.implemented[PreferencesHaveChangedID]; !ok {
|
||||
panic("product hooks must implement PreferencesHaveChanged")
|
||||
}
|
||||
|
||||
a.productHooks.(PreferencesHaveChangedIFace).PreferencesHaveChanged(c, preferences)
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue