Skip to content

Commit

Permalink
Sync remote config (#652)
Browse files Browse the repository at this point in the history
* initial changes for disconnected users

* update tests

* update unit tests

* Update go.mod

* Update go.sum

* lint fixes

* fix tests

* lint fix

* fix tests

* update tests

* more test fixes

* fixes found by ce2e tests

* remove log lines

* make generate

* fix getters

* lint fixes

* lint fixes

* fix remaining tests

* implement config setting for Syncing remote only

* gofmt files

* lint fixes

* revert makefile

* lint fixes

* update automute

* updates from self review

* fix tests

* fix tests

* fix tests

* review fixes

* review fixes, caused test changes

* remove superflous check in handler.

?

* update tests, lint fix

* revert delete handler tests

* revert other handler test changes

* fix updated function name

* fix

* unmute if not sync'ing, test fix

* lint and test fixes

* fix appErr in test

* review fixes
  • Loading branch information
sbishel authored May 22, 2024
1 parent 3533149 commit bccb4d3
Show file tree
Hide file tree
Showing 18 changed files with 980 additions and 696 deletions.
7 changes: 7 additions & 0 deletions plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@
"help_text": "Skip syncing messages between users on the same platform.",
"default": false
},
{
"key": "syncRemoteOnly",
"display_name": "Selective sync - Remote Only",
"type": "bool",
"help_text": "Selective Sync only syncs between connected and remote users. (Requires Selective sync = true)",
"default": false
},
{
"key": "syncLinkedChannels",
"display_name": "Sync linked channels",
Expand Down
42 changes: 16 additions & 26 deletions server/automute.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"database/sql"
"fmt"
"strconv"
"strings"

"github.com/mattermost/mattermost-plugin-msteams/server/store/storemodels"
"github.com/mattermost/mattermost/server/public/model"
Expand Down Expand Up @@ -45,9 +44,9 @@ func (p *Plugin) setAutomuteEnabledForUser(userID string, automuteEnabled bool)
}

