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>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
faaac9cb74
Коммит
a87e8d1c20
@@ -6,6 +6,8 @@ package config
|
|||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v5/mlog"
|
"github.com/mattermost/mattermost-server/v5/mlog"
|
||||||
"github.com/mattermost/mattermost-server/v5/model"
|
"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.
|
// featureFlagsFromMap sets the feature flags from a map[string]string.
|
||||||
// It starts with baseFeatureFlags and only sets values that are
|
// It starts with baseFeatureFlags and only sets values that are
|
||||||
// given by the upstream management system.
|
// 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 {
|
func featureFlagsFromMap(featuresMap map[string]string, baseFeatureFlags model.FeatureFlags) model.FeatureFlags {
|
||||||
refStruct := reflect.ValueOf(&baseFeatureFlags).Elem()
|
refStruct := reflect.ValueOf(&baseFeatureFlags).Elem()
|
||||||
for fieldName, fieldValue := range featuresMap {
|
for fieldName, fieldValue := range featuresMap {
|
||||||
@@ -83,13 +86,20 @@ func featureFlagsFromMap(featuresMap map[string]string, baseFeatureFlags model.F
|
|||||||
continue
|
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
|
return baseFeatureFlags
|
||||||
}
|
}
|
||||||
|
|
||||||
// featureFlagsToMap returns the feature flags as a map[string]string
|
// 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 {
|
func featureFlagsToMap(featureFlags *model.FeatureFlags) map[string]string {
|
||||||
refStructVal := reflect.ValueOf(*featureFlags)
|
refStructVal := reflect.ValueOf(*featureFlags)
|
||||||
refStructType := reflect.TypeOf(*featureFlags)
|
refStructType := reflect.TypeOf(*featureFlags)
|
||||||
@@ -100,7 +110,12 @@ func featureFlagsToMap(featureFlags *model.FeatureFlags) map[string]string {
|
|||||||
if !refFieldVal.IsValid() {
|
if !refFieldVal.IsValid() {
|
||||||
continue
|
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
|
return ret
|
||||||
|
|||||||
@@ -67,6 +67,41 @@ func TestFeatureFlagsFromMap(t *testing.T) {
|
|||||||
Base: model.FeatureFlags{},
|
Base: model.FeatureFlags{},
|
||||||
ExpectedTestValue: "",
|
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) {
|
t.Run(name, func(t *testing.T) {
|
||||||
require.Equal(t, tc.ExpectedTestValue, featureFlagsFromMap(tc.FeatureMap, tc.Base).TestFeature)
|
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.
|
// Exists only for unit and manual testing.
|
||||||
// When set to a value, will be returned by the ping endpoint.
|
// When set to a value, will be returned by the ping endpoint.
|
||||||
TestFeature string
|
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
|
// Toggle on and off scheduled jobs for cloud user limit emails see MM-29999
|
||||||
CloudDelinquentEmailJobsEnabled bool
|
CloudDelinquentEmailJobsEnabled bool
|
||||||
@@ -14,5 +17,6 @@ type FeatureFlags struct {
|
|||||||
|
|
||||||
func (f *FeatureFlags) SetDefaults() {
|
func (f *FeatureFlags) SetDefaults() {
|
||||||
f.TestFeature = "off"
|
f.TestFeature = "off"
|
||||||
|
f.TestBoolFeature = false
|
||||||
f.CloudDelinquentEmailJobsEnabled = false
|
f.CloudDelinquentEmailJobsEnabled = false
|
||||||
}
|
}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user