Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reusable fragments #275

Open
rpiaggio opened this issue Oct 29, 2021 · 1 comment
Open

Reusable fragments #275

rpiaggio opened this issue Oct 29, 2021 · 1 comment

Comments

@rpiaggio
Copy link
Collaborator

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.

@rpiaggio rpiaggio changed the title Subqueries? Reusable fragments Nov 4, 2021
@rpiaggio
Copy link
Collaborator Author

rpiaggio commented Nov 4, 2021

I remembered that GraphQL supports fragments.

So what we probably want to do here is to be able to define reusable fragments (which are schema-bound):

trait GraphQLFragment[S] {
  def subDocument: String

  type Data

  implicit def jsonDecoder: Decoder[Data]
}

Fragment definition would be of the form:

@GraphQL
object TargetFragment extends GraphQLFragment[Target] {
  val subDocument = """
  | fragment targetBaseFields on Target {
  |   id
  |   name
  |   magnitudes {
  |     band
  |     etc
  |   }
  | }
  """.stripMargin
}

And usage something like:

 val obsQueryDocument = """
 | query {
 |   observations {
 |     targets {
 |       $TargetFragment
 |     }
 |   }
 | }
 """.stripMargin

Challenges:

  1. Type-check and generate output types for fragments (will grackle do this just for fragments?).
  2. Extract the fragment name (should be easy, could be generated into a val in the GraphQLFragment.
  3. As mentioned before, interpolating the string in scalafix. It should end up being a string that:
    a) has ...fragmentName in lieu of $Fragment.
    b) has the fragment document concatenated at the end.
    So, maybe interpolation is not the right choice?
  4. Inserting the type alias to $Fragment.Data in the correct position within the generated types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant