* Extend Plugin API

* Use Error, NoError in tests

* Address suggestions

* Simplify code

* Add new line

* Add featureflag and GetCollectionMetadataByIds hook

* Add enter at the end of file

* make build-templates

* Fix test

* Fix GetCollectionMetadataByIds ret type

* Add GetTopicMetadataByIds hook

* Add log

* Extract i18n

* Add experimental notice on hooks

* Update model/feature_flags.go

* Swap user to userId

* Change userId to userID

Co-authored-by: Jesse Hallam <jesse.hallam@gmail.com>
Этот коммит содержится в:
Shota Gvinepadze
2022-11-03 22:43:30 +04:00
коммит произвёл GitHub
родитель cc69c917f2
Коммит b8da473da7
16 изменённых файлов: 847 добавлений и 3 удалений

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

@@ -77,6 +77,12 @@ type Channels struct {
postReminderMut sync.Mutex
postReminderTask *model.ScheduledTask
// collectionTypes maps collection types array to the registering plugin
collectionTypes map[string][]string
// topicTypes maps topic types array to collection types
topicTypes map[string][]string
collectionAndTopicTypesMut sync.Mutex
}
func init() {
@@ -94,9 +100,11 @@ func init() {
func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
ch := &Channels{
srv: s,
imageProxy: imageproxy.MakeImageProxy(s.platform, s.httpService, s.Log()),
uploadLockMap: map[string]bool{},
srv: s,
imageProxy: imageproxy.MakeImageProxy(s.platform, s.httpService, s.Log()),
uploadLockMap: map[string]bool{},
collectionTypes: map[string][]string{},
topicTypes: map[string][]string{},
}
// To get another service:

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

@@ -0,0 +1,45 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package app
import (
"net/http"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
"github.com/mattermost/mattermost-server/v6/utils"
)
func (a *App) registerCollectionAndTopic(pluginID, collectionType, topicType string) error {
// we have a race condition due to multiple plugins calling this method
a.ch.collectionAndTopicTypesMut.Lock()
defer a.ch.collectionAndTopicTypesMut.Unlock()
// check if collectionType was already registered by other plugin
for existingPluginID, existingCollectionTypes := range a.ch.collectionTypes {
if existingPluginID != pluginID && utils.StringInSlice(collectionType, existingCollectionTypes) {
return model.NewAppError("registerCollectionAndTopic", "app.collection.add_collection.exists.app_error", nil, "", http.StatusBadRequest)
}
}
// check if topicType was already registered to other collection
for existingCollectionType, existingTopicTypes := range a.ch.topicTypes {
if existingCollectionType != collectionType && utils.StringInSlice(topicType, existingTopicTypes) {
return model.NewAppError("registerCollectionAndTopic", "app.collection.add_topic.exists.app_error", nil, "", http.StatusBadRequest)
}
}
a.ch.collectionTypes[pluginID] = appendIfUnique(a.ch.collectionTypes[pluginID], collectionType)
a.ch.topicTypes[collectionType] = appendIfUnique(a.ch.topicTypes[collectionType], topicType)
a.ch.srv.Log().Info("registered collection and topic type", mlog.String("plugin_id", pluginID), mlog.String("collection_type", collectionType), mlog.String("topic_type", topicType))
return nil
}
func appendIfUnique(slice []string, a string) []string {
if utils.StringInSlice(a, slice) {
return slice
}
return append(slice, a)
}

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

@@ -486,6 +486,11 @@ func (a *App) DisablePlugin(id string) *model.AppError {
}
func (ch *Channels) disablePlugin(id string) *model.AppError {
for _, collectionType := range ch.collectionTypes[id] {
delete(ch.topicTypes, collectionType)
}
delete(ch.collectionTypes, id)
pluginsEnvironment := ch.GetPluginsEnvironment()
if pluginsEnvironment == nil {
return model.NewAppError("DisablePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)

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

@@ -1231,3 +1231,9 @@ func (api *PluginAPI) GetCloudLimits() (*model.ProductLimits, error) {
limits, err := api.app.Cloud().GetCloudLimits("")
return limits, err
}
// RegisterCollectionAndTopic informs the server that this plugin handles
// the given collection and topic types.
func (api *PluginAPI) RegisterCollectionAndTopic(collectionType, topicType string) error {
return api.app.registerCollectionAndTopic(api.id, collectionType, topicType)
}

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

@@ -2017,3 +2017,86 @@ func TestPluginAPIIsEnterpriseReady(t *testing.T) {
assert.Equal(t, true, api.IsEnterpriseReady())
}
func TestRegisterCollectionAndTopic(t *testing.T) {
os.Setenv("MM_FEATUREFLAGS_THREADSEVERYWHERE", "true")
defer os.Unsetenv("MM_FEATUREFLAGS_THREADSEVERYWHERE")
th := Setup(t)
defer th.TearDown()
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.FeatureFlags.ThreadsEverywhere = true
})
api := th.SetupPluginAPI()
err := api.RegisterCollectionAndTopic("collection1", "topic1")
assert.NoError(t, err)
err = api.RegisterCollectionAndTopic("collection1", "topic1")
assert.NoError(t, err)
err = api.RegisterCollectionAndTopic("collection1", "topic2")
assert.NoError(t, err)
err = api.RegisterCollectionAndTopic("collection2", "topic3")
assert.NoError(t, err)
err = api.RegisterCollectionAndTopic("collection2", "topic1")
assert.Error(t, err)
pluginCode := `
package main
import (
"github.com/pkg/errors"
"github.com/mattermost/mattermost-server/v6/plugin"
)
type MyPlugin struct {
plugin.MattermostPlugin
}
func (p *MyPlugin) OnActivate() error {
if err := p.API.RegisterCollectionAndTopic("collectionTypeToBeRepeated", "some topic"); err != nil {
return errors.Wrap(err, "cannot register collection")
}
if err := p.API.RegisterCollectionAndTopic("some collection", "topicToBeRepeated"); err != nil {
return errors.Wrap(err, "cannot register collection")
}
return nil
}
func main() {
plugin.ClientMain(&MyPlugin{})
}
`
pluginDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
webappPluginDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.PluginSettings.Directory = pluginDir
*cfg.PluginSettings.ClientDirectory = webappPluginDir
})
newPluginAPI := func(manifest *model.Manifest) plugin.API {
return th.App.NewPluginAPI(th.Context, manifest)
}
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(th.App.Srv()), pluginDir, webappPluginDir, th.App.Log(), nil)
require.NoError(t, err)
th.App.ch.SetPluginsEnvironment(env)
pluginID := "testplugin"
pluginManifest := `{"id": "testplugin", "server": {"executable": "backend.exe"}}`
backend := filepath.Join(pluginDir, pluginID, "backend.exe")
utils.CompileGo(t, pluginCode, backend)
os.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(pluginManifest), 0600)
manifest, activated, reterr := env.Activate(pluginID)
require.NoError(t, reterr)
require.NotNil(t, manifest)
require.True(t, activated)
err = api.RegisterCollectionAndTopic("collectionTypeToBeRepeated", "some other topic")
assert.Error(t, err)
err = api.RegisterCollectionAndTopic("some other collection", "topicToBeRepeated")
assert.Error(t, err)
}