Error when returning ListType(<InterfaceType>)

119 views
Skip to first unread message

Chris Toomey

unread,
May 11, 2017, 3:07:53 AM5/11/17
to sangria-graphql
As distilled in the code below, which is almost as simple as the playground schema, when I try to execute a query that returns a list of InterfaceType (IFType), I get the error

{
  "data": null,
  "errors": [
    {
      "message": "Can't find appropriate subtype for field at path getIFs[0] (line 1, column 3):\n{ getIFs { id } }\n  ^",
      "path": [
        "getIFs",
        0
      ],
      "locations": [
        {
          "line": 1,
          "column": 3
        }
      ]
    }
  ]
}

If I change the query field to return a list of AType it works fine.

I.e., when line 72 executes, I get the above error, but when line 73 executes, I get the expected result.

What do I need to change in order to be able to have my query return IFTypes instead of ATypes?

     1	package domain.graphql
     2	
     3	import sangria.execution.Executor
     4	import sangria.marshalling.json4s.native._
     5	import sangria.parser.QueryParser
     6	import sangria.schema._
     7	
     8	import scala.util.{Failure, Success}
     9	
    10	object ChrisDebug {
    11	
    12	  trait IF {
    13	    def id: String
    14	  }
    15	
    16	  case class A(id: String, num: Int) extends IF
    17	
    18	  case class B(id: String, bool: Boolean) extends IF
    19	
    20	  type IFRepo = Seq[IF]
    21	
    22	  val myIFRepo: IFRepo = Seq(
    23	    A("foo", 12),
    24	    A("bar", 13)
    25	  )
    26	
    27	  val IFType: InterfaceType[Unit, IF] = InterfaceType(
    28	    name = "IFType",
    29	    description = "Base trait",
    30	    fieldsFn = () => fields[Unit, IF](
    31	      Field("id", StringType, Some("id"), resolve = _.value.id)))
    32	
    33	  val AType = ObjectType(
    34	    "AType",
    35	    "A",
    36	    interfaces[Unit, A](IFType),
    37	    fields[Unit, A](
    38	      Field("id", StringType,
    39	        Some("id"),
    40	        resolve = _.value.id),
    41	      Field("num", IntType,
    42	        Some("num"),
    43	        resolve = _.value.num)
    44	    ))
    45	
    46	  val IFQueryType = ObjectType(
    47	    "IFQueryType",
    48	    "IFQueryType",
    49	    fields[IFRepo, Unit](
    50	      Field("getIFs", ListType(IFType),
    51	        resolve = (ctx) => ctx.ctx)
    52	    )
    53	  )
    54	  val IFSchema = Schema(IFQueryType)
    55	
    56	  val AQueryType = ObjectType(
    57	    "AQueryType",
    58	    "AQueryType",
    59	    fields[IFRepo, Unit](
    60	      Field("getIFs", ListType(AType),
    61	        resolve = (ctx) => ctx.ctx.asInstanceOf[Seq[A]])
    62	    )
    63	  )
    64	  val ASchema = Schema(AQueryType)
    65	
    66	  def doQuery() = {
    67	    val query = "{ getIFs { id } }"
    68	    QueryParser.parse(query) match {
    69	      case Success(queryAst) =>
    70	        import scala.concurrent.ExecutionContext.Implicits.global // scalastyle:off
    71	        val res =
    72	          if (true) Executor.execute(IFSchema, queryAst, myIFRepo)
    73	          else Executor.execute(ASchema, queryAst, myIFRepo)
    74	        res
    75	      case Failure(err)      => throw err
    76	    }
    77	  }
    78	
    79	}

I'm using sangria and sangria-json4s-native 1.2.0 and Play 2.4.

thx,
Chris


Chris Toomey

unread,
May 11, 2017, 7:12:22 PM5/11/17
to sangria-graphql
FYI, I debugged this and traced the cause to the fact that the IFSchema was not aware of AType, and thus it was unable to resolve that my A objects were instances of AType. I was able to resolve the problem by making IFSchema aware of AType by changing line 54 to 

val IFSchema = Schema(query = IFQueryType, additionalTypes = List(AType))

My IFQueryType only contains a single field that only references the interface type IFType, but I incorrectly assumed the concrete types implementing it would be automatically identified and added via reflection/introspection. Seems like that's a bug (a field referencing an interface is unable without the concrete types implementing it) or at least a strong enhancement candidate. I'll create an issue for it.

Oleg Ilyenko

unread,
May 11, 2017, 7:30:40 PM5/11/17
to sangria-graphql
Hi Chris,

Thanks a lot for a detailed description of the issue. Glad that you found that root cause. I think this kind of issue can be compensated with better documentation and error reporting, but it would be quite challenging to fix.
 Given and `IFType` there is no way to know that there is an `AType` if there no explicit reference to it.

One possible solution I'm aware of is to mutate some JVM-global state in a constructor of the `AType` and register it in some kind of registry. Later on, `Schema` can access this registry and discover `AType`. I don't think that it is an appropriate solution for a library though, so I would prefer to avoid introducing it for obvious reasons. If I remember correctly, reference implementation had this solution for a while. But now they have refactored it. So now it looks and works in the same way as in sangria (with explicit `additionalTypes`). Are you aware of any better approach?

As I mentioned, I would suggest improving the documentation and error messages. Do you already have ideas about which parts of the docs or error messages can be improved?

Cheers,
Oleg

Chris Toomey

unread,
May 11, 2017, 7:45:43 PM5/11/17
to sangria-graphql
Hi Oleg, I created https://github.com/sangria-graphql/sangria/issues/242 for tracking this. If you'll add your reply to that I'll follow up there.

BTW, thanks for this awesome library and great documentation and examples, it's obvious you've put a ton of work into this.

Chris

Oleg Ilyenko

unread,
May 11, 2017, 8:02:48 PM5/11/17
to sangria-graphql
Thanks for kind your words and creating the issue! I would be easier to track the progress on the GitHub.
Reply all
Reply to author
Forward
0 new messages