From d0e0ae8db022dd7cd161f581a3ca2ad9da3d77d3 Mon Sep 17 00:00:00 2001
From: Arman Bilge
Date: Mon, 17 Apr 2023 17:29:52 +0000
Subject: [PATCH 1/3] wip self-contained subqueries
---
.../main/scala/test/StarWarsSubquery2.scala | 1 -
.../src/main/scala/clue/gen/GraphQLGen.scala | 71 ++++++++++++-------
.../src/main/scala/clue/gen/QueryGen.scala | 2 +-
3 files changed, 45 insertions(+), 29 deletions(-)
diff --git a/gen/output/src/main/scala/test/StarWarsSubquery2.scala b/gen/output/src/main/scala/test/StarWarsSubquery2.scala
index 8925adb1..03c81999 100644
--- a/gen/output/src/main/scala/test/StarWarsSubquery2.scala
+++ b/gen/output/src/main/scala/test/StarWarsSubquery2.scala
@@ -9,7 +9,6 @@ import clue.GraphQLSubquery
import io.circe.Json
import test.StarWars
-
object StarWarsSubquery2 extends GraphQLSubquery.Typed[StarWars, Json]("Character") {
override val subquery: String = """
{
diff --git a/gen/rules/src/main/scala/clue/gen/GraphQLGen.scala b/gen/rules/src/main/scala/clue/gen/GraphQLGen.scala
index e6e2adb0..ef060b58 100644
--- a/gen/rules/src/main/scala/clue/gen/GraphQLGen.scala
+++ b/gen/rules/src/main/scala/clue/gen/GraphQLGen.scala
@@ -32,6 +32,37 @@ class GraphQLGen(config: GraphQLGenConfig)
newLineIndent + lines.replaceAll("\\n", newLineIndent)
}
+ private object GraphQLAnnotated {
+ def unapply(tree: Tree): Option[(List[Mod], String, Template)] = tree match {
+ case Defn.Trait(
+ mods @ GraphQLAnnotation(_),
+ templateName,
+ Nil,
+ _,
+ template
+ ) =>
+ Some((mods, templateName.value, template))
+
+ case Defn.Class(
+ mods @ GraphQLAnnotation(_),
+ templateName,
+ Nil,
+ _,
+ template
+ ) =>
+ Some((mods, templateName.value, template))
+
+ case Defn.Object(
+ mods @ GraphQLAnnotation(_),
+ name,
+ template
+ ) =>
+ Some((mods, name.value, template))
+
+ case _ => None
+ }
+ }
+
override def fix(implicit doc: SemanticDocument): Patch = {
val importPatch: List[IO[Patch]] =
doc.tokens.collect {
@@ -42,18 +73,6 @@ class GraphQLGen(config: GraphQLGenConfig)
val genPatch: List[IO[Patch]] =
doc.tree
.collect {
- case obj @ Defn.Object(
- GraphQLAnnotation(_),
- name,
- template
- ) => // Annotated objects are copied as-is
- // TODO: We should be able to validate the query!
- IO.pure(
- Patch.replaceTree(
- obj,
- indented(obj)(q"object $name $template".toString)
- ) + Patch.removeGlobalImport(GraphQLAnnotation.symbol)
- )
case obj @ Defn.Object(
GraphQLStubAnnotation(_),
_,
@@ -87,18 +106,14 @@ class GraphQLGen(config: GraphQLGenConfig)
)
) + Patch.removeGlobalImport(GraphQLSchemaAnnotation.symbol)
}
- case obj @ Defn.Trait(
- mods @ GraphQLAnnotation(_),
- templateName,
- Nil,
- _,
+ case obj @ GraphQLAnnotated(
+ mods,
+ objName,
Template(early, inits, self, stats)
) if inits.exists {
case Init(Type.Apply(Type.Name("GraphQLOperation"), _), _, _) => true
case _ => false
} =>
- val objName = templateName.value
-
extractSchemaType(inits) match {
case None =>
abort(
@@ -155,18 +170,20 @@ class GraphQLGen(config: GraphQLGenConfig)
}
}
}
- case obj @ Defn.Class(
+ case obj @ GraphQLAnnotated(
mods @ GraphQLAnnotation(_),
- templateName,
- Nil,
- _,
+ objName,
Template(early, inits, self, stats)
) if inits.exists {
- case Init(Type.Apply(Type.Name("GraphQLSubquery"), _), _, _) => true
- case _ => false
+ case Init(
+ Type.Apply(Type.Name("GraphQLSubquery"), _) |
+ Type.Apply(Type.Select(Term.Name("GraphQLSubquery"), Type.Name("Typed")), _),
+ _,
+ _
+ ) =>
+ true
+ case _ => false
} =>
- val objName = templateName.value
-
extractSchemaAndRootTypes(inits) match {
case None =>
abort(
diff --git a/gen/rules/src/main/scala/clue/gen/QueryGen.scala b/gen/rules/src/main/scala/clue/gen/QueryGen.scala
index 42abfe2c..c657c33f 100644
--- a/gen/rules/src/main/scala/clue/gen/QueryGen.scala
+++ b/gen/rules/src/main/scala/clue/gen/QueryGen.scala
@@ -27,7 +27,7 @@ trait QueryGen extends Generator {
protected def extractSchemaAndRootTypes(list: List[Init]): Option[(Type.Name, String)] =
list.collect {
case Init(
- Type.Apply(Type.Name("GraphQLSubquery"), List(tpe @ Type.Name(_))),
+ Type.Apply(_, (tpe @ Type.Name(_)) :: _),
_,
List(List(Lit.String(rootType)))
) =>
From d7df34dbddbaec3db52ab03efba8818dc329383c Mon Sep 17 00:00:00 2001
From: Arman Bilge
Date: Wed, 26 Apr 2023 17:59:41 +0000
Subject: [PATCH 2/3] Validated typed operations and subqueries
---
.../main/scala/test/StarWarsSubquery2.scala | 1 +
.../src/main/scala/clue/gen/GraphQLGen.scala | 94 +++++++++++++------
2 files changed, 67 insertions(+), 28 deletions(-)
diff --git a/gen/output/src/main/scala/test/StarWarsSubquery2.scala b/gen/output/src/main/scala/test/StarWarsSubquery2.scala
index 03c81999..8925adb1 100644
--- a/gen/output/src/main/scala/test/StarWarsSubquery2.scala
+++ b/gen/output/src/main/scala/test/StarWarsSubquery2.scala
@@ -9,6 +9,7 @@ import clue.GraphQLSubquery
import io.circe.Json
import test.StarWars
+
object StarWarsSubquery2 extends GraphQLSubquery.Typed[StarWars, Json]("Character") {
override val subquery: String = """
{
diff --git a/gen/rules/src/main/scala/clue/gen/GraphQLGen.scala b/gen/rules/src/main/scala/clue/gen/GraphQLGen.scala
index ef060b58..e92fdedf 100644
--- a/gen/rules/src/main/scala/clue/gen/GraphQLGen.scala
+++ b/gen/rules/src/main/scala/clue/gen/GraphQLGen.scala
@@ -111,8 +111,14 @@ class GraphQLGen(config: GraphQLGenConfig)
objName,
Template(early, inits, self, stats)
) if inits.exists {
- case Init(Type.Apply(Type.Name("GraphQLOperation"), _), _, _) => true
- case _ => false
+ case Init(
+ Type.Apply(Type.Name("GraphQLOperation"), _) |
+ Type.Apply(Type.Select(Term.Name("GraphQLOperation"), Type.Name("Typed")), _),
+ _,
+ _
+ ) =>
+ true
+ case _ => false
} =>
extractSchemaType(inits) match {
case None =>
@@ -141,17 +147,33 @@ class GraphQLGen(config: GraphQLGenConfig)
) >> IO {
val operation = queryResult.toOption.get
- // Modifications to add the missing definitions.
- val modObjDefs = scala.Function.chain(
- List(
- addImports(schemaType.value),
- addVars(schema, operation, config),
- addData(schema, operation, config, document.subqueries),
- addVarEncoder,
- addDataDecoder,
- addConvenienceMethod(schemaType, operation, objName)
- )
- )
+ val typed = inits.exists {
+ case Init(
+ Type.Apply(
+ Type.Select(Term.Name("GraphQLOperation"), Type.Name("Typed")),
+ _
+ ),
+ _,
+ _
+ ) =>
+ true
+ case _ => false
+ }
+
+ val modObjDefs =
+ if (typed) // everything is already defined
+ identity[List[Stat]](_)
+ else // Modifications to add the missing definitions.
+ scala.Function.chain(
+ List(
+ addImports(schemaType.value),
+ addVars(schema, operation, config),
+ addData(schema, operation, config, document.subqueries),
+ addVarEncoder,
+ addDataDecoder,
+ addConvenienceMethod(schemaType, operation, objName)
+ )
+ )
val newMods = GraphQLAnnotation.removeFrom(mods)
@@ -211,21 +233,37 @@ class GraphQLGen(config: GraphQLGenConfig)
) >> IO {
val operation = queryResult.toOption.get
- // Modifications to add the missing definitions.
- val modObjDefs = scala.Function.chain(
- List(
- addImports(schemaType.value),
- addData(
- schema,
- operation,
- config,
- subquery.subqueries,
- schema.types.find(_.name == rootTypeName)
- ),
- addDataDecoder,
- addConvenienceMethod(schemaType, operation, objName)
- )
- )
+ val typed = inits.exists {
+ case Init(
+ Type.Apply(
+ Type.Select(Term.Name("GraphQLSubquery"), Type.Name("Typed")),
+ _
+ ),
+ _,
+ _
+ ) =>
+ true
+ case _ => false
+ }
+
+ val modObjDefs =
+ if (typed) // everything is already defined
+ identity[List[Stat]](_)
+ else // Modifications to add the missing definitions.
+ scala.Function.chain(
+ List(
+ addImports(schemaType.value),
+ addData(
+ schema,
+ operation,
+ config,
+ subquery.subqueries,
+ schema.types.find(_.name == rootTypeName)
+ ),
+ addDataDecoder,
+ addConvenienceMethod(schemaType, operation, objName)
+ )
+ )
val newMods = GraphQLAnnotation.removeFrom(mods).filterNot {
case Mod.Abstract() => true
From 55aff20d532f8fbcadc7a525f6aca6210285a8ae Mon Sep 17 00:00:00 2001
From: Arman Bilge
Date: Wed, 26 Apr 2023 18:14:52 +0000
Subject: [PATCH 3/3] DRY
---
.../src/main/scala/clue/gen/GraphQLGen.scala | 88 +++++++++----------
1 file changed, 43 insertions(+), 45 deletions(-)
diff --git a/gen/rules/src/main/scala/clue/gen/GraphQLGen.scala b/gen/rules/src/main/scala/clue/gen/GraphQLGen.scala
index e92fdedf..f44a525d 100644
--- a/gen/rules/src/main/scala/clue/gen/GraphQLGen.scala
+++ b/gen/rules/src/main/scala/clue/gen/GraphQLGen.scala
@@ -63,6 +63,44 @@ class GraphQLGen(config: GraphQLGenConfig)
}
}
+ private def isGraphQLOperation(inits: List[Init]) =
+ inits.exists {
+ case Init(Type.Apply(Type.Name("GraphQLOperation"), _), _, _) => true
+ case _ => false
+ }
+
+ private def isGraphQLOperationTyped(inits: List[Init]) =
+ inits.exists {
+ case Init(Type.Apply(
+ Type.Select(Term.Name("GraphQLOperation"), Type.Name("Typed")),
+ _
+ ),
+ _,
+ _
+ ) =>
+ true
+ case _ => false
+ }
+
+ private def isGraphQLSubquery(inits: List[Init]) =
+ inits.exists {
+ case Init(Type.Apply(Type.Name("GraphQLSubquery"), _), _, _) => true
+ case _ => false
+ }
+
+ private def isGraphQLSubqueryTyped(inits: List[Init]) =
+ inits.exists {
+ case Init(Type.Apply(
+ Type.Select(Term.Name("GraphQLSubquery"), Type.Name("Typed")),
+ _
+ ),
+ _,
+ _
+ ) =>
+ true
+ case _ => false
+ }
+
override def fix(implicit doc: SemanticDocument): Patch = {
val importPatch: List[IO[Patch]] =
doc.tokens.collect {
@@ -110,16 +148,7 @@ class GraphQLGen(config: GraphQLGenConfig)
mods,
objName,
Template(early, inits, self, stats)
- ) if inits.exists {
- case Init(
- Type.Apply(Type.Name("GraphQLOperation"), _) |
- Type.Apply(Type.Select(Term.Name("GraphQLOperation"), Type.Name("Typed")), _),
- _,
- _
- ) =>
- true
- case _ => false
- } =>
+ ) if isGraphQLOperation(inits) || isGraphQLOperationTyped(inits) =>
extractSchemaType(inits) match {
case None =>
abort(
@@ -147,23 +176,12 @@ class GraphQLGen(config: GraphQLGenConfig)
) >> IO {
val operation = queryResult.toOption.get
- val typed = inits.exists {
- case Init(
- Type.Apply(
- Type.Select(Term.Name("GraphQLOperation"), Type.Name("Typed")),
- _
- ),
- _,
- _
- ) =>
- true
- case _ => false
- }
+ val typed = isGraphQLOperationTyped(inits)
val modObjDefs =
if (typed) // everything is already defined
identity[List[Stat]](_)
- else // Modifications to add the missing definitions.
+ else // Modifications to add the missing definitions.
scala.Function.chain(
List(
addImports(schemaType.value),
@@ -196,16 +214,7 @@ class GraphQLGen(config: GraphQLGenConfig)
mods @ GraphQLAnnotation(_),
objName,
Template(early, inits, self, stats)
- ) if inits.exists {
- case Init(
- Type.Apply(Type.Name("GraphQLSubquery"), _) |
- Type.Apply(Type.Select(Term.Name("GraphQLSubquery"), Type.Name("Typed")), _),
- _,
- _
- ) =>
- true
- case _ => false
- } =>
+ ) if isGraphQLSubquery(inits) || isGraphQLSubqueryTyped(inits) =>
extractSchemaAndRootTypes(inits) match {
case None =>
abort(
@@ -233,18 +242,7 @@ class GraphQLGen(config: GraphQLGenConfig)
) >> IO {
val operation = queryResult.toOption.get
- val typed = inits.exists {
- case Init(
- Type.Apply(
- Type.Select(Term.Name("GraphQLSubquery"), Type.Name("Typed")),
- _
- ),
- _,
- _
- ) =>
- true
- case _ => false
- }
+ val typed = isGraphQLSubqueryTyped(inits)
val modObjDefs =
if (typed) // everything is already defined