Skip to content

Commit 636b2bd

Browse files
authored
Fix fragment validation when a field is both in a fragment and outside a fragment (#2491)
* Fix fragment validation when a field is both in a fragment and outside a fragment * Recursive schema
1 parent 64e1cf4 commit 636b2bd

File tree

3 files changed

+65
-3
lines changed

3 files changed

+65
-3
lines changed

core/src/main/scala/caliban/validation/FragmentValidator.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ object FragmentValidator {
2121
val parentsCache = mutable.Map.empty[Int, Chunk[String]]
2222
val groupsCache = mutable.Map.empty[Int, Chunk[Set[SelectedField]]]
2323

24-
def sameResponseShapeByName(set: Iterable[Selection]): Chunk[String] = {
24+
def sameResponseShapeByName(set: Iterable[Selection], parentType: __Type): Chunk[String] = {
2525
val keyHash = MurmurHash3.unorderedHash(set)
2626
shapeCache.get(keyHash) match {
2727
case Some(value) => value
@@ -35,7 +35,7 @@ object FragmentValidator {
3535
.getOrElse("")}.${f2.fieldDef.name}. Try using an alias."
3636
)
3737
} else
38-
sameResponseShapeByName(f1.selection.selectionSet ::: f2.selection.selectionSet)
38+
sameResponseShapeByName(f1.selection.selectionSet ::: f2.selection.selectionSet, f1.fieldDef._type)
3939
}
4040
})
4141
shapeCache.update(keyHash, res)
@@ -120,7 +120,7 @@ object FragmentValidator {
120120
}
121121
}
122122

123-
val conflicts = sameResponseShapeByName(selectionSet) ++ sameForCommonParentsByName(selectionSet)
123+
val conflicts = sameResponseShapeByName(selectionSet, parentType) ++ sameForCommonParentsByName(selectionSet)
124124
if (conflicts.nonEmpty) {
125125
Left(ValidationError(conflicts.head, ""))
126126
} else {
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package caliban
2+
3+
import caliban.schema.Annotations._
4+
5+
object FragmentSchema {
6+
7+
@GQLDescription("Queries")
8+
case class Query(
9+
bar: Option[Bar],
10+
foo: List[Foo]
11+
)
12+
13+
case class Foo(
14+
id: String
15+
)
16+
case class Bar(
17+
id: String,
18+
baz: Option[Baz]
19+
)
20+
21+
case class Baz(
22+
id: String,
23+
foo: Option[Foo]
24+
)
25+
26+
val resolverFooBar: RootResolver[Query, Unit, Unit] = RootResolver(
27+
Query(
28+
bar = Some(Bar("1", Some(Baz("2", Some(Foo("3")))))),
29+
foo = List(Foo("1"), Foo("2"))
30+
)
31+
)
32+
}

core/src/test/scala/caliban/execution/FragmentSpec.scala

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import caliban.Value.StringValue
88
import caliban.schema.Annotations.GQLDefault
99
import caliban.schema.Schema.auto._
1010
import caliban.schema.ArgBuilder.auto._
11+
import caliban.schema.Schema
1112
import zio.test.Assertion._
1213
import zio.test._
1314

@@ -50,6 +51,35 @@ object FragmentSpec extends ZIOSpecDefault {
5051
res <- int.execute(query)
5152
} yield assertTrue(res.data.toString == """{"amos":{"name":"Amos Burton"}}""")
5253
},
54+
test("fragment with inner and outer") {
55+
implicit lazy val bazSchema: Schema[Any, FragmentSchema.Baz] = Schema.gen
56+
57+
val interpreter = graphQL(FragmentSchema.resolverFooBar).interpreter
58+
val query = gqldoc("""
59+
query {
60+
bar {
61+
...Outer
62+
}
63+
}
64+
fragment Outer on Bar {
65+
baz {
66+
foo {
67+
id
68+
}
69+
...Inner
70+
}
71+
}
72+
fragment Inner on Baz {
73+
foo {
74+
id
75+
}
76+
}
77+
""")
78+
for {
79+
int <- interpreter
80+
res <- int.execute(query)
81+
} yield assertTrue(res.errors.isEmpty)
82+
},
5383
test("fragment on union") {
5484
val query = gqldoc("""
5585
{

0 commit comments

Comments
 (0)