Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
Yann Simon
@yanns
Nick Hudkins
@nickhudkins
Wonderful :), I've got a couple of PRs that I am cleaning up for sangria at the moment, just some bug fixes, and then I can tackle the jvm bytecode stuff.
I'd love to co-ordinate with the maintainers and get a plan together if possible. Even to just make a decision on "Should we support 2.11" etc...
Nick Hudkins
@nickhudkins
Screen Shot 2020-10-22 at 1.21.34 PM.png
Oliver Wetterau
@owetterau
@mkotsur Did you solve your problem with deriveInputObjectType/ deriveEnumTypeissue? I have the same problem ("invalid value: CNil") and am using Circe, too...
Miklos Szots
@smiklos
Hi all,
Recently we've been introducing some bugs in our api when sangira types and circe types don't align. There's this strange duality between those types like enums and scalar aliases that both need to be mapped for sangria and circe. Is there a better approach for this? Can't sangria derive or reuse the circe encoders or the other way around?
Dan Di Spaltro
@dispalt
im not the author but have used it extensively and this is the biggest shortcoming of sangria @smiklos
Nick Hudkins
@nickhudkins
Hey @smiklos and @dispalt sangria by itself has no "allegiance" to any JSON library. I am not sure that I follow what the situation is when your GraphQL types and Circe types don't align. Could you provide a simplified test case to show the behavior and help us understand what you'd be looking for?
Specifically, all things "Circe" and Sangria are handled within the sangria-circe package here: https://github.com/sangria-graphql/sangria-circe and the purpose of the library is only to provide marshalling and unmarshalling of GraphQL types as the schema is being materialized, or input is received.
Miklos Szots
@smiklos
Basically, the sangria-circe module parses json into an AST. This is great, and I understand that this is modular so one can use another marshaller like jackson or so.
The problem arises from the fact that all circe usage is two step, marshalling and encoding the result of the AST into a case class hierarchy.
It's this encoding that doesn't align with Sangria types. I need to both define a case object hierarchy as an enumtype so sangria renders it properly, but I also should not foget to create a circe derivation out of it so case classes can be generated. Lacking that, sangria will validate/parse the json but will crash when case classes are to be created
If I tell Circe how to encode/decode my enum/value class or whatever, I don't feel I should repeate this to Sangria as well. I'ts already done once for circe
the issue is mostly prominent with strong typing (newtype/ extends anyval) because there you need to create a scalar alias for sangria to understand that it's not a nested field (the value in the anyval) but just a regular top level field. Then you need to tell circe how to encode/decode this as well. Same for enums
Nick Hudkins
@nickhudkins
Have an example I can take a look at?
Or, any chance you could fork: https://github.com/sangria-graphql/sangria-akka-http-example and recreate the issue? The only time Circe and Sangria "interact" is when it is being materialized. Are you using Json WITHIN your resolvers?
Miklos Szots
@smiklos
Here is one
case class ResultData(resultId: ResultId)

object ResultId {

  //Already derived it as a string
  //This is a factory for creating ResultId from String
  implicit val encoder: Encoder[ResultId] = Encoder.encodeString.contramap(_.id)
  implicit val decoder: Decoder[ResultId] = Decoder.decodeString.map(ResultId(_))

  //Now I repeat myself
  //This is also a factory for creating ResultId from String
  implicit val graphQlType = ScalarAlias[ResultId, String](
    StringType,
    _.id,
    id => Right(ResultId(id))
  )
}

