Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
PsyfireX
@PsyfireX
The first minor obstacle is circe-optics appears to only be available in Scala 2, which it's transitive dependencies are causing conflicts. The fastest workaround will probably be dropping circe-optics, since I only have a few places which use it, although I'm also asking for advice on the circe gitter.
PsyfireX
@PsyfireX
@yanns I haven't yet dove deep into this exception yet, but it looks like I'm running into issues with deriveObjectType and macros.
    implicit val _ot: ObjectType[Ctx, UpdatePostGql] =
      deriveObjectType[Ctx, UpdatePostGql](
        ObjectTypeName("UpdatePost")
      )
Exception occurred while executing macro expansion.
scala.MatchError: OrType(AndType(TypeRef(TermRef(TermRef(TermRef(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object ai),object tagr),object graphql),object tagr),object schema),object types),class BodyInputGql),AppliedType(TypeRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class sangria)),object util),tag),type Tagged),List(TypeRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class sangria)),object marshalling),FromInput),InputObjectResult)))),TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),Null)) (of class dotty.tools.dotc.core.Types$CachedOrType)
    at dotty.tools.dotc.transform.TypeUtils$.companionRef(TypeUtils.scala:91)
    at dotty.tools.dotc.typer.Synthesizer.companionPath(Synthesizer.scala:252)
    at dotty.tools.dotc.typer.Synthesizer.productMirror(Synthesizer.scala:311)
    at dotty.tools.dotc.typer.Synthesizer.$init$$$anonfun$6$$anonfun$1$$anonfun$1$$anonfun$1(Synthesizer.scala:396)
    at dotty.tools.dotc.typer.Synthesizer.makeMirror(Synthesizer.scala:388)
    at dotty.tools.dotc.typer.Synthesizer.$init$$$anonfun$6$$anonfun$1(Synthesizer.scala:396)
    at dotty.tools.dotc.typer.Synthesizer.$init$$$anonfun$8$$anonfun$1(Synthesizer.scala:413)
    at dotty.tools.dotc.typer.Synthesizer.recur$1(Synthesizer.scala:556)
    at dotty.tools.dotc.typer.Synthesizer.tryAll(Synthesizer.scala:561)
    at dotty.tools.dotc.typer.Implicits.inferImplicitArg(Implicits.scala:857)
    at dotty.tools.dotc.typer.Implicits.inferImplicitArg$(Implicits.scala:785)
    at dotty.tools.dotc.typer.Typer.inferImplicitArg(Typer.scala:117)
    at dotty.tools.dotc.typer.Typer.implicitArgs$1(Typer.scala:3451)
    at dotty.tools.dotc.typer.Typer.addImplicitArgs$1(Typer.scala:3487)
    at dotty.tools.dotc.typer.Typer.adaptNoArgsImplicitMethod$1(Typer.scala:3563)
    at dotty.tools.dotc.typer.Typer.adaptNoArgs$1(Typer.scala:3761)
    at dotty.tools.dotc.typer.Typer.adapt1(Typer.scala:3995)
    at dotty.tools.dotc.typer.Typer.adapt(Typer.scala:3329)
    at dotty.tools.dotc.typer.Typer.readapt$1(Typer.scala:3340)
    at dotty.tools.dotc.typer.Typer.adapt1(Typer.scala:3982)
    at dotty.tools.dotc.typer.Typer.adapt(Typer.scala:3329)
    at dotty.tools.dotc.typer.Implicits.typedImplicit(Implicits.scala:1053)
    at dotty.tools.dotc.typer.Implicits.typedImplicit$(Implicits.scala:785)
    at dotty.tools.dotc.typer.Typer.typedImplicit(Typer.scala:117)
    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.tryImplicit(Implicits.scala:1173)
    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.rank$1(Implicits.scala:1272)
    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.searchImplicit(Implicits.scala:1442)
    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.searchImplicit(Implicits.scala:1470)
    at dotty.tools.dotc.typer.Implicits$ImplicitSearch.bestImplicit(Implicits.scala:1503)
    at dotty.tools.dotc.typer.Implicits.inferImplicit(Implicits.scala:997)
    at dotty.tools.dotc.typer.Implicits.inferImplicit$(Implicits.scala:785)
    at dotty.tools.dotc.typer.Typer.inferImplicit(Typer.scala:117)
    at dotty.tools.dotc.typer.Implicits.inferImplicitArg(Implicits.scala:851)
    at dotty.tools.dotc.typer.Implicits.inferImplicitArg$(Implicits.scala:785)
    at dotty.tools.dotc.typer.Typer.inferImplicitArg(Typer.scala:117)
    at dotty.tools.dotc.typer.Typer.implicitArgs$1(Typer.scala:3451)
    at dotty.tools.dotc.typer.Typer.addImplicitArgs$1(Typer.scala:3487)
    at dotty.tools.dotc.typer.Typer.adaptNoArgsImplicitMethod$1(Typer.scala:3563)
    at dotty.tools.dotc.typer.Typer.adaptNoArgs$1(Typer.scala:3761)
    at dotty.tools.dotc.typer.Typer.adapt1(Typer.scala:3995)
    at dotty.tools.dotc.typer.Typer.adapt(Typer.scala:3329)
    at dotty.tools.dotc.typer.Typer.readapt$1(Typer.scala:3340)
    at dotty.tools.dotc.typer.Typer.adapt1(Typer.scala:3982)
    at dotty.tools.dot
