From 5e7e4416232ebce7a348d0fd17cbb2be6b346e07 Mon Sep 17 00:00:00 2001 From: Ibrahim Serdar Acikgoz Date: Fri, 20 Dec 2024 19:29:02 +0100 Subject: [PATCH] model/config: filtering should work with structs too (#29663) --- server/public/model/config.go | 31 +++++++++++++-- server/public/model/config_test.go | 61 +++++++++++++++++++++++++++++- 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/server/public/model/config.go b/server/public/model/config.go index ee882a9cfbf..8ac263b27e0 100644 --- a/server/public/model/config.go +++ b/server/public/model/config.go @@ -3689,7 +3689,7 @@ func (o *Config) Clone() *Config { } func (o *Config) ToJSONFiltered(tagType, tagValue string) ([]byte, error) { - filteredConfigMap := structToMapFilteredByTag(*o, tagType, tagValue) + filteredConfigMap := configToMapFilteredByTag(*o, tagType, tagValue) for key, value := range filteredConfigMap { v, ok := value.(map[string]any) if ok && len(v) == 0 { @@ -4614,8 +4614,8 @@ func FilterConfig(cfg *Config, opts ConfigFilterOptions) (map[string]any, error) } for i := range opts.TagFilters { - filteredCfg = structToMapFilteredByTag(filteredCfg, opts.TagFilters[i].TagType, opts.TagFilters[i].TagName) - filteredDefaultCfg = structToMapFilteredByTag(defaultCfg, opts.TagFilters[i].TagType, opts.TagFilters[i].TagName) + filteredCfg = configToMapFilteredByTag(filteredCfg, opts.TagFilters[i].TagType, opts.TagFilters[i].TagName) + filteredDefaultCfg = configToMapFilteredByTag(filteredDefaultCfg, opts.TagFilters[i].TagType, opts.TagFilters[i].TagName) } if opts.RemoveDefaults { @@ -4636,8 +4636,26 @@ func FilterConfig(cfg *Config, opts ConfigFilterOptions) (map[string]any, error) return filteredCfg, nil } -// structToMapFilteredByTag converts a struct into a map removing those fields that has the tag passed +// configToMapFilteredByTag converts a struct into a map removing those fields that has the tag passed // as argument +// t shall be either a Config struct value or a map[string]any +func configToMapFilteredByTag(t any, typeOfTag, filterTag string) map[string]any { + switch t.(type) { + case map[string]any: + var tc *Config + b, err := json.Marshal(t) + if err != nil { + // since this is an internal function, we can panic here + // because it should never happen + panic(err) + } + json.Unmarshal(b, &tc) + t = *tc + } + + return structToMapFilteredByTag(t, typeOfTag, filterTag) +} + func structToMapFilteredByTag(t any, typeOfTag, filterTag string) map[string]any { defer func() { if r := recover(); r != nil { @@ -4688,6 +4706,11 @@ func structToMapFilteredByTag(t any, typeOfTag, filterTag string) map[string]any // removeEmptyMapsAndSlices removes all the empty maps and slices from a map func removeEmptyMapsAndSlices(m map[string]any) { for k, v := range m { + if v == nil { + delete(m, k) + continue + } + switch vt := v.(type) { case map[string]any: removeEmptyMapsAndSlices(vt) diff --git a/server/public/model/config_test.go b/server/public/model/config_test.go index e599494284c..3f196e977ca 100644 --- a/server/public/model/config_test.go +++ b/server/public/model/config_test.go @@ -1559,7 +1559,7 @@ func TestConfigFilteredByTag(t *testing.T) { c := Config{} c.SetDefaults() - cfgMap := structToMapFilteredByTag(c, ConfigAccessTagType, ConfigAccessTagCloudRestrictable) + cfgMap := configToMapFilteredByTag(c, ConfigAccessTagType, ConfigAccessTagCloudRestrictable) // Remove entire sections but the map is still there clusterSettings, ok := cfgMap["SqlSettings"].(map[string]any) @@ -2088,4 +2088,63 @@ func TestFilterConfig(t *testing.T) { require.NoError(t, err) require.Equal(t, 1.0, m["PluginSettings"].(map[string]any)["Plugins"].(map[string]any)["com.mattermost.plugin-a"].(map[string]any)["setting"]) }) + + t.Run("should be able to filter specific tag", func(t *testing.T) { + cfg := &Config{} + cfg.SetDefaults() + + m, err := FilterConfig(cfg, ConfigFilterOptions{ + GetConfigOptions: GetConfigOptions{}, + TagFilters: []FilterTag{ + { + TagType: ConfigAccessTagType, + TagName: ConfigAccessTagCloudRestrictable, + }, + }, + }) + require.NoError(t, err) + require.NotEmpty(t, m) + + fileSettings, ok := m["FileSettings"] + require.True(t, ok) + + enableFileAttachments, ok := fileSettings.(map[string]any)["EnableFileAttachments"] + require.True(t, ok) + require.Equal(t, true, enableFileAttachments) + + // All fields of SqlSettings are ConfigAccessTagCloudRestrictable + _, ok = m["SqlSettings"] + require.False(t, ok) + }) + + t.Run("should be able to filter multiple tags", func(t *testing.T) { + cfg := &Config{} + cfg.SetDefaults() + + m, err := FilterConfig(cfg, ConfigFilterOptions{ + GetConfigOptions: GetConfigOptions{}, + TagFilters: []FilterTag{ + { + TagType: ConfigAccessTagType, + TagName: "site_file_sharing_and_downloads", + }, + { + TagType: ConfigAccessTagType, + TagName: ConfigAccessTagCloudRestrictable, + }, + }, + }) + require.NoError(t, err) + require.NotEmpty(t, m) + + fileSettings, ok := m["FileSettings"] + require.True(t, ok) + // EnableFileAttachments has "site_file_sharing_and_downloads" tag + _, ok = fileSettings.(map[string]any)["EnableFileAttachments"] + require.False(t, ok) + + // All fields of SqlSettings are ConfigAccessTagCloudRestrictable + _, ok = m["SqlSettings"] + require.False(t, ok) + }) }