salsify/avromatic

Are Avro Union fields supported?

Closed this issue · 6 comments

Trying to generate a model for this example schema that uses a Union of Foo and Bar...

require 'avromatic'

json = '[{
    "type": "record",
    "name": "Foo",
    "fields": [
      {"name": "fooMessage", "type": "string"}
    ]
  },

  {
    "type": "record",
    "name": "Bar",
    "fields": [
      {"name": "barMessage", "type": "string"}
    ]
  },

  {
    "type": "record",
    "name": "Root",
    "fields": [
      {"name": "header", "type": "string"},
      {"name": "message", "type": ["Foo", "Bar"]}
    ]
  }
]
'
Avromatic::Model.model(schema: Avro::Schema.parse(json))

...I encounter an exception:

/Users/ascassidy/.rvm/gems/ruby-2.3.0/gems/avromatic-0.7.0/lib/avromatic/model/attributes.rb:54:in `define_avro_attributes': undefined method `fields' for #<Avro::S
chema::UnionSchema:0x007f9b789eb040> (NoMethodError)
        from /Users/ascassidy/.rvm/gems/ruby-2.3.0/gems/avromatic-0.7.0/lib/avromatic/model/attributes.rb:27:in `add_avro_fields'
        from /Users/ascassidy/.rvm/gems/ruby-2.3.0/gems/avromatic-0.7.0/lib/avromatic/model/builder.rb:57:in `block (2 levels) in define_included_method'
        from /Users/ascassidy/.rvm/gems/ruby-2.3.0/gems/avromatic-0.7.0/lib/avromatic/model/builder.rb:22:in `include'
        from /Users/ascassidy/.rvm/gems/ruby-2.3.0/gems/avromatic-0.7.0/lib/avromatic/model/builder.rb:22:in `block in model'
        from /Users/ascassidy/.rvm/gems/ruby-2.3.0/gems/avromatic-0.7.0/lib/avromatic/model/builder.rb:21:in `initialize'
        from /Users/ascassidy/.rvm/gems/ruby-2.3.0/gems/avromatic-0.7.0/lib/avromatic/model/builder.rb:21:in `new'
        from /Users/ascassidy/.rvm/gems/ruby-2.3.0/gems/avromatic-0.7.0/lib/avromatic/model/builder.rb:21:in `model'
        from /Users/ascassidy/.rvm/gems/ruby-2.3.0/gems/avromatic-0.7.0/lib/avromatic/model.rb:47:in `model'
        from lib/test/avro/generate.rb:32:in `<main>'

After some initial digging I've come to a couple of conclusions:

  1. AvroTurf (and/or Avromatic) does not like the syntax where multiple types are defined in an array (though it works fine with the avro-tools.jar utility)
  2. Avromatic currently only supports union of null and a single type

Can you confirm please? And is point (2) likely to be addressed in the near future? I can try to take a look at it but I'm not a Ruby guy by trade

tjwp commented

@cyclops23 Sorry for the delay responding to this.

The json that you have here represents a union type of Foo, Bar and Root at the top-level. Avromatic only works with record types currently. It should provide an error that explains this, and I'll be committing a fix to provide that error.

My suspicion is that you want a model for the Root record type in this union. To define that type by itself using Avro JSON the Foo and Bar definitions must be embedded:

{
  "type": "record",
  "name": "Root",
  "fields": [
    {
      "name": "header",
      "type": "string"
    },
    {
      "name": "message",
      "type": [
        {
          "type": "record",
          "name": "Foo",
          "fields": [
            {
              "name": "foo_message",
              "type": "string"
            }
          ]
        },
        {
          "type": "record",
          "name": "Bar",
          "fields": [
            {
              "name": "bar_message",
              "type": "string"
            }
          ]
        }
      ]
    }
  ]
}

Using avro-builder you could define that in Ruby as:

record :Foo do
  required :foo_message, :string
end

record :Bar do
  required :bar_message, :string
end

record :Root do
  required :header, :string
  required :message, :union, types: [:Foo, :Bar]
end

However you are correct that Avromatic currently only supports a union of null and one other type, so this type will not work with the current version. It looks like that union functionality is something that we will need in the near future, so I expect that I will be implementing it soon.

I'll keep this issue open and update it when that happens.

Thanks for the follow up!

tjwp commented

@cyclops23 I just pushed a pre-release (0.9.0.rc0) that has union support. We'll be testing it with a project internally before it gets an official release.

tjwp commented

Pushed another pre-release (0.9.0.rc1) with some fixes for handling nested, complex types.

tjwp commented

Union support is marked as experimental, but included in v0.9.0.