Skip to content

generics based optional type doesn't go well with graphql unions #400

@pavelsmejkal

Description

@pavelsmejkal

Describe the bug
Generated MarshalJSON and UnmarshalJSON functions for type union-based fragments are invalid and need manual intervention.

The documentation about the optional_generic_type gives a possibility to specify the type, but doesn't require and specific contract so the generator doesn't know anything about the internal structure. When UnmarshalJSON and __premarshalJSON functions are generated, the code does't count, with the fact, that the value is wrapped inside of struct.

MarshalJSON

src := v.{{$field.Selector}}

The code needs to be aware how to get value from
src := v.Source => src := v.Source.Value()

UnmarshalJSON

Similar issue, but with an extra step, the wrapper usually has an internal bool attribute that needs to be set to true to signal that the value was set.

golang var v Source err = __unmarshalSource( src, &v) // unmarchal to variable if err != nil { return fmt.Errorf( "unable to unmarshal PersonalizedSnippetPreviewFragment.Source: %w", err) } dst.SetValue(v) // set to omittable

src, {{if $field.GoType.IsPointer}}*{{end}}dst)

To Reproduce
Steps to reproduce the behavior. Please include whether the problem happens at code-generation time or at runtime.

We are using our own generic type for optional types
optional_generic_type: "our package /model.Omittable"

Then we have a following union type using type union in graphql schema and fragments

Source =
    SourceA
  | SourceB
  | SourceN

type Message {
  source: Source
}

fragment FragmentMessage on Message {
      source {
        ...FragmentSource
      }
}

fragment FragmentSource on Source {
  ... on SourceA {
    ... some fields
  }

  ... on SourceB {
     ... some fields
  }
  ... on SourceN {
     ... some fields
  }
}

Expected behavior
The union type based fragments should be working without manual intervention in generated code.

genqlient version
github.com/Khan/genqlient v0.7.0 but i haven't found any trace that latest would work

Additional context
I guess that some contract on optional wrappers is required or some extra settings that would allow to specify functions that would return or set values for example:
yaml optional_generic_type_methods: getter: Value setter: SetValue

where functions signatures are:

``golang
type Omittable[T any] struct {
value T
set bool
}

func (o Omittable[T]) Value() T {
if !o.set {
var zero T
return zero
}
return o.value
}

func (o *Omittable[T]) SetValue(v T) {
o.set = true
o.value = v
}
``

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions