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 1 commit
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
6 changes: 4 additions & 2 deletions modules/ast/src/main/scala/sangria/ast/QueryAst.scala
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,8 @@ case class InterfaceTypeDefinition(
description: Option[StringValue] = None,
comments: Vector[Comment] = Vector.empty,
trailingComments: Vector[Comment] = Vector.empty,
location: Option[AstLocation] = None)
location: Option[AstLocation] = None,
interfaces: Vector[NamedType] = Vector.empty)
extends TypeDefinition
with WithTrailingComments
with WithDescription {
Expand Down Expand Up @@ -622,7 +623,8 @@ case class InterfaceTypeExtensionDefinition(
directives: Vector[Directive] = Vector.empty,
comments: Vector[Comment] = Vector.empty,
trailingComments: Vector[Comment] = Vector.empty,
location: Option[AstLocation] = None)
location: Option[AstLocation] = None,
interfaces: Vector[NamedType] = Vector.empty)
extends ObjectLikeTypeExtensionDefinition
with WithTrailingComments {
def rename(newName: String): InterfaceTypeExtensionDefinition = copy(name = newName)
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, d, c, tc, p, i) =>
q"_root_.sangria.ast.InterfaceTypeExtensionDefinition($n, $f, $d, $c, $tc, $p, $i)"
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, d, desc, c, tc, p, i) =>
q"_root_.sangria.ast.InterfaceTypeDefinition($n, $f, $d, $desc, $c, $tc, $p, $i)"
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
7 changes: 5 additions & 2 deletions modules/core/src/main/scala/sangria/ast/AstVisitor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,10 @@ object AstVisitor {
description,
comment,
trailingComments,
_) =>
_,
interfaces) =>
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, dirs, comment, tc, _, ints) =>
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
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ case class IntrospectionInterfaceType(
name: String,
description: Option[String],
fields: Seq[IntrospectionField],
possibleTypes: Seq[IntrospectionNamedTypeRef])
possibleTypes: Seq[IntrospectionNamedTypeRef],
interfaces: Seq[IntrospectionNamedTypeRef])
extends IntrospectionType {
val kind = TypeKind.Interface
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,8 @@ package object introspection {
resolve = _.value._2 match {
case t: ObjectType[_, _] =>
Some(t.allInterfaces.asInstanceOf[Vector[Type]].map(true -> _))
case _: InterfaceType[_, _] =>
Some(Vector.empty)
case t: InterfaceType[_, _] =>
Some(t.interfaces.asInstanceOf[List[Type]].map(true -> _))
filosganga marked this conversation as resolved.
Show resolved Hide resolved
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, dirs, description, _, _, _, interfaces) =>
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, dirs, _, _, _, interfaces) =>
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
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,16 @@ object SchemaRenderer {
ast.InterfaceTypeDefinition(
tpe.name,
renderFieldsI(tpe.fields),
description = renderDescription(tpe.description))
description = renderDescription(tpe.description),
interfaces = renderImplementedInterfaces(tpe))

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

def renderUnion(tpe: IntrospectionUnionType) =
ast.UnionTypeDefinition(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ trait AstSchemaBuilder[Ctx] {
fields: () => List[InputField[_]],
mat: AstSchemaMaterializer[Ctx]): InputObjectType[T]

def buildInterfaceType(
origin: MatOrigin,
definition: ast.InterfaceTypeDefinition,
extensions: List[ast.InterfaceTypeExtensionDefinition],
fields: () => List[Field[Ctx, Any]],
interfaces: List[InterfaceType[Ctx, Any]],
mat: AstSchemaMaterializer[Ctx]): Option[InterfaceType[Ctx, Any]]

@deprecated
def buildInterfaceType(
origin: MatOrigin,
definition: ast.InterfaceTypeDefinition,
Expand Down Expand Up @@ -439,6 +448,15 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
definition: ast.InterfaceTypeDefinition,
extensions: List[ast.InterfaceTypeExtensionDefinition],
fields: () => List[Field[Ctx, Any]],
mat: AstSchemaMaterializer[Ctx]): Option[InterfaceType[Ctx, Any]] =
buildInterfaceType(origin, definition, extensions, fields, List.empty, mat)

def buildInterfaceType(
origin: MatOrigin,
definition: ast.InterfaceTypeDefinition,
extensions: List[ast.InterfaceTypeExtensionDefinition],
fields: () => List[Field[Ctx, Any]],
interfaces: List[InterfaceType[Ctx, Any]],
mat: AstSchemaMaterializer[Ctx]): Option[InterfaceType[Ctx, Any]] = {
val directives = definition.directives ++ extensions.flatMap(_.directives)

Expand All @@ -447,7 +465,7 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
name = typeName(definition),
description = typeDescription(definition),
fieldsFn = fields,
interfaces = Nil,
interfaces = interfaces,
manualPossibleTypes = () => Nil,
astDirectives = directives,
astNodes = (definition +: extensions).toVector
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -692,11 +692,13 @@ class AstSchemaMaterializer[Ctx] private (
tpe: ast.InterfaceTypeDefinition): Option[InterfaceType[Ctx, Any]] = {
val extensions = findInterfaceExtensions(tpe.name)

// TODO generate interfaces
builder.buildInterfaceType(
origin,
tpe,
extensions.toList,
() => buildFields(origin, tpe, tpe.fields, extensions).toList,
extendInterfaces(origin, tpe, extensions),
this)
}

Expand Down Expand Up @@ -765,6 +767,18 @@ class AstSchemaMaterializer[Ctx] private (
(ei ++ oi).toList
}

def extendInterfaces(
origin: MatOrigin,
tpe: ast.InterfaceTypeDefinition,
extensions: Vector[ast.InterfaceTypeExtensionDefinition]): List[InterfaceType[Ctx, Any]] = {
val extraInts = extensions.flatMap(_.interfaces)

val ei = extraInts.map(getInterfaceType(origin, _))
val oi = tpe.interfaces.map(getInterfaceType(origin, _))

(ei ++ oi).toList
}

def buildFields(
origin: MatOrigin,
tpe: TypeDefinition,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,18 @@ trait IntrospectionSchemaBuilder[Ctx] {
mat: IntrospectionSchemaMaterializer[Ctx, _])
: Option[InputObjectType[InputObjectType.DefaultInput]]

@deprecated("Interfaces can implement interfaces", "") // TODO put version
def buildInterfaceType(
definition: IntrospectionInterfaceType,
fields: () => List[Field[Ctx, Any]],
mat: IntrospectionSchemaMaterializer[Ctx, _]): Option[InterfaceType[Ctx, Any]]

def buildInterfaceType(
definition: IntrospectionInterfaceType,
fields: () => List[Field[Ctx, Any]],
interfaces: List[InterfaceType[Ctx, Any]],
mat: IntrospectionSchemaMaterializer[Ctx, _]): Option[InterfaceType[Ctx, Any]]

def buildUnionType(
definition: IntrospectionUnionType,
types: List[ObjectType[Ctx, _]],
Expand Down Expand Up @@ -158,12 +165,19 @@ class DefaultIntrospectionSchemaBuilder[Ctx] extends IntrospectionSchemaBuilder[
definition: IntrospectionInterfaceType,
fields: () => List[Field[Ctx, Any]],
mat: IntrospectionSchemaMaterializer[Ctx, _]) =
buildInterfaceType(definition, fields, List.empty, mat)

def buildInterfaceType(
definition: IntrospectionInterfaceType,
fields: () => List[Field[Ctx, Any]],
interfaces: List[InterfaceType[Ctx, Any]],
mat: IntrospectionSchemaMaterializer[Ctx, _]) =
Some(
InterfaceType[Ctx, Any](
name = typeName(definition),
description = typeDescription(definition),
fieldsFn = fields,
interfaces = Nil,
interfaces = interfaces,
manualPossibleTypes = () => Nil,
astDirectives = Vector.empty,
astNodes = Vector.empty
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,11 @@ class IntrospectionSchemaMaterializer[Ctx, T: InputUnmarshaller](
this)

def buildInterfaceDef(tpe: IntrospectionInterfaceType) =
builder.buildInterfaceType(tpe, () => tpe.fields.toList.flatMap(buildField(tpe, _)), this)
builder.buildInterfaceType(
tpe,
() => tpe.fields.toList.flatMap(buildField(tpe, _)),
tpe.interfaces.toList.map(getInterfaceType),
this)

def buildUnionDef(tpe: IntrospectionUnionType) =
builder.buildUnionType(tpe, tpe.possibleTypes.toList.map(getObjectType), this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ class SchemaRenderSpec
| query: Root
|}
|
|interface Baaz {
|interface Baaz implements Foo {
| int: Int
| str: String
|}
Expand Down
23 changes: 13 additions & 10 deletions modules/parser/src/main/scala/sangria/parser/QueryParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -411,16 +411,19 @@ private[parser] sealed trait TypeSystemDefinitions {
}

private[this] def InterfaceTypeDefinition = rule {
Description ~ Comments ~ trackPos ~ interface ~ Name ~ (DirectivesConst.? ~> (_.getOrElse(
Vector.empty))) ~ FieldsDefinition.? ~> ((descr, comment, location, name, dirs, fields) =>
ast.InterfaceTypeDefinition(
name,
fields.fold(Vector.empty[ast.FieldDefinition])(_._1.toVector),
dirs,
descr,
comment,
fields.fold(Vector.empty[ast.Comment])(_._2),
location))
Description ~ Comments ~ trackPos ~ interface ~ Name ~ (ImplementsInterfaces.? ~> (_.getOrElse(
Vector.empty))) ~ (DirectivesConst.? ~> (_.getOrElse(Vector.empty))) ~ FieldsDefinition.? ~> (
(descr, comment, location, name, interfaces, dirs, fields) =>
ast.InterfaceTypeDefinition(
name,
fields.fold(Vector.empty[ast.FieldDefinition])(_._1.toVector),
dirs,
descr,
comment,
fields.fold(Vector.empty[ast.Comment])(_._2),
location,
interfaces
))
}

private[this] def UnionTypeDefinition = rule {
Expand Down
Loading