focalboard/mattermost-plugin/product/boards_product.go
Doug Lauder 605c0079eb
Multi product architecture (#3309)
* skeleton lifecycle

* bare minimum to satisfy mm-server import

* added boards_imports.go

* move boards_imports.go to correct package

* bump mmserver version; remove replace in go.mod; use module workspaces; remove logger service

* rename product.go --> boards.go

* add FileInfoStore and Cloud services for product; create minimal pluginAPI interfaces for all packages

* rename Boards -> BoardsProduct

* compile success

* remove hooks service; guard for nil BoardsApp

* update to latest mmserver ver

* upgrade mmserver to master tip

* upgrade mmserver to master tip

* bump plugin-api to master tip

* fix users service

* fix OnActivate crash; normalize AppError returns

* fileBackend interface for server/app

* feature flag

* bump mmserver version

* fix linter errors

* make go.work when linting

* fix go.work creation for CI

* add execute flag for script

* fix more linter errors

* always create a go.work

* fix ci go.work

* OS agnostic go.work generator

* fix path

* fix path again

* partially disable cypress test

* fix case Id --> ID

* bump mmserver version

* include  in go.work for dev

* addressed review comments.

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
2022-07-15 07:51:50 +02:00

287 lines
8.5 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package product
import (
"errors"
"fmt"
"github.com/mattermost/focalboard/mattermost-plugin/server/boards"
"github.com/mattermost/mattermost-server/v6/app"
mm_model "github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/plugin"
"github.com/mattermost/mattermost-server/v6/product"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
const (
boardsProductName = "boards"
boardsProductID = "com.mattermost.boards"
)
var errServiceTypeAssert = errors.New("type assertion failed")
func init() {
app.RegisterProduct("boards", app.ProductManifest{
Initializer: newBoardsProduct,
Dependencies: map[app.ServiceKey]struct{}{
app.TeamKey: {},
app.ChannelKey: {},
app.UserKey: {},
app.PostKey: {},
app.BotKey: {},
app.ClusterKey: {},
app.ConfigKey: {},
app.LogKey: {},
app.LicenseKey: {},
app.FilestoreKey: {},
app.FileInfoStoreKey: {},
app.RouterKey: {},
app.CloudKey: {},
app.KVStoreKey: {},
app.StoreKey: {},
app.SystemKey: {},
},
})
}
type boardsProduct struct {
teamService product.TeamService
channelService product.ChannelService
userService product.UserService
postService product.PostService
permissionsService product.PermissionService
botService product.BotService
clusterService product.ClusterService
configService product.ConfigService
logger mlog.LoggerIFace
licenseService product.LicenseService
filestoreService product.FilestoreService
fileInfoStoreService product.FileInfoStoreService
routerService product.RouterService
cloudService product.CloudService
kvStoreService product.KVStoreService
storeService product.StoreService
systemService product.SystemService
boardsApp *boards.BoardsApp
}
//nolint:gocyclo
func newBoardsProduct(mmServer *app.Server, services map[app.ServiceKey]interface{}) (app.Product, error) {
boards := &boardsProduct{}
for key, service := range services {
switch key {
case app.TeamKey:
teamService, ok := service.(product.TeamService)
if !ok {
return nil, fmt.Errorf("invalid service key '%s': %w", key, errServiceTypeAssert)
}
boards.teamService = teamService
case app.ChannelKey:
channelService, ok := service.(product.ChannelService)
if !ok {
return nil, fmt.Errorf("invalid service key '%s': %w", key, errServiceTypeAssert)
}
boards.channelService = channelService
case app.UserKey:
userService, ok := service.(product.UserService)
if !ok {
return nil, fmt.Errorf("invalid service key '%s': %w", key, errServiceTypeAssert)
}
boards.userService = userService
case app.PostKey:
postService, ok := service.(product.PostService)
if !ok {
return nil, fmt.Errorf("invalid service key '%s': %w", key, errServiceTypeAssert)
}
boards.postService = postService
case app.PermissionsKey:
permissionsService, ok := service.(product.PermissionService)
if !ok {
return nil, fmt.Errorf("invalid service key '%s': %w", key, errServiceTypeAssert)
}
boards.permissionsService = permissionsService
case app.BotKey:
botService, ok := service.(product.BotService)
if !ok {
return nil, fmt.Errorf("invalid service key '%s': %w", key, errServiceTypeAssert)
}
boards.botService = botService
case app.ClusterKey:
clusterService, ok := service.(product.ClusterService)
if !ok {
return nil, fmt.Errorf("invalid service key '%s': %w", key, errServiceTypeAssert)
}
boards.clusterService = clusterService
case app.ConfigKey:
configService, ok := service.(product.ConfigService)
if !ok {
return nil, fmt.Errorf("invalid service key '%s': %w", key, errServiceTypeAssert)
}
boards.configService = configService
case app.LogKey:
logger, ok := service.(mlog.LoggerIFace)
if !ok {
return nil, fmt.Errorf("invalid service key '%s': %w", key, errServiceTypeAssert)
}
boards.logger = logger.With(mlog.String("product", boardsProductName))
case app.LicenseKey:
licenseService, ok := service.(product.LicenseService)
if !ok {
return nil, fmt.Errorf("invalid service key '%s': %w", key, errServiceTypeAssert)
}
boards.licenseService = licenseService
case app.FilestoreKey:
filestoreService, ok := service.(product.FilestoreService)
if !ok {
return nil, fmt.Errorf("invalid service key '%s': %w", key, errServiceTypeAssert)
}
boards.filestoreService = filestoreService
case app.FileInfoStoreKey:
fileInfoStoreService, ok := service.(product.FileInfoStoreService)
if !ok {
return nil, fmt.Errorf("invalid service key '%s': %w", key, errServiceTypeAssert)
}
boards.fileInfoStoreService = fileInfoStoreService
case app.RouterKey:
routerService, ok := service.(product.RouterService)
if !ok {
return nil, fmt.Errorf("invalid service key '%s': %w", key, errServiceTypeAssert)
}
boards.routerService = routerService
case app.CloudKey:
cloudService, ok := service.(product.CloudService)
if !ok {
return nil, fmt.Errorf("invalid service key '%s': %w", key, errServiceTypeAssert)
}
boards.cloudService = cloudService
case app.KVStoreKey:
kvStoreService, ok := service.(product.KVStoreService)
if !ok {
return nil, fmt.Errorf("invalid service key '%s': %w", key, errServiceTypeAssert)
}
boards.kvStoreService = kvStoreService
case app.StoreKey:
storeService, ok := service.(product.StoreService)
if !ok {
return nil, fmt.Errorf("invalid service key '%s': %w", key, errServiceTypeAssert)
}
boards.storeService = storeService
case app.SystemKey:
systemService, ok := service.(product.SystemService)
if !ok {
return nil, fmt.Errorf("invalid service key '%s': %w", key, errServiceTypeAssert)
}
boards.systemService = systemService
case app.HooksKey: // not needed
}
}
return boards, nil
}
func (bp *boardsProduct) Start() error {
if !bp.configService.Config().FeatureFlags.BoardsProduct {
bp.logger.Info("Boards product disabled via feature flag")
return nil
}
bp.logger.Info("Starting boards service")
adapter := newServiceAPIAdapter(bp)
boardsApp, err := boards.NewBoardsApp(adapter)
if err != nil {
return fmt.Errorf("failed to create Boards service: %w", err)
}
bp.boardsApp = boardsApp
if err := bp.boardsApp.Start(); err != nil {
return fmt.Errorf("failed to start Boards service: %w", err)
}
return nil
}
func (bp *boardsProduct) Stop() error {
bp.logger.Info("Stopping boards service")
if bp.boardsApp == nil {
return nil
}
if err := bp.boardsApp.Stop(); err != nil {
return fmt.Errorf("error while stopping Boards service: %w", err)
}
return nil
}
//
// These callbacks are called by the suite automatically
//
func (bp *boardsProduct) OnConfigurationChange() error {
if bp.boardsApp == nil {
return nil
}
return bp.boardsApp.OnConfigurationChange()
}
func (bp *boardsProduct) OnWebSocketConnect(webConnID, userID string) {
if bp.boardsApp == nil {
return
}
bp.boardsApp.OnWebSocketConnect(webConnID, userID)
}
func (bp *boardsProduct) OnWebSocketDisconnect(webConnID, userID string) {
if bp.boardsApp == nil {
return
}
bp.boardsApp.OnWebSocketDisconnect(webConnID, userID)
}
func (bp *boardsProduct) WebSocketMessageHasBeenPosted(webConnID, userID string, req *mm_model.WebSocketRequest) {
if bp.boardsApp == nil {
return
}
bp.boardsApp.WebSocketMessageHasBeenPosted(webConnID, userID, req)
}
func (bp *boardsProduct) OnPluginClusterEvent(ctx *plugin.Context, ev mm_model.PluginClusterEvent) {
if bp.boardsApp == nil {
return
}
bp.boardsApp.OnPluginClusterEvent(ctx, ev)
}
func (bp *boardsProduct) MessageWillBePosted(ctx *plugin.Context, post *mm_model.Post) (*mm_model.Post, string) {
if bp.boardsApp == nil {
return post, ""
}
return bp.boardsApp.MessageWillBePosted(ctx, post)
}
func (bp *boardsProduct) MessageWillBeUpdated(ctx *plugin.Context, newPost, oldPost *mm_model.Post) (*mm_model.Post, string) {
if bp.boardsApp == nil {
return newPost, ""
}
return bp.boardsApp.MessageWillBeUpdated(ctx, newPost, oldPost)
}
func (bp *boardsProduct) OnCloudLimitsUpdated(limits *mm_model.ProductLimits) {
if bp.boardsApp == nil {
return
}
bp.boardsApp.OnCloudLimitsUpdated(limits)
}
func (bp *boardsProduct) RunDataRetention(nowTime, batchSize int64) (int64, error) {
if bp.boardsApp == nil {
return 0, nil
}
return bp.boardsApp.RunDataRetention(nowTime, batchSize)
}