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

Add support for interface implementing interface #981

Merged
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
56 changes: 54 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,43 @@ lazy val ast = project
ProblemFilters.exclude[DirectMissingMethodProblem]("sangria.ast.DirectiveDefinition.apply"),
ProblemFilters.exclude[DirectMissingMethodProblem]("sangria.ast.DirectiveDefinition.copy"),
ProblemFilters.exclude[DirectMissingMethodProblem]("sangria.ast.DirectiveDefinition.this"),
ProblemFilters.exclude[MissingTypesProblem]("sangria.ast.DirectiveDefinition$")
ProblemFilters.exclude[MissingTypesProblem]("sangria.ast.DirectiveDefinition$"),
ProblemFilters.exclude[IncompatibleResultTypeProblem](
"sangria.introspection.IntrospectionInterfaceType.*"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"sangria.introspection.IntrospectionInterfaceType.this"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"sangria.introspection.IntrospectionInterfaceType.copy"),
ProblemFilters.exclude[MissingTypesProblem]("sangria.ast.InterfaceTypeDefinition$"),
ProblemFilters.exclude[IncompatibleResultTypeProblem](
"sangria.ast.InterfaceTypeDefinition.*"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"sangria.ast.InterfaceTypeDefinition.this"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"sangria.ast.InterfaceTypeDefinition.copy"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"sangria.ast.InterfaceTypeDefinition.tupled"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"sangria.ast.InterfaceTypeDefinition.curried"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"sangria.ast.InterfaceTypeDefinition.<init>*"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"sangria.ast.InterfaceTypeDefinition.apply$default$*"),
ProblemFilters.exclude[MissingTypesProblem]("sangria.ast.InterfaceTypeExtensionDefinition$"),
ProblemFilters.exclude[IncompatibleResultTypeProblem](
"sangria.ast.InterfaceTypeExtensionDefinition.*"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"sangria.ast.InterfaceTypeExtensionDefinition.this"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"sangria.ast.InterfaceTypeExtensionDefinition.copy"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"sangria.ast.InterfaceTypeExtensionDefinition.tupled"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"sangria.ast.InterfaceTypeExtensionDefinition.curried"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"sangria.ast.InterfaceTypeExtensionDefinition.<init>*"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"sangria.ast.InterfaceTypeExtensionDefinition.apply$default$*")
),
apiURL := {
val ver = CrossVersion.binaryScalaVersion(scalaVersion.value)
Expand Down Expand Up @@ -140,7 +176,23 @@ lazy val core = project
ProblemFilters.exclude[DirectMissingMethodProblem]("sangria.schema.Field.subs"),
ProblemFilters.exclude[DirectMissingMethodProblem]("sangria.schema.Field.apply"),
ProblemFilters.exclude[DirectMissingMethodProblem]("sangria.execution.Resolver.*"),
ProblemFilters.exclude[DirectMissingMethodProblem]("sangria.execution.Resolver#*")
ProblemFilters.exclude[DirectMissingMethodProblem]("sangria.execution.Resolver#*"),
ProblemFilters.exclude[ReversedMissingMethodProblem](
"sangria.schema.AstSchemaBuilder.buildInterfaceType"),
ProblemFilters.exclude[ReversedMissingMethodProblem](
"sangria.schema.AstSchemaBuilder.extendInterfaceType"),
ProblemFilters.exclude[ReversedMissingMethodProblem](
"sangria.schema.IntrospectionSchemaBuilder.buildInterfaceType"),
ProblemFilters.exclude[MissingTypesProblem](
"sangria.introspection.IntrospectionInterfaceType$"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"sangria.introspection.IntrospectionInterfaceType.this"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"sangria.introspection.IntrospectionInterfaceType.tupled"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"sangria.introspection.IntrospectionInterfaceType.curried"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"sangria.introspection.IntrospectionInterfaceType.copy")
),
Test / testOptions += Tests.Argument(TestFrameworks.ScalaTest, "-oF"),
libraryDependencies ++= Seq(
Expand Down
45 changes: 45 additions & 0 deletions modules/ast/src/main/scala/sangria/ast/QueryAst.scala
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@ case class ObjectTypeDefinition(
case class InterfaceTypeDefinition(
name: String,
fields: Vector[FieldDefinition],
interfaces: Vector[NamedType],
directives: Vector[Directive] = Vector.empty,
description: Option[StringValue] = None,
comments: Vector[Comment] = Vector.empty,
Expand All @@ -541,6 +542,27 @@ case class InterfaceTypeDefinition(
def rename(newName: String): InterfaceTypeDefinition = copy(name = newName)
}

object InterfaceTypeDefinition {

def apply(
name: String,
fields: Vector[FieldDefinition],
directives: Vector[Directive],
description: Option[StringValue],
comments: Vector[Comment],
trailingComments: Vector[Comment],
location: Option[AstLocation]): InterfaceTypeDefinition = new InterfaceTypeDefinition(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯 for taking care of "source compatibility".
I've checked that this apply is only used in tests.

name,
fields,
Vector.empty,
directives,
description,
comments,
trailingComments,
location
)
}

/** @group typesystem
*/
case class UnionTypeDefinition(
Expand Down Expand Up @@ -619,15 +641,38 @@ case class ObjectTypeExtensionDefinition(
case class InterfaceTypeExtensionDefinition(
name: String,
fields: Vector[FieldDefinition],
interfaces: Vector[NamedType],
directives: Vector[Directive] = Vector.empty,
comments: Vector[Comment] = Vector.empty,
trailingComments: Vector[Comment] = Vector.empty,
location: Option[AstLocation] = None)
extends ObjectLikeTypeExtensionDefinition
with WithTrailingComments {

def rename(newName: String): InterfaceTypeExtensionDefinition = copy(name = newName)
}

object InterfaceTypeExtensionDefinition {

def apply(
name: String,
fields: Vector[FieldDefinition],
directives: Vector[Directive],
comments: Vector[Comment],
trailingComments: Vector[Comment],
location: Option[AstLocation]): InterfaceTypeExtensionDefinition =
InterfaceTypeExtensionDefinition(
name,
fields,
Vector.empty,
directives,
comments,
trailingComments,
location
)

}

/** @group typesystem
*/
case class InputObjectTypeExtensionDefinition(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ trait AstLiftable {

case ObjectTypeExtensionDefinition(n, i, f, d, c, tc, p) =>
q"_root_.sangria.ast.ObjectTypeExtensionDefinition($n, $i, $f, $d, $c, $tc, $p)"
case InterfaceTypeExtensionDefinition(n, f, d, c, tc, p) =>
q"_root_.sangria.ast.InterfaceTypeExtensionDefinition($n, $f, $d, $c, $tc, $p)"
case InterfaceTypeExtensionDefinition(n, f, i, d, c, tc, p) =>
q"_root_.sangria.ast.InterfaceTypeExtensionDefinition($n, $f, $i, $d, $c, $tc, $p)"
case InputObjectTypeExtensionDefinition(n, f, d, c, tc, p) =>
q"_root_.sangria.ast.InputObjectTypeExtensionDefinition($n, $f, $d, $c, $tc, $p)"
case UnionTypeExtensionDefinition(n, t, d, c, p) =>
Expand All @@ -97,8 +97,8 @@ trait AstLiftable {
q"_root_.sangria.ast.EnumTypeDefinition($n, $v, $d, $desc, $c, $tc, $p)"
case InputObjectTypeDefinition(n, f, d, desc, c, tc, p) =>
q"_root_.sangria.ast.InputObjectTypeDefinition($n, $f, $d, $desc, $c, $tc, $p)"
case InterfaceTypeDefinition(n, f, d, desc, c, tc, p) =>
q"_root_.sangria.ast.InterfaceTypeDefinition($n, $f, $d, $desc, $c, $tc, $p)"
case InterfaceTypeDefinition(n, f, i, d, desc, c, tc, p) =>
q"_root_.sangria.ast.InterfaceTypeDefinition($n, $f, $i, $d, $desc, $c, $tc, $p)"
case ObjectTypeDefinition(n, i, f, d, desc, c, tc, p) =>
q"_root_.sangria.ast.ObjectTypeDefinition($n, $i, $f, $d, $desc, $c, $tc, $p)"
case ScalarTypeDefinition(n, d, desc, c, p) =>
Expand Down
12 changes: 8 additions & 4 deletions modules/core/src/main/scala-3/sangria/macros/ToExprGivens.scala
Original file line number Diff line number Diff line change
Expand Up @@ -158,15 +158,17 @@ trait ToExprGivens {
${ Expr(tc) },
${ Expr(p) })
}
case InterfaceTypeExtensionDefinition(n, f, d, c, tc, p) =>
case InterfaceTypeExtensionDefinition(n, f, i, d, c, tc, p) =>
'{
sangria.ast.InterfaceTypeExtensionDefinition(
${ Expr(n) },
${ Expr(f) },
${ Expr(i) },
${ Expr(d) },
${ Expr(c) },
${ Expr(tc) },
${ Expr(p) })
${ Expr(p) }
)
}
case InputObjectTypeExtensionDefinition(n, f, d, c, tc, p) =>
'{
Expand Down Expand Up @@ -237,16 +239,18 @@ trait ToExprGivens {
${ Expr(tc) },
${ Expr(p) })
}
case InterfaceTypeDefinition(n, f, d, desc, c, tc, p) =>
case InterfaceTypeDefinition(n, f, i, d, desc, c, tc, p) =>
'{
sangria.ast.InterfaceTypeDefinition(
${ Expr(n) },
${ Expr(f) },
${ Expr(i) },
${ Expr(d) },
${ Expr(desc) },
${ Expr(c) },
${ Expr(tc) },
${ Expr(p) })
${ Expr(p) }
)
}
case ObjectTypeDefinition(n, i, f, d, desc, c, tc, p) =>
'{
Expand Down
5 changes: 4 additions & 1 deletion modules/core/src/main/scala/sangria/ast/AstVisitor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -306,12 +306,14 @@ object AstVisitor {
case n @ InterfaceTypeDefinition(
_,
fields,
interfaces,
dirs,
description,
comment,
trailingComments,
_) =>
if (breakOrSkip(onEnter(n))) {
interfaces.foreach(d => loop(d))
fields.foreach(d => loop(d))
dirs.foreach(d => loop(d))
description.foreach(s => loop(s))
Expand Down Expand Up @@ -360,8 +362,9 @@ object AstVisitor {
tc.foreach(s => loop(s))
breakOrSkip(onLeave(n))
}
case n @ InterfaceTypeExtensionDefinition(_, fields, dirs, comment, tc, _) =>
case n @ InterfaceTypeExtensionDefinition(_, fields, ints, dirs, comment, tc, _) =>
if (breakOrSkip(onEnter(n))) {
ints.foreach(d => loop(d))
fields.foreach(d => loop(d))
dirs.foreach(d => loop(d))
comment.foreach(s => loop(s))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ object IntrospectionParser {
possibleTypes = mapFieldOpt(tpe, "possibleTypes")
.map(um.getListValue)
.getOrElse(Vector.empty)
.map(i => parseNamedTypeRef(i, path :+ "possibleTypes"))
.map(i => parseNamedTypeRef(i, path :+ "possibleTypes")),
interfaces = mapFieldOpt(tpe, "interfaces")
.map(um.getListValue)
.getOrElse(Vector.empty)
.map(i => parseNamedTypeRef(i, path :+ "interfaces"))
)

private def parseUnion[In: InputUnmarshaller](tpe: In, path: Vector[String]) =
Expand Down
16 changes: 14 additions & 2 deletions modules/core/src/main/scala/sangria/introspection/model.scala
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,21 @@ case class IntrospectionInterfaceType(
name: String,
description: Option[String],
fields: Seq[IntrospectionField],
possibleTypes: Seq[IntrospectionNamedTypeRef])
extends IntrospectionType {
interfaces: Seq[IntrospectionNamedTypeRef],
possibleTypes: Seq[IntrospectionNamedTypeRef]
) extends IntrospectionType {

val kind = TypeKind.Interface

}

object IntrospectionInterfaceType {
def apply(
name: String,
description: Option[String],
fields: Seq[IntrospectionField],
possibleTypes: Seq[IntrospectionNamedTypeRef]): IntrospectionInterfaceType =
IntrospectionInterfaceType(name, description, fields, Seq.empty, possibleTypes)
}

case class IntrospectionUnionType(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,10 +272,8 @@ package object introspection {
"interfaces",
OptionType(ListType(__Type)),
resolve = _.value._2 match {
case t: ObjectType[_, _] =>
case t: ObjectLikeType[_, _] =>
Some(t.allInterfaces.asInstanceOf[Vector[Type]].map(true -> _))
case _: InterfaceType[_, _] =>
Some(Vector.empty)
case _ => None
}
),
Expand Down
20 changes: 14 additions & 6 deletions modules/core/src/main/scala/sangria/renderer/QueryRenderer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -668,12 +668,16 @@ object QueryRenderer {
renderDirs(dirs, config, indent, frontSep = true) +
renderInputFieldDefinitions(fields, itd, indent, config, frontSep = true)

case itd @ InterfaceTypeDefinition(name, fields, dirs, description, _, _, _) =>
case itd @ InterfaceTypeDefinition(name, fields, interfaces, dirs, description, _, _, _) =>
renderDescription(itd, prev, indent, config) +
renderComment(itd, description.orElse(prev), indent, config) +
indent.str + "interface" + config.mandatorySeparator + name +
renderDirs(dirs, config, indent, frontSep = true) +
renderFieldDefinitions(fields, itd, indent, config, frontSep = true)
(if (interfaces.nonEmpty) config.mandatorySeparator else "") +
renderInterfaces(interfaces, config, indent, withSep = false) +
(if (dirs.nonEmpty) config.separator else "") +
renderDirs(dirs, config, indent, withSep = false) +
(if (fields.nonEmpty) config.separator else "") +
renderFieldDefinitions(fields, itd, indent, config)

case utd @ UnionTypeDefinition(name, types, dirs, description, _, _) =>
val typesString =
Expand Down Expand Up @@ -728,11 +732,15 @@ object QueryRenderer {
renderDirs(dirs, config, indent, withSep = fields.nonEmpty) +
renderFieldDefinitions(fields, ted, indent, config)

case ext @ InterfaceTypeExtensionDefinition(name, fields, dirs, _, _, _) =>
case ext @ InterfaceTypeExtensionDefinition(name, fields, interfaces, dirs, _, _, _) =>
renderComment(ext, prev, indent, config) +
indent.str + "extend" + config.mandatorySeparator + "interface" + config.mandatorySeparator + name +
renderDirs(dirs, config, indent, frontSep = true) +
renderFieldDefinitions(fields, ext, indent, config, frontSep = true)
(if (interfaces.nonEmpty) config.mandatorySeparator else "") +
renderInterfaces(interfaces, config, indent, withSep = false) +
(if (dirs.nonEmpty) config.separator else "") +
renderDirs(dirs, config, indent, withSep = false) +
(if (fields.nonEmpty) config.separator else "") +
renderFieldDefinitions(fields, ext, indent, config)

case ext @ UnionTypeExtensionDefinition(name, types, dirs, _, _) =>
val typesString =
Expand Down
18 changes: 16 additions & 2 deletions modules/core/src/main/scala/sangria/renderer/SchemaRenderer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ object SchemaRenderer {
def renderImplementedInterfaces(tpe: IntrospectionObjectType) =
tpe.interfaces.map(t => ast.NamedType(t.name)).toVector

def renderImplementedInterfaces(tpe: IntrospectionInterfaceType) =
tpe.interfaces.map(t => ast.NamedType(t.name)).toVector

def renderImplementedInterfaces(tpe: ObjectLikeType[_, _]) =
tpe.allInterfaces.map(t => ast.NamedType(t.name))

Expand Down Expand Up @@ -217,14 +220,25 @@ object SchemaRenderer {
ast.InterfaceTypeDefinition(
tpe.name,
renderFieldsI(tpe.fields),
description = renderDescription(tpe.description))
renderImplementedInterfaces(tpe),
Vector.empty,
renderDescription(tpe.description),
Vector.empty,
Vector.empty,
None
)

def renderInterface(tpe: InterfaceType[_, _]) =
ast.InterfaceTypeDefinition(
tpe.name,
renderFields(tpe.uniqueFields),
renderImplementedInterfaces(tpe),
tpe.astDirectives,
renderDescription(tpe.description))
renderDescription(tpe.description),
Vector.empty,
Vector.empty,
None
)

def renderUnion(tpe: IntrospectionUnionType) =
ast.UnionTypeDefinition(
Expand Down
Loading