Skip to content
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
7 changes: 6 additions & 1 deletion modules/core/src/main/scala/compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1146,7 +1146,12 @@ object QueryCompiler {
Elab.transformChild(child => if (include) child else Filter(Eql(FieldTypeRef / "isDeprecated", Const(false)), child))
case (TypeTypeRef, "enumValues", List(Binding("includeDeprecated", BooleanValue(include)))) =>
Elab.transformChild(child => if (include) child else Filter(Eql(EnumValueTypeRef / "isDeprecated", Const(false)), child))

case (TypeTypeRef, "inputFields", List(Binding("includeDeprecated", BooleanValue(include)))) =>
Elab.transformChild(child => if (include) child else Filter(Eql(Introspection.__InputValueType / "isDeprecated", Const(false)), child))
case (FieldTypeRef, "args", List(Binding("includeDeprecated", BooleanValue(include)))) =>
Elab.transformChild(child => if (include) child else Filter(Eql(Introspection.__InputValueType / "isDeprecated", Const(false)), child))
case (Introspection.__DirectiveType, "args", List(Binding("includeDeprecated", BooleanValue(include)))) =>
Elab.transformChild(child => if (include) child else Filter(Eql(Introspection.__InputValueType / "isDeprecated", Const(false)), child))
case _ =>
Elab.unit
}
Expand Down
29 changes: 17 additions & 12 deletions modules/core/src/main/scala/introspection.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,22 @@ object Introspection {
# may be non-null for custom SCALAR, otherwise null.
specifiedByURL: String

# OBJECT and INTERFACE only
fields(includeDeprecated: Boolean = false): [__Field!]
# must be non-null for OBJECT and INTERFACE, otherwise null.
fields(includeDeprecated: Boolean! = false): [__Field!]

# OBJECT only
# must be non-null for OBJECT and INTERFACE, otherwise null.
interfaces: [__Type!]

# INTERFACE and UNION only
# must be non-null for INTERFACE and UNION, otherwise null.
possibleTypes: [__Type!]

# ENUM only
enumValues(includeDeprecated: Boolean = false): [__EnumValue!]
# must be non-null for ENUM, otherwise null.
enumValues(includeDeprecated: Boolean! = false): [__EnumValue!]

# INPUT_OBJECT only
inputFields: [__InputValue!]
# must be non-null for INPUT_OBJECT, otherwise null.
inputFields(includeDeprecated: Boolean! = false): [__InputValue!]

# NON_NULL and LIST only
# must be non-null for NON_NULL and LIST, otherwise null.
ofType: __Type

# must be non-null for INPUT_OBJECT, otherwise null.
Expand All @@ -70,7 +70,7 @@ object Introspection {
type __Field {
name: String!
description: String
args: [__InputValue!]!
args(includeDeprecated: Boolean! = false): [__InputValue!]!
type: __Type!
isDeprecated: Boolean!
deprecationReason: String
Expand All @@ -81,6 +81,8 @@ object Introspection {
description: String
type: __Type!
defaultValue: String
isDeprecated: Boolean!
deprecationReason: String
}

type __EnumValue {
Expand All @@ -105,7 +107,7 @@ object Introspection {
name: String!
description: String
locations: [__DirectiveLocation!]!
args: [__InputValue!]!
args(includeDeprecated: Boolean! = false): [__InputValue!]!
isRepeatable: Boolean!
}

Expand All @@ -117,6 +119,7 @@ object Introspection {
FRAGMENT_DEFINITION
FRAGMENT_SPREAD
INLINE_FRAGMENT
VARIABLE_DEFINITION
SCHEMA
SCALAR
OBJECT
Expand Down Expand Up @@ -272,7 +275,9 @@ object Introspection {
ValueField("name", _.name),
ValueField("description", _.description),
ValueField("type", _.tpe.dealias),
ValueField("defaultValue", _.defaultValue.map(SchemaRenderer.renderValue))
ValueField("defaultValue", _.defaultValue.map(SchemaRenderer.renderValue)),
ValueField("isDeprecated", _.isDeprecated),
ValueField("deprecationReason", _.deprecationReason)
),
ValueObjectMapping(__EnumValueType).on[EnumValueDefinition](
ValueField("name", _.name),
Expand Down
43 changes: 20 additions & 23 deletions modules/core/src/main/scala/schema.scala
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,21 @@ sealed trait TypeWithFields extends NamedType {
@deprecated("Use interfaces instead", "0.20.0")
def allInterfaces: List[NamedType] = interfaces
}
/**
* Mixin for types that can be deprecated.
*/
protected trait Deprecatable {
def directives: List[Directive]

def deprecatedDirective: Option[Directive] =
directives.find(_.name == "deprecated")
def isDeprecated: Boolean = deprecatedDirective.isDefined
def deprecationReason: Option[String] =
for {
dir <- deprecatedDirective
reason <- dir.args.collectFirst { case Binding("reason", StringValue(reason)) => reason }
} yield reason
}

/**
* Scalar extensions allow additional directives to be applied to a pre-existing Scalar type
Expand Down Expand Up @@ -903,16 +918,7 @@ case class EnumValueDefinition(
name: String,
description: Option[String],
directives: List[Directive]
) {
def deprecatedDirective: Option[Directive] =
directives.find(_.name == "deprecated")
def isDeprecated: Boolean = deprecatedDirective.isDefined
def deprecationReason: Option[String] =
for {
dir <- deprecatedDirective
reason <- dir.args.collectFirst { case Binding("reason", StringValue(reason)) => reason }
} yield reason
}
) extends Deprecatable

/**
* Input objects are composite types used as inputs into queries defined as a list of named input
Expand Down Expand Up @@ -979,16 +985,7 @@ case class Field(
args: List[InputValue],
tpe: Type,
directives: List[Directive]
) {
def deprecatedDirective: Option[Directive] =
directives.find(_.name == "deprecated")
def isDeprecated: Boolean = deprecatedDirective.isDefined
def deprecationReason: Option[String] =
for {
dir <- deprecatedDirective
reason <- dir.args.collectFirst { case Binding("reason", StringValue(reason)) => reason }
} yield reason
}
) extends Deprecatable

/**
* @param defaultValue a String encoding (using the GraphQL language) of the default value used by
Expand All @@ -1000,7 +997,7 @@ case class InputValue(
tpe: Type,
defaultValue: Option[Value],
directives: List[Directive]
)
) extends Deprecatable

sealed trait Value

Expand Down Expand Up @@ -1266,9 +1263,9 @@ object DirectiveDef {
|fields on a type or deprecated enum values.
""".stripMargin.trim
),
List(InputValue("reason", Some("Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted using the Markdown syntax, as specified by [CommonMark](https://commonmark.org/)."), NullableType(StringType), Some(StringValue("No longer supported")), Nil)),
List(InputValue("reason", Some("Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted using the Markdown syntax, as specified by [CommonMark](https://commonmark.org/)."), StringType, Some(StringValue("No longer supported")), Nil)),
false,
List(DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.ENUM_VALUE)
List(DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.ARGUMENT_DEFINITION, DirectiveLocation.INPUT_FIELD_DEFINITION, DirectiveLocation.ENUM_VALUE)
)

val SpecifiedBy: DirectiveDef =
Expand Down
34 changes: 34 additions & 0 deletions modules/core/src/test/scala/compiler/DirectivesSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,40 @@ final class DirectivesSuite extends CatsEffectSuite {
assertEquals(schema.map(testDirectiveDefs), List(expected).success)
}

test("Directive definition with deprecated argument") {
val expected =
DirectiveDef(
"foo",
None,
List(
InputValue(
"arg0",
None,
ScalarType.StringType,
None,
List(Directive("deprecated", List(Binding("reason", Value.StringValue("Use arg1 instead")))))
),
InputValue("arg1", None, ScalarType.StringType, None, Nil)
),
false,
List(FIELD)
)

val schema =
Schema("""
type Query {
foo: Int
}

directive @foo(
arg0: String! @deprecated(reason: "Use arg1 instead"),
arg1: String!
) on FIELD
""")

assertEquals(schema.map(testDirectiveDefs), List(expected).success)
}

test("Schema with directives") {
val schema =
"""|schema @foo {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ final class DirectiveValidationSuite extends CatsEffectSuite {
f10: Int @deprecated
f11: Int @deprecated(reason: 1)
f12: Int @deprecated(x: "foo")
f13: Int @deprecated(reason: null)
}

directive @withArg(i: Int) on FIELD_DEFINITION
Expand All @@ -193,6 +194,7 @@ final class DirectiveValidationSuite extends CatsEffectSuite {
Problem("""Unknown argument(s) 'x' in directive withRequiredArg"""),
Problem("""Expected String found '1' for 'reason' in directive deprecated"""),
Problem("""Unknown argument(s) 'x' in directive deprecated"""),
Problem("""Expected String found 'null' for 'reason' in directive deprecated"""),
)

assertEquals(schema.toProblems, problems)
Expand Down
Loading