GithubHelp home page GithubHelp logo

clue's People

Contributors

armanbilge avatar cquiroz avatar dependabot[bot] avatar lucuma-steward[bot] avatar mergify[bot] avatar rpiaggio avatar scala-steward avatar swalker2m avatar toddburnside avatar tpolecat avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

clue's Issues

Publish for JDK 8, test on JDK 11+?

Currently clue has a dependency on the http4s-jdk-http-client, which requires Java 11.

Now that there is a common web socket client API in http4s/http4s#5520, it should be possible to make this library agnostic to the backend and remove the dependency on the jdk-http-client (and thus on JDK 11). This would allow you to publish for JDK 8.

Of course, testing would still require the jdk-http-client for now, so those would have to compile/run on JDK 11. Hopefully we'll get an ember-client websocket backend in the future that runs on JDK8 (and also Node.js :).

Subqueries and interfaces, not working at least in one case

In the ODB we have in the schema:

type Gcal implements StepConfig {
  continuum: GcalContinuum
  arcs: [GcalArc!]!
  filter: GcalFilter!
  diffuser: GcalDiffuser!
  shutter: GcalShutter!
  stepType: StepType!
}

If we define a subquery:

@GraphQL
abstract class GcalStepConfigSubquery extends GraphQLSubquery[ObservationDB]("Gcal"):
  override val subquery: String = """
        {
          continuum
          arcs
          filter
          diffuser
          shutter
        }
      """

@GraphQLStub
object GcalStepConfigSubquery

and then query:

query($$obsId: ObservationId!) {
  observation(observationId: $$obsId) {
    execution {
      executionConfig {
        ... on GmosSouthExecutionConfig {
          acquisition {
            nextAtom {
              steps {
                stepConfig {
                  ... on Gcal $GcalStepConfigSubquery
                }
              }
            }
          }
        }
      }
    }
  }
}

This results in:

