diff --git a/server/channels/app/plugin_api_test.go b/server/channels/app/plugin_api_test.go index 1a49a5dda2..49556bf584 100644 --- a/server/channels/app/plugin_api_test.go +++ b/server/channels/app/plugin_api_test.go @@ -164,39 +164,50 @@ func TestPublicFilesPathConfiguration(t *testing.T) { } func TestPluginAPIGetUserPreference(t *testing.T) { - th := Setup(t).InitBasic() - defer th.TearDown() - api := th.SetupPluginAPI() + t.Run("should return preferences when called", func(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + api := th.SetupPluginAPI() - err := api.UpdatePreferencesForUser(th.BasicUser.Id, []model.Preference{ - { - UserId: th.BasicUser.Id, - Category: model.PreferenceCategoryDisplaySettings, - Name: model.PreferenceNameUseMilitaryTime, - Value: "true", - }, - { - UserId: th.BasicUser.Id, - Category: "test_category", - Name: "test_key", - Value: "test_value", - }, + err := api.UpdatePreferencesForUser(th.BasicUser.Id, []model.Preference{ + { + UserId: th.BasicUser.Id, + Category: model.PreferenceCategoryDisplaySettings, + Name: model.PreferenceNameUseMilitaryTime, + Value: "true", + }, + { + UserId: th.BasicUser.Id, + Category: "test_category", + Name: "test_key", + Value: "test_value", + }, + }) + require.Nil(t, err) + + preference, err := api.GetPreferenceForUser(th.BasicUser.Id, model.PreferenceCategoryDisplaySettings, model.PreferenceNameUseMilitaryTime) + + require.Nil(t, err) + assert.Equal(t, model.PreferenceCategoryDisplaySettings, preference.Category) + assert.Equal(t, model.PreferenceNameUseMilitaryTime, preference.Name) + assert.Equal(t, "true", preference.Value) + + preference, err = api.GetPreferenceForUser(th.BasicUser.Id, "test_category", "test_key") + + require.Nil(t, err) + assert.Equal(t, "test_category", preference.Category) + assert.Equal(t, "test_key", preference.Name) + assert.Equal(t, "test_value", preference.Value) }) - require.Nil(t, err) - preference, err := api.GetPreferenceForUser(th.BasicUser.Id, model.PreferenceCategoryDisplaySettings, model.PreferenceNameUseMilitaryTime) + t.Run("should return an error when a user doesn't have a preference set", func(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + api := th.SetupPluginAPI() - require.Nil(t, err) - assert.Equal(t, model.PreferenceCategoryDisplaySettings, preference.Category) - assert.Equal(t, model.PreferenceNameUseMilitaryTime, preference.Name) - assert.Equal(t, "true", preference.Value) - - preference, err = api.GetPreferenceForUser(th.BasicUser.Id, "test_category", "test_key") - - require.Nil(t, err) - assert.Equal(t, "test_category", preference.Category) - assert.Equal(t, "test_key", preference.Name) - assert.Equal(t, "test_value", preference.Value) + _, err := api.GetPreferenceForUser(th.BasicUser.Id, "something", "that doesn't exist") + assert.NotNil(t, err) + }) } func TestPluginAPIGetUserPreferences(t *testing.T) { @@ -2402,53 +2413,111 @@ func TestPluginServeMetrics(t *testing.T) { require.Equal(t, "METRICS SUBPATH", string(body)) } -func TestPluginUpdateChannelMembersNotifications(t *testing.T) { - t.Run("using API directly", func(t *testing.T) { - th := Setup(t).InitBasic() - defer th.TearDown() +func TestPluginGetChannelsForTeamForUser(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() - api := th.SetupPluginAPI() + user := th.CreateUser() - channel := th.CreateChannel(th.Context, th.BasicTeam) - th.AddUserToChannel(th.BasicUser, channel) - th.AddUserToChannel(th.BasicUser2, channel) + team1 := th.CreateTeam() + th.LinkUserToTeam(user, team1) + team2 := th.CreateTeam() + th.LinkUserToTeam(user, team2) - member1, err := api.GetChannelMember(channel.Id, th.BasicUser.Id) - require.Nil(t, err) - require.Equal(t, "", member1.NotifyProps["test_field"]) - require.Equal(t, model.IgnoreChannelMentionsDefault, member1.NotifyProps[model.IgnoreChannelMentionsNotifyProp]) - member2, err := api.GetChannelMember(channel.Id, th.BasicUser2.Id) - require.Nil(t, err) - require.Equal(t, "", member2.NotifyProps["test_field"]) - require.Equal(t, model.IgnoreChannelMentionsDefault, member2.NotifyProps[model.IgnoreChannelMentionsNotifyProp]) + channel1 := th.CreateChannel(th.Context, team1) + th.AddUserToChannel(user, channel1) + channel2 := th.CreateChannel(th.Context, team2) + th.AddUserToChannel(user, channel2) - err = api.PatchChannelMembersNotifications( - []*model.ChannelMemberIdentifier{ - {ChannelId: channel.Id, UserId: th.BasicUser.Id}, - {ChannelId: channel.Id, UserId: th.BasicUser2.Id}, - }, - map[string]string{ - "test_field": "test_value", - model.IgnoreChannelMentionsNotifyProp: model.IgnoreChannelMentionsOn, - }, - ) + dmChannel := th.CreateDmChannel(user) - require.Nil(t, err) + pluginCode := ` + package main + import ( + "github.com/mattermost/mattermost/server/public/model" + "github.com/mattermost/mattermost/server/public/plugin" + "github.com/pkg/errors" + ) - updated1, err := api.GetChannelMember(member1.ChannelId, member1.UserId) - require.Nil(t, err) - updated2, err := api.GetChannelMember(member2.ChannelId, member2.UserId) - require.Nil(t, err) + const ( + userID = "` + user.Id + `" + teamID1 = "` + team1.Id + `" + teamID2 = "` + team2.Id + `" + channelID1 = "` + channel1.Id + `" + channelID2 = "` + channel2.Id + `" + dmChannelID = "` + dmChannel.Id + `" + ) - assert.Equal(t, member1.NotifyProps[model.MarkUnreadNotifyProp], updated1.NotifyProps[model.MarkUnreadNotifyProp]) - assert.Equal(t, "test_value", updated1.NotifyProps["test_field"]) - assert.Equal(t, model.IgnoreChannelMentionsOn, updated1.NotifyProps[model.IgnoreChannelMentionsNotifyProp]) - assert.Equal(t, member2.NotifyProps[model.MarkUnreadNotifyProp], updated2.NotifyProps[model.MarkUnreadNotifyProp]) - assert.Equal(t, "test_value", updated2.NotifyProps["test_field"]) - assert.Equal(t, model.IgnoreChannelMentionsOn, updated2.NotifyProps[model.IgnoreChannelMentionsNotifyProp]) - }) + type TestPlugin struct { + plugin.MattermostPlugin + } - t.Run("using plugin", func(t *testing.T) { + func checkForChannels(channels []*model.Channel, expectedLength int, channel1Expected, channel2Expected, dmChannelExpected bool) string { + if len(channels) != expectedLength { + return "Returned the wrong number of channels" + } + + var channel1Found, channel2Found, dmChannelFound bool + for _, channel := range channels { + if channel.Id == channelID1 { + channel1Found = true + } else if channel.Id == channelID2 { + channel2Found = true + } else if channel.Id == dmChannelID { + dmChannelFound = true + } + } + + if channel1Found && !channel1Expected { + return "Channel 1 found" + } else if !channel1Found && channel1Expected { + return "Channel 1 not found" + } else if channel2Found && !channel2Expected { + return "Channel 2 found" + } else if !channel2Found && channel2Expected { + return "Channel 2 not found" + } else if dmChannelFound && !dmChannelExpected { + return "DM Channel found" + } else if !dmChannelFound && dmChannelExpected { + return "DM Channel not found" + } else { + return "" + } + } + + func (p *TestPlugin) OnActivate() error { + if channels, appErr := p.API.GetChannelsForTeamForUser(teamID1, userID, true); appErr != nil { + return appErr + } else if msg := checkForChannels(channels, 4, true, false, true); msg != "" { + return errors.New(msg + " when called with team ID 1") + } + + if channels, appErr := p.API.GetChannelsForTeamForUser(teamID2, userID, true); appErr != nil { + return appErr + } else if msg := checkForChannels(channels, 4, false, true, true); msg != "" { + return errors.New(msg + " when called with team ID 2") + } + + if channels, appErr := p.API.GetChannelsForTeamForUser("", userID, true); appErr != nil { + return appErr + } else if msg := checkForChannels(channels, 7, true, true, true); msg != "" { + return errors.New(msg + " when called with empty team ID") + } + + return nil + } + + func main() { + plugin.ClientMain(&TestPlugin{}) + }` + pluginID := "testplugin" + pluginManifest := `{"id": "testplugin", "server": {"executable": "backend.exe"}}` + + setupPluginAPITest(t, pluginCode, pluginManifest, pluginID, th.App, th.Context) +} + +func TestPluginPatchChannelMembersNotifications(t *testing.T) { + t.Run("should be able to set fields for multiple members", func(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() @@ -2515,4 +2584,63 @@ func TestPluginUpdateChannelMembersNotifications(t *testing.T) { assert.Equal(t, "test_value", updated2.NotifyProps["test_field"]) assert.Equal(t, model.IgnoreChannelMentionsOn, updated2.NotifyProps[model.IgnoreChannelMentionsNotifyProp]) }) + + t.Run("should be able to clear a field", func(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + + channel := th.CreateChannel(th.Context, th.BasicTeam) + th.AddUserToChannel(th.BasicUser, channel) + + member, err := th.App.GetChannelMember(th.Context, channel.Id, th.BasicUser.Id) + require.Nil(t, err) + + member.NotifyProps["test_field"] = "test_value" + _, err = th.App.updateChannelMember(th.Context, member) + require.Nil(t, err) + + member, err = th.App.GetChannelMember(th.Context, channel.Id, th.BasicUser.Id) + require.Nil(t, err) + require.Equal(t, "test_value", member.NotifyProps["test_field"]) + + pluginCode := ` + package main + import ( + "github.com/mattermost/mattermost/server/public/plugin" + "github.com/mattermost/mattermost/server/public/model" + ) + + const ( + channelID = "` + channel.Id + `" + userID = "` + th.BasicUser.Id + `" + ) + + type TestPlugin struct { + plugin.MattermostPlugin + } + + func (p *TestPlugin) OnActivate() error { + return p.API.PatchChannelMembersNotifications( + []*model.ChannelMemberIdentifier{ + {ChannelId: channelID, UserId: userID}, + }, + map[string]string{ + "test_field": "", + }, + ) + } + + func main() { + plugin.ClientMain(&TestPlugin{}) + }` + pluginID := "testplugin" + pluginManifest := `{"id": "testplugin", "server": {"executable": "backend.exe"}}` + + setupPluginAPITest(t, pluginCode, pluginManifest, pluginID, th.App, th.Context) + + updated, err := th.App.GetChannelMember(th.Context, member.ChannelId, member.UserId) + require.Nil(t, err) + + assert.Equal(t, "", updated.NotifyProps["test_field"]) + }) } diff --git a/server/public/plugin/api.go b/server/public/plugin/api.go index edc9bd0078..b90e84e08c 100644 --- a/server/public/plugin/api.go +++ b/server/public/plugin/api.go @@ -166,7 +166,8 @@ type API interface { // Minimum server version: 5.6 GetUsersInTeam(teamID string, page int, perPage int) ([]*model.User, *model.AppError) - // GetPreferenceForUser gets a single preference for a user. + // GetPreferenceForUser gets a single preference for a user. An error is returned if the user has no preference + // set with the given category and name, an error is returned. // // @tag User // @tag Preference @@ -463,7 +464,8 @@ type API interface { // Minimum server version: 5.2 GetChannelByNameForTeamName(teamName, channelName string, includeDeleted bool) (*model.Channel, *model.AppError) - // GetChannelsForTeamForUser gets a list of channels for given user ID in given team ID. + // GetChannelsForTeamForUser gets a list of channels for given user ID in given team ID, including DMs. + // If an empty string is passed as the team ID, the user's channels on all teams and their DMs will be returned. // // @tag Channel // @tag Team