PsyfireX
@PsyfireX
It appears this type of error mostly applies to classes using @GraphQLExclude and @GraphQLField annotations.
  case class UpdatePostGql(
    @GraphQLExclude
    ctx: Ctx,
    @GraphQLExclude
    id: PostId,
  ) {
    @GraphQLField
    def publishDraft(): Future[PostGql] = ???

    @GraphQLField
    def body(body: BodyInputGql): Future[PostGql] = ???

    @GraphQLField
    def publishVersion(index: Int): Future[PostGql] = ???
  }
Yann Simon
@yanns
@PsyfireX very good feedback! Could you open an issue for this?
It seems that you already have found a way to reproduce it. It should help fixing the issue.
PsyfireX
@PsyfireX
Sure, I was about ready to head to bed - so I'll do that when I wake up. I'll also try to distill it into a piece of code that doesn't rely on my project as well, as to make it more reproducable.
PsyfireX
@PsyfireX
I finally was able to make it reproducible without depending on my project:
sangria-graphql/sangria#905
Yann Simon
@yanns
Thanks a lot!
PsyfireX
@PsyfireX

@yanns No problem, a much bigger thank you for maintaining and supporting Sangria.

Do you have any ideas about when you expect the bug to be fixed? I'm just trying to determine whether or not to go through the effort of switching from derived to manual, since the one final place this bug appears is the very top-level query type containing all of the other query types.

PsyfireX
@PsyfireX
(macros are over-my-head, otherwise I'd look into it)
PsyfireX
@PsyfireX
Disregard my above comment, I see you've already started looking at it! (of course that's not an estimate, but perhaps worth waiting a short while instead of doing a rewrite).
Yann Simon
@yanns
@PsyfireX even if I've started working on it, I'm not sure about when I'll have a fix. I've not written the scala 3 macros, and I need time to understand them.
PsyfireX
@PsyfireX
@yanns Update: I created a workaround to the sangria-circe issues in 905, by engaging in some "ImplicitLaundering" by grabbing the implicit in another class which has the import of io.circe.generic.auto and then using that implicit in another class. The bug still persists, but I was able to unblock myself on that front. I essentially have the following code .... but replace fiA with about 100 or so FromInputs for actual GraphQL classes.
case class A(text: String)
object A{
  implicit val _it: InputObjectType[A] = deriveInputObjectType[A]()
}
// This is a hack to workaround https://github.com/sangria-graphql/sangria/issues/905
object ImplicitLaundering{
  // grab the implicits from `io.circe.generic.auto`
  object Generate {
    import io.circe.generic.auto.*
    def getFI[T]()(implicit t:FromInput[T]): FromInput[T] = t

