Fix for feature flag handling (#1753)

* fix for handling feature flags changing

* update tests

* fix tests

* lint fixes

* feedback updates

* use getConfig instead

* linter fix

* more linter

* revert changes

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
This commit is contained in:
Scott Bishel 2021-11-09 07:56:04 -07:00 committed by GitHub
parent ad0e3a1248
commit f74eea11ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 84 additions and 89 deletions

View file

@ -2,9 +2,6 @@ package main
import ( import (
"reflect" "reflect"
"github.com/mattermost/focalboard/server/utils"
"github.com/pkg/errors"
) )
// configuration captures the plugin's external configuration as exposed in the Mattermost server // configuration captures the plugin's external configuration as exposed in the Mattermost server
@ -71,21 +68,28 @@ func (p *Plugin) setConfiguration(configuration *configuration) {
} }
// OnConfigurationChange is invoked when configuration changes may have been made. // OnConfigurationChange is invoked when configuration changes may have been made.
func (p *Plugin) OnConfigurationChange() error { func (p *Plugin) OnConfigurationChange() error { //nolint
// Have we been setup by OnActivate? // Have we been setup by OnActivate?
if p.wsPluginAdapter == nil { if p.wsPluginAdapter == nil {
return nil return nil
} }
configuration := &configuration{} mmconfig := p.API.GetConfig()
// Load the public configuration fields from the Mattermost server configuration.
if err := p.API.LoadPluginConfiguration(&configuration); err != nil { // handle plugin configuration settings
return errors.Wrap(err, "failed to load plugin configuration") enableShareBoards := false
if mmconfig.PluginSettings.Plugins[pluginName][sharedBoardsName] == true {
enableShareBoards = true
}
configuration := &configuration{
EnablePublicSharedBoards: enableShareBoards,
} }
p.setConfiguration(configuration) p.setConfiguration(configuration)
p.server.UpdateClientConfig(utils.StructToMap(configuration)) p.server.Config().EnablePublicSharedBoards = enableShareBoards
p.wsPluginAdapter.BroadcastConfigChange(*p.server.App().GetClientConfig())
// handle feature flags
p.server.Config().FeatureFlags = parseFeatureFlags(mmconfig.FeatureFlags.ToMap())
p.server.UpdateAppConfig()
p.wsPluginAdapter.BroadcastConfigChange(*p.server.App().GetClientConfig())
return nil return nil
} }

View file

