1
0
mirror of https://github.com/mattermost/focalboard.git synced 2025-10-31 00:17:42 +02:00

Refactor notify init (#3043)

* remove unneeded store interfaces

* - reduce dependencies for notifications service
- notifications service no longer concerned with web socket notifications

gnored, and an empty message aborts the commit.

* use app to add member to board

* remove unneeded API

* remove dependency on app package

* fix webapp jest

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
This commit is contained in:
Doug Lauder
2022-05-16 11:53:41 -04:00
committed by GitHub
parent 9dbb0c88e1
commit 11bd3720f1
10 changed files with 103 additions and 90 deletions

View File

@@ -2,18 +2,19 @@ package main
import (
"fmt"
"time"
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/config"
"github.com/mattermost/focalboard/server/services/notify/notifymentions"
"github.com/mattermost/focalboard/server/services/notify/notifysubscriptions"
"github.com/mattermost/focalboard/server/services/notify/plugindelivery"
"github.com/mattermost/focalboard/server/services/permissions"
"github.com/mattermost/focalboard/server/services/store"
"github.com/mattermost/focalboard/server/ws"
pluginapi "github.com/mattermost/mattermost-plugin-api"
"github.com/mattermost/mattermost-server/v6/model"
mm_model "github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
@@ -28,8 +29,7 @@ type notifyBackendParams struct {
cfg *config.Configuration
client *pluginapi.Client
permissions permissions.PermissionsService
store store.Store
wsAdapter ws.Adapter
appAPI *appAPI
serverRoot string
logger *mlog.Logger
}
@@ -41,10 +41,9 @@ func createMentionsNotifyBackend(params notifyBackendParams) (*notifymentions.Ba
}
backendParams := notifymentions.BackendParams{
Store: params.store,
AppAPI: params.appAPI,
Permissions: params.permissions,
Delivery: delivery,
WSAdapter: params.wsAdapter,
Logger: params.logger,
}
@@ -61,10 +60,9 @@ func createSubscriptionsNotifyBackend(params notifyBackendParams) (*notifysubscr
backendParams := notifysubscriptions.BackendParams{
ServerRoot: params.serverRoot,
Store: params.store,
AppAPI: params.appAPI,
Permissions: params.permissions,
Delivery: delivery,
WSAdapter: params.wsAdapter,
Logger: params.logger,
NotifyFreqCardSeconds: params.cfg.NotifyFreqCardSeconds,
NotifyFreqBoardSeconds: params.cfg.NotifyFreqBoardSeconds,
@@ -75,7 +73,7 @@ func createSubscriptionsNotifyBackend(params notifyBackendParams) (*notifysubscr
}
func createDelivery(client *pluginapi.Client, serverRoot string) (*plugindelivery.PluginDelivery, error) {
bot := &model.Bot{
bot := &mm_model.Bot{
Username: botUsername,
DisplayName: botDisplayname,
Description: botDescription,
@@ -90,38 +88,102 @@ func createDelivery(client *pluginapi.Client, serverRoot string) (*plugindeliver
return plugindelivery.New(botID, serverRoot, pluginAPI), nil
}
// pluginAPIAdapter provides a simple wrapper around the component based Plugin API
// which flattens the API to satisfy an interface.
type pluginAPIAdapter struct {
client *pluginapi.Client
}
func (da *pluginAPIAdapter) GetDirectChannel(userID1, userID2 string) (*model.Channel, error) {
func (da *pluginAPIAdapter) GetDirectChannel(userID1, userID2 string) (*mm_model.Channel, error) {
return da.client.Channel.GetDirect(userID1, userID2)
}
func (da *pluginAPIAdapter) CreatePost(post *model.Post) error {
func (da *pluginAPIAdapter) CreatePost(post *mm_model.Post) error {
return da.client.Post.CreatePost(post)
}
func (da *pluginAPIAdapter) GetUserByID(userID string) (*model.User, error) {
func (da *pluginAPIAdapter) GetUserByID(userID string) (*mm_model.User, error) {
return da.client.User.Get(userID)
}
func (da *pluginAPIAdapter) GetUserByUsername(name string) (*model.User, error) {
func (da *pluginAPIAdapter) GetUserByUsername(name string) (*mm_model.User, error) {
return da.client.User.GetByUsername(name)
}
func (da *pluginAPIAdapter) GetTeamMember(teamID string, userID string) (*model.TeamMember, error) {
func (da *pluginAPIAdapter) GetTeamMember(teamID string, userID string) (*mm_model.TeamMember, error) {
return da.client.Team.GetMember(teamID, userID)
}
func (da *pluginAPIAdapter) GetChannelByID(channelID string) (*model.Channel, error) {
func (da *pluginAPIAdapter) GetChannelByID(channelID string) (*mm_model.Channel, error) {
return da.client.Channel.Get(channelID)
}
func (da *pluginAPIAdapter) GetChannelMember(channelID string, userID string) (*model.ChannelMember, error) {
func (da *pluginAPIAdapter) GetChannelMember(channelID string, userID string) (*mm_model.ChannelMember, error) {
return da.client.Channel.GetMember(channelID, userID)
}
func (da *pluginAPIAdapter) CreateMember(teamID string, userID string) (*model.TeamMember, error) {
func (da *pluginAPIAdapter) CreateMember(teamID string, userID string) (*mm_model.TeamMember, error) {
return da.client.Team.CreateMember(teamID, userID)
}
type appIface interface {
CreateSubscription(sub *model.Subscription) (*model.Subscription, error)
AddMemberToBoard(member *model.BoardMember) (*model.BoardMember, error)
}
// appAPI provides app and store APIs for notification services. Where appropriate calls are made to the
// app layer to leverage the additional websocket notification logic present there, and other times the
// store APIs are called directly.
type appAPI struct {
store store.Store
app appIface
}
func (a *appAPI) init(store store.Store, app appIface) {
a.store = store
a.app = app
}
func (a *appAPI) GetBlockHistory(blockID string, opts model.QueryBlockHistoryOptions) ([]model.Block, error) {
return a.store.GetBlockHistory(blockID, opts)
}
func (a *appAPI) GetSubTree2(boardID, blockID string, opts model.QuerySubtreeOptions) ([]model.Block, error) {
return a.store.GetSubTree2(boardID, blockID, opts)
}
func (a *appAPI) GetBoardAndCardByID(blockID string) (board *model.Board, card *model.Block, err error) {
return a.store.GetBoardAndCardByID(blockID)
}
func (a *appAPI) GetUserByID(userID string) (*model.User, error) {
return a.store.GetUserByID(userID)
}
func (a *appAPI) CreateSubscription(sub *model.Subscription) (*model.Subscription, error) {
return a.app.CreateSubscription(sub)
}
func (a *appAPI) GetSubscribersForBlock(blockID string) ([]*model.Subscriber, error) {
return a.store.GetSubscribersForBlock(blockID)
}
func (a *appAPI) UpdateSubscribersNotifiedAt(blockID string, notifyAt int64) error {
return a.store.UpdateSubscribersNotifiedAt(blockID, notifyAt)
}
func (a *appAPI) UpsertNotificationHint(hint *model.NotificationHint, notificationFreq time.Duration) (*model.NotificationHint, error) {
return a.store.UpsertNotificationHint(hint, notificationFreq)
}
func (a *appAPI) GetNextNotificationHint(remove bool) (*model.NotificationHint, error) {
return a.store.GetNextNotificationHint(remove)
}
func (a *appAPI) GetMemberForBoard(boardID, userID string) (*model.BoardMember, error) {
return a.store.GetMemberForBoard(boardID, userID)
}
func (a *appAPI) AddMemberToBoard(member *model.BoardMember) (*model.BoardMember, error) {
return a.app.AddMemberToBoard(member)
}

View File

@@ -122,9 +122,8 @@ func (p *Plugin) OnActivate() error {
backendParams := notifyBackendParams{
cfg: cfg,
client: client,
store: db,
appAPI: &appAPI{store: db},
permissions: permissionsService,
wsAdapter: p.wsPluginAdapter,
serverRoot: baseURL + "/boards",
logger: logger,
}
@@ -161,6 +160,8 @@ func (p *Plugin) OnActivate() error {
return err
}
backendParams.appAPI.init(db, server.App())
p.server = server
return server.Start()
}

View File

@@ -48,6 +48,5 @@ func (a *App) notifySubscriptionChanged(subscription *model.Subscription) {
mlog.Err(err),
)
}
a.notifications.BroadcastSubscriptionChange(board.TeamID, subscription)
a.wsAdapter.BroadcastSubscriptionChange(board.TeamID, subscription)
}

View File

@@ -4,11 +4,7 @@ package notifymentions
import "github.com/mattermost/focalboard/server/model"
type Store interface {
GetUserByID(userID string) (*model.User, error)
type AppAPI interface {
GetMemberForBoard(boardID, userID string) (*model.BoardMember, error)
SaveMember(bm *model.BoardMember) (*model.BoardMember, error)
CreateSubscription(sub *model.Subscription) (*model.Subscription, error)
AddMemberToBoard(member *model.BoardMember) (*model.BoardMember, error)
}

View File

@@ -11,7 +11,6 @@ import (
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/notify"
"github.com/mattermost/focalboard/server/services/permissions"
"github.com/mattermost/focalboard/server/ws"
"github.com/wiggin77/merror"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
@@ -30,19 +29,17 @@ type MentionListener interface {
}
type BackendParams struct {
Store Store
AppAPI AppAPI
Permissions permissions.PermissionsService
Delivery MentionDelivery
WSAdapter ws.Adapter
Logger *mlog.Logger
}
// Backend provides the notification backend for @mentions.
type Backend struct {
store Store
appAPI AppAPI
permissions permissions.PermissionsService
delivery MentionDelivery
wsAdapter ws.Adapter
logger *mlog.Logger
mux sync.RWMutex
@@ -51,10 +48,9 @@ type Backend struct {
func New(params BackendParams) *Backend {
return &Backend{
store: params.Store,
appAPI: params.AppAPI,
permissions: params.Permissions,
delivery: params.Delivery,
wsAdapter: params.WSAdapter,
logger: params.Logger,
}
}
@@ -189,7 +185,7 @@ func (b *Backend) deliverMentionNotification(username string, extract string, ev
return "", fmt.Errorf("%s cannot mention non-team member %s : %w", evt.ModifiedBy.UserID, mentionedUser.Id, ErrMentionPermission)
}
// add mentioned user to board (if not already a member)
member, err := b.store.GetMemberForBoard(evt.Board.ID, mentionedUser.Id)
member, err := b.appAPI.GetMemberForBoard(evt.Board.ID, mentionedUser.Id)
if member == nil || model.IsErrNotFound(err) {
// currently all memberships are created as editors by default
newBoardMember := &model.BoardMember{
@@ -197,7 +193,7 @@ func (b *Backend) deliverMentionNotification(username string, extract string, ev
BoardID: evt.Board.ID,
SchemeEditor: true,
}
if member, err = b.store.SaveMember(newBoardMember); err != nil {
if _, err = b.appAPI.AddMemberToBoard(newBoardMember); err != nil {
return "", fmt.Errorf("cannot add mentioned user %s to board %s: %w", mentionedUser.Id, evt.Board.ID, err)
}
b.logger.Debug("auto-added mentioned user to board",
@@ -205,7 +201,6 @@ func (b *Backend) deliverMentionNotification(username string, extract string, ev
mlog.String("board_id", evt.Board.ID),
mlog.String("board_type", string(evt.Board.Type)),
)
b.wsAdapter.BroadcastMemberChange(evt.TeamID, evt.Board.ID, member)
} else {
b.logger.Debug("skipping auto-add mentioned user to board; already a member",
mlog.String("user_id", mentionedUser.Id),

View File

@@ -9,20 +9,15 @@ import (
"github.com/mattermost/focalboard/server/model"
)
type Store interface {
GetBlock(blockID string) (*model.Block, error)
type AppAPI interface {
GetBlockHistory(blockID string, opts model.QueryBlockHistoryOptions) ([]model.Block, error)
GetSubTree2(boardID, blockID string, opts model.QuerySubtreeOptions) ([]model.Block, error)
GetBoardAndCardByID(blockID string) (board *model.Board, card *model.Block, err error)
GetUserByID(userID string) (*model.User, error)
GetMemberForBoard(boardID, userID string) (*model.BoardMember, error)
SaveMember(bm *model.BoardMember) (*model.BoardMember, error)
CreateSubscription(sub *model.Subscription) (*model.Subscription, error)
GetSubscribersForBlock(blockID string) ([]*model.Subscriber, error)
GetSubscribersCountForBlock(blockID string) (int, error)
UpdateSubscribersNotifiedAt(blockID string, notifyAt int64) error
UpsertNotificationHint(hint *model.NotificationHint, notificationFreq time.Duration) (*model.NotificationHint, error)

View File

@@ -49,7 +49,7 @@ type diffGenerator struct {
board *model.Board
card *model.Block
store Store
store AppAPI
hint *model.NotificationHint
lastNotifyAt int64
logger *mlog.Logger

View File

@@ -32,7 +32,7 @@ var (
// blocks.
type notifier struct {
serverRoot string
store Store
store AppAPI
permissions permissions.PermissionsService
delivery SubscriptionDelivery
logger *mlog.Logger
@@ -46,7 +46,7 @@ type notifier struct {
func newNotifier(params BackendParams) *notifier {
return &notifier{
serverRoot: params.ServerRoot,
store: params.Store,
store: params.AppAPI,
permissions: params.Permissions,
delivery: params.Delivery,
logger: params.Logger,

View File

@@ -10,7 +10,6 @@ import (
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/notify"
"github.com/mattermost/focalboard/server/services/permissions"
"github.com/mattermost/focalboard/server/ws"
"github.com/wiggin77/merror"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
@@ -22,10 +21,9 @@ const (
type BackendParams struct {
ServerRoot string
Store Store
AppAPI AppAPI
Permissions permissions.PermissionsService
Delivery SubscriptionDelivery
WSAdapter ws.Adapter
Logger *mlog.Logger
NotifyFreqCardSeconds int
NotifyFreqBoardSeconds int
@@ -33,11 +31,10 @@ type BackendParams struct {
// Backend provides the notification backend for subscriptions.
type Backend struct {
store Store
appAPI AppAPI
permissions permissions.PermissionsService
delivery SubscriptionDelivery
notifier *notifier
wsAdapter ws.Adapter
logger *mlog.Logger
notifyFreqCardSeconds int
notifyFreqBoardSeconds int
@@ -45,11 +42,10 @@ type Backend struct {
func New(params BackendParams) *Backend {
return &Backend{
store: params.Store,
appAPI: params.AppAPI,
delivery: params.Delivery,
permissions: params.Permissions,
notifier: newNotifier(params),
wsAdapter: params.WSAdapter,
logger: params.Logger,
notifyFreqCardSeconds: params.NotifyFreqCardSeconds,
notifyFreqBoardSeconds: params.NotifyFreqBoardSeconds,
@@ -105,17 +101,16 @@ func (b *Backend) BlockChanged(evt notify.BlockChangeEvent) error {
SubscriberID: evt.ModifiedBy.UserID,
}
if _, err = b.store.CreateSubscription(sub); err != nil {
if _, err = b.appAPI.CreateSubscription(sub); err != nil {
b.logger.Warn("Cannot subscribe card author to card",
mlog.String("card_id", evt.BlockChanged.ID),
mlog.Err(err),
)
}
b.wsAdapter.BroadcastSubscriptionChange(evt.TeamID, sub)
}
// notify board subscribers
subs, err := b.store.GetSubscribersForBlock(evt.Board.ID)
subs, err := b.appAPI.GetSubscribersForBlock(evt.Board.ID)
if err != nil {
merr.Append(fmt.Errorf("cannot fetch subscribers for board %s: %w", evt.Board.ID, err))
}
@@ -128,7 +123,7 @@ func (b *Backend) BlockChanged(evt notify.BlockChangeEvent) error {
}
// notify card subscribers
subs, err = b.store.GetSubscribersForBlock(evt.Card.ID)
subs, err = b.appAPI.GetSubscribersForBlock(evt.Card.ID)
if err != nil {
merr.Append(fmt.Errorf("cannot fetch subscribers for card %s: %w", evt.Card.ID, err))
}
@@ -138,7 +133,7 @@ func (b *Backend) BlockChanged(evt notify.BlockChangeEvent) error {
// notify block subscribers (if/when other types can be subscribed to)
if evt.Board.ID != evt.BlockChanged.ID && evt.Card.ID != evt.BlockChanged.ID {
subs, err := b.store.GetSubscribersForBlock(evt.BlockChanged.ID)
subs, err := b.appAPI.GetSubscribersForBlock(evt.BlockChanged.ID)
if err != nil {
merr.Append(fmt.Errorf("cannot fetch subscribers for block %s: %w", evt.BlockChanged.ID, err))
}
@@ -161,7 +156,7 @@ func (b *Backend) notifySubscribers(subs []*model.Subscriber, blockID string, id
ModifiedByID: modifiedByID,
}
hint, err := b.store.UpsertNotificationHint(hint, b.getBlockUpdateFreq(idType))
hint, err := b.appAPI.UpsertNotificationHint(hint, b.getBlockUpdateFreq(idType))
if err != nil {
return fmt.Errorf("cannot upsert notification hint: %w", err)
}
@@ -200,7 +195,7 @@ func (b *Backend) OnMention(userID string, evt notify.BlockChangeEvent) {
}
var err error
if sub, err = b.store.CreateSubscription(sub); err != nil {
if _, err = b.appAPI.CreateSubscription(sub); err != nil {
b.logger.Warn("Cannot subscribe mentioned user to card",
mlog.String("user_id", userID),
mlog.String("card_id", evt.Card.ID),
@@ -208,16 +203,9 @@ func (b *Backend) OnMention(userID string, evt notify.BlockChangeEvent) {
)
return
}
b.wsAdapter.BroadcastSubscriptionChange(evt.TeamID, sub)
b.logger.Debug("Subscribed mentioned user to card",
mlog.String("user_id", userID),
mlog.String("card_id", evt.Card.ID),
)
}
// BroadcastSubscriptionChange sends a websocket message with details of the changed subscription to all
// connected users in the team.
func (b *Backend) BroadcastSubscriptionChange(teamID string, subscription *model.Subscription) {
b.wsAdapter.BroadcastSubscriptionChange(teamID, subscription)
}

View File

@@ -30,10 +30,6 @@ type BlockChangeEvent struct {
ModifiedBy *model.BoardMember
}
type SubscriptionChangeNotifier interface {
BroadcastSubscriptionChange(teamID string, subscription *model.Subscription)
}
// Backend provides an interface for sending notifications.
type Backend interface {
Start() error
@@ -110,22 +106,3 @@ func (s *Service) BlockChanged(evt BlockChangeEvent) {
}
}
}
// BroadcastSubscriptionChange sends a websocket message with details of the changed subscription to all
// connected users in the workspace.
func (s *Service) BroadcastSubscriptionChange(teamID string, subscription *model.Subscription) {
s.mux.RLock()
backends := make([]Backend, len(s.backends))
copy(backends, s.backends)
s.mux.RUnlock()
for _, backend := range backends {
if scn, ok := backend.(SubscriptionChangeNotifier); ok {
s.logger.Debug("Delivering subscription change notification",
mlog.String("block_id", subscription.BlockID),
mlog.String("subscriber_id", subscription.SubscriberID),
)
scn.BroadcastSubscriptionChange(teamID, subscription)
}
}
}