[error] scalafix.internal.v1.FileException: unexpected error processing file /Users/rpiaggio/gemini/explore/common-graphql/src/main/scala/queries/common/GeneratedSequenceSQL.scala
[error] Caused by: java.lang.Exception: Could not resolve type for field [subquery10] - Is this a valid field present in the schema?
[error]         at clue.gen.QueryGen.$anonfun$resolveData$9(QueryGen.scala:270)
[error]         at scala.Option.fold(Option.scala:263)
[error]         at clue.gen.QueryGen.clue$gen$QueryGen$$go$1(QueryGen.scala:272)
[error]         at clue.gen.QueryGen$$anonfun$$nestedInanonfun$resolveData$19$1.applyOrElse(QueryGen.scala:327)
[error]         at clue.gen.QueryGen$$anonfun$$nestedInanonfun$resolveData$19$1.applyOrElse(QueryGen.scala:326)
[error]         at scala.collection.immutable.List.collect(List.scala:267)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$19(QueryGen.scala:326)
[error]         at scala.collection.immutable.List.map(List.scala:250)
[error]         at clue.gen.QueryGen.clue$gen$QueryGen$$go$1(QueryGen.scala:323)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$11(QueryGen.scala:274)
[error]         at scala.Option.map(Option.scala:242)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$10(QueryGen.scala:274)
[error]         at scala.Option.fold(Option.scala:263)
[error]         at clue.gen.QueryGen.clue$gen$QueryGen$$go$1(QueryGen.scala:272)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$20(QueryGen.scala:331)
[error]         at scala.collection.immutable.List.map(List.scala:250)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$19(QueryGen.scala:331)
[error]         at scala.collection.immutable.List.map(List.scala:246)
[error]         at clue.gen.QueryGen.clue$gen$QueryGen$$go$1(QueryGen.scala:323)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$11(QueryGen.scala:274)
[error]         at scala.Option.map(Option.scala:242)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$10(QueryGen.scala:274)
[error]         at scala.Option.fold(Option.scala:263)
[error]         at clue.gen.QueryGen.clue$gen$QueryGen$$go$1(QueryGen.scala:272)
[error]         at clue.gen.QueryGen$$anonfun$$nestedInanonfun$resolveData$19$1.applyOrElse(QueryGen.scala:327)
[error]         at clue.gen.QueryGen$$anonfun$$nestedInanonfun$resolveData$19$1.applyOrElse(QueryGen.scala:326)
[error]         at scala.collection.immutable.List.collect(List.scala:275)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$19(QueryGen.scala:326)
[error]         at scala.collection.immutable.List.map(List.scala:246)
[error]         at clue.gen.QueryGen.clue$gen$QueryGen$$go$1(QueryGen.scala:323)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$11(QueryGen.scala:274)
[error]         at scala.Option.map(Option.scala:242)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$10(QueryGen.scala:274)
[error]         at scala.Option.fold(Option.scala:263)
[error]         at clue.gen.QueryGen.clue$gen$QueryGen$$go$1(QueryGen.scala:272)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$20(QueryGen.scala:331)
[error]         at scala.collection.immutable.List.map(List.scala:246)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$19(QueryGen.scala:331)
[error]         at scala.collection.immutable.List.map(List.scala:246)
[error]         at clue.gen.QueryGen.clue$gen$QueryGen$$go$1(QueryGen.scala:323)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$11(QueryGen.scala:274)
[error]         at scala.Option.map(Option.scala:242)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$10(QueryGen.scala:274)
[error]         at scala.Option.fold(Option.scala:263)
[error]         at clue.gen.QueryGen.clue$gen$QueryGen$$go$1(QueryGen.scala:272)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$20(QueryGen.scala:331)
[error]         at scala.collection.immutable.List.map(List.scala:250)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$19(QueryGen.scala:331)
[error]         at scala.collection.immutable.List.map(List.scala:246)
[error]         at clue.gen.QueryGen.clue$gen$QueryGen$$go$1(QueryGen.scala:323)
[error]         at clue.gen.QueryGen$$anonfun$$nestedInanonfun$resolveData$19$1.applyOrElse(QueryGen.scala:327)
[error]         at clue.gen.QueryGen$$anonfun$$nestedInanonfun$resolveData$19$1.applyOrElse(QueryGen.scala:326)
[error]         at scala.collection.immutable.List.collect(List.scala:267)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$19(QueryGen.scala:326)
[error]         at scala.collection.immutable.List.map(List.scala:250)
[error]         at clue.gen.QueryGen.clue$gen$QueryGen$$go$1(QueryGen.scala:323)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$11(QueryGen.scala:274)
[error]         at scala.Option.map(Option.scala:242)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$10(QueryGen.scala:274)
[error]         at scala.Option.fold(Option.scala:263)
[error]         at clue.gen.QueryGen.clue$gen$QueryGen$$go$1(QueryGen.scala:272)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$11(QueryGen.scala:274)
[error]         at scala.Option.map(Option.scala:242)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$10(QueryGen.scala:274)
[error]         at scala.Option.fold(Option.scala:263)
[error]         at clue.gen.QueryGen.clue$gen$QueryGen$$go$1(QueryGen.scala:272)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$11(QueryGen.scala:274)
[error]         at scala.Option.map(Option.scala:242)
[error]         at clue.gen.QueryGen.$anonfun$resolveData$10(QueryGen.scala:274)
[error]         at scala.Option.fold(Option.scala:263)
[error]         at clue.gen.QueryGen.clue$gen$QueryGen$$go$1(QueryGen.scala:272)
[error]         at clue.gen.QueryGen.resolveData(QueryGen.scala:376)
[error]         at clue.gen.QueryGen.resolveData$(QueryGen.scala:220)
[error]         at clue.gen.GraphQLGen.resolveData(GraphQLGen.scala:15)
[error]         at clue.gen.QueryGen.$anonfun$addData$1(QueryGen.scala:416)
[error]         at scala.Function$.$anonfun$chain$2(Function.scala:23)
[error]         at scala.collection.LinearSeqOps.foldLeft(LinearSeq.scala:183)
[error]         at scala.collection.LinearSeqOps.foldLeft$(LinearSeq.scala:179)
[error]         at scala.collection.immutable.List.foldLeft(List.scala:79)
[error]         at scala.Function$.$anonfun$chain$1(Function.scala:23)
[error]         at clue.gen.GraphQLGen$$anonfun$2.$anonfun$applyOrElse$8(GraphQLGen.scala:138)
[error]         at apply @ clue.gen.GraphQLGen$$anonfun$2.$anonfun$applyOrElse$7(GraphQLGen.scala:115)
[error]         at >> @ clue.gen.GraphQLGenConfig.$anonfun$retrieveSchema$11(GraphQLGenConfig.scala:70)
[error]         at complete @ clue.gen.GraphQLGenConfig.$anonfun$getSchema$3(GraphQLGenConfig.scala:84)
[error]         at >> @ clue.gen.GraphQLGenConfig.$anonfun$retrieveSchema$11(GraphQLGenConfig.scala:70)
[error]         at apply @ clue.gen.GraphQLGenConfig.$anonfun$retrieveSchema$9(GraphQLGenConfig.scala:56)
[error]         at flatten @ clue.gen.GraphQLGenConfig.$anonfun$getSchema$1(GraphQLGenConfig.scala:87)
[error]         at flatTap @ clue.gen.GraphQLGenConfig.$anonfun$getSchema$2(GraphQLGenConfig.scala:84)
[error] (graphql / Compile / scalafix) scalafix.sbt.ScalafixFailed: UnexpectedError

"Spec" compliance

I'm using "spec" here very loosely ๐Ÿ˜… in order of specy-ness:

  1. https://spec.graphql.org/June2018/
  2. https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md
  3. https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md
    Note that apollographql/subscriptions-transport-ws is archived and points to there instead.
  4. https://github.com/enisdenjo/graphql-sse/blob/master/PROTOCOL.md

I still have a lot of reading to do since I'm new to GraphQL. Some of my notes so far:

  • the client interfaces for query/mutation/subscription should use only concepts from (1); e.g. the core model should not be tied to the http spec or notions of persistent connections. It seems ok to use circe.Json as an intermediary format since that's pretty close to what the spec describes
    // Request format from Spec: https://github.com/APIs-guru/graphql-over-http
  • backends for these clients should probably be implemented directly in terms of http4s (ws-)clients according to (2), (3), (4); this does not rule out common abstractions for sharing of code, but it seems like that should remain an internal implementation detail since these specs may evolve independently
  • special functionality to reconnect/recreate subscriptions sounds like middleware territory to me

extract extensions in errors

When getting extensions like:

{
    "errors": [
        {
            "message": "Source too bright, well half filled in 0.76 seconds",
            "extension": {
                "errorCode": "SOURCE_TOO_BRIGHT",
                "error": {
                    "halfWell": 0.76
                }
            }
        }
    ],
    "data": null
}

They don't seem to show up on the error results instead getting:

NonEmptyList(GraphQLError(Source too bright, well half filled in 0.76 seconds,None,None,None))

Reusable fragments

It can be quite common to perform the same subquery in multiple queries. It's therefore desirable to be able to abstract this subquery away and be able to include it into different queries for reuse (and reuse of the typeclass instances of the result class - which we may already have if we're mapping into an external type).

I imagine something along the lines of:

trait SubQuery[A] {
  def subDocument: String

  implicit def jsonDecoder: Decoder[A]
}

And a string interpolator to insert the subDocument in a Query.

An example of usage would be:

object TargetSubQuery extends SubQuery[Target] {
  val subDocument = """
    id
    name
    magnitudes {
      band
      etc
    }
  """
}

And then somewhere else, in a Query:

val obsQueryDocument = gql"""
  query {
    observations {
      targets {
        $TargetSubQuery
      }
    }
  }
"""

The biggest choke point I see with this is how to get grackle to process obsQueryDocument. We could trick it somehow. Otherwise we would have to process the interpolated string within the scalafix rule, which means evaluating it, which I don't know how to do (maybe we could imitate mdoc?). But I think that other than that, we can get away with generating a reference to an external type (Target in this case, obtained from the SubQuery) and resolving the interpolated string only at runtime.

Don't close subscription on error

Upon receiving an error message on a subscription, we are currently terminating it.

This goes contrary to the spec.

The way around this would be to provide a Stream[Either[Error, A]] instead of a Stream[A]. Actually, it'd be nice to have methods that support both.

Scala.js classes for results?

In order to reduce the size of Scala.js code, we could add of the option of generating native Scala.js traits/classes to hold query results instead of Scala case classes.

Ideally, these would behave as facades into the JS object of the result, using js.Array instead of List, js.UndefOr instead of Option, etc. We wouldn't even need a Decoder, which means revising the base Query type.

Things that may be tricky:

  • Generating Reusability, Eq and Show instances (we do have all the info to generate them though).
  • Generating lenses. Again, we have everything to generate them, but we would have to clone objects, I don't think the Focus macro would work.

It remains to be seen how much of an improvement this would be, in order to evaluate it it's worth the effort.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.