镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Expose the Java member and resolver bound to each field data fetcher #937

Open

Description

@oryan-block

I want to suggest an idea and checked that ...

  • ... to my best knowledge, my idea wouldn't break something for other users
  • ... the documentation does not mention anything about my idea
  • ... there are no open or closed issues that are related to my idea

Description

Expose what each field data fetcher is bound to through a public, read-only interface.

When the schema is built, MethodFieldResolver, PropertyFieldResolver and MapFieldResolver pick the Java member for each field and wrap it in MethodFieldResolverDataFetcher, LightMethodFieldResolverDataFetcher, PropertyFieldResolverDataFetcher or MapFieldResolverDataFetcher. These classes are internal, and the member, the resolver instance and the argument to parameter mapping are private. The only public hook, getWrappedFetchingObject(environment), needs an environment and cannot tell a GraphQLResolver<T> instance from a source object.

Something like this, implemented by all four fetchers:

interface FieldResolverDataFetcher : DataFetcher<Any?> {
    // the Java member backing the field: a Method, a Field, or null for map entries
    val member: Member?

    // the GraphQLResolver instance the member is invoked on, or null when it is invoked on the source object
    val resolver: Any?

    // the Java parameter index a schema argument is bound to, accounting for the source parameter and trailing context
    fun parameterIndexOf(argumentName: String): Int?
}

All of this is already known in MethodFieldResolver.createDataFetcher() (method, search.source, getJavaMethodParameterIndex), so it would mostly mean keeping it on the fetcher and exposing it. Adding a public interface to the internal classes should not break existing users.

Use Cases

Checking the schema against the Java code that resolves it, from a test or at startup. Ours is deprecation: every @deprecated field and argument must be resolved by a method or parameter annotated with @Deprecated, so an AOP aspect can log usage and tell us when a field is safe to remove. Doing that correctly requires the member tools actually bound. Matching by name gets overrides, inherited methods, data class getters and renamed parameters wrong.

Today this needs:

  • reflection on the private method and sourceResolver fields of the internal fetcher classes, which can break on any upgrade;
  • calling sourceResolver.resolve(null, null) and treating an exception as "bound to the source object";
  • re-implementing getIndexOffset() to find the parameter behind an argument.

With the interface, all three become codeRegistry.getDataFetcher(coordinates, field) as? FieldResolverDataFetcher. The same information would help other tooling too: authorization or validation checks on resolver methods, documentation generators, and tracing instrumentation that names the Java method.

A smaller, related addition: a read-only lookup from GraphQL type name to the Java class tools mapped it to. This would let the same kind of check find the Java class behind an input type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions