Skip to content

Commit

Permalink
feat: dereference schemas where possible (hamba#291)
Browse files Browse the repository at this point in the history
  • Loading branch information
nrwiersma authored Aug 20, 2023
1 parent 37aef08 commit 5a4b8be
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 4 deletions.
2 changes: 1 addition & 1 deletion cmd/avrosv/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func TestAvroSv_Verbose(t *testing.T) {
{
name: "dumps a schema with a reference to an existing schema",
args: []string{"avrosv", "-v", "testdata/schema.avsc", "testdata/withref-schema.avsc"},
out: "{\"name\":\"testref\",\"type\":\"record\",\"fields\":[{\"name\":\"someref\",\"type\":\"test\"}]}\n",
out: "{\"name\":\"testref\",\"type\":\"record\",\"fields\":[{\"name\":\"someref\",\"type\":{\"name\":\"test\",\"type\":\"record\",\"fields\":[{\"name\":\"someString\",\"type\":\"string\"}]}}]}\n",
wantErr: false,
},
}
Expand Down
14 changes: 13 additions & 1 deletion gen/gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -1225,7 +1225,7 @@ func (s *RefSchema) Type() Type {
}

// Schema returns the schema being referenced.
func (s *RefSchema) Schema() Schema {
func (s *RefSchema) Schema() NamedSchema {
return s.actual
}

Expand Down
27 changes: 26 additions & 1 deletion schema_parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ func ParseBytesWithCache(schema []byte, namespace string, cache *SchemaCache) (S
json = string(schema)
}

return parseType(namespace, json, cache)
s, err := parseType(namespace, json, cache)
if err != nil {
return nil, err
}
return derefSchema(s), nil
}

func parseType(namespace string, v any, cache *SchemaCache) (Schema, error) {
Expand Down Expand Up @@ -530,3 +534,24 @@ func decodeMap(in, v any, meta *mapstructure.Metadata) error {
decoder, _ := mapstructure.NewDecoder(cfg)
return decoder.Decode(in)
}

func derefSchema(schema Schema) Schema {
seen := map[string]struct{}{}

return walkSchema(schema, func(schema Schema) Schema {
if ns, ok := schema.(NamedSchema); ok {
seen[ns.FullName()] = struct{}{}
return schema
}

ref, isRef := schema.(*RefSchema)
if !isRef {
return schema
}

if _, haveSeen := seen[ref.Schema().FullName()]; !haveSeen {
return ref.Schema()
}
return schema
})
}
9 changes: 9 additions & 0 deletions schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1255,6 +1255,15 @@ func TestSchema_FingerprintUsingInvalidType(t *testing.T) {
assert.Error(t, err)
}

func TestSchema_MultiFile(t *testing.T) {
got, err := avro.ParseFiles("testdata/superhero-part1.avsc", "testdata/superhero-part2.avsc")

require.NoError(t, err)
want, err := avro.ParseFiles("testdata/superhero.avsc")
require.NoError(t, err)
assert.Equal(t, want, got)
}

func TestSchema_Interop(t *testing.T) {
schm := `
{
Expand Down
21 changes: 21 additions & 0 deletions schema_walk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package avro

func walkSchema(schema Schema, fn func(Schema) Schema) Schema {
schema = fn(schema)

switch s := schema.(type) {
case *RecordSchema:
for _, f := range s.Fields() {
f.typ = walkSchema(f.typ, fn)
}
case *ArraySchema:
s.items = walkSchema(s.items, fn)
case *MapSchema:
s.values = walkSchema(s.values, fn)
case *UnionSchema:
for i, st := range s.types {
s.types[i] = walkSchema(st, fn)
}
}
return schema
}
12 changes: 12 additions & 0 deletions testdata/superhero-part1.avsc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"type": "record",
"name": "Superpower",
"namespace": "com.model",
"fields": [
{"name": "id", "type": "int"},
{"name": "name", "type": "string"},
{"name": "damage", "type": "float"},
{"name": "energy", "type": "float"},
{"name": "passive", "type": "boolean"}
]
}
16 changes: 16 additions & 0 deletions testdata/superhero-part2.avsc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"type": "record",
"name": "Superhero",
"namespace": "com.model",
"fields": [
{"name": "id", "type": "int"},
{"name": "affiliation_id", "type": "int"},
{"name": "name", "type": "string"},
{"name": "life", "type": "float"},
{"name": "energy", "type": "float"},
{"name": "powers", "type": {
"type": "array",
"items": {"type": "Superpower"}}
}
]
}

0 comments on commit 5a4b8be

Please sign in to comment.