Skip to content

Commit d0d975a

Browse files
authored
Allow merging multiple union types (#670)
* Add functionality to merge union types As noted in #651, it's currently not possible to merge in a data type, which means when we want to use an `allOf`, we end up only being able to add a single type. This introduces a new `Merge` method on generated union types, which delegates to our `runtime.JsonMerge` method, which uses the `jsonmerge` library. We need to handle the case that a slice may be `nil`, and that the upstream library does not have support for JSON arrays. Closes #651. * Add GoDoc comments for generated union types
1 parent a3eb000 commit d0d975a

2 files changed

Lines changed: 106 additions & 0 deletions

File tree

jsonmerge.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package runtime
2+
3+
import (
4+
"encoding/json"
5+
6+
"github.com/apapsch/go-jsonmerge/v2"
7+
)
8+
9+
// JsonMerge merges two JSON representation into a single object. `data` is the
10+
// existing representation and `patch` is the new data to be merged in
11+
func JsonMerge(data, patch json.RawMessage) (json.RawMessage, error) {
12+
merger := jsonmerge.Merger{
13+
CopyNonexistent: true,
14+
}
15+
if data == nil {
16+
data = []byte(`{}`)
17+
}
18+
if patch == nil {
19+
patch = []byte(`{}`)
20+
}
21+
merged, err := merger.MergeBytes(data, patch)
22+
if err != nil {
23+
return nil, err
24+
}
25+
return merged, nil
26+
}

jsonmerge_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package runtime
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestJsonMerge(t *testing.T) {
10+
t.Run("when object", func(t *testing.T) {
11+
t.Run("Merges properties defined in both objects", func(t *testing.T) {
12+
data := `{"foo": 1}`
13+
patch := `{"foo": null}`
14+
expected := `{"foo":null}`
15+
16+
actual, err := JsonMerge([]byte(data), []byte(patch))
17+
assert.NoError(t, err)
18+
assert.Equal(t, expected, string(actual))
19+
})
20+
21+
t.Run("Sets property defined in only src object", func(t *testing.T) {
22+
data := `{}`
23+
patch := `{"source":"merge-me"}`
24+
expected := `{"source":"merge-me"}`
25+
26+
actual, err := JsonMerge([]byte(data), []byte(patch))
27+
assert.NoError(t, err)
28+
assert.Equal(t, expected, string(actual))
29+
})
30+
31+
t.Run("Handles child objects", func(t *testing.T) {
32+
data := `{"channel":{"status":"valid"}}`
33+
patch := `{"channel":{"id":1}}`
34+
expected := `{"channel":{"id":1,"status":"valid"}}`
35+
36+
actual, err := JsonMerge([]byte(data), []byte(patch))
37+
assert.NoError(t, err)
38+
assert.Equal(t, expected, string(actual))
39+
})
40+
41+
t.Run("Handles empty objects", func(t *testing.T) {
42+
data := `{}`
43+
patch := `{}`
44+
expected := `{}`
45+
46+
actual, err := JsonMerge([]byte(data), []byte(patch))
47+
assert.NoError(t, err)
48+
assert.Equal(t, expected, string(actual))
49+
})
50+
51+
t.Run("Handles nil data", func(t *testing.T) {
52+
patch := `{"foo":"bar"}`
53+
expected := `{"foo":"bar"}`
54+
55+
actual, err := JsonMerge(nil, []byte(patch))
56+
assert.NoError(t, err)
57+
assert.Equal(t, expected, string(actual))
58+
})
59+
60+
t.Run("Handles nil patch", func(t *testing.T) {
61+
data := `{"foo":"bar"}`
62+
expected := `{"foo":"bar"}`
63+
64+
actual, err := JsonMerge([]byte(data), nil)
65+
assert.NoError(t, err)
66+
assert.Equal(t, expected, string(actual))
67+
})
68+
})
69+
t.Run("when array", func(t *testing.T) {
70+
t.Run("it does not merge", func(t *testing.T) {
71+
data := `[{"foo": 1}]`
72+
patch := `[{"foo": null}]`
73+
expected := `[{"foo":1}]`
74+
75+
actual, err := JsonMerge([]byte(data), []byte(patch))
76+
assert.NoError(t, err)
77+
assert.Equal(t, expected, string(actual))
78+
})
79+
})
80+
}

0 commit comments

Comments
 (0)