Jarlakxen / drunk

A simple GraphQL client on top of Sangria, Akka HTTP and Circe.
Apache License 2.0
62 stars 21 forks source link

Client implementation errror "Cannot deduce decoder" #10

Open MichaelHornung opened 5 years ago

MichaelHornung commented 5 years ago

Hi,

i implemented this client:

package com.github.jarlakxen.drunk
import com.github.jarlakxen.drunk.GraphQLClient.GraphQLResponse
import org.scalatest.FlatSpec
import com.github.jarlakxen.drunk.circe.deriveByTypenameDecoder
import com.typesafe.scalalogging.LazyLogging
import de.salt.d4s.model.entities.{Delivery, DeliveryItem}
import io.circe._
import io.circe.generic.semiauto._
import sangria.macros._
import java.util.concurrent.TimeUnit.MILLISECONDS

import scala.concurrent.duration.Duration
import scala.concurrent.{Await, ExecutionContext, Future, duration}

class GraphQLSoDelClientSpec extends FlatSpec with LazyLogging {

  implicit val deliveryDecoder: Decoder[Delivery] = deriveDecoder
  implicit val deliveryItemDecoder: Decoder[DeliveryItem] = deriveDecoder
  val duration = Duration(4000, MILLISECONDS)

  //case class DeliveryQuery(delivery: Delivery)
  type DeliveryQuery = Map[String, List[Delivery]]
  case class Pagination(offset: Int, size: Int)

  implicit val deliveryQueryDecoder: Decoder[DeliveryQuery] = deriveByTypenameDecoder()

  implicit val pagniationEncoder: Encoder[Pagination] = deriveEncoder
  val client = GraphQLClient(s"http://delivery-generator-mho.d4s.salt-solutions.de/graphql")
  implicit val ec = ExecutionContext.global

  "server" should "respond correctly to deliveryQuery" in {

    val query =
      graphql"""
               query{delivery( id: "4727")
               {
               docid
               docType
               system
               status
               items{itemid product quantity unit date}}
               }
    """

    val page1: GraphQLCursor[DeliveryQuery, Pagination] = client.query(query, Pagination(0, 10))
    val data: Future[GraphQLResponse[DeliveryQuery]] = page1.result

    val result = Await.result(data, duration)

    logger.debug(result.toString)
  }
}

Running i get this error:

Cannot deduce decoder. Missing __typename: DownField(data)
DecodingFailure(Cannot deduce decoder. Missing __typename, List(DownField(data)))

Can anyone help me to fix it?

MichaelHornung commented 5 years ago

Any ideas? Would be happy to get this solved.

remimi commented 5 years ago

Hello đź‘‹

I know that i had this issue when my query order did not match my case class definition order. For example :

query{delivery( id: "4727"){
    docid
    docType
    system
    status
}

The case class should be

case class Delivery(
    docid: String,
    docType: String,
    system: String,
    status: String
)

In the same exact order as you querying. At least i had this issue using drunk with auto decoder from circe. Don't know if it can solve your problem

paulpdaniels commented 4 years ago

@MichaelHornung I'm assuming you have either found your answer or moved on but the issue is likely that you don't have anything declared for the discriminators. The implementation looks like this:

  /**
    * This a special [[io.circe.Decoder]] for decode a polymorphic JSON response using the __typename field.
    */
  def deriveByTypenameDecoder[T](discriminators: (String, Decoder[_ <: T])*) = new Decoder[T] {
    val discriminatorsMap = discriminators.toMap
    override def apply(c: HCursor) = c.downField(ast.TypenameFieldName).as[String] match {
      case Right(tpe) if discriminatorsMap.contains(tpe) =>
        discriminatorsMap(tpe)(c)
      case Right(tpe) =>
        Left(DecodingFailure(s"Cannot deduce decoder $tpe for ${ast.TypenameFieldName}", c.history))
      case _ =>
        Left(DecodingFailure(s"Cannot deduce decoder. Missing ${ast.TypenameFieldName}", c.history))
    }
  }

Meaning you need to supply the list of discriminators in order for it to be able to resolve which subtype you want