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

Fixed fragment applicability to union types; no interfaces in unions #592

Merged
merged 2 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions modules/core/src/main/scala/compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1016,9 +1016,12 @@ object QueryCompiler {
*/
def fragmentApplies(typeCond: NamedType, ctpe: NamedType): Boolean =
(typeCond.dealias, ctpe.dealias) match {
case (_: InterfaceType, u: UnionType) => u.members.forall(_ <:< typeCond)
case (_, u: UnionType) => u.members.exists(typeCond <:< _)
case _ => typeCond <:< ctpe || ctpe <:< typeCond
case (u: UnionType, _) =>
u.members.exists(m => fragmentApplies(m, ctpe))
case (_, u: UnionType) =>
u.members.exists(m => fragmentApplies(typeCond, m))
case _ =>
typeCond <:< ctpe || ctpe <:< typeCond
}

def elaborateBinding(b: Binding, vars: Vars): Elab[Binding] =
Expand Down
2 changes: 1 addition & 1 deletion modules/core/src/main/scala/schema.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1749,7 +1749,7 @@ object SchemaValidator {
case None => List(Problem(s"Undefined type '${member.name}' included in union '${tpe.name}'"))
case Some(mtpe) =>
mtpe match {
case (_: ObjectType) | (_: InterfaceType) => Nil
case (_: ObjectType) => Nil
case _ => List(Problem(s"Non-object type '${member.name}' included in union '${tpe.name}'"))
}
}
Expand Down
80 changes: 80 additions & 0 deletions modules/core/src/test/scala/compiler/FragmentSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,86 @@ final class FragmentSuite extends CatsEffectSuite {
assertIO(res, List(expectedResult))
}

test("typed union nested fragment query") {
val query = """
query FragmentUnionTyping {
user: user(id: "1") {
favourite {
__typename
...userOrPageFragment
}
}
}

fragment userFragment on User {
id
name
}

fragment pageFragment on Page {
id
title
}

fragment userOrPageFragment on UserOrPage {
...userFragment
...pageFragment
}
"""

val User = FragmentMapping.schema.ref("User")
val Page = FragmentMapping.schema.ref("Page")

val expected =
Select("user", None,
Unique(
Filter(Eql(FragmentMapping.UserType / "id", Const("1")),
Select("favourite", None,
Group(List(
Introspect(FragmentMapping.schema, Select("__typename", None, Empty)),
Narrow(
User,
Group(List(
Select("id", None, Empty),
Select("name", None, Empty)
))
),
Narrow(
Page,
Group(List(
Select("id", None, Empty),
Select("title", None, Empty)
))
)
))
)
)
)
)

val expectedResult = json"""
{
"data" : {
"user" : {
"favourite" : {
"__typename" : "User",
"id" : "2",
"name" : "Bob"
}
}
}
}
"""

val compiled = FragmentMapping.compiler.compile(query)

assertEquals(compiled.map(_.query), Result.Success(expected))

val res = runOperation(compiled)

assertIO(res, List(expectedResult))
}

test("supertype fragment query (1)") {
val query = """
query withFragments {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ final class ExtensionsSuite extends CatsEffectSuite {
interface Interface {
id: String!
}
union Union = Object | Interface
union Union = Object
enum Enum { A, B, C }
input Input { id: String! }

Expand Down
25 changes: 18 additions & 7 deletions modules/core/src/test/scala/sdl/SDLSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -331,10 +331,13 @@ final class SDLSuite extends CatsEffectSuite {
|interface Intrf {
| bar: String
|}
|type Obj implements Intrf {
|type Obj1 implements Intrf {
| bar: String
|}
|union Union = Intrf | Obj
|type Obj2 implements Intrf {
| bar: String
|}
|union Union = Obj1 | Obj2
|enum Enum {
| A
| B
Expand All @@ -352,7 +355,11 @@ final class SDLSuite extends CatsEffectSuite {
|extend interface Intrf @Intrf {
| baz: Boolean
|}
|extend type Obj @Obj {
|extend type Obj1 @Obj {
| baz: Boolean
| quux: String
|}
|extend type Obj2 @Obj {
| baz: Boolean
| quux: String
|}
Expand All @@ -378,7 +385,7 @@ final class SDLSuite extends CatsEffectSuite {
assertEquals(ser, schema.success)
}

test("round-trip extensions (no fields or members") {
test("round-trip extensions (no fields or members)") {
val schema =
"""|schema {
| query: MyQuery
Expand All @@ -390,10 +397,13 @@ final class SDLSuite extends CatsEffectSuite {
|interface Intrf {
| bar: String
|}
|type Obj implements Intrf {
|type Obj1 implements Intrf {
| bar: String
|}
|type Obj2 implements Intrf {
| bar: String
|}
|union Union = Intrf | Obj
|union Union = Obj1 | Obj2
|enum Enum {
| A
| B
Expand All @@ -404,7 +414,8 @@ final class SDLSuite extends CatsEffectSuite {
|extend schema @Sch
|extend scalar Scalar @Sca
|extend interface Intrf @Intrf
|extend type Obj @Obj
|extend type Obj1 @Obj
|extend type Obj2 @Obj
|extend union Union @Uni
|extend enum Enum @Enu
|extend input Input @Inp
Expand Down
Loading