Fixed merge anchor bug #800

This commit is contained in:
Mike Farah 2021-05-09 13:26:02 +10:00
parent 37f3e21970
commit ae4b606707
4 changed files with 128 additions and 8 deletions

View File

@ -3,6 +3,97 @@ Use the `alias` and `anchor` operators to read and write yaml aliases and anchor
`yq` supports merge aliases (like `<<: *blah`) however this is no longer in the standard yaml spec (1.2) and so `yq` will automatically add the `!!merge` tag to these nodes as it is effectively a custom tag. `yq` supports merge aliases (like `<<: *blah`) however this is no longer in the standard yaml spec (1.2) and so `yq` will automatically add the `!!merge` tag to these nodes as it is effectively a custom tag.
## Merge one map
see https://yaml.org/type/merge.html
Given a sample.yml file of:
```yaml
- &CENTER
x: 1
y: 2
- &LEFT
x: 0
y: 2
- &BIG
r: 10
- &SMALL
r: 1
- !!merge <<: *CENTER
r: 10
```
then
```bash
yq eval '.[4] | explode(.)' sample.yml
```
will output
```yaml
x: 1
y: 2
r: 10
```
## Merge multiple maps
see https://yaml.org/type/merge.html
Given a sample.yml file of:
```yaml
- &CENTER
x: 1
y: 2
- &LEFT
x: 0
y: 2
- &BIG
r: 10
- &SMALL
r: 1
- !!merge <<:
- *CENTER
- *BIG
```
then
```bash
yq eval '.[4] | explode(.)' sample.yml
```
will output
```yaml
r: 10
x: 1
y: 2
```
## Override
see https://yaml.org/type/merge.html
Given a sample.yml file of:
```yaml
- &CENTER
x: 1
y: 2
- &LEFT
x: 0
y: 2
- &BIG
r: 10
- &SMALL
r: 1
- !!merge <<:
- *BIG
- *LEFT
- *SMALL
x: 1
```
then
```bash
yq eval '.[4] | explode(.)' sample.yml
```
will output
```yaml
r: 10
x: 1
y: 2
```
## Get anchor ## Get anchor
Given a sample.yml file of: Given a sample.yml file of:
```yaml ```yaml
@ -183,9 +274,9 @@ bar:
c: bar_c c: bar_c
foobarList: foobarList:
b: bar_b b: bar_b
a: foo_a thing: foo_thing
thing: bar_thing
c: foobarList_c c: foobarList_c
a: foo_a
foobar: foobar:
c: foo_c c: foo_c
a: foo_a a: foo_a

View File