    def fiA = getFI[A]()
  }

  // Make implicits available with `import ImplicitLaundering.Implicits.*` but without `import io.circe.generic.auto.*`
  object Implicits {
    import Generate.*
    implicit val _fiA: FromInput[A] = fiA
  }
}

My server now:
1) SUCCESS: starts, and successfully returns the Schema (introspection).
2) SUCCESS: Processes input, runs my backend code, and then...
3) FAIL: Fails to generate the output on several endpoints, using various ObjectTypes.

"class scala.concurrent.impl.Promise$DefaultPromise cannot be cast to class ai.tagr.graphql.tagr.schema.types.UserGql (scala.concurrent.impl.Promise$DefaultPromise and ai.tagr.graphql.tagr.schema.types.UserGql are in unnamed module of loader 'app') 
java.lang.ClassCastException: class scala.concurrent.impl.Promise$DefaultPromise cannot be cast to class ai.tagr.graphql.tagr.schema.types.UserGql (scala.concurrent.impl.Promise$DefaultPromise and ai.tagr.graphql.tagr.schema.types.UserGql are in unnamed module of loader 'app') 
 ai.tagr.graphql.tagr.schema.types.UserGql$.$init$$$anonfun$1$$anonfun$13(UserGql.scala:59) 
 sangria.execution.Resolver.resolveField(Resolver.scala:1513) 
 sangria.execution.Resolver.collectActionsPar$$anonfun$1(Resolver.scala:713) 
 scala.collection.IterableOnceOps.foldLeft(IterableOnce.scala:646) 
 scala.collection.IterableOnceOps.foldLeft$(IterableOnce.scala:642) 
 scala.collection.AbstractIterable.foldLeft(Iterable.scala:926) 
 sangria.execution.Resolver.collectActionsPar(Resolver.scala:722) 
 sangria.execution.Resolver.resolveValue(Resolver.scala:1331) 
 sangria.execution.Resolver.resolveStandardFieldResolutionSeq(Resolver.scala:441) 
 sangria.execution.Resolver.resolveSingleFieldSeq(Resolver.scala:400) 
 sangria.execution.Resolver.resolveSeq$$anonfun$1$$anonfun$1(Resolver.scala:356) 
 scala.concurrent.impl.Promise$Transformation.run(Promise.scala:470) 
 java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) 
 java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) 
 java.base/java.lang.Thread.run(Thread.java:834)"

I'm not really sure what this error means. However, in this example, UserGql is a fairly basic return/output ObjectType and I get the same error on my other endpoints.

PsyfireX
@PsyfireX
Same error, with a very simplified endpoint:
    @GraphQLField
    def ping(in: String): Future[String] = {
      implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global
      Future.apply(in)
    }
PsyfireX
@PsyfireX
I created a ticket for the above:
sangria-graphql/sangria#906
Yann Simon
@yanns
yes it looks like the issue I had on https://github.com/sangria-graphql/sangria/issues/905#issuecomment-1229833443 where the wrong implicit is searched for.
PsyfireX
@PsyfireX

Issue 905 and 906 look very different. 905 is an implicit resolution issue or conflict with io.circe , which causes compiler errors. 906 appears to be Sangria getting confused about how to render a Future[???] even with simple types like String

