sangria-graphql / sangria

Scala GraphQL implementation
https://sangria-graphql.github.io
Apache License 2.0
1.96k stars 226 forks source link

[Ljava.lang.Object; error when using JSON arrays #265

Closed elad-stern closed 7 years ago

elad-stern commented 7 years ago

I'm trying to send an array of elements as an argument (e.g. [String!]!) but I keep getting the following error: Variable '$temp' expected value of type '[String!]!' but got: [Ljava.lang.Object;@1232fa01. Reason: [at index #0] String value expected

This happens for any type (String, Int, Boolean, etc.) that attempts to use an array.

I've over simplified my setup to test this issue and even this very basic example doesn't work for me (it's missing a couple of pieces for simplicity, don't worry about them.. this works when not using an array):

val tempArg = Argument("temp", ListInputType(StringType))

val queryFields: List[Field[SangriaCtx, Unit]] = fields(
    Field("temp", OptionType(SomeObjectType),
      description = Some("Returns some object"),
      arguments = tempArg :: Nil,
      resolve = c => {
        // do anything that returns SomeObjectType..
      })

Query:

query temp($temp: [String!]!) {
  temp(temp: $temp) {
    name
  }
}

{
  "temp": [] // or ["word"], for example
}

Strangely (?) sending a string will cast it to a Vector with length of 1...

Please let me know if I'm doing something wrong.. Thanks, Elad

OlegIlyenko commented 7 years ago

Thanks for reporting this issue. I wonder where does this java array is coming from ([Ljava.lang.Object;@1232fa01).

I tried to reproduce this issue with this small example:

class SangriaCtx

val tempArg = Argument("temp", ListInputType(StringType))

val queryFields: List[Field[SangriaCtx, Unit]] = fields(
  Field("temp", ListType(StringType),
    description = Some("Returns some object"),
    arguments = tempArg :: Nil,
    resolve = c => {
      c.arg(tempArg)
    }))

val QueryType = ObjectType("Query", "Query entry point.", queryFields)

val query =
  graphql"""
    query temp($$temp: [String!]!) {
      temp(temp: $$temp)
    }
  """

// I also tried `[]`
val vars =
  """
    {
      "temp": ["a", "b"]
    }
  """.parseJson

val schema = Schema(QueryType)

val result = Executor.execute(schema, query, new SangriaCtx, variables = vars)

But I get an expected successful result:

{
  "data": {
    "temp": ["a", "b"]
  }
}

Based on the validation message, I think this issue might be caused by a custom InputUnmarshaller. Have you defined any custom InputUnmarshaller in your project?

elad-stern commented 7 years ago

We're not using a custom InputUnmarshaller but your comment gave me a different search direction and I believe the issue is with how our web server (finatra) is handling the request..

case class GraphqlRequest(query: String,
                          variables: Option[Map[String, Any]],
                          operationName: Option[String])

variables already contains the malformed type so it's not a Sangria issue..

Do you have any idea what I can do to fix this?

OlegIlyenko commented 7 years ago

The variables: Option[Map[String, Any]] part concerns me. I think this the root cause of the issue if it is something that comes from finatra. I would suggest parsing variables JSON with some JSON lib (like circe, spray-json, json4s, etc.). This will give you something like Option[JValue]/Option[JsValue]/Option[Json] which should work fine.

OlegIlyenko commented 7 years ago

Alternatively, you can also write a custom InputUnmarshaller for this finatra-specific JSON format represented with Map[String, Any]. Though my first suggestion is probably a bit simpler to implement and use

elad-stern commented 7 years ago

Thanks a million, I'll get right on this!

elad-stern commented 7 years ago

OK, so this has been an incredibly specific issue that probably wouldn't concern most people but it was eventually resolved. The root cause was indeed Finatra server. Finatra uses Jackson for JSON parsing and they encountered a performance issue you can read about here. They resolved it by opting to work with java objects instead of scala ones, but allowed an optional flag DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY to be turned off in case it causes any issues - which it did for us.

Anyway, long story short, we deactivated this feature and everything now works as expected. Thanks!

OlegIlyenko commented 7 years ago

Looks like the issue is now resolved, but please feel free to reopen.

mgadda commented 6 years ago

@elad-stern are you not concerned with the seriously degraded performance associated with disabling USE_JAVA_ARRAY_FOR_JSON_ARRAY?

elad-stern commented 6 years ago

@mgadda it's not a use-case for us so no. We do very little JSON parsing, nearly all of it is used with Sangria (which is just a tiny fraction of our traffic) and we've experienced absolutely no performance reduction in the past 6 months since the change.

mgadda commented 6 years ago

Makes sense. Thanks!