case class ResultId(id: String) extends AnyVal
Nick Hudkins
@nickhudkins
Ah, ok, so you would hope that by defining the ScalarAlias, that you'd get your encoder / decoders for whatever marshalling library you're using correct?
Miklos Szots
@smiklos
well, that would be ideal isn't it. Not whatever, I'm more than happy if it only supports circe (;> )
Nick Hudkins
@nickhudkins
Haha :) ok, let me give some thought to that.
Miklos Szots
@smiklos
We could possibly create a conversion from sangria type to circe or the other way around. The tricky thing in both cases is that both sangria and circe is more than happy to compile when using auto derivation without any of these extra implicits. a bit better solution would be to be able to derive one of the encoder/decoder pairs from the other. A lot better solution would be when sangria generates marshaller, it would auto derive circe encoders
Nick Hudkins
@nickhudkins
ya, we already "have" to do this as we marshall / unmarshall input and output types: https://github.com/sangria-graphql/sangria-circe/blob/master/src/main/scala/sangria/marshalling/circe.scala#L92-L104
Yann Simon
@yanns
I'm not 100% but circe should never be used when parsing the query. The json library is only used when creating the payload to send over the wire.
Nick Hudkins
@nickhudkins
Absolutely. I think @smiklos's use case is that, is that if a custom scalar exists, you might need to convert to and from... within your application
outside of accepting it as input, and rendering it as output
My "does this seem like something Sangria (or json support for sangria) should handle" screams... no, because Sangria doesn't have any care about that.
Miklos Szots
@smiklos
even more often we use scalar aliases. trying to keep the input already prevalidated by enfocing contstraints (like strong types, lengths of string etc).
It just feels strange that I need to tell circe that it should encode/decode those types differently when I already did tell sangria about it
I totally understand the separation of concerns here but that doesn't fix the usability of it. It just feels strange. Somehow being generic clashes with being convenient
Nick Hudkins
@nickhudkins
I still am not sure what the use case is here
in your case, let's say you don't define the encoder and decoder...
if a resolver indicates a field as a ResultId.graphqlType, and you return a string from said resolver, it will be coerced according to your toScalar function id => Right(ResultId(id))
Miklos Szots
@smiklos
Then circe will believe that I sent the input json as
{
 resultId: {
   id: "something"
}
instead of 
{
resultId: "something"
}
and so it will fail
Nick Hudkins
@nickhudkins
I think I'm back to being confused :) lol
Miklos Szots
@smiklos
my use case is that I want to abuse scala aliases because that's the recommended approach according to the docs. Turning simple scalars to narrower/valid types.
But because of that, I also need to tame circe derivation at the same time. Have I always accepted Strings and numbers, it would all be fine
:D
Nick Hudkins
@nickhudkins
Just to be clear, we are talking about InputTypes right?
Miklos Szots
@smiklos
yes
Nick Hudkins
@nickhudkins
ok, so then I believe you're not looking for ScalaAlias, but instead, need the FromInput type classes
or sorry... ToInput, or possibly both, my brain is scrambled
Miklos Szots
@smiklos

I tihnk that's something the circe support takes care of already.

I do work with scalarAliases as I though that's needed for input objects as well

since they define a conversion both to and from
I expect sangria that it uses these aliases to take simple String values from the request and turn them into something better , or fail otherwise
Now we have this better type, it's time to make a case class (that is the input object) out of it. Yet, without also declaring circe decoder for the type, circe fails to decode as it didn't get the memo from sangria that the field was already parsed and validated
so perhaps, the problem is that for Circe to go from AST to Case class also needs to know how to take that scalar value and convert it to the field on the case class. This makes sense, this is default circe behavior. But, sangria took over that responsibility when it started parsing the request and doing validations. IT clearly does that, since it makes use of these scalar types and scalar aliases
Miklos Szots
@smiklos
input json -> sangria-parser (via circe) -> String -> scalar alias -> ResultId
but when it's time to put this ResultId into a case class it seems we start over
input json -> circe -> case class
very important to mention, (perhaps a bit late) that I'm strictly talking about deriveInputObject and not manually creating the fields
Nick Hudkins
@nickhudkins
If you can give me a full example, I think I may be able to help
This IS important :)
what's your deriveInputObject call look like?
Miklos Szots
@smiklos
import io.circe.{Decoder, Encoder}
import sangria.macros.derive.deriveInputObjectType
import sangria.schema.{ScalarAlias, StringType}

case class ResultData(resultId: ResultId)

object ResultData {

  implicit val derived = deriveInputObjectType[ResultData]()
}

object ResultId {

  //Already derived it as a string
  //This is a factory for creating ResultId from String
  implicit val encoder: Encoder[ResultId] = Encoder.encodeString.contramap(_.id)
  implicit val decoder: Decoder[ResultId] = Decoder.decodeString.map(ResultId(_))

  //Now I repeat myself
  //This is also a factory for creating ResultId from String
  implicit val graphQlType: ScalarAlias[ResultId, String] = ScalarAlias[ResultId, String](
    StringType,
    _.id,
    id => Right(ResultId(id))
  )
}

case class ResultId(id: String) extends AnyVal