Closed szymon-rd closed 4 years ago
Yeah, that shouldn't be a problem. I just tried the following and it compiles:
import caliban._
import zio.Task
object Test {
case class B()
case class GetAParams(i: Int)
case class GetBParams(i: Int)
case class Query(getA: GetAParams => Task[A])
case class A(getB: GetBParams => Task[B])
val api = GraphQL.graphQL(RootResolver(Query(???)))
}
You didn't say what is B though, so there might be something else causing the compile error.
One way to find out is to call Schema.gen
directly on your case classes, which gives a more detailed error message about what's missing, like:
Schema.gen[A]
Schema.gen[B]
Schema.gen[Query]
Ok, so that's the full example that reproduces the error:
case class B()
case class GetAParams(i: Int)
case class GetBParams(i: Int)
case class Queryp(getA: GetAParams => Task[A])
case class A(getB: GetBParams => Task[List[B]])
Schema.gen[Queryp]
I missed the List[B] part.
I see. That is something that happens when there is too much nesting: Magnolia (the library that derives the schema for your types) has trouble deriving everything at once and needs a little help.
Adding this will solve it:
implicit val schemaA = Schema.gen[A]
That way, when it derives the schema for Query
, is already has the intermediate schema for A
and it makes its job easier. I usually do this (see here) as it can reduce compile times and also give better error messages.
Thank you, but I also missed one thing: The type B is recursive, and has reference to UIO[A], and that gives following error:
[Error] ... magnolia.Deferred is used for derivation of recursive typeclasses
In that case you should make the intermediate schema a lazy val
.
Example:
import caliban.schema._
import zio.Task
object Test {
case class B(getA: GetAParams => Task[A])
case class GetAParams(i: Int)
case class GetBParams(i: Int)
case class Queryp(getA: GetAParams => Task[A])
case class A(getB: GetBParams => Task[List[B]])
implicit lazy val schemaA = Schema.gen[A]
Schema.gen[Queryp]
}
Some examples of this pattern within caliban:
I noticed if 2 classes reference each other, it works better if only one of them is made lazy. Magnolia can be a little picky with those.
Actually it gets even worse... I have 4 types, each of them is member of another, and they hold reference to their parent. It forms kind of a chain. Do you know any method to resolve issue like this? I tried a lot of combination with this lazy schema vals, but it doesn't seem to help.
Hmm, it's hard to help you without the code. Any way you could extract it in something reproducible?
Something like this work for example:
import caliban.schema._
import zio.Task
object Test {
case class A(getB: GetBParams => Task[List[E]])
case class B(getA: GetAParams => Task[A])
case class C(getB: GetAParams => Task[B])
case class D(getC: GetAParams => Task[C])
case class E(getC: GetAParams => Task[D])
case class GetAParams(i: Int)
case class GetBParams(i: Int)
case class Queryp(getA: GetAParams => Task[A])
implicit lazy val schemaA = Schema.gen[A]
Schema.gen[Queryp]
}
By "chain" I meant a structure like this:
case class C(parent: Task[B], x: Task[C])
case class B(parent: Task[A], getC: GetBParams => Task[C])
case class GetAParams(i: Int)
case class GetBParams(i: Int)
case class Queryp(getA: GetAParams => Task[A])
case class A(getB: GetBParams => Task[List[B]], getC: GetBParams => Task[List[C]])
implicit lazy val z = Schema.gen[A]
implicit val y = Schema.gen[Queryp]
And that reproduces the error I have in my case. And thanks for your help :) I kind of got it randomly to work in my case by creating something like implicit lazy Scheme.gen[List[A]], but I can't share my full schema and I just try to reproduce errors I get with different setup. But with the snippet above I just can't find the combination that works. Anyways, even when I get it to work for some reason it introduces a type BList and CList in A type, that are defined like this: union CList = Nil | [C!], with custom scalar Nil (type Nil = { _: Boolean})
Actually your example works for me, it generates the following shema:
schema {
query: Queryp
}
type A {
getB(i: Int!): [B!]
getC(i: Int!): [C!]
}
type B {
parent: A
getC(i: Int!): C
}
type C {
parent: B
x: C
}
type Queryp {
getA(i: Int!): A
}
Do you have the latest version of Caliban? I upgraded Magnolia in 0.7.6 and 0.7.7, maybe it improved things. Otherwise not sure what it is, I tried with 2.12.11 and 2.13.2, both worked 🤔
Thank you for your help, I will try to reproduce it in exact way next week - I'm having problems with replicating the exact behaviour without putting here my company's code :) But I'm using Caliban 0.8.0 on Scala 2.12.
Ok, so after playing with it a bit more I fixed it and I think that the underlying issue was the fact that we were using outdated mercator. Not magnolia, but mercator, as dependency of another library :) Thank you for you help though, I will know what to do when I encounter some actual issues with generating Schema.
Great news! Glad you got it working.
I have a following schema:
Then I apply it to the graphQL(RootResulover(Query(...))) and I get following error:
And I wonder - is it possible to achieve what I've written there? I.e. type A with parameterized getB field.