Giter Site home page Giter Site logo

Comments (4)

tiagonuneslx avatar tiagonuneslx commented on July 30, 2024

I'm very much interested in this feature as well. I am currently studying the library codebase with the same exact purpose.

My main use case is I want a resolver to return a Connection of a generic type:

data class Connection<T>(
    val totalCount: Int,
    val edges: List<Edge<T>>,
    val pageInfo: PageInfo
) {
    data class Edge<T>(
        val node: T,
        val cursor: Int
    )

    data class PageInfo(
        val endCursor: Int,
        val hasNextPage: Boolean
    )
}

I really like the design you're proposing, as a starting point!

In this case, if I wanted a resolver to return a Connection<User>, two types would be added to the Schema:

  • Connection_User
  • Edge_User

And eventually, we could add support for multiple generics. Then we could specify the type of cursor with Connection<T, C>.
Then, we would have these types added to the Schema when returning a Connection<User, Int>

  • Connection_User_Int
  • Edge_User_Int
  • PageInfo_Int

I really, really hope we're able to achieve this 🙂

Edit: Of course the syntax would need to be revised if we wanted to support deeper level generics (e.g. Connection<Entity<User>, Int>>), but as a starting point it looks good to me.

from kgraphql.

tiagonuneslx avatar tiagonuneslx commented on July 30, 2024

Progress Update

The main issue is that getting the actual returnType of a KProperty with a generic returnType is not trivial.

e.g.

val kType = typeOf<Connection<Person<String>, Int>>()
println(kType) // Connection<Person<String>, Int> ✅
(kType.classifier as KClass<*>).memberProperties.forEach { property ->
    println(property.returnType) // List<Edge<T, K>> ❌ Should be List<Edge<String, Int>>
}

Unfortunately, kotlin reflection is still very limited, and this is something that's not yet supported.

I've been studying how other projects using reflection deal with this issue:

  • Jackson: uses TypeReference and TypeFactory
  • gson: uses TypeToken.getParameterized (based on Guava)
  • Moshi (reflection): Most functional approach: Util.resolve (based on gson and Guava)

These libraries, using java reflection and Super Type Tokens pattern, are able to resolve a ParameterizedType to a java.lang.reflect.Type with the actual type parameters.

I chose to experiment with the Moshi approach, copying everything that was necessary to resolve a property returnType.

Sadly, there is no way to resolve to a KType, only to a java.lang.reflect.Type. All these libraries I studied work exclusively with java.lang.reflect.Type under the hood.

This differs from the KGraphQL library, which associates definitions by KClass<*>, and relies on KType and KClass in many places to compile a schema. We'll never be able to work with generics while we associate by KClass<*>, because it doesn't hold information about type parameters. We also can't rely on KType, because for compiling properties with generic returnType, we don't have access to a KType with resolved type parameters (only to a java.lang.reflect.Type with resolved type parameters).

Conclusion: For this to work, internal changes, mainly to the compilation and definitions, need to be done to rely exclusively on java.lang.reflect.Type.

Note: I have tried very hard to find a way to get the properties returnTypes as KType with resolved type parameters, so that we could associate definitions by KType and keep compiling using KType and KClass<*>, but it's not possible (I'd love for someone to prove me wrong).

I am working on these changes and I've made a quick POF, pushed to this branch. It's still lacking a lot, but I'd say the results are already very promising:

class Connection<T : Any, K : Any>(
    val totalCount: Int,
    val edges: List<Edge<T, K>>,
    val pageInfo: PageInfo<K>
) {
    class Edge<T : Any, K: Any>(
        val node: T,
        val cursor: K
    )

    class PageInfo<K: Any>(
        val endCursor: K,
        val hasNextPage: Boolean
    )
}

val names = listOf("Kai", "Eliana", "Jayden", "Ezra", "Luca", "Rowan", "Nova", "Amara")

fun main() {
    val schema = KGraphQL.schema {
        configure {
            this.useDefaultPrettyPrinter = true
        }
        query("names") {
            resolver { ->
                Connection(
                    totalCount = names.size,
                    edges = names.subList(0, 2).mapIndexed { index, name ->
                        Connection.Edge(
                            node = name,
                            cursor = index
                        )
                    },
                    pageInfo = Connection.PageInfo(
                        endCursor = 1,
                        hasNextPage = true
                    )
                )
            }.returns<Connection<String, Int>>()
        }
    }

    schema.types.forEach { println(it.name) }
    println()
    println(schema.executeBlocking("{ names { totalCount, edges { node, cursor }, pageInfo { endCursor, hasNextPage } } }"))

Console:

__Schema
__Directive
__InputValue
__Type
__EnumValue
__Field
Connection<String, Integer>
Edge<String, Integer>
PageInfo<Integer>
__TypeKind
__DirectiveLocation
String
Boolean
Float
Short
Int
Long
Query

{
  "data" : {
    "names" : {
      "totalCount" : 8,
      "edges" : [ {
        "node" : "Kai",
        "cursor" : 0
      }, {
        "node" : "Eliana",
        "cursor" : 1
      } ],
      "pageInfo" : {
        "endCursor" : 1,
        "hasNextPage" : true
      }
    }
  }
}

from kgraphql.

afgarcia86 avatar afgarcia86 commented on July 30, 2024

This would be great to see

from kgraphql.

IceBlizz6 avatar IceBlizz6 commented on July 30, 2024

This would be great to see

I think i could finish this,
But the project looks pretty dead at the moment.
I've been waiting a long time for subscription support which i'm not sure is in development anymore.

If the future looked promising then i would love to assist more,
but as it stands i have started to look into alternatives like ExpediaGroup/graphql-kotlin.

from kgraphql.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.