From 41ddbeb6428bae2fe4d031d0cd99ef780bf1e308 Mon Sep 17 00:00:00 2001 From: Nassim Bounouas Date: Thu, 28 May 2020 19:15:47 +0200 Subject: [PATCH] MM-21307 Get/Update/Delete user preferences Plugin API (#13962) * MM-21307 Get/Update/Delete user preferences Plugin API * MM-21307 additional test case on TestPluginAPIDeleteUserPreferences * MM-21307 remove useless loop * Update plugin/api.go * Update plugin/api.go * Update plugin/api.go * Update plugin/api.go * Update plugin/api.go * Update plugin/api.go * gofmt -s Co-authored-by: mattermod Co-authored-by: Jesse Hallam --- app/plugin_api.go | 12 +++ app/plugin_api_test.go | 133 +++++++++++++++++++++++++++- plugin/api.go | 21 +++++ plugin/api_timer_layer_generated.go | 21 +++++ plugin/client_rpc_generated.go | 87 ++++++++++++++++++ plugin/plugintest/api.go | 57 ++++++++++++ 6 files changed, 327 insertions(+), 4 deletions(-) diff --git a/app/plugin_api.go b/app/plugin_api.go index 14a6028236..d8fa9ca47e 100644 --- a/app/plugin_api.go +++ b/app/plugin_api.go @@ -247,6 +247,18 @@ func (api *PluginAPI) GetUsersInTeam(teamId string, page int, perPage int) ([]*m return api.app.GetUsersInTeam(options) } +func (api *PluginAPI) GetPreferencesForUser(userId string) ([]model.Preference, *model.AppError) { + return api.app.GetPreferencesForUser(userId) +} + +func (api *PluginAPI) UpdatePreferencesForUser(userId string, preferences []model.Preference) *model.AppError { + return api.app.UpdatePreferences(userId, preferences) +} + +func (api *PluginAPI) DeletePreferencesForUser(userId string, preferences []model.Preference) *model.AppError { + return api.app.DeletePreferences(userId, preferences) +} + func (api *PluginAPI) UpdateUser(user *model.User) (*model.User, *model.AppError) { return api.app.UpdateUser(user, true) } diff --git a/app/plugin_api_test.go b/app/plugin_api_test.go index 52b443e22f..ab4bd2b25d 100644 --- a/app/plugin_api_test.go +++ b/app/plugin_api_test.go @@ -130,6 +130,131 @@ func TestPublicFilesPathConfiguration(t *testing.T) { assert.NoError(t, err) assert.Equal(t, publicFilesPath, publicFilesFolderInTest) } + +func TestPluginAPIGetUserPreferences(t *testing.T) { + th := Setup(t) + defer th.TearDown() + api := th.SetupPluginAPI() + + user1, err := th.App.CreateUser(&model.User{ + Email: strings.ToLower(model.NewId()) + "success+test@example.com", + Password: "password", + Username: "user1" + model.NewId(), + }) + require.Nil(t, err) + defer th.App.PermanentDeleteUser(user1) + + preferences, err := api.GetPreferencesForUser(user1.Id) + require.Nil(t, err) + assert.Equal(t, 1, len(preferences)) + + assert.Equal(t, user1.Id, preferences[0].UserId) + assert.Equal(t, model.PREFERENCE_CATEGORY_TUTORIAL_STEPS, preferences[0].Category) + assert.Equal(t, user1.Id, preferences[0].Name) + assert.Equal(t, "0", preferences[0].Value) +} + +func TestPluginAPIDeleteUserPreferences(t *testing.T) { + th := Setup(t) + defer th.TearDown() + api := th.SetupPluginAPI() + + user1, err := th.App.CreateUser(&model.User{ + Email: strings.ToLower(model.NewId()) + "success+test@example.com", + Password: "password", + Username: "user1" + model.NewId(), + }) + require.Nil(t, err) + defer th.App.PermanentDeleteUser(user1) + + preferences, err := api.GetPreferencesForUser(user1.Id) + require.Nil(t, err) + assert.Equal(t, 1, len(preferences)) + + err = api.DeletePreferencesForUser(user1.Id, preferences) + require.Nil(t, err) + preferences, err = api.GetPreferencesForUser(user1.Id) + require.Nil(t, err) + assert.Equal(t, 0, len(preferences)) + + user2, err := th.App.CreateUser(&model.User{ + Email: strings.ToLower(model.NewId()) + "success+test@example.com", + Password: "password", + Username: "user2" + model.NewId(), + }) + require.Nil(t, err) + defer th.App.PermanentDeleteUser(user2) + + preference := model.Preference{ + Name: user2.Id, + UserId: user2.Id, + Category: model.PREFERENCE_CATEGORY_THEME, + Value: `{"color": "#ff0000", "color2": "#faf"}`, + } + err = api.UpdatePreferencesForUser(user2.Id, []model.Preference{preference}) + require.Nil(t, err) + + preferences, err = api.GetPreferencesForUser(user2.Id) + require.Nil(t, err) + assert.Equal(t, 2, len(preferences)) + + err = api.DeletePreferencesForUser(user2.Id, []model.Preference{preference}) + require.Nil(t, err) + preferences, err = api.GetPreferencesForUser(user2.Id) + require.Nil(t, err) + assert.Equal(t, 1, len(preferences)) + assert.Equal(t, model.PREFERENCE_CATEGORY_TUTORIAL_STEPS, preferences[0].Category) +} + +func TestPluginAPIUpdateUserPreferences(t *testing.T) { + th := Setup(t) + defer th.TearDown() + api := th.SetupPluginAPI() + + user1, err := th.App.CreateUser(&model.User{ + Email: strings.ToLower(model.NewId()) + "success+test@example.com", + Password: "password", + Username: "user1" + model.NewId(), + }) + require.Nil(t, err) + defer th.App.PermanentDeleteUser(user1) + + preferences, err := api.GetPreferencesForUser(user1.Id) + require.Nil(t, err) + assert.Equal(t, 1, len(preferences)) + assert.Equal(t, user1.Id, preferences[0].UserId) + assert.Equal(t, model.PREFERENCE_CATEGORY_TUTORIAL_STEPS, preferences[0].Category) + assert.Equal(t, user1.Id, preferences[0].Name) + assert.Equal(t, "0", preferences[0].Value) + + preference := model.Preference{ + Name: user1.Id, + UserId: user1.Id, + Category: model.PREFERENCE_CATEGORY_THEME, + Value: `{"color": "#ff0000", "color2": "#faf"}`, + } + + err = api.UpdatePreferencesForUser(user1.Id, []model.Preference{preference}) + require.Nil(t, err) + + preferences, err = api.GetPreferencesForUser(user1.Id) + require.Nil(t, err) + + assert.Equal(t, 2, len(preferences)) + expectedCategories := []string{model.PREFERENCE_CATEGORY_TUTORIAL_STEPS, model.PREFERENCE_CATEGORY_THEME} + for _, pref := range preferences { + assert.Contains(t, expectedCategories, pref.Category) + assert.Equal(t, user1.Id, pref.UserId) + assert.Equal(t, user1.Id, pref.Name) + if pref.Category == model.PREFERENCE_CATEGORY_TUTORIAL_STEPS { + assert.Equal(t, "0", pref.Value) + } else { + newTheme, _ := json.Marshal(map[string]string{"color": "#ff0000", "color2": "#faf"}) + assert.Equal(t, string(newTheme), preferences[0].Value) + } + } +} + func TestPluginAPIGetUsers(t *testing.T) { th := Setup(t) defer th.TearDown() @@ -744,7 +869,7 @@ func TestInstallPlugin(t *testing.T) { import ( "net/http" - + "github.com/pkg/errors" "github.com/mattermost/mattermost-server/v5/plugin" @@ -753,7 +878,7 @@ func TestInstallPlugin(t *testing.T) { type configuration struct { DownloadURL string } - + type Plugin struct { plugin.MattermostPlugin @@ -766,7 +891,7 @@ func TestInstallPlugin(t *testing.T) { } return nil } - + func (p *Plugin) OnActivate() error { resp, err := http.Get(p.configuration.DownloadURL) if err != nil { @@ -783,7 +908,7 @@ func TestInstallPlugin(t *testing.T) { func main() { plugin.ClientMain(&Plugin{}) } - + `, `{"id": "testinstallplugin", "backend": {"executable": "backend.exe"}, "settings_schema": { "settings": [ diff --git a/plugin/api.go b/plugin/api.go index df46dc02f8..2fec5ab05b 100644 --- a/plugin/api.go +++ b/plugin/api.go @@ -152,6 +152,27 @@ type API interface { // Minimum server version: 5.6 GetUsersInTeam(teamId string, page int, perPage int) ([]*model.User, *model.AppError) + // GetPreferencesForUser gets a user's preferences. + // + // @tag User + // @tag Preference + // Minimum server version: 5.26 + GetPreferencesForUser(userId string) ([]model.Preference, *model.AppError) + + // UpdatePreferencesForUser updates a user's preferences. + // + // @tag User + // @tag Preference + // Minimum server version: 5.26 + UpdatePreferencesForUser(userId string, preferences []model.Preference) *model.AppError + + // DeletePreferencesForUser deletes a user's preferences. + // + // @tag User + // @tag Preference + // Minimum server version: 5.26 + DeletePreferencesForUser(userId string, preferences []model.Preference) *model.AppError + // GetTeamIcon gets the team icon. // // @tag Team diff --git a/plugin/api_timer_layer_generated.go b/plugin/api_timer_layer_generated.go index 8036931144..378cc88470 100644 --- a/plugin/api_timer_layer_generated.go +++ b/plugin/api_timer_layer_generated.go @@ -182,6 +182,27 @@ func (api *apiTimerLayer) GetUsersInTeam(teamId string, page int, perPage int) ( return _returnsA, _returnsB } +func (api *apiTimerLayer) GetPreferencesForUser(userId string) ([]model.Preference, *model.AppError) { + startTime := timePkg.Now() + _returnsA, _returnsB := api.apiImpl.GetPreferencesForUser(userId) + api.recordTime(startTime, "GetPreferencesForUser", true) + return _returnsA, _returnsB +} + +func (api *apiTimerLayer) UpdatePreferencesForUser(userId string, preferences []model.Preference) *model.AppError { + startTime := timePkg.Now() + _returnsA := api.apiImpl.UpdatePreferencesForUser(userId, preferences) + api.recordTime(startTime, "UpdatePreferencesForUser", true) + return _returnsA +} + +func (api *apiTimerLayer) DeletePreferencesForUser(userId string, preferences []model.Preference) *model.AppError { + startTime := timePkg.Now() + _returnsA := api.apiImpl.DeletePreferencesForUser(userId, preferences) + api.recordTime(startTime, "DeletePreferencesForUser", true) + return _returnsA +} + func (api *apiTimerLayer) GetTeamIcon(teamId string) ([]byte, *model.AppError) { startTime := timePkg.Now() _returnsA, _returnsB := api.apiImpl.GetTeamIcon(teamId) diff --git a/plugin/client_rpc_generated.go b/plugin/client_rpc_generated.go index 17a259a3e4..fe5b41989a 100644 --- a/plugin/client_rpc_generated.go +++ b/plugin/client_rpc_generated.go @@ -1068,6 +1068,93 @@ func (s *apiRPCServer) GetUsersInTeam(args *Z_GetUsersInTeamArgs, returns *Z_Get return nil } +type Z_GetPreferencesForUserArgs struct { + A string +} + +type Z_GetPreferencesForUserReturns struct { + A []model.Preference + B *model.AppError +} + +func (g *apiRPCClient) GetPreferencesForUser(userId string) ([]model.Preference, *model.AppError) { + _args := &Z_GetPreferencesForUserArgs{userId} + _returns := &Z_GetPreferencesForUserReturns{} + if err := g.client.Call("Plugin.GetPreferencesForUser", _args, _returns); err != nil { + log.Printf("RPC call to GetPreferencesForUser API failed: %s", err.Error()) + } + return _returns.A, _returns.B +} + +func (s *apiRPCServer) GetPreferencesForUser(args *Z_GetPreferencesForUserArgs, returns *Z_GetPreferencesForUserReturns) error { + if hook, ok := s.impl.(interface { + GetPreferencesForUser(userId string) ([]model.Preference, *model.AppError) + }); ok { + returns.A, returns.B = hook.GetPreferencesForUser(args.A) + } else { + return encodableError(fmt.Errorf("API GetPreferencesForUser called but not implemented.")) + } + return nil +} + +type Z_UpdatePreferencesForUserArgs struct { + A string + B []model.Preference +} + +type Z_UpdatePreferencesForUserReturns struct { + A *model.AppError +} + +func (g *apiRPCClient) UpdatePreferencesForUser(userId string, preferences []model.Preference) *model.AppError { + _args := &Z_UpdatePreferencesForUserArgs{userId, preferences} + _returns := &Z_UpdatePreferencesForUserReturns{} + if err := g.client.Call("Plugin.UpdatePreferencesForUser", _args, _returns); err != nil { + log.Printf("RPC call to UpdatePreferencesForUser API failed: %s", err.Error()) + } + return _returns.A +} + +func (s *apiRPCServer) UpdatePreferencesForUser(args *Z_UpdatePreferencesForUserArgs, returns *Z_UpdatePreferencesForUserReturns) error { + if hook, ok := s.impl.(interface { + UpdatePreferencesForUser(userId string, preferences []model.Preference) *model.AppError + }); ok { + returns.A = hook.UpdatePreferencesForUser(args.A, args.B) + } else { + return encodableError(fmt.Errorf("API UpdatePreferencesForUser called but not implemented.")) + } + return nil +} + +type Z_DeletePreferencesForUserArgs struct { + A string + B []model.Preference +} + +type Z_DeletePreferencesForUserReturns struct { + A *model.AppError +} + +func (g *apiRPCClient) DeletePreferencesForUser(userId string, preferences []model.Preference) *model.AppError { + _args := &Z_DeletePreferencesForUserArgs{userId, preferences} + _returns := &Z_DeletePreferencesForUserReturns{} + if err := g.client.Call("Plugin.DeletePreferencesForUser", _args, _returns); err != nil { + log.Printf("RPC call to DeletePreferencesForUser API failed: %s", err.Error()) + } + return _returns.A +} + +func (s *apiRPCServer) DeletePreferencesForUser(args *Z_DeletePreferencesForUserArgs, returns *Z_DeletePreferencesForUserReturns) error { + if hook, ok := s.impl.(interface { + DeletePreferencesForUser(userId string, preferences []model.Preference) *model.AppError + }); ok { + returns.A = hook.DeletePreferencesForUser(args.A, args.B) + } else { + return encodableError(fmt.Errorf("API DeletePreferencesForUser called but not implemented.")) + } + return nil +} + type Z_GetTeamIconArgs struct { A string } diff --git a/plugin/plugintest/api.go b/plugin/plugintest/api.go index 8bf095173f..6a8abc13b2 100644 --- a/plugin/plugintest/api.go +++ b/plugin/plugintest/api.go @@ -387,6 +387,22 @@ func (_m *API) DeletePost(postId string) *model.AppError { return r0 } +// DeletePreferencesForUser provides a mock function with given fields: userId, preferences +func (_m *API) DeletePreferencesForUser(userId string, preferences []model.Preference) *model.AppError { + ret := _m.Called(userId, preferences) + + var r0 *model.AppError + if rf, ok := ret.Get(0).(func(string, []model.Preference) *model.AppError); ok { + r0 = rf(userId, preferences) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.AppError) + } + } + + return r0 +} + // DeleteTeam provides a mock function with given fields: teamId func (_m *API) DeleteTeam(teamId string) *model.AppError { ret := _m.Called(teamId) @@ -1405,6 +1421,31 @@ func (_m *API) GetPostsSince(channelId string, time int64) (*model.PostList, *mo return r0, r1 } +// GetPreferencesForUser provides a mock function with given fields: userId +func (_m *API) GetPreferencesForUser(userId string) ([]model.Preference, *model.AppError) { + ret := _m.Called(userId) + + var r0 []model.Preference + if rf, ok := ret.Get(0).(func(string) []model.Preference); ok { + r0 = rf(userId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]model.Preference) + } + } + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string) *model.AppError); ok { + r1 = rf(userId) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 +} + // GetProfileImage provides a mock function with given fields: userId func (_m *API) GetProfileImage(userId string) ([]byte, *model.AppError) { ret := _m.Called(userId) @@ -2861,6 +2902,22 @@ func (_m *API) UpdatePost(post *model.Post) (*model.Post, *model.AppError) { return r0, r1 } +// UpdatePreferencesForUser provides a mock function with given fields: userId, preferences +func (_m *API) UpdatePreferencesForUser(userId string, preferences []model.Preference) *model.AppError { + ret := _m.Called(userId, preferences) + + var r0 *model.AppError + if rf, ok := ret.Get(0).(func(string, []model.Preference) *model.AppError); ok { + r0 = rf(userId, preferences) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.AppError) + } + } + + return r0 +} + // UpdateTeam provides a mock function with given fields: team func (_m *API) UpdateTeam(team *model.Team) (*model.Team, *model.AppError) { ret := _m.Called(team)