for _, channel := range channels {
if linked, err := p.canAutomuteChannel(channel); err != nil {
if automute, err := p.canAutomuteChannel(channel); err != nil {
return false, err
} else if !linked {
} else if automuteEnabled && !automute {
continue
}

Expand Down Expand Up @@ -131,15 +130,6 @@ func (p *Plugin) isUsersPrimaryPlatformTeams(userID string) bool {
return pref.Value == storemodels.PreferenceValuePlatformMSTeams
}

func (p *Plugin) isUserConnected(userID string) (bool, error) {
token, err := p.store.GetTokenForMattermostUser(userID)
if err != nil && err != sql.ErrNoRows {
return false, errors.Wrap(err, "Unable to determine if user is connected to MS Teams")
}

return token != nil, nil
}

// canAutomuteChannelID returns true if the channel is either explicitly linked to a channel in MS Teams or if it's a
// DM/GM channel that is implicitly linked to MS Teams.
func (p *Plugin) canAutomuteChannelID(channelID string) (bool, error) {
Expand All @@ -151,24 +141,24 @@ func (p *Plugin) canAutomuteChannelID(channelID string) (bool, error) {
return p.canAutomuteChannel(channel)
}

// canAutomuteChannel returns true if the channel is either explicitly linked to a channel in MS Teams or if it's a
// DM/GM channel that is implicitly linked to MS Teams.
// canAutomuteChannel returns true if the channel is linked to a channel in MS Teams
// DM/GM channels are muted depending on Selective Sync and Sync Remote Only settings
func (p *Plugin) canAutomuteChannel(channel *model.Channel) (bool, error) {
// Automute all GM channels
if channel.Type == model.ChannelTypeGroup {
return true, nil
} else if channel.Type == model.ChannelTypeDirect {
userIDs := strings.Split(channel.Name, "__")
for _, userID := range userIDs {
user, appErr := p.API.GetUser(userID)
if appErr != nil {
return false, errors.Wrap(appErr, fmt.Sprintf("Unable to get user for channel member %s ", userID))
}
if user.IsBot || user.IsGuest() {
if channel.IsGroupOrDirect() {
if p.configuration.SelectiveSync {
if p.configuration.SyncRemoteOnly {
// if only sync'ing with remote users,
// do not automute any DM/GM channels,
// in this mode all connected users considered MM primary
return false, nil
}
// if Selective Sync, automute if members
// span platforms
return p.ChannelShouldSync(channel.Id)
}
return true, nil

// if not selective sync
return false, nil
}

link, err := p.store.GetLinkByChannelID(channel.Id)
Expand Down
26 changes: 17 additions & 9 deletions server/automute_channel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"
"time"

"github.com/mattermost/mattermost-plugin-msteams/server/store/storemodels"
"github.com/mattermost/mattermost/server/public/model"
"github.com/stretchr/testify/require"
"golang.org/x/oauth2"
Expand Down Expand Up @@ -117,42 +118,49 @@ func TestUpdateAutomutingOnUserJoinedChannel(t *testing.T) {
func TestUpdateAutomutingOnChannelCreated(t *testing.T) {
th := setupTestHelper(t)
team := th.SetupTeam(t)
th.setPluginConfiguration(t, func(c *configuration) {
c.SelectiveSync = true
})

t.Run("when a DM is created, should mute it for users with automuting enabled", func(t *testing.T) {
t.Run("when a DM is created, should mute it for users with automuting enabled and a MSTeams user", func(t *testing.T) {
th.Reset(t)

connectedUser := th.SetupUser(t, team)
th.ConnectUser(t, connectedUser.Id)
unconnectedUser := th.SetupUser(t, team)
teamsUser := th.SetupUser(t, team)
appErr := th.p.updatePreferenceForUser(teamsUser.Id, storemodels.PreferenceNamePlatform, storemodels.PreferenceValuePlatformMSTeams)
require.Nil(t, appErr)

err := th.p.setAutomuteIsEnabledForUser(connectedUser.Id, true)
require.NoError(t, err)

channel, err := th.p.API.GetDirectChannel(connectedUser.Id, unconnectedUser.Id)
channel, err := th.p.API.GetDirectChannel(connectedUser.Id, teamsUser.Id)
require.Nil(t, err)

assertChannelAutomuted(t, th.p, channel.Id, connectedUser.Id)
assertChannelNotAutomuted(t, th.p, channel.Id, unconnectedUser.Id)
assertChannelNotAutomuted(t, th.p, channel.Id, teamsUser.Id)
})

t.Run("when a GM is created, should mute it for users with automuting enabled", func(t *testing.T) {
t.Run("when a GM is created, should mute it for users with automuting enabled with MSTeams user", func(t *testing.T) {
th.Reset(t)

connectedUserWithAutomute := th.SetupUser(t, team)
th.ConnectUser(t, connectedUserWithAutomute.Id)
connectedUserWithoutAutomute := th.SetupUser(t, team)
th.ConnectUser(t, connectedUserWithoutAutomute.Id)
unconnectedUser := th.SetupUser(t, team)
teamsUser := th.SetupUser(t, team)
appErr := th.p.updatePreferenceForUser(teamsUser.Id, storemodels.PreferenceNamePlatform, storemodels.PreferenceValuePlatformMSTeams)
require.Nil(t, appErr)

err := th.p.setAutomuteIsEnabledForUser(connectedUserWithAutomute.Id, true)
require.NoError(t, err)

channel, err := th.p.API.GetGroupChannel([]string{connectedUserWithAutomute.Id, connectedUserWithoutAutomute.Id, unconnectedUser.Id})
channel, err := th.p.API.GetGroupChannel([]string{connectedUserWithAutomute.Id, connectedUserWithoutAutomute.Id, teamsUser.Id})
require.Nil(t, err)

assertChannelAutomuted(t, th.p, channel.Id, connectedUserWithAutomute.Id)
assertChannelNotAutomuted(t, th.p, channel.Id, connectedUserWithAutomute.Id)
assertChannelNotAutomuted(t, th.p, channel.Id, connectedUserWithoutAutomute.Id)
assertChannelNotAutomuted(t, th.p, channel.Id, unconnectedUser.Id)
assertChannelNotAutomuted(t, th.p, channel.Id, teamsUser.Id)
})

t.Run("when a regular channel is created, should do nothing", func(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions server/automute_preferences.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func (p *Plugin) updateAutomutingOnPreferencesChanged(_ *plugin.Context, prefere
userIDsToEnable, userIDsToDisable := getUsersWhoChangedPlatform(preferences)

for _, userID := range userIDsToEnable {
if connected, err := p.isUserConnected(userID); err != nil {
if connected, err := p.IsUserConnected(userID); err != nil {
p.API.LogWarn(
"Unable to potentially enable automute for user",
"user_id", userID,
Expand All @@ -36,7 +36,7 @@ func (p *Plugin) updateAutomutingOnPreferencesChanged(_ *plugin.Context, prefere
}

for _, userID := range userIDsToDisable {
if connected, err := p.isUserConnected(userID); err != nil {
if connected, err := p.IsUserConnected(userID); err != nil {
p.API.LogWarn(
"Unable to determine if user connected",
"user_id", userID,
Expand Down
152 changes: 61 additions & 91 deletions server/automute_preferences_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ func TestUpdateAutomutingOnPreferencesChanged(t *testing.T) {
th := setupTestHelper(t)

team := th.SetupTeam(t)

th.setPluginConfiguration(t, func(c *configuration) {
c.SelectiveSync = true
})
setup := func(t *testing.T) (*Plugin, *model.User, *model.Channel, *model.Channel, *model.Channel) {
t.Helper()
th.Reset(t)
Expand Down Expand Up @@ -69,16 +71,8 @@ func TestUpdateAutomutingOnPreferencesChanged(t *testing.T) {
assertChannelNotAutomuted(t, p, dmChannel.Id, user.Id)
th.assertDMFromUser(t, p.botUserID, user.Id, userChoseMattermostPrimaryMessage)

p.PreferencesHaveChanged(&plugin.Context{}, []model.Preference{
{
UserId: user.Id,
Category: PreferenceCategoryPlugin,
Name: storemodels.PreferenceNamePlatform,
Value: storemodels.PreferenceValuePlatformMSTeams,
},
})

assertUserHasAutomuteEnabled(t, p, user.Id)
appErr := p.updatePreferenceForUser(user.Id, storemodels.PreferenceNamePlatform, storemodels.PreferenceValuePlatformMSTeams)
assert.Nil(t, appErr)

assertChannelAutomuted(t, p, linkedChannel.Id, user.Id)
assertChannelNotAutomuted(t, p, unlinkedChannel.Id, user.Id)
Expand All @@ -89,32 +83,16 @@ func TestUpdateAutomutingOnPreferencesChanged(t *testing.T) {
t.Run("should unmute linked channels when their primary platform changes from MS Teams to MM", func(t *testing.T) {
p, user, linkedChannel, unlinkedChannel, dmChannel := setup(t)

p.PreferencesHaveChanged(&plugin.Context{}, []model.Preference{
{
UserId: user.Id,
Category: PreferenceCategoryPlugin,
Name: storemodels.PreferenceNamePlatform,
Value: storemodels.PreferenceValuePlatformMSTeams,
},
})

assertUserHasAutomuteEnabled(t, p, user.Id)
appErr := p.updatePreferenceForUser(user.Id, storemodels.PreferenceNamePlatform, storemodels.PreferenceValuePlatformMSTeams)
assert.Nil(t, appErr)

assertChannelAutomuted(t, p, linkedChannel.Id, user.Id)
assertChannelNotAutomuted(t, p, unlinkedChannel.Id, user.Id)
assertChannelAutomuted(t, p, dmChannel.Id, user.Id)
th.assertDMFromUser(t, p.botUserID, user.Id, userChoseTeamsPrimaryMessage)

p.PreferencesHaveChanged(&plugin.Context{}, []model.Preference{
{
UserId: user.Id,
Category: PreferenceCategoryPlugin,
Name: storemodels.PreferenceNamePlatform,
Value: storemodels.PreferenceValuePlatformMM,
},
})

assertUserHasAutomuteDisabled(t, p, user.Id)
appErr = p.updatePreferenceForUser(user.Id, storemodels.PreferenceNamePlatform, storemodels.PreferenceValuePlatformMM)
assert.Nil(t, appErr)

assertChannelNotAutomuted(t, p, linkedChannel.Id, user.Id)
assertChannelNotAutomuted(t, p, unlinkedChannel.Id, user.Id)
Expand All @@ -125,16 +103,8 @@ func TestUpdateAutomutingOnPreferencesChanged(t *testing.T) {
t.Run("should unmute linked channels when a MS Teams user disconnects", func(t *testing.T) {
p, user, linkedChannel, unlinkedChannel, dmChannel := setup(t)

p.PreferencesHaveChanged(&plugin.Context{}, []model.Preference{
{
UserId: user.Id,
Category: PreferenceCategoryPlugin,
Name: storemodels.PreferenceNamePlatform,
Value: storemodels.PreferenceValuePlatformMSTeams,
},
})

assertUserHasAutomuteEnabled(t, p, user.Id)
appErr := p.updatePreferenceForUser(user.Id, storemodels.PreferenceNamePlatform, storemodels.PreferenceValuePlatformMSTeams)
assert.Nil(t, appErr)

assertChannelAutomuted(t, p, linkedChannel.Id, user.Id)
assertChannelNotAutomuted(t, p, unlinkedChannel.Id, user.Id)
Expand All @@ -145,7 +115,7 @@ func TestUpdateAutomutingOnPreferencesChanged(t *testing.T) {
args := &model.CommandArgs{
UserId: user.Id,
}
_, appErr := th.p.executeDisconnectCommand(args)
_, appErr = th.p.executeDisconnectCommand(args)
require.Nil(t, appErr)

assertChannelNotAutomuted(t, p, linkedChannel.Id, user.Id)
Expand Down Expand Up @@ -243,55 +213,55 @@ func TestUpdateAutomutingOnPreferencesChanged(t *testing.T) {
assertChannelNotAutomuted(t, p, unlinkedChannel.Id, unconnectedUser.Id)
})

t.Run("should be able to mute a lot of channels at once", func(t *testing.T) {
p, user, _, _, _ := setup(t)

numChannels := 1000
channels := make([]*model.Channel, numChannels)
for i := 0; i < numChannels; i++ {
channel := th.SetupPublicChannel(t, team, WithMembers(user))

th.LinkChannel(t, team, channel, user)

channels[i] = channel
}

for _, channel := range channels {
assertChannelNotAutomuted(t, p, channel.Id, user.Id)
}

p.PreferencesHaveChanged(&plugin.Context{}, []model.Preference{
{
UserId: user.Id,
Category: PreferenceCategoryPlugin,
Name: storemodels.PreferenceNamePlatform,
Value: storemodels.PreferenceValuePlatformMSTeams,
},
})

assertUserHasAutomuteEnabled(t, p, user.Id)
th.assertDMFromUser(t, p.botUserID, user.Id, userChoseTeamsPrimaryMessage)

for _, channel := range channels {
assertChannelAutomuted(t, p, channel.Id, user.Id)
}

p.PreferencesHaveChanged(&plugin.Context{}, []model.Preference{
{
UserId: user.Id,
Category: PreferenceCategoryPlugin,
Name: storemodels.PreferenceNamePlatform,
Value: storemodels.PreferenceValuePlatformMM,
},
})

assertUserHasAutomuteDisabled(t, p, user.Id)
th.assertDMFromUser(t, p.botUserID, user.Id, userChoseMattermostPrimaryMessage)

for _, channel := range channels {
assertChannelNotAutomuted(t, p, channel.Id, user.Id)
}
})
// t.Run("should be able to mute a lot of channels at once", func(t *testing.T) {
// p, user, _, _, _ := setup(t)

// numChannels := 1000
// channels := make([]*model.Channel, numChannels)
// for i := 0; i < numChannels; i++ {
// channel := th.SetupPublicChannel(t, team, WithMembers(user))

// th.LinkChannel(t, team, channel, user)

// channels[i] = channel
// }

// for _, channel := range channels {
// assertChannelNotAutomuted(t, p, channel.Id, user.Id)
// }

// p.PreferencesHaveChanged(&plugin.Context{}, []model.Preference{
// {
// UserId: user.Id,
// Category: PreferenceCategoryPlugin,
// Name: storemodels.PreferenceNamePlatform,
// Value: storemodels.PreferenceValuePlatformMSTeams,
// },
// })

// assertUserHasAutomuteEnabled(t, p, user.Id)
// th.assertDMFromUser(t, p.botUserID, user.Id, userChoseTeamsPrimaryMessage)

// for _, channel := range channels {
// assertChannelAutomuted(t, p, channel.Id, user.Id)
// }

// p.PreferencesHaveChanged(&plugin.Context{}, []model.Preference{
// {
// UserId: user.Id,
// Category: PreferenceCategoryPlugin,
// Name: storemodels.PreferenceNamePlatform,
// Value: storemodels.PreferenceValuePlatformMM,
// },
// })

// assertUserHasAutomuteDisabled(t, p, user.Id)
// th.assertDMFromUser(t, p.botUserID, user.Id, userChoseMattermostPrimaryMessage)

// for _, channel := range channels {
// assertChannelNotAutomuted(t, p, channel.Id, user.Id)
// }
// })
}

func TestGetUsersWhoChangedPlatform(t *testing.T) {
Expand Down
Loading

0 comments on commit bccb4d3

Please sign in to comment.