@ -1,90 +1,85 @@
package main package main
import ( import (
"errors"
"testing" "testing"
"github.com/mattermost/focalboard/server/model" "github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/server"
"github.com/mattermost/focalboard/server/services/config"
"github.com/mattermost/focalboard/server/ws" "github.com/mattermost/focalboard/server/ws"
serverModel "github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/plugin/plugintest" "github.com/mattermost/mattermost-server/v6/plugin/plugintest"
"github.com/mattermost/mattermost-server/v6/plugin/plugintest/mock"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
var errLoadPluginConfiguration = errors.New("loadPluginConfiguration Error") type TestHelper struct {
Server *server.Server
}
func TestConfiguration(t *testing.T) { func SetupTestHelper() *TestHelper {
t.Run("null configuration", func(t *testing.T) { th := &TestHelper{}
plugin := &Plugin{} th.Server = newTestServer()
assert.NotNil(t, plugin.getConfiguration()) return th
}
func newTestServer() *server.Server {
srv, err := server.New(server.Params{
Cfg: &config.Configuration{},
}) })
if err != nil {
panic(err)
}
t.Run("changing configuration", func(t *testing.T) { return srv
plugin := &Plugin{} }
configuration1 := &configuration{EnablePublicSharedBoards: false} func TestConfigurationNullConfiguration(t *testing.T) {
plugin.setConfiguration(configuration1) plugin := &Plugin{}
assert.Equal(t, configuration1, plugin.getConfiguration()) assert.NotNil(t, plugin.getConfiguration())
configuration2 := &configuration{EnablePublicSharedBoards: true}
plugin.setConfiguration(configuration2)
assert.Equal(t, configuration2, plugin.getConfiguration())
assert.NotEqual(t, configuration1, plugin.getConfiguration())
assert.False(t, plugin.getConfiguration() == configuration1)
assert.True(t, plugin.getConfiguration() == configuration2)
})
t.Run("setting same configuration", func(t *testing.T) {
plugin := &Plugin{}
configuration1 := &configuration{}
plugin.setConfiguration(configuration1)
assert.Panics(t, func() {
plugin.setConfiguration(configuration1)
})
})
t.Run("clearing configuration", func(t *testing.T) {
plugin := &Plugin{}
configuration1 := &configuration{EnablePublicSharedBoards: true}
plugin.setConfiguration(configuration1)
assert.NotPanics(t, func() {
plugin.setConfiguration(nil)
})
assert.NotNil(t, plugin.getConfiguration())
assert.NotEqual(t, configuration1, plugin.getConfiguration())
})
} }
func TestOnConfigurationChange(t *testing.T) { func TestOnConfigurationChange(t *testing.T) {
t.Run("Test LoadPlugin Error", func(t *testing.T) { stringRef := ""
api := &plugintest.API{}
api.On("LoadPluginConfiguration",
mock.Anything).Return(func(dest interface{}) error {
return errLoadPluginConfiguration
})
p := Plugin{} basePlugins := make(map[string]map[string]interface{})
p.SetAPI(api) basePlugins[pluginName] = make(map[string]interface{})
p.wsPluginAdapter = &ws.PluginAdapter{} basePlugins[pluginName][sharedBoardsName] = true
err := p.OnConfigurationChange() baseFeatureFlags := &serverModel.FeatureFlags{
assert.Error(t, err) BoardsFeatureFlags: "Feature1-Feature2",
}) }
basePluginSettings := &serverModel.PluginSettings{
Directory: &stringRef,
Plugins: basePlugins,
}
baseConfig := &serverModel.Config{
FeatureFlags: baseFeatureFlags,
PluginSettings: *basePluginSettings,
}
t.Run("Test Load Plugin Success", func(t *testing.T) { t.Run("Test Load Plugin Success", func(t *testing.T) {
th := SetupTestHelper()
api := &plugintest.API{} api := &plugintest.API{}
api.On("LoadPluginConfiguration", mock.AnythingOfType("**main.configuration")).Return(nil) api.On("GetUnsanitizedConfig").Return(baseConfig)
p := Plugin{} p := Plugin{}
p.SetAPI(api) p.SetAPI(api)
p.server = th.Server
p.wsPluginAdapter = &FakePluginAdapter{} p.wsPluginAdapter = &FakePluginAdapter{}
err := p.OnConfigurationChange() err := p.OnConfigurationChange()
require.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, 1, count) assert.Equal(t, 1, count)
// make sure both App and Server got updated
assert.True(t, p.server.Config().EnablePublicSharedBoards)
assert.True(t, p.server.App().GetClientConfig().EnablePublicSharedBoards)
assert.Equal(t, "true", p.server.Config().FeatureFlags["Feature1"])
assert.Equal(t, "true", p.server.Config().FeatureFlags["Feature2"])
assert.Equal(t, "", p.server.Config().FeatureFlags["Feature3"])
}) })
} }

View file

@ -166,17 +166,7 @@ func (p *Plugin) createBoardsConfig(mmconfig mmModel.Config, baseURL string, ser
enablePublicSharedBoards = true enablePublicSharedBoards = true
} }
featureFlags := make(map[string]string) featureFlags := parseFeatureFlags(mmconfig.FeatureFlags.ToMap())
for key, value := range mmconfig.FeatureFlags.ToMap() {
// Break out FeatureFlags and pass remaining
if key == boardsFeatureFlagName {
for _, flag := range strings.Split(value, "-") {
featureFlags[flag] = "true"
}
} else {
featureFlags[key] = value
}
}
return &config.Configuration{ return &config.Configuration{
ServerRoot: baseURL + "/plugins/focalboard", ServerRoot: baseURL + "/plugins/focalboard",
@ -204,6 +194,21 @@ func (p *Plugin) createBoardsConfig(mmconfig mmModel.Config, baseURL string, ser
} }
} }
func parseFeatureFlags(configFeatureFlags map[string]string) map[string]string {
featureFlags := make(map[string]string)
for key, value := range configFeatureFlags {
// Break out FeatureFlags and pass remaining
if key == boardsFeatureFlagName {
for _, flag := range strings.Split(value, "-") {
featureFlags[flag] = "true"
}
} else {
featureFlags[key] = value
}
}
return featureFlags
}
func (p *Plugin) OnWebSocketConnect(webConnID, userID string) { func (p *Plugin) OnWebSocketConnect(webConnID, userID string) {
p.wsPluginAdapter.OnWebSocketConnect(webConnID, userID) p.wsPluginAdapter.OnWebSocketConnect(webConnID, userID)
} }

View file

@ -352,16 +352,7 @@ func (s *Server) App() *app.App {
return s.app return s.app
} }
func (s *Server) UpdateClientConfig(pluginConfig map[string]interface{}) { func (s *Server) UpdateAppConfig() {
for index, value := range pluginConfig {
if index == "EnablePublicSharedBoards" {
b, ok := value.(bool)
if !ok {
s.logger.Warn("Invalid value for config value", mlog.String(index, value.(string)))
}
s.config.EnablePublicSharedBoards = b
}
}
s.app.SetConfig(s.config) s.app.SetConfig(s.config)
} }