ogen icon indicating copy to clipboard operation
ogen copied to clipboard

Support getting underlying value from oneOf type

Open abemedia opened this issue 1 year ago • 4 comments

Description

When you define a oneOf type that has shared fields (e.g. ID) and you want to access this field, regardless of the underlying type, you need to perform a switch. If we had a method to return the underlying type as an interface it would make code less brittle as a new type being added to the spec would not need the switch to be updated in these circumstances.

See the example below:

description: Anything
oneOf:
  - $ref: ./Foo.yaml
  - $ref: ./Bar.yaml
  - $ref: ./Baz.yaml
  
discriminator:
  propertyName: type
  mapping:
    Foo: ./Foo.yaml
    Bar: ./Bar.yaml
    Baz: ./Baz.yaml
type Anything struct {
	Type  AnythingType // switch on this field
	Foo   Foo
	Bar   Bar
	Baz   Baz
}

// This is the method I'd like to see generated.
func (a Anything) GetValue() any {
	switch a.Type {
	case FooAnything:
		return a.Foo
	case BarAnything:
		return a.Bar
	case BazAnything:
		return a.Baz
	default:
		return nil
	}
}

This would allow code like this:

type GetIDer interface {
	GetID int64
}

func getID(a Anything) int64 {
	if v, ok := a.GetValue().(GetIDer); ok {
		return v.GetID()
	}
	return 0
}

Right now we cannot get around being very explicit such as the example below, with the added issue of it breaking if another type is added to the spec.

func getID(a Anything) int64 {
	switch a.Type {
	case FooAnything:
		return a.Foo.ID
	case BarAnything:
		return a.Bar.ID
	case BazAnything:
		return a.Baz.ID
	default:
		return 0
	}
}

References

abemedia avatar May 31 '24 14:05 abemedia

Could you do something like this?

func getID(r ResponseType) int64 {
  g, ok := r.(interface{ GetID() int64 })
  if ok { return g.GetID() }
  return 0
}

dtjm avatar Mar 18 '25 19:03 dtjm

No because the ID prop is on the respective types (Foo, Bar and Baz in the example above) so it still requires a switch on the type property. That's why my suggestion is to implement a function on each oneOf type to get the set value. This is fairly simple to implement in the generator templates.

abemedia avatar Mar 19 '25 01:03 abemedia

Perhaps one way to support this could be to simplify the scope of the work, and support "output only" generation (encoder, response, ...)?

This way, there would be no need to identity discriminator for decoding.

acolombier avatar Aug 22 '25 11:08 acolombier

I think it would be much simpler to just add another method to the generated sum types. I could easily implement that but was hoping for feedback from the maintainers first.

abemedia avatar Aug 22 '25 13:08 abemedia