(Although as someone not familiar with the code, I guess it's technically possible they could be related, just seems unlikely)

PsyfireX
@PsyfireX
I decided to do a workaround for #906, swapping all endpoints returning Future[T] to just return T instead. I don't know what the performance difference is, but I'm still months away from dealing with any notable traffic. Once 905 and 906 are fixed, I'll remove my workarounds. Anyway, I appear to have a working GraphQL server in Scala 3, and will use the Scala 3 branch as "master." I'll report any other bugs I run into if any.
Yann Simon
@yanns
thanks for your patience, and congrats about being the 1st one to use AFAIK!
PsyfireX
@PsyfireX
Thanks! I'm currently not blocked, and rolling forward with other feature development. I'll revisit cleanup of the workarounds on my project later, after 905 and 906 are fixed. There were a couple other minor issues I ran into around io.circe but they were easy to workaround and not caused by Sangria.
Guillaume Forgeon
@guillaumefgn_twitter

Hi everybody!
I am facing a "problem" with Sangria (I'm new to it AND to Scala so maybe my problem is due to my lack of knowledge !).
If someone can help me... i'm kinda despesperated :)

So here it is:

  • I have a fetcher 'A' which returns a list of ids
  • I have another fetcher 'B' which needs the list of ids returned by the fetcher 'A' and returns a list of 'EntityB'
  • I have a field whose type id a Option[Connection[EntityB]]
  • Then, for now I have something like this in my resolver:
DeferredValue(Fetchers.fetcherA.deferOpt(ctx.value.id))
              .map(entitiesFromA => {
                DeferredValue(
                  Fetchers.fetcherB.deferSeqOpt(entitiesFromA)
                ).map {
                  case Nil => None
                  case entitiesB =>
                    Some(
                      Connection.connectionFromSeq(
                        entitiesB,
                        args,
                        SliceInfo(sliceStart = offet, size = offset + entitesB.size)
                      )
                    )
                }

Of course, it doesn't work. Compiler needs a Option[Connection[EntityB]] and it gets a DeferredValue[Nothing, Option[Connection[EntityB]]].
I think my problem comes from the nested fetcher calls + Connection stuff. I don't really know how to do.
If someone has an idea, do not hesitate to tell me.
Thank you!

Yann Simon
@yanns

From what I understand:

  • FetcherA takes one ID and returns a list of IDs
  • FetcherB can return a list of EntityB based on the previous list of IDs

I'm not very used to use Fetchers, but I think that fetcherA could be a Relation[..., ID] and used like:

Fetchers.fetcherB.deferRelSeq(Fetchers.fetcherA, ctx.value.id)
Yann Simon
@yanns
If people have interest using a sangria server with GraphQL federation: https://sangria-graphql.github.io/learn/#graphql-federation
Yann Simon
@yanns
Sangria now officially supports GraphQL Federation: https://github.com/apollographql/apollo-federation-subgraph-compatibility#scala
Alexandre Lebrun
@xela85

Hello,
I would like to upgrade a project to Sangria 3, and this project uses sangria-relay module.
By looking at https://github.com/sangria-graphql/sangria-relay, I can see that the project is regularly updated (e.g. scala-steward updates).
However, I don't see any new release in Maven or GitHub. I am missing something ?

Thanks in advance :)

Yann Simon
@yanns
Hi @xela85 . You should still be able to upgrade the sangria version.
But I'll cut a release soon to have all dep updated.
Yann Simon
@yanns
Do you need scala 3 support?
Marty Phee
@martyphee

Looking for some direction. Learning and haven't seen examples.

I'm prototyping a GraphQL service for our platform data.
gRPC to Platform Services returngin Protobuf -> mapping to case classes -> serving over GraphQL

I tried deriveObjectType directly with the Protobuf and that had its own challenges.

So now I'm trying to mapping to case classes and I have a number of case classes with a bunch of Option fields

Like

case class Policy(
    policyId: UUID,
    cancelEffectiveTime: Option[Instant] = None,
    cancelledBy: Option[PolicyAction] = None,
    carrier: Option[Carrier] = None,
    renewal: Option[Renewal] = None,
    underwritingCriteria: Option[UnderwritingCriteria] = None
)

I'm wondering the best way to define these for the schema

I've done

private val policyType = deriveObjectType[PlatformRepo, Policy]()

But get errors like

Can't find suitable GraphQL output type for Option[zego.controlpanel.domain.Renewal]. If you have defined it already, please consider making it implicit and ensure that it's available in the scope.

Which I'm then trying

  private val policyType = deriveObjectType[PlatformRepo, Policy](
    ReplaceField(
      "carrier",
      Field(
        "carrier",
        OptionType(carrierType),
        resolve = ctx => ctx.value.carrier
      )
    ),
  .
  .
  .
  )

Is this the proper/best way to define Option(s) the schema? It works.
I'm going to have a number of case classes with Option fields and want to find the most efficient way of mapping them.

Yann Simon
@yanns
no, you can keep private val policyType = deriveObjectType[PlatformRepo, Policy]()
and add some implicits, like private implicit val carrierType: ObjectType[PlatformRepo, Carrier] = ... so that deriveObjectType[PlatformRepo, Policy]() can use it.
otherwise deriveObjectType[PlatformRepo, Policy]() does not know how to expose your domain classes (like Carrier, PolicyAction...) to the GraphQL schema.
Yann Simon
@yanns
I hope this helps.
Marty Phee
@martyphee
Thank you. That's what I kind of suspected. Just wanted to know if I was missing something. Thank you
Paul Sayre
@pauls-ai2
I've been working with Fetcher and Deferred's. Is there something similar to what Future has with Await.result() for getting a value out of a Deferred? I'm not talking about DeferredValue().map(), because that still wraps the transformed results with a Deferred. More like Option().get, which gives you the value.
Yann Simon
@yanns
Not that I know. Do you need that for testing purposes?
If this API would exist, it would be blocking, and users might use it on production code, leading the scalability issues.
The DeferredResolver returns Future that you can await. Does this help?
Paul Sayre
@pauls-ai2
That could work. I have a DeferredResolver from DeferredResolver.fetchers(). But it looks like the .resolve() needs a queryState, which is an Any, but is converted to a FetcherCache. I can't figure out where to get that cache from. From what I can tell, FetcherConfig can give an empty cache, but not one that has been populated by previous fetches.
rney-lookout
@rney-lookout
Hey, noticed that the sangria-federated project is getting some more frequent updates. Is the community looking at achieving Federation 2.x level support so it can work with the Apollo Router? Was concern moving to Caliban was required for us to start supporting the router.
Yann Simon
@yanns
@pauls-ai2 Can you maybe give more info about what you are trying to do? Is it for testing?
@rney-lookout yes, we are actively working on it, and we want to go to production with that. You're welcome to join the fun, and provide any feedback on the library.
We're also working closely with Apollo, leading to https://github.com/apollographql/apollo-federation-subgraph-compatibility#scala
In our prototype, we can use sangria for a Federation v2 subgraph that we use from the router.
Paul Sayre
@pauls-ai2

@yanns, no, it's not for testing. This is for production.

My project is for an Academic Research website (think Google Scholar). At the core, we have Paper's and Author's. A Paper can have many Author's, and an Author can have many Paper's.

In one place, I'm trying to return a Connection[Author] for a Paper. To do this, I need to:
0) Look up a Paper from a Fetcher (sets value of context.value)
1) Fetch the author IDs for the Paper (returns a Future)
2) Take a slice of those author IDs to create a page of IDs
3) Fetch the Author's with those IDs from a Fetcher (returns a Deferred)
4) Return a Connection[Author] (or Future[*], Deferred[*]...don't care since sangria can handle either)

So I have a mix of Future's and Deferred's. Since my team has experience in Future's, I was trying to get the code to rely more on those than on Deferred (which would be unique to this part of the code). Ideally, I'd like to be able to reuse the Fetcher's such that I can get records as intermediaries, and still have them cache (think CoAuthors using a Paper as an edge in the graph).

Yann Simon
@yanns
My approach: I try to code everything with Future. I keep the sangria code very thin, only calling other implementations.
I don't see Deferred as Future. Deferred is a marker for a value that should be resolved by a Fetcher or DeferredResolver.
Using a Deferred does not do anything unless it's resolved.
Using a Future triggers the computation of the value (with side-effect). So it's quite different.
Adrian
@adrian-salajan
Hi, is it possible for sangria to derive an ADT (sum type) ? i can't find anything in the docs
Yann Simon
@yanns
not sure about this. deriveEnumType seems to support ADT: https://github.com/sangria-graphql/sangria/blob/main/modules/derivation/src/test/scala/sangria/macros/derive/DeriveEnumTypeMacroSpec.scala but I'm not sure if it's what you are looking for.