-
Notifications
You must be signed in to change notification settings - Fork 60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for auto resolving generic types #210
Comments
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
And eventually, we could add support for multiple generics. Then we could specify the type of cursor with
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. |
Progress UpdateThe 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:
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 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 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:
|
This would be great to see |
I think i could finish this, If the future looked promising then i would love to assist more, |
I want to suggest a possible way for the library to handle generic types without requiring behavior that is explicitly defined by the user.
I've been messing around a bit with the codebase and it seems like the problem is that the lookup in some caches and functions is KClass<*>.
KType.jvmErasure will erase the provided generic type.
We need to retain the information in the KType object.
So i tried to replace the lookup type with a combination of KType and KClass<*> and the result seems promising.
I think maybe just KType may be enough but my understanding of the codebase is still very limited.
I'm not entirely sure how this would be combined with the existing user provided generic type resolver.
I think we could run it through the provided type resolver first and then let the system process it after if the returned type still has atleast one generic type parameter.
Idea here is that we construct a new type for each combination of the provided generic type.
MyGenericType<MyUser> becomes MyGenericType_MyUser
MyGenericType<MyDirectory> becomes MyGenericType_MyDirectory
Ouput
I was considering a pull request myself but i think my code is just a bit too messy.
The text was updated successfully, but these errors were encountered: