1
0
mirror of https://github.com/mattermost/focalboard.git synced 2025-02-01 19:14:35 +02:00

Post message channel change (#3717)

* initial implementation

* checking changes

* use boards bot

* update mocks

* linter fixes

* linter fixes

* clean up

* revert manifest change

* another lint fix

* use common error

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
This commit is contained in:
Scott Bishel 2022-08-23 08:48:41 -06:00 committed by GitHub
parent 27a4da126d
commit 0403c22f3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 109 additions and 34 deletions

View File

@ -12,17 +12,9 @@ import (
"github.com/mattermost/focalboard/server/services/permissions"
"github.com/mattermost/focalboard/server/services/store"
mm_model "github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
const (
botUsername = "boards"
botDisplayname = "Boards"
botDescription = "Created by Boards plugin."
)
type notifyBackendParams struct {
cfg *config.Configuration
servicesAPI model.ServicesAPI
@ -71,15 +63,11 @@ func createSubscriptionsNotifyBackend(params notifyBackendParams) (*notifysubscr
}
func createDelivery(servicesAPI model.ServicesAPI, serverRoot string) (*plugindelivery.PluginDelivery, error) {
bot := &mm_model.Bot{
Username: botUsername,
DisplayName: botDisplayname,
Description: botDescription,
OwnerId: model.SystemUserID,
}
bot := model.FocalboardBot
botID, err := servicesAPI.EnsureBot(bot)
if err != nil {
return nil, fmt.Errorf("failed to ensure %s bot: %w", botDisplayname, err)
return nil, fmt.Errorf("failed to ensure %s bot: %w", bot.DisplayName, err)
}
return plugindelivery.New(botID, serverRoot, servicesAPI), nil

View File

@ -20,6 +20,9 @@ var (
ErrInsufficientLicense = errors.New("appropriate license required")
)
const linkBoardMessage = "@%s linked Board [%s](%s) with this channel"
const unlinkBoardMessage = "@%s unlinked Board [%s](%s) with this channel"
func (a *App) GetBoard(boardID string) (*model.Board, error) {
board, err := a.store.GetBoard(boardID)
if model.IsErrNotFound(err) {
@ -302,18 +305,54 @@ func (a *App) CreateBoard(board *model.Board, userID string, addMember bool) (*m
func (a *App) PatchBoard(patch *model.BoardPatch, boardID, userID string) (*model.Board, error) {
var oldMembers []*model.BoardMember
var oldChannelID string
if patch.ChannelID != nil && *patch.ChannelID == "" {
var err error
oldMembers, err = a.GetMembersForBoard(boardID)
if err != nil {
a.logger.Error("Unable to get the board members", mlog.Err(err))
}
board, err := a.store.GetBoard(boardID)
if model.IsErrNotFound(err) {
return nil, model.NewErrNotFound(boardID)
}
if err != nil {
return nil, err
}
oldChannelID = board.ChannelID
}
updatedBoard, err := a.store.PatchBoard(boardID, patch, userID)
if err != nil {
return nil, err
}
if patch.ChannelID != nil {
var username string
user, err := a.store.GetUserByID(userID)
if err != nil {
a.logger.Error("Unable to get the board updater", mlog.Err(err))
username = "unknown"
} else {
username = user.Username
}
boardLink := utils.MakeBoardLink(a.config.ServerRoot, updatedBoard.TeamID, updatedBoard.ID)
if *patch.ChannelID != "" {
// TODO: this needs translated when available on the server
message := fmt.Sprintf(linkBoardMessage, username, updatedBoard.Title, boardLink)
err := a.store.PostMessage(message, "", *patch.ChannelID)
if err != nil {
a.logger.Error("Unable to post the link message to channel", mlog.Err(err))
}
} else if *patch.ChannelID == "" {
message := fmt.Sprintf(unlinkBoardMessage, username, updatedBoard.Title, boardLink)
err := a.store.PostMessage(message, "", oldChannelID)
if err != nil {
a.logger.Error("Unable to post the link message to channel", mlog.Err(err))
}
}
}
a.blockChangeNotifier.Enqueue(func() error {
a.wsAdapter.BroadcastBoardChange(updatedBoard.TeamID, updatedBoard)
if patch.ChannelID != nil && *patch.ChannelID != "" {

View File

@ -14,6 +14,19 @@ import (
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
const (
botUsername = "boards"
botDisplayname = "Boards"
botDescription = "Created by Boards plugin."
)
var FocalboardBot = &mm_model.Bot{
Username: botUsername,
DisplayName: botDisplayname,
Description: botDescription,
OwnerId: SystemUserID,
}
type ServicesAPI interface {
// Channels service
GetDirectChannel(userID1, userID2 string) (*mm_model.Channel, error)

View File

@ -18,10 +18,7 @@ import (
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
var systemsBot = &mmModel.Bot{
Username: mmModel.BotSystemBotUsername,
DisplayName: "System",
}
var boardsBotID string
// servicesAPI is the interface required my the MattermostAuthLayer to interact with
// the mattermost-server. You can use plugin-api or product-api adapter implementations.
@ -852,18 +849,18 @@ func (s *MattermostAuthLayer) GetChannel(teamID, channelID string) (*mmModel.Cha
return channel, nil
}
func (s *MattermostAuthLayer) getSystemBotID() (string, error) {
botID, err := s.servicesAPI.EnsureBot(systemsBot)
if err != nil {
s.logger.Error("failed to ensure system bot", mlog.String("username", systemsBot.Username), mlog.Err(err))
func (s *MattermostAuthLayer) getBoardsBotID() (string, error) {
if boardsBotID == "" {
var err error
boardsBotID, err = s.servicesAPI.EnsureBot(model.FocalboardBot)
s.logger.Error("failed to ensure boards bot", mlog.Err(err))
return "", err
}
return botID, nil
return boardsBotID, nil
}
func (s *MattermostAuthLayer) SendMessage(message, postType string, receipts []string) error {
botID, err := s.getSystemBotID()
botID, err := s.getBoardsBotID()
if err != nil {
return err
}
@ -880,14 +877,7 @@ func (s *MattermostAuthLayer) SendMessage(message, postType string, receipts []s
continue
}
post := &mmModel.Post{
Message: message,
UserId: botID,
ChannelId: channel.Id,
Type: postType,
}
if _, err := s.servicesAPI.CreatePost(post); err != nil {
if err := s.PostMessage(message, postType, channel.Id); err != nil {
s.logger.Error(
"failed to send message to receipt from SendMessage",
mlog.String("receipt", receipt),
@ -896,7 +886,28 @@ func (s *MattermostAuthLayer) SendMessage(message, postType string, receipts []s
continue
}
}
return nil
}
func (s *MattermostAuthLayer) PostMessage(message, postType, channelID string) error {
botID, err := s.getBoardsBotID()
if err != nil {
return err
}
post := &mmModel.Post{
Message: message,
UserId: botID,
ChannelId: channelID,
Type: postType,
}
if _, err := s.servicesAPI.CreatePost(post); err != nil {
s.logger.Error(
"failed to send message to receipt from PostMessage",
mlog.Err(err),
)
}
return nil
}

View File

@ -1251,6 +1251,20 @@ func (mr *MockStoreMockRecorder) PatchUserProps(arg0, arg1 interface{}) *gomock.
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PatchUserProps", reflect.TypeOf((*MockStore)(nil).PatchUserProps), arg0, arg1)
}
// PostMessage mocks base method.
func (m *MockStore) PostMessage(arg0, arg1, arg2 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PostMessage", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
return ret0
}
// PostMessage indicates an expected call of PostMessage.
func (mr *MockStoreMockRecorder) PostMessage(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PostMessage", reflect.TypeOf((*MockStore)(nil).PostMessage), arg0, arg1, arg2)
}
// RefreshSession mocks base method.
func (m *MockStore) RefreshSession(arg0 *model.Session) error {
m.ctrl.T.Helper()

View File

@ -707,6 +707,11 @@ func (s *SQLStore) PatchUserProps(userID string, patch model.UserPropPatch) erro
}
func (s *SQLStore) PostMessage(message string, postType string, channelID string) error {
return s.postMessage(s.db, message, postType, channelID)
}
func (s *SQLStore) RefreshSession(session *model.Session) error {
return s.refreshSession(s.db, session)

View File

@ -279,6 +279,10 @@ func (s *SQLStore) sendMessage(db sq.BaseRunner, message, postType string, recei
return errUnsupportedOperation
}
func (s *SQLStore) postMessage(db sq.BaseRunner, message, postType string, channel string) error {
return errUnsupportedOperation
}
func (s *SQLStore) getUserTimezone(_ sq.BaseRunner, _ string) (string, error) {
return "", errUnsupportedOperation
}

View File

@ -154,6 +154,7 @@ type Store interface {
GetCloudLimits() (*mmModel.ProductLimits, error)
SearchUserChannels(teamID, userID, query string) ([]*mmModel.Channel, error)
GetChannel(teamID, channelID string) (*mmModel.Channel, error)
PostMessage(message, postType, channelID string) error
SendMessage(message, postType string, receipts []string) error
// Insights