diff --git a/core/src/main/scala-2/caliban/schema/SchemaDerivation.scala b/core/src/main/scala-2/caliban/schema/SchemaDerivation.scala index 27bd2f81fe..ea8cf1a0f7 100644 --- a/core/src/main/scala-2/caliban/schema/SchemaDerivation.scala +++ b/core/src/main/scala-2/caliban/schema/SchemaDerivation.scala @@ -57,7 +57,8 @@ trait SchemaDerivation[R] extends LowPriorityDerivedSchema { ) ) .toList, - Some(ctx.typeName.full) + Some(ctx.typeName.full), + Some(getDirectives(ctx)) ) else makeObject( @@ -132,14 +133,16 @@ trait SchemaDerivation[R] extends LowPriorityDerivedSchema { annotations.collectFirst { case GQLDeprecated(reason) => reason } ) }, - Some(ctx.typeName.full) + Some(ctx.typeName.full), + Some(getDirectives(ctx.annotations)) ) else if (!isInterface) makeUnion( Some(getName(ctx)), getDescription(ctx), subtypes.map { case (t, _) => fixEmptyUnionObject(t) }, - Some(ctx.typeName.full) + Some(ctx.typeName.full), + Some(getDirectives(ctx.annotations)) ) else { val impl = subtypes.map(_._1.copy(interfaces = () => Some(List(toType(isInput, isSubscription))))) @@ -157,7 +160,14 @@ trait SchemaDerivation[R] extends LowPriorityDerivedSchema { .flatten .toList - makeInterface(Some(getName(ctx)), getDescription(ctx), commonFields, impl, Some(ctx.typeName.full)) + makeInterface( + Some(getName(ctx)), + getDescription(ctx), + commonFields, + impl, + Some(ctx.typeName.full), + Some(getDirectives(ctx.annotations)) + ) } } diff --git a/core/src/main/scala-3/caliban/schema/SchemaDerivation.scala b/core/src/main/scala-3/caliban/schema/SchemaDerivation.scala index 5a55ea60ee..4888da2986 100644 --- a/core/src/main/scala-3/caliban/schema/SchemaDerivation.scala +++ b/core/src/main/scala-3/caliban/schema/SchemaDerivation.scala @@ -81,14 +81,16 @@ trait SchemaDerivation[A] { annotations.collectFirst { case GQLDeprecated(reason) => reason } ) }, - Some(info.full) + Some(info.full), + Some(getDirectives(annotations)) ) } else if (!isInterface) makeUnion( Some(getName(annotations, info)), getDescription(annotations), subTypes.map { case (_, t, _) => fixEmptyUnionObject(t) }, - Some(info.full) + Some(info.full), + Some(getDirectives(annotations)) ) else { val impl = subTypes.map(_._2.copy(interfaces = () => Some(List(toType(isInput, isSubscription))))) @@ -111,7 +113,8 @@ trait SchemaDerivation[A] { getDescription(annotations), commonFields, impl, - Some(info.full) + Some(info.full), + Some(getDirectives(annotations)) ) } @@ -147,7 +150,8 @@ trait SchemaDerivation[A] { Some(fieldAnnotations.collect { case GQLDirective(dir) => dir }).filter(_.nonEmpty) ) }, - Some(info.full) + Some(info.full), + Some(getDirectives(annotations)) ) else makeObject( diff --git a/core/src/main/scala/caliban/Rendering.scala b/core/src/main/scala/caliban/Rendering.scala index b68461afa9..ca9c1b208f 100644 --- a/core/src/main/scala/caliban/Rendering.scala +++ b/core/src/main/scala/caliban/Rendering.scala @@ -31,7 +31,9 @@ object Rendering { .fold(List.empty[String])(_.flatMap(_.name)) .mkString(" | ") Some( - s"""${renderDescription(t.description)}${renderKind(t.kind)} ${renderTypeName(t)} = $renderedTypes""" + s"""${renderDescription(t.description)}${renderKind(t.kind)} ${renderTypeName(t)}${renderDirectives( + t.directives + )} = $renderedTypes""" ) case _ => val renderedDirectives: String = renderDirectives(t.directives) diff --git a/core/src/main/scala/caliban/schema/Types.scala b/core/src/main/scala/caliban/schema/Types.scala index 718ab8da62..7c89140b53 100644 --- a/core/src/main/scala/caliban/schema/Types.scala +++ b/core/src/main/scala/caliban/schema/Types.scala @@ -24,7 +24,8 @@ object Types { name: Option[String], description: Option[String], values: List[__EnumValue], - origin: Option[String] + origin: Option[String], + directives: Option[List[Directive]] = None ): __Type = __Type( __TypeKind.ENUM, @@ -32,7 +33,8 @@ object Types { description, enumValues = args => if (args.includeDeprecated.getOrElse(false)) Some(values) else Some(values.filter(!_.isDeprecated)), - origin = origin + origin = origin, + directives = directives ) def makeObject( @@ -57,24 +59,41 @@ object Types { name: Option[String], description: Option[String], fields: List[__InputValue], - origin: Option[String] = None + origin: Option[String] = None, + directives: Option[List[Directive]] = None ): __Type = - __Type(__TypeKind.INPUT_OBJECT, name, description, inputFields = Some(fields), origin = origin) + __Type( + __TypeKind.INPUT_OBJECT, + name, + description, + inputFields = Some(fields), + origin = origin, + directives = directives + ) def makeUnion( name: Option[String], description: Option[String], subTypes: List[__Type], - origin: Option[String] = None + origin: Option[String] = None, + directives: Option[List[Directive]] = None ): __Type = - __Type(__TypeKind.UNION, name, description, possibleTypes = Some(subTypes), origin = origin) + __Type( + __TypeKind.UNION, + name, + description, + possibleTypes = Some(subTypes), + origin = origin, + directives = directives + ) def makeInterface( name: Option[String], description: Option[String], fields: () => List[__Field], subTypes: List[__Type], - origin: Option[String] = None + origin: Option[String] = None, + directives: Option[List[Directive]] = None ): __Type = __Type( __TypeKind.INTERFACE, @@ -83,7 +102,8 @@ object Types { fields = args => if (args.includeDeprecated.getOrElse(false)) Some(fields()) else Some(fields().filter(!_.isDeprecated)), possibleTypes = Some(subTypes), - origin = origin + origin = origin, + directives = directives ) /** diff --git a/core/src/test/scala/caliban/RenderingSpec.scala b/core/src/test/scala/caliban/RenderingSpec.scala index 37991b2ca2..9c3d72e3a5 100644 --- a/core/src/test/scala/caliban/RenderingSpec.scala +++ b/core/src/test/scala/caliban/RenderingSpec.scala @@ -22,26 +22,30 @@ object RenderingSpec extends DefaultRunnableSpec { |"Description of custom scalar emphasizing proper captain ship names" |scalar CaptainShipName @specifiedBy(url: "http://someUrl") | - |union Role = Captain | Engineer | Mechanic | Pilot + |union Role @uniondirective = Captain | Engineer | Mechanic | Pilot | - |enum Origin { + |enum Origin @enumdirective { | BELT | EARTH | MARS | MOON @deprecated(reason: "Use: EARTH | MARS | BELT") |} | - |input CharacterInput { + |input CharacterInput @inputobjdirective { | name: String! @external | nicknames: [String!]! @required | origin: Origin! |} | + |interface Human { + | name: String! @external + |} + | |type Captain { | shipName: CaptainShipName! |} | - |type Character @key(name: "name") { + |type Character implements Human @key(name: "name") { | name: String! @external | nicknames: [String!]! @required | origin: Origin! @@ -56,6 +60,10 @@ object RenderingSpec extends DefaultRunnableSpec { | shipName: String! |} | + |type Narrator implements Human { + | name: String! + |} + | |type Pilot { | shipName: String! |} @@ -67,6 +75,7 @@ object RenderingSpec extends DefaultRunnableSpec { | character(name: String!): Character @deprecated(reason: "Use `characters`") | charactersIn(names: [String!]!): [Character!]! | exists(character: CharacterInput!): Boolean! + | human: Human! |}""".stripMargin.trim) ) }, diff --git a/core/src/test/scala/caliban/TestUtils.scala b/core/src/test/scala/caliban/TestUtils.scala index 42d743a4af..f291ff6cc6 100644 --- a/core/src/test/scala/caliban/TestUtils.scala +++ b/core/src/test/scala/caliban/TestUtils.scala @@ -24,6 +24,7 @@ object TestUtils { case class C(id: String, blah: Boolean) extends Interface } + @GQLDirective(Directive("enumdirective")) sealed trait Origin object Origin { @@ -34,6 +35,7 @@ object TestUtils { case object MOON extends Origin } + @GQLDirective(Directive("uniondirective")) sealed trait Role case class CaptainShipName(value: String) @@ -53,13 +55,20 @@ object TestUtils { case class Mechanic(shipName: String) extends Role } + @GQLInterface + sealed trait Human + @GQLDirective(Directive("key", Map("name" -> StringValue("name")))) case class Character( @GQLDirective(Directive("external")) name: String, @GQLDirective(Directive("required")) nicknames: List[String], origin: Origin, role: Option[Role] - ) + ) extends Human + + case class Narrator( + name: String + ) extends Human case class OrganizationId(self: Long) extends AnyVal case class Event(organizationId: OrganizationId, title: String) @@ -68,6 +77,7 @@ object TestUtils { case class WrappedPainter(self: Painter) extends AnyVal @GQLInputName("CharacterInput") + @GQLDirective(Directive("inputobjdirective")) case class CharacterInput( @GQLDirective(Directive("external")) name: String, @GQLDirective(Directive("required")) nicknames: List[String], @@ -98,7 +108,8 @@ object TestUtils { @GQLDescription("Return all characters from a given origin") characters: CharactersArgs => List[Character], @GQLDeprecated("Use `characters`") character: CharacterArgs => Option[Character], charactersIn: CharacterInArgs => List[Character], - exists: CharacterObjectArgs => Boolean + exists: CharacterObjectArgs => Boolean, + human: Human ) @GQLDescription("Queries") @@ -122,7 +133,8 @@ object TestUtils { args => characters.filter(c => args.origin.forall(c.origin == _)), args => characters.find(c => c.name == args.name), args => characters.filter(c => args.names.contains(c.name)), - args => characters.exists(_.name == args.character.name) + args => characters.exists(_.name == args.character.name), + Narrator("narrator") ) ) val resolverIO = RootResolver(