Skip to content

@optional directive should probably not mark compiled field's type in selections as non-null. #6453

Open
@eduardb

Description

@eduardb

Description

If a fild in a query is annotated with the @optional directive, I expect not only for the field in the generated model to be nullable, but also the compiled field's type in the selection too.

Example schema:

type Foo {
  id: ID!
}

type Query {
  foo: Foo!
}

Example fragment:

fragment FooFragment on Foo {
    id @optional
}

Generated model:

public data class FooFragment(
  /**
   * The Globally Unique ID of this object
   */
  public val id: String?,
) : Fragment.Data

Generated selections:

public object FooFragmentSelections {
  public val __root: List<CompiledSelection> = listOf(
        CompiledField.Builder(
          name = "id",
          type = GraphQLID.type.notNull()
        ).build()
      )
}

Expected selections (notice the lack of .notNull()):

public object FooFragmentSelections {
  public val __root: List<CompiledSelection> = listOf(
        CompiledField.Builder(
          name = "id",
          type = GraphQLID.type
        ).build()
      )
}

This expected behaviour would make it possible to implement a hacky CacheResolver that can resolve partial cached responses by marking non-nullable fields in the schema as nullable on the client-side, and falling back to a default / null value, something like this:

class AbsenceTolerantCacheResolver(private val nextResolver: CacheResolver) : CacheResolver {
    override fun resolveField(
        field: CompiledField,
        variables: Executable.Variables,
        parent: Map<String, Any?>,
        parentId: String,
    ): Any? {

        try {
            return nextResolver.resolveField(field, variables, parent, parentId)
        } catch (ex: CacheMissException) {
            if (field.type is CompiledNotNullType) { // this sadly evaluates to true for `@optional`-annotated fields
                throw ex
            }

            return null
        }
    }
}

(Brough over from the kotlinlang Slack.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions