consul/lib/translate.go

105 lines
2.9 KiB
Go
Raw Permalink Normal View History

// Copyright (c) HashiCorp, Inc.
[COMPLIANCE] License changes (#18443) * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at <Blog URL>, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
2023-08-11 13:12:13 +00:00
// SPDX-License-Identifier: BUSL-1.1
package lib
import (
"strings"
)
// TranslateKeys recursively translates all keys from m in-place to their
// canonical form as defined in dict which maps an alias name to the canonical
// name. If m already has a value for the canonical name then that one is used
// and the value for the alias name is discarded. Alias names are matched
// case-insensitive.
//
// Example:
//
// m = TranslateKeys(m, map[string]string{"snake_case": "CamelCase"})
//
// If the canonical string provided is the empty string, the effect is to stop
// recursing into any key matching the left hand side. In this case the left
// hand side must use periods to specify a full path e.g.
// `connect.proxy.config`. The path must be the canonical key names (i.e.
// CamelCase) AFTER translation so NodeName not node_name. These are still match
// in a case-insensitive way.
//
// This is needed for example because parts of the Service Definition are
// "opaque" maps of metadata or config passed to another process or component.
// If we allow translation to recurse we might mangle the "opaque" keys given
// where the clash with key names in other parts of the definition :sob:
//
// Example:
//
// m - TranslateKeys(m, map[string]string{
// "foo_bar": "FooBar",
// "widget.config": "",
// // Assume widgets is an array, this will prevent recursing into any
// // item's config field
// "widgets.config": "",
// })
//
// Deprecated: Use lib/decode.HookTranslateKeys instead.
func TranslateKeys(v map[string]interface{}, dict map[string]string) {
// Convert all dict keys for exclusions to lower. so we can match against them
// unambiguously with a single lookup.
for k, v := range dict {
if v == "" {
dict[strings.ToLower(k)] = ""
}
}
ck(v, dict, "")
}
func ck(v interface{}, dict map[string]string, pathPfx string) interface{} {
// In array case we don't add a path segment for the item as they are all
// assumed to be same which is why we check the prefix doesn't already end in
// a .
if pathPfx != "" && !strings.HasSuffix(pathPfx, ".") {
pathPfx += "."
}
switch x := v.(type) {
case map[string]interface{}:
for k, v := range x {
lowerK := strings.ToLower(k)
// Check if this path has been excluded
val, ok := dict[pathPfx+lowerK]
if ok && val == "" {
// Don't recurse into this key
continue
}
canonKey, ok := dict[lowerK]
// no canonical key? -> use this key
if !ok {
x[k] = ck(v, dict, pathPfx+lowerK)
continue
}
// delete the alias
delete(x, k)
// if there is a value for the canonical key then keep it
if _, ok := x[canonKey]; ok {
continue
}
// otherwise translate to the canonical key
x[canonKey] = ck(v, dict, pathPfx+strings.ToLower(canonKey))
}
return x
case []interface{}:
var a []interface{}
for _, xv := range x {
a = append(a, ck(xv, dict, pathPfx))
}
return a
default:
return v
}
}