Repository navigation
Fix __typename at the query root
#964
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -55,6 +55,35 @@ final class QueryDirectivesSuite extends CatsEffectSuite { | |
| assertIO(res, expected) | ||
| } | ||
|
|
||
| test("query with cursor transforming directive on __typename") { | ||
| val query = """ | ||
| query { | ||
| __typename @upperCase | ||
| user { | ||
| __typename @upperCase | ||
| name @upperCase | ||
| } | ||
| } | ||
| """ | ||
|
|
||
| // The cursor transform rewrites field values, not the type name | ||
| val expected = json""" | ||
| { | ||
| "data" : { | ||
| "__typename" : "Query", | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like the directive has been silently ignored here ... I'm pretty sure on main it would have been an error.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On main this would fail (in nested fields) with
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, but I think we should still fail here in the same way as on main. |
||
| "user" : { | ||
| "__typename" : "User", | ||
| "name" : "MARY" | ||
| } | ||
| } | ||
| } | ||
| """ | ||
|
|
||
| val res = QueryDirectivesMapping.compileAndRun(query) | ||
|
|
||
| assertIO(res, expected) | ||
| } | ||
|
|
||
| test("query with directive (1)") { | ||
| val query = """ | ||
| query { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ import io.circe.literal._ | |
| import munit.CatsEffectSuite | ||
|
|
||
| import grackle._ | ||
| import grackle.Query._ | ||
| import grackle.QueryCompiler.IntrospectionLevel._ | ||
| import grackle.syntax._ | ||
|
|
||
|
|
@@ -1762,6 +1763,102 @@ final class IntrospectionSuite extends CatsEffectSuite { | |
| assertIO(res, expected) | ||
| } | ||
|
|
||
| test("typename query at the root") { | ||
| val query = """ | ||
| { | ||
| __typename | ||
| } | ||
| """ | ||
|
|
||
| val expected = json""" | ||
| { | ||
| "data" : { | ||
| "__typename" : "QueryRoot" | ||
| } | ||
| } | ||
| """ | ||
|
|
||
| val mapping = new RenamedRootMapping | ||
| val res = mapping.compileAndRun(query).map(json => (json, mapping.rootCursorFieldNames.get)) | ||
|
|
||
| // Only `__typename`, so the mapping's root cursor should not be built | ||
| assertIO(res, (expected, Nil)) | ||
| } | ||
|
|
||
| test("typename query at the root with other root fields") { | ||
| val query = """ | ||
| { | ||
| __typename | ||
| foo | ||
| __schema { | ||
| queryType { | ||
| name | ||
| } | ||
| } | ||
| } | ||
| """ | ||
|
|
||
| val expected = json""" | ||
| { | ||
| "data" : { | ||
| "__typename" : "QueryRoot", | ||
| "foo" : 0, | ||
| "__schema" : { | ||
| "queryType" : { | ||
| "name" : "QueryRoot" | ||
| } | ||
| } | ||
| } | ||
| } | ||
| """ | ||
|
|
||
| val mapping = new RenamedRootMapping | ||
| val res = mapping.compileAndRun(query).map(json => (json, mapping.rootCursorFieldNames.get)) | ||
|
|
||
| // The mapping's root cursor should only be asked for `foo` | ||
| assertIO(res, (expected, List("foo"))) | ||
| } | ||
|
|
||
| test("typename query at the mutation root") { | ||
| val query = """ | ||
| mutation { | ||
| __typename | ||
| } | ||
| """ | ||
|
|
||
| val expected = json""" | ||
| { | ||
| "data" : { | ||
| "__typename" : "MutationRoot" | ||
| } | ||
| } | ||
| """ | ||
|
|
||
| val res = (new RenamedRootMapping).compileAndRun(query) | ||
|
|
||
| assertIO(res, expected) | ||
| } | ||
|
|
||
| test("aliased typename query at the root") { | ||
| val query = """ | ||
| { | ||
| t: __typename | ||
| } | ||
| """ | ||
|
|
||
| val expected = json""" | ||
| { | ||
| "data" : { | ||
| "t" : "QueryRoot" | ||
| } | ||
| } | ||
| """ | ||
|
|
||
| val res = (new RenamedRootMapping).compileAndRun(query) | ||
|
|
||
| assertIO(res, expected) | ||
| } | ||
|
|
||
| test("typename query with narrowing") { | ||
| val query = """ | ||
| { | ||
|
|
@@ -2255,3 +2352,84 @@ object DescribedMapping extends ValueMapping[IO] { | |
| ) | ||
| ) | ||
| } | ||
|
|
||
| class RenamedRootMapping extends ValueMapping[IO] { | ||
| val schema = | ||
| schema""" | ||
| schema { | ||
| query: QueryRoot | ||
| mutation: MutationRoot | ||
| } | ||
| type QueryRoot { | ||
| foo: Int! | ||
| } | ||
| type MutationRoot { | ||
| baz: Int! | ||
| } | ||
| """ | ||
|
|
||
| val QueryType = schema.queryType | ||
| val MutationType = schema.ref("MutationRoot") | ||
|
|
||
| // The names of the root fields passed to `defaultRootCursor`, in call order | ||
| val rootCursorFieldNames = new java.util.concurrent.atomic.AtomicReference(List.empty[String]) | ||
|
|
||
| override def defaultRootCursor( | ||
| query: Query, | ||
| tpe: Type, | ||
| parentCursor: Option[Cursor]): IO[Result[(Query, Cursor)]] = | ||
| IO(rootCursorFieldNames.updateAndGet(_ ++ ungroup(query).map(q => | ||
| rootName(q).fold(q.render)(_._1)))) *> | ||
| super.defaultRootCursor(query, tpe, parentCursor) | ||
|
|
||
| val typeMappings = | ||
| List( | ||
| ObjectMapping( | ||
| tpe = QueryType, | ||
| fieldMappings = List( | ||
| ValueField.fromValue("foo", 0) | ||
| ) | ||
| ), | ||
| ObjectMapping( | ||
| tpe = MutationType, | ||
| fieldMappings = List( | ||
| RootEffect.computeCursor("baz")((p, e) => IO.pure(Result(valueCursor(p, e, 1)))) | ||
| ) | ||
| ) | ||
| ) | ||
| } | ||
|
|
||
| class TypenameShapesMapping extends ValueMapping[IO] { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this actually used? |
||
| val schema = | ||
| schema""" | ||
| type Query { | ||
| foo: Int! | ||
| bar: Bar! | ||
| } | ||
| type Bar { | ||
| baz: Int! | ||
| } | ||
| """ | ||
|
|
||
| val QueryType = schema.queryType | ||
| val BarType = schema.ref("Bar") | ||
|
|
||
| val typeMappings = | ||
| List( | ||
| ObjectMapping( | ||
| tpe = QueryType, | ||
| fieldMappings = List( | ||
| ValueField.fromValue("foo", 0), | ||
| ValueField.fromValue("bar", ()) | ||
| ) | ||
| ), | ||
| ObjectMapping( | ||
| tpe = BarType, | ||
| fieldMappings = List( | ||
| ValueField.fromValue("baz", 1) | ||
| ) | ||
| ) | ||
| ) | ||
| } | ||
|
|
||
| object TypenameShapesMapping extends TypenameShapesMapping | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In think this is going to mean that
__typenamealways appears first in the result. eg. suppose we had{ foo __typename }instead of{ __typename foo }. I think there maybe similar pre-existing issues for non-top-level uses, but I'm pretty sure this is introducing a new instance.