[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"
)
const ServerKey product.ServiceKey = "server"
// 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.
type licenseSvc interface {
@@ -86,19 +88,24 @@ type Channels struct {
}
func init() {
RegisterProduct("channels", ProductManifest{
Initializer: func(s *Server, services map[ServiceKey]any) (Product, error) {
return NewChannels(s, services)
product.RegisterProduct("channels", product.Manifest{
Initializer: func(services map[product.ServiceKey]any) (product.Product, error) {
return NewChannels(services)
},
Dependencies: map[ServiceKey]struct{}{
ConfigKey: {},
LicenseKey: {},
FilestoreKey: {},
Dependencies: map[product.ServiceKey]struct{}{
ServerKey: {},
product.ConfigKey: {},
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{
srv: s,
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
// 3. Add the service key to the slice.
// 4. Add a new case in the switch statement.
requiredServices := []ServiceKey{
ConfigKey,
LicenseKey,
FilestoreKey,
requiredServices := []product.ServiceKey{
product.ConfigKey,
product.LicenseKey,
product.FilestoreKey,
}
for _, svcKey := range requiredServices {
svc, ok := services[svcKey]
@@ -124,19 +131,19 @@ func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
}
switch svcKey {
// Keep adding more services here
case ConfigKey:
case product.ConfigKey:
cfgSvc, ok := svc.(product.ConfigService)
if !ok {
return nil, errors.New("Config service did not satisfy ConfigSvc interface")
}
ch.cfgSvc = cfgSvc
case FilestoreKey:
case product.FilestoreKey:
filestore, ok := svc.(filestore.FileBackend)
if !ok {
return nil, errors.New("Filestore service did not satisfy FileBackend interface")
}
ch.filestore = filestore
case LicenseKey:
case product.LicenseKey:
svc, ok := svc.(licenseSvc)
if !ok {
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()
services[RouterKey] = ch.routerSvc
services[product.RouterKey] = ch.routerSvc
// Setup routes.
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("/{anything:.*}", ch.ServePluginRequest)
services[PostKey] = &postServiceWrapper{
services[product.PostKey] = &postServiceWrapper{
app: &App{ch: ch},
}
services[PermissionsKey] = &permissionsServiceWrapper{
services[product.PermissionsKey] = &permissionsServiceWrapper{
app: &App{ch: ch},
}
services[TeamKey] = &teamServiceWrapper{
services[product.TeamKey] = &teamServiceWrapper{
app: &App{ch: ch},
}
services[BotKey] = &botServiceWrapper{
services[product.BotKey] = &botServiceWrapper{
app: &App{ch: ch},
}
services[HooksKey] = &hooksService{
services[product.HooksKey] = &hooksService{
ch: ch,
}
services[UserKey] = &App{ch: ch}
services[product.UserKey] = &App{ch: ch}
services[PreferencesKey] = &preferencesServiceWrapper{
services[product.PreferencesKey] = &preferencesServiceWrapper{
app: &App{ch: ch},
}

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

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

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

@@ -6,27 +6,13 @@ package app
import (
"fmt"
"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(
productMap map[string]ProductManifest,
serviceMap map[ServiceKey]any,
productMap map[string]product.Manifest,
serviceMap map[product.ServiceKey]any,
) error {
// create a product map to consume
pmap := make(map[string]struct{})
@@ -57,7 +43,7 @@ func (s *Server) initializeProducts(
// some products can register themselves/their services
initializer := manifest.Initializer
prod, err := initializer(s, serviceMap)
prod, err := initializer(serviceMap)
if err != nil {
return fmt.Errorf("error initializing product %q: %w", product, err)
}

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

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

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

@@ -75,30 +75,6 @@ import (
// declaring this as var to allow overriding in tests
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 {
// RootRouter is the starting point for all HTTP requests to the server.
RootRouter *mux.Router
@@ -160,7 +136,7 @@ type Server struct {
tracer *tracing.Tracer
products map[string]Product
products map[string]product.Product
hooksManager *product.HooksManager
}
@@ -187,7 +163,7 @@ func NewServer(options ...Option) (*Server, error) {
RootRouter: rootRouter,
LocalRouter: localRouter,
timezones: timezones.New(),
products: make(map[string]Product),
products: make(map[string]product.Product),
}
for _, option := range options {
@@ -262,24 +238,25 @@ func NewServer(options ...Option) (*Server, error) {
// ensure app implements `product.UserService`
var _ product.UserService = (*App)(nil)
serviceMap := map[ServiceKey]any{
ChannelKey: &channelsWrapper{srv: s},
ConfigKey: s.platform,
LicenseKey: s.licenseWrapper,
FilestoreKey: s.platform.FileBackend(),
FileInfoStoreKey: &fileInfoWrapper{srv: s},
ClusterKey: s.platform,
UserKey: New(ServerConnector(s.Channels())),
LogKey: s.platform.Log(),
CloudKey: &cloudWrapper{cloud: s.Cloud},
KVStoreKey: s.platform,
StoreKey: store.NewStoreServiceAdapter(s.Store()),
SystemKey: &systemServiceAdapter{server: s},
serviceMap := map[product.ServiceKey]any{
ServerKey: s,
product.ChannelKey: &channelsWrapper{srv: s},
product.ConfigKey: s.platform,
product.LicenseKey: s.licenseWrapper,
product.FilestoreKey: s.platform.FileBackend(),
product.FileInfoStoreKey: &fileInfoWrapper{srv: s},
product.ClusterKey: s.platform,
product.UserKey: New(ServerConnector(s.Channels())),
product.LogKey: s.platform.Log(),
product.CloudKey: &cloudWrapper{cloud: s.Cloud},
product.KVStoreKey: s.platform,
product.StoreKey: store.NewStoreServiceAdapter(s.Store()),
product.SystemKey: &systemServiceAdapter{server: s},
}
// Step 4: Initialize products.
// Depends on s.httpService.
err = s.initializeProducts(products, serviceMap)
err = s.initializeProducts(product.GetProducts(), serviceMap)
if err != nil {
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"
)