[MM-48883] Remove app package dependency for products (#21853)

Этот коммит содержится в:
Ibrahim Serdar Acikgoz
2022-12-14 15:24:04 +03:00
коммит произвёл GitHub
родитель 61317883bf
Коммит 8d90c7042f
8 изменённых файлов: 162 добавлений и 137 удалений

Просмотреть файл

@@ -23,6 +23,8 @@ import (
"github.com/mattermost/mattermost-server/v6/shared/mlog" "github.com/mattermost/mattermost-server/v6/shared/mlog"
) )
const ServerKey product.ServiceKey = "server"
// licenseSvc is added to act as a starting point for future integrated products. // licenseSvc is added to act as a starting point for future integrated products.
// It has the same signature and functionality with the license related APIs of the plugin-api. // It has the same signature and functionality with the license related APIs of the plugin-api.
type licenseSvc interface { type licenseSvc interface {
@@ -86,19 +88,24 @@ type Channels struct {
} }
func init() { func init() {
RegisterProduct("channels", ProductManifest{ product.RegisterProduct("channels", product.Manifest{
Initializer: func(s *Server, services map[ServiceKey]any) (Product, error) { Initializer: func(services map[product.ServiceKey]any) (product.Product, error) {
return NewChannels(s, services) return NewChannels(services)
}, },
Dependencies: map[ServiceKey]struct{}{ Dependencies: map[product.ServiceKey]struct{}{
ConfigKey: {}, ServerKey: {},
LicenseKey: {}, product.ConfigKey: {},
FilestoreKey: {}, product.LicenseKey: {},
product.FilestoreKey: {},
}, },
}) })
} }
func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) { func NewChannels(services map[product.ServiceKey]any) (*Channels, error) {
s, ok := services[ServerKey].(*Server)
if !ok {
return nil, errors.New("server not passed")
}
ch := &Channels{ ch := &Channels{
srv: s, srv: s,
imageProxy: imageproxy.MakeImageProxy(s.platform, s.httpService, s.Log()), imageProxy: imageproxy.MakeImageProxy(s.platform, s.httpService, s.Log()),
@@ -112,10 +119,10 @@ func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
// 2. Add the field to *Channels // 2. Add the field to *Channels
// 3. Add the service key to the slice. // 3. Add the service key to the slice.
// 4. Add a new case in the switch statement. // 4. Add a new case in the switch statement.
requiredServices := []ServiceKey{ requiredServices := []product.ServiceKey{
ConfigKey, product.ConfigKey,
LicenseKey, product.LicenseKey,
FilestoreKey, product.FilestoreKey,
} }
for _, svcKey := range requiredServices { for _, svcKey := range requiredServices {
svc, ok := services[svcKey] svc, ok := services[svcKey]
@@ -124,19 +131,19 @@ func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
} }
switch svcKey { switch svcKey {
// Keep adding more services here // Keep adding more services here
case ConfigKey: case product.ConfigKey:
cfgSvc, ok := svc.(product.ConfigService) cfgSvc, ok := svc.(product.ConfigService)
if !ok { if !ok {
return nil, errors.New("Config service did not satisfy ConfigSvc interface") return nil, errors.New("Config service did not satisfy ConfigSvc interface")
} }
ch.cfgSvc = cfgSvc ch.cfgSvc = cfgSvc
case FilestoreKey: case product.FilestoreKey:
filestore, ok := svc.(filestore.FileBackend) filestore, ok := svc.(filestore.FileBackend)
if !ok { if !ok {
return nil, errors.New("Filestore service did not satisfy FileBackend interface") return nil, errors.New("Filestore service did not satisfy FileBackend interface")
} }
ch.filestore = filestore ch.filestore = filestore
case LicenseKey: case product.LicenseKey:
svc, ok := svc.(licenseSvc) svc, ok := svc.(licenseSvc)
if !ok { if !ok {
return nil, errors.New("License service did not satisfy licenseSvc interface") return nil, errors.New("License service did not satisfy licenseSvc interface")
@@ -198,7 +205,7 @@ func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
} }
ch.routerSvc = newRouterService() ch.routerSvc = newRouterService()
services[RouterKey] = ch.routerSvc services[product.RouterKey] = ch.routerSvc
// Setup routes. // Setup routes.
pluginsRoute := ch.srv.Router.PathPrefix("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}").Subrouter() pluginsRoute := ch.srv.Router.PathPrefix("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}").Subrouter()
@@ -206,29 +213,29 @@ func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
pluginsRoute.HandleFunc("/public/{public_file:.*}", ch.ServePluginPublicRequest) pluginsRoute.HandleFunc("/public/{public_file:.*}", ch.ServePluginPublicRequest)
pluginsRoute.HandleFunc("/{anything:.*}", ch.ServePluginRequest) pluginsRoute.HandleFunc("/{anything:.*}", ch.ServePluginRequest)
services[PostKey] = &postServiceWrapper{ services[product.PostKey] = &postServiceWrapper{
app: &App{ch: ch}, app: &App{ch: ch},
} }
services[PermissionsKey] = &permissionsServiceWrapper{ services[product.PermissionsKey] = &permissionsServiceWrapper{
app: &App{ch: ch}, app: &App{ch: ch},
} }
services[TeamKey] = &teamServiceWrapper{ services[product.TeamKey] = &teamServiceWrapper{
app: &App{ch: ch}, app: &App{ch: ch},
} }
services[BotKey] = &botServiceWrapper{ services[product.BotKey] = &botServiceWrapper{
app: &App{ch: ch}, app: &App{ch: ch},
} }
services[HooksKey] = &hooksService{ services[product.HooksKey] = &hooksService{
ch: ch, ch: ch,
} }
services[UserKey] = &App{ch: ch} services[product.UserKey] = &App{ch: ch}
services[PreferencesKey] = &preferencesServiceWrapper{ services[product.PreferencesKey] = &preferencesServiceWrapper{
app: &App{ch: ch}, app: &App{ch: ch},
} }

Просмотреть файл

@@ -32,8 +32,8 @@ type licenseWrapper struct {
srv *Server srv *Server
} }
func (w *licenseWrapper) Name() ServiceKey { func (w *licenseWrapper) Name() product.ServiceKey {
return LicenseKey return product.LicenseKey
} }
func (w *licenseWrapper) GetLicense() *model.License { func (w *licenseWrapper) GetLicense() *model.License {

Просмотреть файл

@@ -20,6 +20,7 @@ import (
"github.com/mattermost/mattermost-server/v6/app/platform" "github.com/mattermost/mattermost-server/v6/app/platform"
"github.com/mattermost/mattermost-server/v6/config" "github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/model" "github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/product"
fmocks "github.com/mattermost/mattermost-server/v6/shared/filestore/mocks" fmocks "github.com/mattermost/mattermost-server/v6/shared/filestore/mocks"
"github.com/mattermost/mattermost-server/v6/shared/i18n" "github.com/mattermost/mattermost-server/v6/shared/i18n"
"github.com/mattermost/mattermost-server/v6/store/storetest/mocks" "github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
@@ -1440,7 +1441,7 @@ func TestPushNotificationRace(t *testing.T) {
Return(&model.Preference{Value: "test"}, nil) Return(&model.Preference{Value: "test"}, nil)
mockStore.On("Preference").Return(&mockPreferenceStore) mockStore.On("Preference").Return(&mockPreferenceStore)
s := &Server{ s := &Server{
products: make(map[string]Product), products: make(map[string]product.Product),
Router: mux.NewRouter(), Router: mux.NewRouter(),
} }
var err error var err error
@@ -1449,12 +1450,13 @@ func TestPushNotificationRace(t *testing.T) {
}, platform.SetFileStore(&fmocks.FileBackend{})) }, platform.SetFileStore(&fmocks.FileBackend{}))
s.SetStore(mockStore) s.SetStore(mockStore)
require.NoError(t, err) require.NoError(t, err)
serviceMap := map[ServiceKey]any{ serviceMap := map[product.ServiceKey]any{
ConfigKey: s.platform, ServerKey: s,
LicenseKey: &licenseWrapper{s}, product.ConfigKey: s.platform,
FilestoreKey: s.FileBackend(), product.LicenseKey: &licenseWrapper{s},
product.FilestoreKey: s.FileBackend(),
} }
ch, err := NewChannels(s, serviceMap) ch, err := NewChannels(serviceMap)
require.NoError(t, err) require.NoError(t, err)
s.products["channels"] = ch s.products["channels"] = ch

Просмотреть файл

@@ -6,27 +6,13 @@ package app
import ( import (
"fmt" "fmt"
"strings" "strings"
"github.com/mattermost/mattermost-server/v6/product"
) )
type Product interface {
Start() error
Stop() error
}
type ProductManifest struct {
Initializer func(*Server, map[ServiceKey]any) (Product, error)
Dependencies map[ServiceKey]struct{}
}
var products = make(map[string]ProductManifest)
func RegisterProduct(name string, m ProductManifest) {
products[name] = m
}
func (s *Server) initializeProducts( func (s *Server) initializeProducts(
productMap map[string]ProductManifest, productMap map[string]product.Manifest,
serviceMap map[ServiceKey]any, serviceMap map[product.ServiceKey]any,
) error { ) error {
// create a product map to consume // create a product map to consume
pmap := make(map[string]struct{}) pmap := make(map[string]struct{})
@@ -57,7 +43,7 @@ func (s *Server) initializeProducts(
// some products can register themselves/their services // some products can register themselves/their services
initializer := manifest.Initializer initializer := manifest.Initializer
prod, err := initializer(s, serviceMap) prod, err := initializer(serviceMap)
if err != nil { if err != nil {
return fmt.Errorf("error initializing product %q: %w", product, err) return fmt.Errorf("error initializing product %q: %w", product, err)
} }

Просмотреть файл

@@ -6,6 +6,7 @@ package app
import ( import (
"testing" "testing"
"github.com/mattermost/mattermost-server/v6/product"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -16,7 +17,7 @@ const (
type productA struct{} type productA struct{}
func newProductA(s *Server, m map[ServiceKey]any) (Product, error) { func newProductA(m map[product.ServiceKey]any) (product.Product, error) {
m[testSrvKey1] = nil m[testSrvKey1] = nil
return &productA{}, nil return &productA{}, nil
} }
@@ -26,7 +27,7 @@ func (p *productA) Stop() error { return nil }
type productB struct{} type productB struct{}
func newProductB(s *Server, m map[ServiceKey]any) (Product, error) { func newProductB(m map[product.ServiceKey]any) (product.Product, error) {
m[testSrvKey2] = nil m[testSrvKey2] = nil
return &productB{}, nil return &productB{}, nil
} }
@@ -36,35 +37,35 @@ func (p *productB) Stop() error { return nil }
func TestInitializeProducts(t *testing.T) { func TestInitializeProducts(t *testing.T) {
t.Run("2 products and no circular dependency", func(t *testing.T) { t.Run("2 products and no circular dependency", func(t *testing.T) {
serviceMap := map[ServiceKey]any{ serviceMap := map[product.ServiceKey]any{
ConfigKey: nil, product.ConfigKey: nil,
LicenseKey: nil, product.LicenseKey: nil,
FilestoreKey: nil, product.FilestoreKey: nil,
ClusterKey: nil, product.ClusterKey: nil,
} }
products := map[string]ProductManifest{ products := map[string]product.Manifest{
"productA": { "productA": {
Initializer: newProductA, Initializer: newProductA,
Dependencies: map[ServiceKey]struct{}{ Dependencies: map[product.ServiceKey]struct{}{
ConfigKey: {}, product.ConfigKey: {},
LicenseKey: {}, product.LicenseKey: {},
FilestoreKey: {}, product.FilestoreKey: {},
ClusterKey: {}, product.ClusterKey: {},
}, },
}, },
"productB": { "productB": {
Initializer: newProductB, Initializer: newProductB,
Dependencies: map[ServiceKey]struct{}{ Dependencies: map[product.ServiceKey]struct{}{
ConfigKey: {}, product.ConfigKey: {},
testSrvKey1: {}, testSrvKey1: {},
FilestoreKey: {}, product.FilestoreKey: {},
ClusterKey: {}, product.ClusterKey: {},
}, },
}, },
} }
server := &Server{ server := &Server{
products: make(map[string]Product), products: make(map[string]product.Product),
} }
err := server.initializeProducts(products, serviceMap) err := server.initializeProducts(products, serviceMap)
@@ -73,36 +74,36 @@ func TestInitializeProducts(t *testing.T) {
}) })
t.Run("2 products and circular dependency", func(t *testing.T) { t.Run("2 products and circular dependency", func(t *testing.T) {
serviceMap := map[ServiceKey]any{ serviceMap := map[product.ServiceKey]any{
ConfigKey: nil, product.ConfigKey: nil,
LicenseKey: nil, product.LicenseKey: nil,
FilestoreKey: nil, product.FilestoreKey: nil,
ClusterKey: nil, product.ClusterKey: nil,
} }
products := map[string]ProductManifest{ products := map[string]product.Manifest{
"productA": { "productA": {
Initializer: newProductA, Initializer: newProductA,
Dependencies: map[ServiceKey]struct{}{ Dependencies: map[product.ServiceKey]struct{}{
ConfigKey: {}, product.ConfigKey: {},
LicenseKey: {}, product.LicenseKey: {},
FilestoreKey: {}, product.FilestoreKey: {},
ClusterKey: {}, product.ClusterKey: {},
testSrvKey2: {}, testSrvKey2: {},
}, },
}, },
"productB": { "productB": {
Initializer: newProductB, Initializer: newProductB,
Dependencies: map[ServiceKey]struct{}{ Dependencies: map[product.ServiceKey]struct{}{
ConfigKey: {}, product.ConfigKey: {},
testSrvKey1: {}, testSrvKey1: {},
FilestoreKey: {}, product.FilestoreKey: {},
ClusterKey: {}, product.ClusterKey: {},
}, },
}, },
} }
server := &Server{ server := &Server{
products: make(map[string]Product), products: make(map[string]product.Product),
} }
err := server.initializeProducts(products, serviceMap) err := server.initializeProducts(products, serviceMap)
@@ -110,19 +111,19 @@ func TestInitializeProducts(t *testing.T) {
}) })
t.Run("2 products and one w/o any dependency", func(t *testing.T) { t.Run("2 products and one w/o any dependency", func(t *testing.T) {
serviceMap := map[ServiceKey]any{ serviceMap := map[product.ServiceKey]any{
ConfigKey: nil, product.ConfigKey: nil,
LicenseKey: nil, product.LicenseKey: nil,
FilestoreKey: nil, product.FilestoreKey: nil,
ClusterKey: nil, product.ClusterKey: nil,
} }
products := map[string]ProductManifest{ products := map[string]product.Manifest{
"productA": { "productA": {
Initializer: newProductA, Initializer: newProductA,
Dependencies: map[ServiceKey]struct{}{ Dependencies: map[product.ServiceKey]struct{}{
ConfigKey: {}, product.ConfigKey: {},
LicenseKey: {}, product.LicenseKey: {},
}, },
}, },
"productB": { "productB": {
@@ -130,7 +131,7 @@ func TestInitializeProducts(t *testing.T) {
}, },
} }
server := &Server{ server := &Server{
products: make(map[string]Product), products: make(map[string]product.Product),
} }
err := server.initializeProducts(products, serviceMap) err := server.initializeProducts(products, serviceMap)

Просмотреть файл

@@ -75,30 +75,6 @@ import (
// declaring this as var to allow overriding in tests // declaring this as var to allow overriding in tests
var SentryDSN = "placeholder_sentry_dsn" var SentryDSN = "placeholder_sentry_dsn"
type ServiceKey string
const (
ChannelKey ServiceKey = "channel"
ConfigKey ServiceKey = "config"
LicenseKey ServiceKey = "license"
FilestoreKey ServiceKey = "filestore"
FileInfoStoreKey ServiceKey = "fileinfostore"
ClusterKey ServiceKey = "cluster"
CloudKey ServiceKey = "cloud"
PostKey ServiceKey = "post"
TeamKey ServiceKey = "team"
UserKey ServiceKey = "user"
PermissionsKey ServiceKey = "permissions"
RouterKey ServiceKey = "router"
BotKey ServiceKey = "bot"
LogKey ServiceKey = "log"
HooksKey ServiceKey = "hooks"
KVStoreKey ServiceKey = "kvstore"
StoreKey ServiceKey = "storekey"
SystemKey ServiceKey = "systemkey"
PreferencesKey ServiceKey = "preferenceskey"
)
type Server struct { type Server struct {
// RootRouter is the starting point for all HTTP requests to the server. // RootRouter is the starting point for all HTTP requests to the server.
RootRouter *mux.Router RootRouter *mux.Router
@@ -160,7 +136,7 @@ type Server struct {
tracer *tracing.Tracer tracer *tracing.Tracer
products map[string]Product products map[string]product.Product
hooksManager *product.HooksManager hooksManager *product.HooksManager
} }
@@ -187,7 +163,7 @@ func NewServer(options ...Option) (*Server, error) {
RootRouter: rootRouter, RootRouter: rootRouter,
LocalRouter: localRouter, LocalRouter: localRouter,
timezones: timezones.New(), timezones: timezones.New(),
products: make(map[string]Product), products: make(map[string]product.Product),
} }
for _, option := range options { for _, option := range options {
@@ -262,24 +238,25 @@ func NewServer(options ...Option) (*Server, error) {
// ensure app implements `product.UserService` // ensure app implements `product.UserService`
var _ product.UserService = (*App)(nil) var _ product.UserService = (*App)(nil)
serviceMap := map[ServiceKey]any{ serviceMap := map[product.ServiceKey]any{
ChannelKey: &channelsWrapper{srv: s}, ServerKey: s,
ConfigKey: s.platform, product.ChannelKey: &channelsWrapper{srv: s},
LicenseKey: s.licenseWrapper, product.ConfigKey: s.platform,
FilestoreKey: s.platform.FileBackend(), product.LicenseKey: s.licenseWrapper,
FileInfoStoreKey: &fileInfoWrapper{srv: s}, product.FilestoreKey: s.platform.FileBackend(),
ClusterKey: s.platform, product.FileInfoStoreKey: &fileInfoWrapper{srv: s},
UserKey: New(ServerConnector(s.Channels())), product.ClusterKey: s.platform,
LogKey: s.platform.Log(), product.UserKey: New(ServerConnector(s.Channels())),
CloudKey: &cloudWrapper{cloud: s.Cloud}, product.LogKey: s.platform.Log(),
KVStoreKey: s.platform, product.CloudKey: &cloudWrapper{cloud: s.Cloud},
StoreKey: store.NewStoreServiceAdapter(s.Store()), product.KVStoreKey: s.platform,
SystemKey: &systemServiceAdapter{server: s}, product.StoreKey: store.NewStoreServiceAdapter(s.Store()),
product.SystemKey: &systemServiceAdapter{server: s},
} }
// Step 4: Initialize products. // Step 4: Initialize products.
// Depends on s.httpService. // Depends on s.httpService.
err = s.initializeProducts(products, serviceMap) err = s.initializeProducts(product.GetProducts(), serviceMap)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to initialize products") return nil, errors.Wrap(err, "failed to initialize products")
} }

24
product/product.go Обычный файл
Просмотреть файл

@@ -0,0 +1,24 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package product
type Product interface {
Start() error
Stop() error
}
type Manifest struct {
Initializer func(map[ServiceKey]any) (Product, error)
Dependencies map[ServiceKey]struct{}
}
var products = make(map[string]Manifest)
func RegisterProduct(name string, m Manifest) {
products[name] = m
}
func GetProducts() map[string]Manifest {
return products
}

28
product/service.go Обычный файл
Просмотреть файл

@@ -0,0 +1,28 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package product
type ServiceKey string
const (
ChannelKey ServiceKey = "channel"
ConfigKey ServiceKey = "config"
LicenseKey ServiceKey = "license"
FilestoreKey ServiceKey = "filestore"
FileInfoStoreKey ServiceKey = "fileinfostore"
ClusterKey ServiceKey = "cluster"
CloudKey ServiceKey = "cloud"
PostKey ServiceKey = "post"
TeamKey ServiceKey = "team"
UserKey ServiceKey = "user"
PermissionsKey ServiceKey = "permissions"
RouterKey ServiceKey = "router"
BotKey ServiceKey = "bot"
LogKey ServiceKey = "log"
HooksKey ServiceKey = "hooks"
KVStoreKey ServiceKey = "kvstore"
StoreKey ServiceKey = "storekey"
SystemKey ServiceKey = "systemkey"
PreferencesKey ServiceKey = "preferenceskey"
)