aPureBase/KGraphQL

Resolver with a lambda which returns a generic

zypus opened this issue ยท 6 comments

zypus commented

I have many different resources for which I define the same kind of queries or mutations, ie. getting all entities, getting an entity by id, getting the count etc.

So I tried to write a generic function which constructs these queries on the SchemaBuilder, something like this:

fun <T> SchemaBuilder.sharedQueries(resourceName: String, repository: Repository<T>)  {
   query("${resourceName}s") {
       resolver { ctx: Context ->
           repository.getAll()
       }
   }

   query(resourceName) {
       resolver { id: Int, ctx: Context ->
           repository.get(id)
       }
   }

   query("${resourceName}Count") {
      resolver { ctx: Context ->
          repository.count()
      }
   }
}

Which is then called like so:

schema {
    sharedQueries<User>("user", userRepository)
    sharedQueries<Role>("role", roleRepository)
    // etc.
}

However, this fails because the generic is not yet resolved when the reflection is at work, resulting in this exception:

Caused by: kotlin.reflect.jvm.internal.KotlinReflectionInternalError: Container of deserialized member is not resolved: local final fun <T> <anonymous>(id: Int, ctx: com.apurebase.kgraphql.Context): T[DeserializedSimpleFunctionDescriptor@3ff57625]
	at kotlin.reflect.jvm.internal.KTypeParameterImpl.getContainerClass(KTypeParameterImpl.kt:79)

Inlining also doesn't help.

So my question is, is there some other way to avoid writing structurally identical queries all the time?

jeggy commented

Hi @zypus, yes I will consider this as a bug.
The pattern you are trying to do here is what I think should be supported.

I will take a closer look to what's needed to get this to work

zypus commented

Thanks a lot @jeggy. I experimented a bit with the code today as I wanted to see if it was possible to implement this without changing too much or breaking any interfaces. My implementation currently just adds another function call to the ResolverDSL which enables the code above. Please see the pull request #140 for my attempt.

jeggy commented

I have merged your Pull Request, but I will keep this issue open, as seen here: https://github.com/aPureBase/KGraphQL/blob/main/kgraphql/src/test/kotlin/com/apurebase/kgraphql/integration/github/GitHubIssue139.kt This is what I would like to have support for

zypus commented

Thank you. Yeah would be great to be able to write something like what you proposed.

This is exactly what I am looking for!
I am playing with a CQRS inspired architecture where for each command or query, I want to generate the corresponding KGraphql interface.

Currently got this, but resuts in the KotlinReflectionInternalError:

val usecases: List<UseCase<*, *>>
val types: List<KClass<*>

install(GraphQL) {
  schema {
    usecases.forEach {
      connection(it)
    }

    types.forEach {
      type(it) {}
    }
  }
}

inline fun <T, reified U : Any, V: UseCase<T, U>> SchemaBuilder.connection(usecase: V) {
  query(usecase::class.simpleName!!) {
    resolver { request: T, ctx: Context ->
      usecase.execute(request, ctx.get<User>())
    }.returns<U>()
  }
}

EDIT: Got it working, will explain and make PR tomorrow

jeggy commented

@ESchouten Thanks, your PR has been merged and is available in version 0.17.8 ๐Ÿ‘