Skip to content

Commit

Permalink
Merge pull request #8106 from hashicorp/dnephin/hook-translate-keys-m…
Browse files Browse the repository at this point in the history
…ake-a-copy

decode: do not modify the source data in HookTranslateKeys
  • Loading branch information
dnephin authored and hashicorp-ci committed Jun 15, 2020
1 parent 17382b5 commit 7cf96ad
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 4 deletions.
15 changes: 11 additions & 4 deletions lib/decode/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,28 @@ func HookTranslateKeys(_, to reflect.Type, data interface{}) (interface{}, error
}

rules := translationsForType(to)
// Avoid making a copy if there are no translation rules
if len(rules) == 0 {
return data, nil
}
result := make(map[string]interface{}, len(source))
for k, v := range source {
lowerK := strings.ToLower(k)
canonKey, ok := rules[lowerK]
if !ok {
result[k] = v
continue
}
delete(source, k)

// if there is a value for the canonical key then keep it
if _, ok := source[canonKey]; ok {
if canonValue, ok := source[canonKey]; ok {
// Assign the value for the case where canonKey == k
result[canonKey] = canonValue
continue
}
source[canonKey] = v
result[canonKey] = v
}
return source, nil
return result, nil
}

// TODO: could be cached if it is too slow
Expand Down
27 changes: 27 additions & 0 deletions lib/decode/decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,33 @@ func TestHookTranslateKeys_TargetStructHasPointerReceiver(t *testing.T) {
require.Equal(t, expected, target, "decode metadata: %#v", md)
}

func TestHookTranslateKeys_DoesNotModifySourceData(t *testing.T) {
raw := map[string]interface{}{
"S": map[string]interface{}{
"None": "no translation",
"OldOne": "value1",
"oldtwo": "value2",
},
}

cfg := Config{}
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
DecodeHook: HookTranslateKeys,
Result: &cfg,
})
require.NoError(t, err)
require.NoError(t, decoder.Decode(raw))

expected := map[string]interface{}{
"S": map[string]interface{}{
"None": "no translation",
"OldOne": "value1",
"oldtwo": "value2",
},
}
require.Equal(t, raw, expected)
}

type translateExample struct {
FieldDefaultCanonical string `alias:"first"`
FieldWithMapstructureTag string `alias:"second" mapstructure:"field_with_mapstruct_tag"`
Expand Down

0 comments on commit 7cf96ad

Please sign in to comment.