MM-30740 Add support for boolean feature flags. (#16311)

* Add support for boolean feature flags.

* Tests

* Use strconv.ParseBool to accept additional values as true.

* Update config/feature_flags.go

Co-authored-by: Christopher Poile <cpoile@gmail.com>

Co-authored-by: Christopher Poile <cpoile@gmail.com>
Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
Christopher Speller
2020-11-25 07:27:06 -08:00
коммит произвёл GitHub
родитель faaac9cb74
Коммит a87e8d1c20
3 изменённых файлов: 78 добавлений и 4 удалений

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

@@ -6,6 +6,8 @@ package config
import (
"math"
"reflect"
"strconv"
"strings"
"github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/model"
@@ -73,7 +75,8 @@ func (f *FeatureFlagSynchronizer) Close() {
// featureFlagsFromMap sets the feature flags from a map[string]string.
// It starts with baseFeatureFlags and only sets values that are
// given by the upstream management system.
// Makes the assumption that all feature flags are strings for now.
// Makes the assumption that all feature flags are strings or booleans.
// Strings are converted to booleans by considering case insensitive "on" or any value considered by strconv.ParseBool as true and any other value as false.
func featureFlagsFromMap(featuresMap map[string]string, baseFeatureFlags model.FeatureFlags) model.FeatureFlags {
refStruct := reflect.ValueOf(&baseFeatureFlags).Elem()
for fieldName, fieldValue := range featuresMap {
@@ -83,13 +86,20 @@ func featureFlagsFromMap(featuresMap map[string]string, baseFeatureFlags model.F
continue
}
refField.Set(reflect.ValueOf(fieldValue))
switch refField.Type().Kind() {
case reflect.Bool:
parsedBoolValue, _ := strconv.ParseBool(fieldValue)
refField.Set(reflect.ValueOf(strings.ToLower(fieldValue) == "on" || parsedBoolValue))
default:
refField.Set(reflect.ValueOf(fieldValue))
}
}
return baseFeatureFlags
}
// featureFlagsToMap returns the feature flags as a map[string]string
// Currently assumes that all feature flags are strings for now.
// Supports boolean and string feature flags.
func featureFlagsToMap(featureFlags *model.FeatureFlags) map[string]string {
refStructVal := reflect.ValueOf(*featureFlags)
refStructType := reflect.TypeOf(*featureFlags)
@@ -100,7 +110,12 @@ func featureFlagsToMap(featureFlags *model.FeatureFlags) map[string]string {
if !refFieldVal.IsValid() {
continue
}
ret[refFieldType.Name] = refFieldVal.String()
switch refFieldType.Type.Kind() {
case reflect.Bool:
ret[refFieldType.Name] = strconv.FormatBool(refFieldVal.Bool())
default:
ret[refFieldType.Name] = refFieldVal.String()
}
}
return ret

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

@@ -67,6 +67,41 @@ func TestFeatureFlagsFromMap(t *testing.T) {
Base: model.FeatureFlags{},
ExpectedTestValue: "",
},
"bool on": {
FeatureMap: map[string]string{"TestBoolFeature": "on"},
Base: model.FeatureFlags{TestBoolFeature: true},
ExpectedTestValue: "",
},
"bool true": {
FeatureMap: map[string]string{"TestBoolFeature": "true"},
Base: model.FeatureFlags{TestBoolFeature: true},
ExpectedTestValue: "",
},
"bool True": {
FeatureMap: map[string]string{"TestBoolFeature": "True"},
Base: model.FeatureFlags{TestBoolFeature: true},
ExpectedTestValue: "",
},
"bool 1": {
FeatureMap: map[string]string{"TestBoolFeature": "1"},
Base: model.FeatureFlags{TestBoolFeature: true},
ExpectedTestValue: "",
},
"bool off": {
FeatureMap: map[string]string{"TestBoolFeature": "off"},
Base: model.FeatureFlags{},
ExpectedTestValue: "",
},
"bool false": {
FeatureMap: map[string]string{"TestBoolFeature": "false"},
Base: model.FeatureFlags{},
ExpectedTestValue: "",
},
"bool other value": {
FeatureMap: map[string]string{"TestBoolFeature": "someotherbadvalue"},
Base: model.FeatureFlags{},
ExpectedTestValue: "",
},
} {
t.Run(name, func(t *testing.T) {
require.Equal(t, tc.ExpectedTestValue, featureFlagsFromMap(tc.FeatureMap, tc.Base).TestFeature)
@@ -97,3 +132,23 @@ func TestFeatureFlagsToMap(t *testing.T) {
})
}
}
func TestFeatureFlagsToMapBool(t *testing.T) {
for name, tc := range map[string]struct {
Flags model.FeatureFlags
TestFeatureValue string
}{
"false": {
TestFeatureValue: "false",
Flags: model.FeatureFlags{},
},
"true": {
TestFeatureValue: "true",
Flags: model.FeatureFlags{TestBoolFeature: true},
},
} {
t.Run(name, func(t *testing.T) {
require.Equal(t, tc.TestFeatureValue, featureFlagsToMap(&tc.Flags)["TestBoolFeature"])
})
}
}

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

@@ -7,6 +7,9 @@ type FeatureFlags struct {
// Exists only for unit and manual testing.
// When set to a value, will be returned by the ping endpoint.
TestFeature string
// Exists only for testing bool functionality. Boolean feature flags interprate "on" or "true" as true and
// all other values as false.
TestBoolFeature bool
// Toggle on and off scheduled jobs for cloud user limit emails see MM-29999
CloudDelinquentEmailJobsEnabled bool
@@ -14,5 +17,6 @@ type FeatureFlags struct {
func (f *FeatureFlags) SetDefaults() {
f.TestFeature = "off"
f.TestBoolFeature = false
f.CloudDelinquentEmailJobsEnabled = false
}