MM-34674 Adding config telemetry for feature flags. (#17456)
* Adding config telemetry for feature flags. * Review fixes.
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
d819eb224c
Коммит
684cd93755
@@ -353,7 +353,7 @@ func GenerateLimitedClientConfig(c *model.Config, telemetryID string, license *m
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, value := range featureFlagsToMap(c.FeatureFlags) {
|
for key, value := range c.FeatureFlags.ToMap() {
|
||||||
props["FeatureFlag"+key] = value
|
props["FeatureFlag"+key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,29 +100,6 @@ func featureFlagsFromMap(featuresMap map[string]string, baseFeatureFlags model.F
|
|||||||
return baseFeatureFlags
|
return baseFeatureFlags
|
||||||
}
|
}
|
||||||
|
|
||||||
// featureFlagsToMap returns the feature flags as a map[string]string
|
|
||||||
// Supports boolean and string feature flags.
|
|
||||||
func featureFlagsToMap(featureFlags *model.FeatureFlags) map[string]string {
|
|
||||||
refStructVal := reflect.ValueOf(*featureFlags)
|
|
||||||
refStructType := reflect.TypeOf(*featureFlags)
|
|
||||||
ret := make(map[string]string)
|
|
||||||
for i := 0; i < refStructVal.NumField(); i++ {
|
|
||||||
refFieldVal := refStructVal.Field(i)
|
|
||||||
refFieldType := refStructType.Field(i)
|
|
||||||
if !refFieldVal.IsValid() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
switch refFieldType.Type.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
ret[refFieldType.Name] = strconv.FormatBool(refFieldVal.Bool())
|
|
||||||
default:
|
|
||||||
ret[refFieldType.Name] = refFieldVal.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func getStructFields(s interface{}) []string {
|
func getStructFields(s interface{}) []string {
|
||||||
structType := reflect.TypeOf(s)
|
structType := reflect.TypeOf(s)
|
||||||
fieldNames := make([]string, 0, structType.NumField())
|
fieldNames := make([]string, 0, structType.NumField())
|
||||||
|
|||||||
@@ -109,47 +109,3 @@ func TestFeatureFlagsFromMap(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFeatureFlagsToMap(t *testing.T) {
|
|
||||||
for name, tc := range map[string]struct {
|
|
||||||
Flags model.FeatureFlags
|
|
||||||
TestFeatureValue string
|
|
||||||
}{
|
|
||||||
"empty": {
|
|
||||||
TestFeatureValue: "",
|
|
||||||
Flags: model.FeatureFlags{},
|
|
||||||
},
|
|
||||||
"simple value": {
|
|
||||||
TestFeatureValue: "expectedvalue",
|
|
||||||
Flags: model.FeatureFlags{TestFeature: "expectedvalue"},
|
|
||||||
},
|
|
||||||
"empty value": {
|
|
||||||
TestFeatureValue: "",
|
|
||||||
Flags: model.FeatureFlags{TestFeature: ""},
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
require.Equal(t, tc.TestFeatureValue, featureFlagsToMap(&tc.Flags)["TestFeature"])
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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"])
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3143,7 +3143,7 @@ type Config struct {
|
|||||||
GuestAccountsSettings GuestAccountsSettings
|
GuestAccountsSettings GuestAccountsSettings
|
||||||
ImageProxySettings ImageProxySettings
|
ImageProxySettings ImageProxySettings
|
||||||
CloudSettings CloudSettings // telemetry: none
|
CloudSettings CloudSettings // telemetry: none
|
||||||
FeatureFlags *FeatureFlags `access:"*_read" json:",omitempty"` // telemetry: none
|
FeatureFlags *FeatureFlags `access:"*_read" json:",omitempty"`
|
||||||
ImportSettings ImportSettings // telemetry: none
|
ImportSettings ImportSettings // telemetry: none
|
||||||
ExportSettings ExportSettings
|
ExportSettings ExportSettings
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,10 @@
|
|||||||
|
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import "reflect"
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
type FeatureFlags struct {
|
type FeatureFlags struct {
|
||||||
// Exists only for unit and manual testing.
|
// Exists only for unit and manual testing.
|
||||||
@@ -72,3 +75,26 @@ func (f *FeatureFlags) Plugins() map[string]string {
|
|||||||
|
|
||||||
return pluginVersions
|
return pluginVersions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToMap returns the feature flags as a map[string]string
|
||||||
|
// Supports boolean and string feature flags.
|
||||||
|
func (f *FeatureFlags) ToMap() map[string]string {
|
||||||
|
refStructVal := reflect.ValueOf(*f)
|
||||||
|
refStructType := reflect.TypeOf(*f)
|
||||||
|
ret := make(map[string]string)
|
||||||
|
for i := 0; i < refStructVal.NumField(); i++ {
|
||||||
|
refFieldVal := refStructVal.Field(i)
|
||||||
|
if !refFieldVal.IsValid() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
refFieldType := refStructType.Field(i)
|
||||||
|
switch refFieldType.Type.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
ret[refFieldType.Name] = strconv.FormatBool(refFieldVal.Bool())
|
||||||
|
default:
|
||||||
|
ret[refFieldType.Name] = refFieldVal.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|||||||
54
model/feature_flags_test.go
Обычный файл
54
model/feature_flags_test.go
Обычный файл
@@ -0,0 +1,54 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFeatureFlagsToMap(t *testing.T) {
|
||||||
|
for name, tc := range map[string]struct {
|
||||||
|
Flags FeatureFlags
|
||||||
|
TestFeatureValue string
|
||||||
|
}{
|
||||||
|
"empty": {
|
||||||
|
TestFeatureValue: "",
|
||||||
|
Flags: FeatureFlags{},
|
||||||
|
},
|
||||||
|
"simple value": {
|
||||||
|
TestFeatureValue: "expectedvalue",
|
||||||
|
Flags: FeatureFlags{TestFeature: "expectedvalue"},
|
||||||
|
},
|
||||||
|
"empty value": {
|
||||||
|
TestFeatureValue: "",
|
||||||
|
Flags: FeatureFlags{TestFeature: ""},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
require.Equal(t, tc.TestFeatureValue, tc.Flags.ToMap()["TestFeature"])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFeatureFlagsToMapBool(t *testing.T) {
|
||||||
|
for name, tc := range map[string]struct {
|
||||||
|
Flags FeatureFlags
|
||||||
|
TestFeatureValue string
|
||||||
|
}{
|
||||||
|
"false": {
|
||||||
|
TestFeatureValue: "false",
|
||||||
|
Flags: FeatureFlags{},
|
||||||
|
},
|
||||||
|
"true": {
|
||||||
|
TestFeatureValue: "true",
|
||||||
|
Flags: FeatureFlags{TestBoolFeature: true},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
require.Equal(t, tc.TestFeatureValue, tc.Flags.ToMap()["TestBoolFeature"])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -66,6 +66,7 @@ const (
|
|||||||
TrackConfigImageProxy = "config_image_proxy"
|
TrackConfigImageProxy = "config_image_proxy"
|
||||||
TrackConfigBleve = "config_bleve"
|
TrackConfigBleve = "config_bleve"
|
||||||
TrackConfigExport = "config_export"
|
TrackConfigExport = "config_export"
|
||||||
|
TrackFeatureFlags = "config_feature_flags"
|
||||||
TrackPermissionsGeneral = "permissions_general"
|
TrackPermissionsGeneral = "permissions_general"
|
||||||
TrackPermissionsSystemScheme = "permissions_system_scheme"
|
TrackPermissionsSystemScheme = "permissions_system_scheme"
|
||||||
TrackPermissionsTeamSchemes = "permissions_team_schemes"
|
TrackPermissionsTeamSchemes = "permissions_team_schemes"
|
||||||
@@ -825,6 +826,14 @@ func (ts *TelemetryService) trackConfig() {
|
|||||||
ts.sendTelemetry(TrackConfigExport, map[string]interface{}{
|
ts.sendTelemetry(TrackConfigExport, map[string]interface{}{
|
||||||
"retention_days": *cfg.ExportSettings.RetentionDays,
|
"retention_days": *cfg.ExportSettings.RetentionDays,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Convert feature flags to map[string]interface{} for sending
|
||||||
|
flags := cfg.FeatureFlags.ToMap()
|
||||||
|
interfaceFlags := make(map[string]interface{})
|
||||||
|
for k, v := range flags {
|
||||||
|
interfaceFlags[k] = v
|
||||||
|
}
|
||||||
|
ts.sendTelemetry(TrackFeatureFlags, interfaceFlags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts *TelemetryService) trackLicense() {
|
func (ts *TelemetryService) trackLicense() {
|
||||||
|
|||||||
@@ -369,6 +369,7 @@ func TestRudderTelemetry(t *testing.T) {
|
|||||||
TrackConfigExperimental,
|
TrackConfigExperimental,
|
||||||
TrackConfigAnalytics,
|
TrackConfigAnalytics,
|
||||||
TrackConfigPlugin,
|
TrackConfigPlugin,
|
||||||
|
TrackFeatureFlags,
|
||||||
TrackActivity,
|
TrackActivity,
|
||||||
TrackServer,
|
TrackServer,
|
||||||
TrackConfigMessageExport,
|
TrackConfigMessageExport,
|
||||||
@@ -411,6 +412,7 @@ func TestRudderTelemetry(t *testing.T) {
|
|||||||
TrackConfigExperimental,
|
TrackConfigExperimental,
|
||||||
TrackConfigAnalytics,
|
TrackConfigAnalytics,
|
||||||
TrackConfigPlugin,
|
TrackConfigPlugin,
|
||||||
|
TrackFeatureFlags,
|
||||||
TrackActivity,
|
TrackActivity,
|
||||||
TrackServer,
|
TrackServer,
|
||||||
TrackConfigMessageExport,
|
TrackConfigMessageExport,
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user