Open PsyfireX opened 1 year ago
Additional notes:
The code did compile previously under Scala 2.13
Debugging macros is beyond my current expertise. However, narrowing in closer to the issue, it appears to happen when mixing ObjectType and InputObjectType, where the input is a method-parameter.
When I use an ObjectType inside and ObjectType, or an InputObjectType inside and InputObjectType it seems to compile just fine:
ObjectType inside ObjectType (no errors)
package ai.tagr.graphql.tagr.schema
import sangria.schema.ObjectType
import scala.concurrent.Future
import sangria.schema.InputObjectType
case class Ctx(ctx: String)
object PostSchema {
import sangria.marshalling.circe._
import io.circe.generic.auto._
import sangria.macros.derive._
case class UpdatePostGql() {
@GraphQLField
def text(text: String): Future[TextGql] = ??? // relevant line
}
object UpdatePostGql{
implicit val _ot: ObjectType[Ctx, UpdatePostGql] =
deriveObjectType[Ctx, UpdatePostGql](
ObjectTypeName("UpdatePost")
)
}
case class TextGql(text: String)
object TextGql{
implicit val _it: ObjectType[Ctx, TextGql] =
deriveObjectType[Ctx, TextGql](
ObjectTypeName("Text")
)
}
}
InputObjectType inside InputObjectType (no errors)
import sangria.schema.ObjectType
import scala.concurrent.Future
import sangria.schema.InputObjectType
case class Ctx(ctx: String)
object PostSchema {
import sangria.marshalling.circe._
import io.circe.generic.auto._
import sangria.macros.derive._
case class UpdatePostGql(id: String) {
@GraphQLField
def text(text: TextInputGql): Future[String] = ??? // relevant line
}
object UpdatePostGql{ // swapped from ObjectType to InputObjectType
implicit val _it: InputObjectType[UpdatePostGql] =
deriveInputObjectType[UpdatePostGql](
InputObjectTypeName("UpdatePost")
)
}
case class TextInputGql(text: String)
object TextInputGql{
implicit val _it: InputObjectType[TextInputGql] =
deriveInputObjectType[TextInputGql](
InputObjectTypeName("TextInput")
)
}
}
update:
This code has the same issue, and uses an id with a ScalarAlias[T, Long]
for input, and has the same issue.
import scala.concurrent.Future
import scala.util.Try
case class Ctx(ctx: String)
case class SomeId(id: Long)
object SomeSchema {
import sangria.marshalling.circe._
import io.circe.generic.auto._
import sangria.macros.derive._
import sangria.schema._
type LongAlias[T] = ScalarAlias[T, Long]
def longAlias[ID](from: ID=>Long, to: Long=>ID): LongAlias[ID] =
ScalarAlias(LongType, from, v ⇒ Right(to(v)))
implicit val someIdType: LongAlias[SomeId] = longAlias[SomeId](_.id, SomeId.apply)
case class SomeOT(id: String) {
@GraphQLField
def getSome1(id: SomeId): Future[String] = ???
}
object SomeOT{
implicit val _ot: ObjectType[Ctx, SomeOT] =
deriveObjectType[Ctx, SomeOT](
ObjectTypeName("SomeOT")
)
}
}
However, I noticed that commenting out:
// import io.circe.generic.auto._
Appears to make the compiler error go away in this very tiny and limited code sample.
Now, I can't do that in my production code. Commenting that line out in my prod code causes a bunch of other errors, and it's apparently being used to support FromInput.
I'll have a look at this. Pinging @jchyb who may have an opinion on what is going on.
I can confirm that the first example compiles if I remove import io.circe.generic.auto._
and if I define the FromInput[TextInputGql]
:
import sangria.marshalling.FromInput
import sangria.schema.ObjectType
import scala.concurrent.Future
import sangria.schema.InputObjectType
case class Ctx(ctx: String)
object PostSchema {
import sangria.macros.derive._
case class UpdatePostGql() {
@GraphQLField
def text(text: TextInputGql): Future[String] = ???
}
object UpdatePostGql {
implicit val _ot: ObjectType[Ctx, UpdatePostGql] =
deriveObjectType[Ctx, UpdatePostGql](
ObjectTypeName("UpdatePost")
)
}
case class TextInputGql(text: String)
object TextInputGql {
implicit val fi: FromInput[TextInputGql] = ???
implicit val _it: InputObjectType[TextInputGql] =
deriveInputObjectType[TextInputGql](
InputObjectTypeName("TextInput")
)
}
}
So it seems that issue is coming from a different resolution of implicits.
I'm trying to check with semi-automatic derivation to better control which implicits are created:
import io.circe.Decoder
import sangria.schema.{InputObjectType, ObjectType}
import scala.concurrent.Future
case class Ctx(ctx: String)
object PostSchema {
import sangria.marshalling.circe._
import io.circe.generic.semiauto._
import sangria.macros.derive._
case class UpdatePostGql() {
@GraphQLField
def text(text: TextInputGql): Future[String] = ???
}
object UpdatePostGql {
implicit val _ot: ObjectType[Ctx, UpdatePostGql] =
deriveObjectType[Ctx, UpdatePostGql](
ObjectTypeName("UpdatePost")
)
}
case class TextInputGql(
@GraphQLInputType(sangria.schema.StringType)
text: String)
object TextInputGql {
implicit val decoder: Decoder[TextInputGql] = deriveDecoder[TextInputGql]
implicit val _it: InputObjectType[TextInputGql] =
deriveInputObjectType[TextInputGql](
InputObjectTypeName("TextInput")
)
}
}
It leads to the compilation error:
[error] 20 | deriveObjectType[Ctx, UpdatePostGql](
[error] | ^
[error] |GraphQlOutputType not found: scala.Function1[java.lang.String, sangria.schema.Action[Ctx, java.lang.String]]
[error] 21 | ObjectTypeName("UpdatePost")
[error] 22 | )
[error] one error found
I've pushed the changes to https://github.com/sangria-graphql/sangria/tree/issue-905 if someone wants to re-use that.
Thank you for starting the investigation! @yanns
I might not be very useful in fixing the bug itself with code-changes (due to no macro experience), however I also encountered what appears to be the exact same issue in both my top-level Query and Mutation types, using deriveContextObject
, producing an almost identical MatchError, and similarly pointing to another InputObjectType
val objectType: ObjectType[Ctx, Unit] =
deriveContextObjectType[Ctx, AllQueries, Unit](_ctx => new AllQueries {
override val ctx: Ctx = _ctx
})
Since you are already started looking at the issue, I'll see if I can also extract this code into a reproducible, isolated, code-sample.
Here is the second code sample, revolving around deriveContextObject
package ai.tagr.graphql.tagr.schema
import scala.concurrent.Future
import scala.util.Try
case class Ctx(ctx: String)
case class SomeId(id: Long)
object SomeSchema {
import sangria.marshalling.circe._
// commenting out the import below eliminates one error around `getSome1`. However, it causes `getSome2` breaks, because it needs a `FromInput[SomeInput,InputObjectResult]`
// import io.circe.generic.auto._
import sangria.macros.derive._
import sangria.schema._
type LongAlias[T] = ScalarAlias[T, Long]
def longAlias[ID](from: ID=>Long, to: Long=>ID): LongAlias[ID] =
ScalarAlias(LongType, from, v ⇒ Right(to(v)))
implicit val someIdType: LongAlias[SomeId] = longAlias[SomeId](_.id, SomeId.apply)
case class SomeInput(text: String)
object SomeInput{
implicit val _it: InputObjectType[SomeInput] = deriveInputObjectType[SomeInput](
InputObjectTypeName("SomeInput")
)
}
trait QueryRoot {
def ctx: Ctx
@GraphQLField
def getSome1(id: SomeId): Future[String] = ???
@GraphQLField
def getSome2(in: SomeInput): Future[String] = ???
}
object QueryRoot {
implicit val _ot: ObjectType[Ctx, Unit] =
deriveContextObjectType[Ctx,QueryRoot,Unit](
_ctx => new QueryRoot{ override def ctx: Ctx = _ctx}
)
}
}
I am taking a look at this right now, and I feel I am close to figuring this out. Slightly delaying implicit resolution in macro seems to make errors consistent between code with import io.circe.generic.auto._
and without it in the first snippet (no idea why yet). It still errors, but at least without the ugly dotc stack trace, just errors with failed implicit resolution. Those suggest that I might need to check what is going on in the sangria.marshalling.circe module. I'll try to prepare a PR with the fixes after figuring out the rest.
@jchyb Thank you! I believe this is the very last piece in order for me to finish converting my entire project over to Scala 3.
Dependencies:
Code:
Error: