MM-35133 trigger sync user immediately after change (#17579)
- ensure changes to user profile sync immediately - refactor sync send
Этот коммит содержится в:
@@ -1071,7 +1071,7 @@ type AppIface interface {
|
||||
UpdateScheme(scheme *model.Scheme) (*model.Scheme, *model.AppError)
|
||||
UpdateSessionsIsGuest(userID string, isGuest bool)
|
||||
UpdateSharedChannel(sc *model.SharedChannel) (*model.SharedChannel, error)
|
||||
UpdateSharedChannelRemoteNextSyncAt(id string, syncTime int64) error
|
||||
UpdateSharedChannelRemoteCursor(id string, cursor model.GetPostsSinceForSyncCursor) error
|
||||
UpdateSidebarCategories(userID, teamID string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError)
|
||||
UpdateSidebarCategoryOrder(userID, teamID string, categoryOrder []string) *model.AppError
|
||||
UpdateTeam(team *model.Team) (*model.Team, *model.AppError)
|
||||
|
||||
@@ -16427,9 +16427,9 @@ func (a *OpenTracingAppLayer) UpdateSharedChannel(sc *model.SharedChannel) (*mod
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) UpdateSharedChannelRemoteNextSyncAt(id string, syncTime int64) error {
|
||||
func (a *OpenTracingAppLayer) UpdateSharedChannelRemoteCursor(id string, cursor model.GetPostsSinceForSyncCursor) error {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UpdateSharedChannelRemoteNextSyncAt")
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UpdateSharedChannelRemoteCursor")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store.SetContext(newCtx)
|
||||
@@ -16439,7 +16439,7 @@ func (a *OpenTracingAppLayer) UpdateSharedChannelRemoteNextSyncAt(id string, syn
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0 := a.app.UpdateSharedChannelRemoteNextSyncAt(id, syncTime)
|
||||
resultVar0 := a.app.UpdateSharedChannelRemoteCursor(id, cursor)
|
||||
|
||||
if resultVar0 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar0))
|
||||
|
||||
@@ -2190,8 +2190,8 @@ func TestSharedChannelSyncForPostActions(t *testing.T) {
|
||||
}, channel, false, true)
|
||||
require.Nil(t, err, "Creating a post should not error")
|
||||
|
||||
assert.Len(t, remoteClusterService.notifications, 1)
|
||||
assert.Equal(t, channel.Id, remoteClusterService.notifications[0])
|
||||
assert.Len(t, remoteClusterService.channelNotifications, 1)
|
||||
assert.Equal(t, channel.Id, remoteClusterService.channelNotifications[0])
|
||||
})
|
||||
|
||||
t.Run("updating a post in a shared channel performs a content sync when sync service is running on that node", func(t *testing.T) {
|
||||
@@ -2217,9 +2217,9 @@ func TestSharedChannelSyncForPostActions(t *testing.T) {
|
||||
_, err = th.App.UpdatePost(th.Context, post, true)
|
||||
require.Nil(t, err, "Updating a post should not error")
|
||||
|
||||
assert.Len(t, remoteClusterService.notifications, 2)
|
||||
assert.Equal(t, channel.Id, remoteClusterService.notifications[0])
|
||||
assert.Equal(t, channel.Id, remoteClusterService.notifications[1])
|
||||
assert.Len(t, remoteClusterService.channelNotifications, 2)
|
||||
assert.Equal(t, channel.Id, remoteClusterService.channelNotifications[0])
|
||||
assert.Equal(t, channel.Id, remoteClusterService.channelNotifications[1])
|
||||
})
|
||||
|
||||
t.Run("deleting a post in a shared channel performs a content sync when sync service is running on that node", func(t *testing.T) {
|
||||
@@ -2246,9 +2246,9 @@ func TestSharedChannelSyncForPostActions(t *testing.T) {
|
||||
require.Nil(t, err, "Deleting a post should not error")
|
||||
|
||||
// one creation and two deletes
|
||||
assert.Len(t, remoteClusterService.notifications, 3)
|
||||
assert.Equal(t, channel.Id, remoteClusterService.notifications[0])
|
||||
assert.Equal(t, channel.Id, remoteClusterService.notifications[1])
|
||||
assert.Equal(t, channel.Id, remoteClusterService.notifications[2])
|
||||
assert.Len(t, remoteClusterService.channelNotifications, 3)
|
||||
assert.Equal(t, channel.Id, remoteClusterService.channelNotifications[0])
|
||||
assert.Equal(t, channel.Id, remoteClusterService.channelNotifications[1])
|
||||
assert.Equal(t, channel.Id, remoteClusterService.channelNotifications[2])
|
||||
})
|
||||
}
|
||||
|
||||
@@ -44,9 +44,9 @@ func TestSharedChannelSyncForReactionActions(t *testing.T) {
|
||||
|
||||
th.TearDown() // We need to enforce teardown because reaction instrumentation happens in a goroutine
|
||||
|
||||
assert.Len(t, sharedChannelService.notifications, 2)
|
||||
assert.Equal(t, channel.Id, sharedChannelService.notifications[0])
|
||||
assert.Equal(t, channel.Id, sharedChannelService.notifications[1])
|
||||
assert.Len(t, sharedChannelService.channelNotifications, 2)
|
||||
assert.Equal(t, channel.Id, sharedChannelService.channelNotifications[0])
|
||||
assert.Equal(t, channel.Id, sharedChannelService.channelNotifications[1])
|
||||
})
|
||||
|
||||
t.Run("removing a reaction in a shared channel performs a content sync when sync service is running on that node", func(t *testing.T) {
|
||||
@@ -79,8 +79,8 @@ func TestSharedChannelSyncForReactionActions(t *testing.T) {
|
||||
|
||||
th.TearDown() // We need to enforce teardown because reaction instrumentation happens in a goroutine
|
||||
|
||||
assert.Len(t, sharedChannelService.notifications, 2)
|
||||
assert.Equal(t, channel.Id, sharedChannelService.notifications[0])
|
||||
assert.Equal(t, channel.Id, sharedChannelService.notifications[1])
|
||||
assert.Len(t, sharedChannelService.channelNotifications, 2)
|
||||
assert.Equal(t, channel.Id, sharedChannelService.channelNotifications[0])
|
||||
assert.Equal(t, channel.Id, sharedChannelService.channelNotifications[1])
|
||||
})
|
||||
}
|
||||
|
||||
@@ -159,6 +159,7 @@ type Server struct {
|
||||
|
||||
telemetryService *telemetry.TelemetryService
|
||||
|
||||
serviceMux sync.RWMutex
|
||||
remoteClusterService remotecluster.RemoteClusterServiceIFace
|
||||
sharedChannelService SharedChannelServiceIFace
|
||||
|
||||
@@ -873,16 +874,19 @@ func (s *Server) startInterClusterServices(license *model.License, app *App) err
|
||||
|
||||
var err error
|
||||
|
||||
s.remoteClusterService, err = remotecluster.NewRemoteClusterService(s)
|
||||
rcs, err := remotecluster.NewRemoteClusterService(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = s.remoteClusterService.Start(); err != nil {
|
||||
s.remoteClusterService = nil
|
||||
if err = rcs.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.serviceMux.Lock()
|
||||
s.remoteClusterService = rcs
|
||||
s.serviceMux.Unlock()
|
||||
|
||||
// Shared Channels service
|
||||
|
||||
// License check
|
||||
@@ -897,15 +901,19 @@ func (s *Server) startInterClusterServices(license *model.License, app *App) err
|
||||
return nil
|
||||
}
|
||||
|
||||
s.sharedChannelService, err = sharedchannel.NewSharedChannelService(s, app)
|
||||
scs, err := sharedchannel.NewSharedChannelService(s, app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = s.sharedChannelService.Start(); err != nil {
|
||||
s.remoteClusterService = nil
|
||||
if err = scs.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.serviceMux.Lock()
|
||||
s.sharedChannelService = scs
|
||||
s.serviceMux.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -967,11 +975,18 @@ func (s *Server) Shutdown() {
|
||||
mlog.Warn("Unable to cleanly shutdown telemetry client", mlog.Err(err))
|
||||
}
|
||||
|
||||
s.serviceMux.RLock()
|
||||
if s.sharedChannelService != nil {
|
||||
if err = s.sharedChannelService.Shutdown(); err != nil {
|
||||
mlog.Error("Error shutting down shared channel services", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
if s.remoteClusterService != nil {
|
||||
if err = s.remoteClusterService.Shutdown(); err != nil {
|
||||
mlog.Error("Error shutting down intercluster services", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
s.serviceMux.RUnlock()
|
||||
|
||||
s.StopHTTPServer()
|
||||
s.stopLocalModeServer()
|
||||
@@ -1992,12 +2007,16 @@ func (s *Server) GetStore() store.Store {
|
||||
// GetRemoteClusterService returns the `RemoteClusterService` instantiated by the server.
|
||||
// May be nil if the service is not enabled via license.
|
||||
func (s *Server) GetRemoteClusterService() remotecluster.RemoteClusterServiceIFace {
|
||||
s.serviceMux.RLock()
|
||||
defer s.serviceMux.RUnlock()
|
||||
return s.remoteClusterService
|
||||
}
|
||||
|
||||
// GetSharedChannelSyncService returns the `SharedChannelSyncService` instantiated by the server.
|
||||
// May be nil if the service is not enabled via license.
|
||||
func (s *Server) GetSharedChannelSyncService() SharedChannelServiceIFace {
|
||||
s.serviceMux.RLock()
|
||||
defer s.serviceMux.RUnlock()
|
||||
return s.sharedChannelService
|
||||
}
|
||||
|
||||
@@ -2010,12 +2029,16 @@ func (s *Server) GetMetrics() einterfaces.MetricsInterface {
|
||||
// SetRemoteClusterService sets the `RemoteClusterService` to be used by the server.
|
||||
// For testing only.
|
||||
func (s *Server) SetRemoteClusterService(remoteClusterService remotecluster.RemoteClusterServiceIFace) {
|
||||
s.serviceMux.Lock()
|
||||
defer s.serviceMux.Unlock()
|
||||
s.remoteClusterService = remoteClusterService
|
||||
}
|
||||
|
||||
// SetSharedChannelSyncService sets the `SharedChannelSyncService` to be used by the server.
|
||||
// For testing only.
|
||||
func (s *Server) SetSharedChannelSyncService(sharedChannelService SharedChannelServiceIFace) {
|
||||
s.serviceMux.Lock()
|
||||
defer s.serviceMux.Unlock()
|
||||
s.sharedChannelService = sharedChannelService
|
||||
}
|
||||
|
||||
|
||||
@@ -133,8 +133,8 @@ func (a *App) GetRemoteClusterForUser(remoteID string, userID string) (*model.Re
|
||||
return rc, nil
|
||||
}
|
||||
|
||||
func (a *App) UpdateSharedChannelRemoteNextSyncAt(id string, syncTime int64) error {
|
||||
return a.Srv().Store.SharedChannel().UpdateRemoteNextSyncAt(id, syncTime)
|
||||
func (a *App) UpdateSharedChannelRemoteCursor(id string, cursor model.GetPostsSinceForSyncCursor) error {
|
||||
return a.Srv().Store.SharedChannel().UpdateRemoteCursor(id, cursor)
|
||||
}
|
||||
|
||||
func (a *App) DeleteSharedChannelRemote(id string) (bool, error) {
|
||||
@@ -153,3 +153,13 @@ func (a *App) GetSharedChannelRemotesStatus(channelID string) ([]*model.SharedCh
|
||||
func (a *App) NotifySharedChannelUserUpdate(user *model.User) {
|
||||
a.sendUpdatedUserEvent(*user)
|
||||
}
|
||||
|
||||
// onUserProfileChange is called when a user's profile has changed
|
||||
// (username, email, profile image, ...)
|
||||
func (a *App) onUserProfileChange(userID string) {
|
||||
syncService := a.Srv().GetSharedChannelSyncService()
|
||||
if syncService == nil || !syncService.Active() {
|
||||
return
|
||||
}
|
||||
syncService.NotifyUserProfileChanged(userID)
|
||||
}
|
||||
|
||||
@@ -18,10 +18,10 @@ func TestServerSyncSharedChannelHandler(t *testing.T) {
|
||||
|
||||
mockService := NewMockSharedChannelService(nil)
|
||||
mockService.active = false
|
||||
th.App.srv.sharedChannelService = mockService
|
||||
th.App.srv.SetSharedChannelSyncService(mockService)
|
||||
|
||||
th.App.srv.SharedChannelSyncHandler(&model.WebSocketEvent{})
|
||||
assert.Empty(t, mockService.notifications)
|
||||
assert.Empty(t, mockService.channelNotifications)
|
||||
})
|
||||
|
||||
t.Run("sync service active and broadcast envelope has ineligible event, it does nothing", func(t *testing.T) {
|
||||
@@ -30,13 +30,13 @@ func TestServerSyncSharedChannelHandler(t *testing.T) {
|
||||
|
||||
mockService := NewMockSharedChannelService(nil)
|
||||
mockService.active = true
|
||||
th.App.srv.sharedChannelService = mockService
|
||||
th.App.srv.SetSharedChannelSyncService(mockService)
|
||||
channel := th.CreateChannel(th.BasicTeam, WithShared(true))
|
||||
|
||||
websocketEvent := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_ADDED_TO_TEAM, model.NewId(), channel.Id, "", nil)
|
||||
|
||||
th.App.srv.SharedChannelSyncHandler(websocketEvent)
|
||||
assert.Empty(t, mockService.notifications)
|
||||
assert.Empty(t, mockService.channelNotifications)
|
||||
})
|
||||
|
||||
t.Run("sync service active and broadcast envelope has eligible event but channel does not exist, it does nothing", func(t *testing.T) {
|
||||
@@ -45,12 +45,12 @@ func TestServerSyncSharedChannelHandler(t *testing.T) {
|
||||
|
||||
mockService := NewMockSharedChannelService(nil)
|
||||
mockService.active = true
|
||||
th.App.srv.sharedChannelService = mockService
|
||||
th.App.srv.SetSharedChannelSyncService(mockService)
|
||||
|
||||
websocketEvent := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POSTED, model.NewId(), model.NewId(), "", nil)
|
||||
|
||||
th.App.srv.SharedChannelSyncHandler(websocketEvent)
|
||||
assert.Empty(t, mockService.notifications)
|
||||
assert.Empty(t, mockService.channelNotifications)
|
||||
})
|
||||
|
||||
t.Run("sync service active when received eligible event, it triggers a shared channel content sync", func(t *testing.T) {
|
||||
@@ -59,13 +59,13 @@ func TestServerSyncSharedChannelHandler(t *testing.T) {
|
||||
|
||||
mockService := NewMockSharedChannelService(nil)
|
||||
mockService.active = true
|
||||
th.App.srv.sharedChannelService = mockService
|
||||
th.App.srv.SetSharedChannelSyncService(mockService)
|
||||
|
||||
channel := th.CreateChannel(th.BasicTeam, WithShared(true))
|
||||
websocketEvent := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POSTED, model.NewId(), channel.Id, "", nil)
|
||||
|
||||
th.App.srv.SharedChannelSyncHandler(websocketEvent)
|
||||
assert.Len(t, mockService.notifications, 1)
|
||||
assert.Equal(t, channel.Id, mockService.notifications[0])
|
||||
assert.Len(t, mockService.channelNotifications, 1)
|
||||
assert.Equal(t, channel.Id, mockService.channelNotifications[0])
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ type SharedChannelServiceIFace interface {
|
||||
Shutdown() error
|
||||
Start() error
|
||||
NotifyChannelChanged(channelId string)
|
||||
NotifyUserProfileChanged(userID string)
|
||||
SendChannelInvite(channel *model.Channel, userId string, rc *model.RemoteCluster, options ...sharedchannel.InviteOption) error
|
||||
Active() bool
|
||||
}
|
||||
@@ -26,7 +27,7 @@ func MockOptionSharedChannelServiceWithActive(active bool) MockOptionSharedChann
|
||||
}
|
||||
|
||||
func NewMockSharedChannelService(service SharedChannelServiceIFace, options ...MockOptionSharedChannelService) *mockSharedChannelService {
|
||||
mrcs := &mockSharedChannelService{service, true, []string{}, 0}
|
||||
mrcs := &mockSharedChannelService{service, true, []string{}, []string{}, 0}
|
||||
for _, option := range options {
|
||||
option(mrcs)
|
||||
}
|
||||
@@ -35,13 +36,18 @@ func NewMockSharedChannelService(service SharedChannelServiceIFace, options ...M
|
||||
|
||||
type mockSharedChannelService struct {
|
||||
SharedChannelServiceIFace
|
||||
active bool
|
||||
notifications []string
|
||||
numInvitations int
|
||||
active bool
|
||||
channelNotifications []string
|
||||
userProfileNotifications []string
|
||||
numInvitations int
|
||||
}
|
||||
|
||||
func (mrcs *mockSharedChannelService) NotifyChannelChanged(channelId string) {
|
||||
mrcs.notifications = append(mrcs.notifications, channelId)
|
||||
mrcs.channelNotifications = append(mrcs.channelNotifications, channelId)
|
||||
}
|
||||
|
||||
func (mrcs *mockSharedChannelService) NotifyUserProfileChanged(userId string) {
|
||||
mrcs.userProfileNotifications = append(mrcs.userProfileNotifications, userId)
|
||||
}
|
||||
|
||||
func (mrcs *mockSharedChannelService) Shutdown() error {
|
||||
|
||||
@@ -981,6 +981,7 @@ func (a *App) SetProfileImageFromFile(userID string, file io.Reader) *model.AppE
|
||||
mlog.Warn("Error with updating last picture update", mlog.Err(err))
|
||||
}
|
||||
a.invalidateUserCacheAndPublish(userID)
|
||||
a.onUserProfileChange(userID)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1311,6 +1312,7 @@ func (a *App) UpdateUser(user *model.User, sendNotifications bool) (*model.User,
|
||||
}
|
||||
|
||||
a.InvalidateCacheForUser(user.Id)
|
||||
a.onUserProfileChange(user.Id)
|
||||
|
||||
return userUpdate.New, nil
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user