Skip to content

Commit 2b16b4c

Browse files
authored
Fix fragment merge issue (#2558)
1 parent 09e5fa2 commit 2b16b4c

File tree

2 files changed

+59
-14
lines changed

2 files changed

+59
-14
lines changed

core/src/main/scala/caliban/execution/Field.scala

+17-14
Original file line numberDiff line numberDiff line change
@@ -46,22 +46,25 @@ case class Field(
4646
private[caliban] val aliasedName: String =
4747
if (alias.isEmpty) name else alias.get
4848

49-
// TODO: Change the name to `allConditionsUnique` in the next minor version
50-
private[caliban] def allFieldsUniqueNameAndCondition: Boolean =
51-
fields.isEmpty || fields.tail.isEmpty || allConditionsUniqueLazy
52-
53-
private lazy val allConditionsUniqueLazy: Boolean = {
54-
val headCondition = fields.head._condition
55-
var rem = fields.tail
56-
var res = true
57-
val nil = Nil
58-
while ((rem ne nil) && res) {
59-
val f = rem.head
60-
if (f._condition == headCondition)
49+
private[caliban] lazy val allFieldsUniqueNameAndCondition: Boolean = {
50+
def inner(fields: List[Field]): Boolean = {
51+
val headCondition = fields.head._condition
52+
53+
val seen = new mutable.HashSet[String]
54+
seen.add(fields.head.aliasedName)
55+
56+
var rem = fields.tail
57+
while (rem ne Nil) {
58+
val f = rem.head
59+
val continue = seen.add(f.aliasedName) && f._condition == headCondition
60+
if (!continue) return false
6161
rem = rem.tail
62-
else res = false
62+
}
63+
true
6364
}
64-
res
65+
66+
val fields0 = fields
67+
fields0.isEmpty || fields0.tail.isEmpty || inner(fields0)
6568
}
6669

6770
def combine(other: Field): Field =

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

+42
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,48 @@ object FragmentSpec extends ZIOSpecDefault {
200200
isSome(anything)
201201
)
202202
},
203+
test("nested fragment selection on the same type") {
204+
import caliban.schema.Schema.auto._
205+
206+
case class A(valueA: String, valueC: String)
207+
case class B(valueB: String)
208+
case class Foo(a: A, b: B)
209+
case class Query(foo: Foo)
210+
211+
val api = graphQL(RootResolver(Query(Foo(A("a", "c"), B("b")))))
212+
213+
val q = gqldoc("""
214+
fragment FragA on Query {
215+
foo {
216+
a {
217+
valueA
218+
valueC
219+
}
220+
}
221+
}
222+
223+
fragment FragB on Query {
224+
foo {
225+
a {
226+
valueA
227+
}
228+
b {
229+
valueB
230+
}
231+
}
232+
}
233+
234+
query {
235+
...FragA
236+
...FragB
237+
}
238+
""")
239+
240+
api.interpreterUnsafe.execute(q).map { res =>
241+
val d = res.data.toString
242+
assertTrue(d == """{"foo":{"a":{"valueA":"a","valueC":"c"},"b":{"valueB":"b"}}}""")
243+
}
244+
},
203245
suite("spec examples")(
204246
suite("simple fields")(
205247
test("merge identical fields") {

0 commit comments

Comments
 (0)