@ -176,7 +176,7 @@ func explodeNode(node *yaml.Node, context Context) error {
} else { } else {
if valueNode.Kind == yaml.SequenceNode { if valueNode.Kind == yaml.SequenceNode {
log.Debugf("an alias merge list!") log.Debugf("an alias merge list!")
for index := 0; index < len(valueNode.Content); index = index + 1 { for index := len(valueNode.Content) - 1; index >= 0; index = index - 1 {
aliasNode := valueNode.Content[index] aliasNode := valueNode.Content[index]
err := applyAlias(node, aliasNode.Alias, index, context.ChildContext(newContent)) err := applyAlias(node, aliasNode.Alias, index, context.ChildContext(newContent))
if err != nil { if err != nil {

View File

@ -4,7 +4,36 @@ import (
"testing" "testing"
) )
var specDocument = `- &CENTER { x: 1, y: 2 }
- &LEFT { x: 0, y: 2 }
- &BIG { r: 10 }
- &SMALL { r: 1 }
`
var expectedSpecResult = "D0, P[4], (!!map)::x: 1\ny: 2\nr: 10\n"
var anchorOperatorScenarios = []expressionScenario{ var anchorOperatorScenarios = []expressionScenario{
{
description: "Merge one map",
subdescription: "see https://yaml.org/type/merge.html",
document: specDocument + "- << : *CENTER\n r: 10\n",
expression: ".[4] | explode(.)",
expected: []string{expectedSpecResult},
},
{
description: "Merge multiple maps",
subdescription: "see https://yaml.org/type/merge.html",
document: specDocument + "- << : [ *CENTER, *BIG ]\n",
expression: ".[4] | explode(.)",
expected: []string{"D0, P[4], (!!map)::r: 10\nx: 1\ny: 2\n"},
},
{
description: "Override",
subdescription: "see https://yaml.org/type/merge.html",
document: specDocument + "- << : [ *BIG, *LEFT, *SMALL ]\n x: 1\n",
expression: ".[4] | explode(.)",
expected: []string{"D0, P[4], (!!map)::r: 10\nx: 1\ny: 2\n"},
},
{ {
description: "Get anchor", description: "Get anchor",
document: `a: &billyBob cat`, document: `a: &billyBob cat`,
@ -91,9 +120,9 @@ bar:
c: bar_c c: bar_c
foobarList: foobarList:
b: bar_b b: bar_b
a: foo_a thing: foo_thing
thing: bar_thing
c: foobarList_c c: foobarList_c
a: foo_a
foobar: foobar:
c: foo_c c: foo_c
a: foo_a a: foo_a
@ -106,7 +135,7 @@ foobar:
expression: `.foo* | explode(.) | (. style="flow")`, expression: `.foo* | explode(.) | (. style="flow")`,
expected: []string{ expected: []string{
"D0, P[foo], (!!map)::{a: foo_a, thing: foo_thing, c: foo_c}\n", "D0, P[foo], (!!map)::{a: foo_a, thing: foo_thing, c: foo_c}\n",
"D0, P[foobarList], (!!map)::{b: bar_b, a: foo_a, thing: bar_thing, c: foobarList_c}\n", "D0, P[foobarList], (!!map)::{b: bar_b, thing: foo_thing, c: foobarList_c, a: foo_a}\n",
"D0, P[foobar], (!!map)::{c: foo_c, a: foo_a, thing: foobar_thing}\n", "D0, P[foobar], (!!map)::{c: foo_c, a: foo_a, thing: foobar_thing}\n",
}, },
}, },
@ -116,7 +145,7 @@ foobar:
expression: `.foo* | explode(explode(.)) | (. style="flow")`, expression: `.foo* | explode(explode(.)) | (. style="flow")`,
expected: []string{ expected: []string{
"D0, P[foo], (!!map)::{a: foo_a, thing: foo_thing, c: foo_c}\n", "D0, P[foo], (!!map)::{a: foo_a, thing: foo_thing, c: foo_c}\n",
"D0, P[foobarList], (!!map)::{b: bar_b, a: foo_a, thing: bar_thing, c: foobarList_c}\n", "D0, P[foobarList], (!!map)::{b: bar_b, thing: foo_thing, c: foobarList_c, a: foo_a}\n",
"D0, P[foobar], (!!map)::{c: foo_c, a: foo_a, thing: foobar_thing}\n", "D0, P[foobar], (!!map)::{c: foo_c, a: foo_a, thing: foobar_thing}\n",
}, },
}, },

View File

@ -71,7 +71,7 @@ func testScenario(t *testing.T, s *expressionScenario) {
t.Error(fmt.Errorf("%v: %v", err, s.expression)) t.Error(fmt.Errorf("%v: %v", err, s.expression))
return return
} }
test.AssertResultComplexWithContext(t, s.expected, resultsToString(context.MatchingNodes), fmt.Sprintf("exp: %v\ndoc: %v", s.expression, s.document)) test.AssertResultComplexWithContext(t, s.expected, resultsToString(context.MatchingNodes), fmt.Sprintf("desc: %v\nexp: %v\ndoc: %v", s.description, s.expression, s.document))
} }
func resultsToString(results *list.List) []string { func resultsToString(results *list.List) []string {