Skip to content

Commit 99eab24

Browse files
committed
API for setting custom directives
Prototype for #913 In this prototype, we check how we can apply custom directives much easier. Furthermore, we check if we can prove type safety, to that a custom directive that can be applied on some elements cannot be applied on others. Limitations: - a directive can be applied on a field definition for example. With that current approach, we cannot formulate that with the type system as a field definition lives in sangria.ast, and we only handle sangria.schema types. - we are introducing new types to mark on which elements a directive can be applied. Those types are kind of duplication of the current [sangria.schema.DirectiveLocation values](https://github.com/sangria-graphql/sangria/blob/f339b5df97bd89c2a24fcfc977a1f20191ffd7fc/modules/core/src/main/scala/sangria/schema/Schema.scala#L1136-L1158).
1 parent f339b5d commit 99eab24

File tree

8 files changed

+304
-49
lines changed

8 files changed

+304
-49
lines changed

build.sbt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,10 @@ lazy val core = project
126126
ProblemFilters.exclude[MissingTypesProblem]("sangria.schema.Directive$"),
127127
ProblemFilters.exclude[MissingTypesProblem]("sangria.schema.MappedAbstractType"),
128128
ProblemFilters.exclude[IncompatibleMethTypeProblem](
129-
"sangria.execution.Resolver.resolveSimpleListValue")
129+
"sangria.execution.Resolver.resolveSimpleListValue"),
130+
ProblemFilters.exclude[DirectMissingMethodProblem]("sangria.schema.Argument.apply"),
131+
ProblemFilters.exclude[DirectMissingMethodProblem]("sangria.schema.Field.subs"),
132+
ProblemFilters.exclude[DirectMissingMethodProblem]("sangria.schema.Field.apply")
130133
),
131134
Test / testOptions += Tests.Argument(TestFrameworks.ScalaTest, "-oF"),
132135
libraryDependencies ++= Seq(

modules/ast/src/main/scala/sangria/ast/QueryAst.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ sealed trait WithDirectives extends AstNode {
325325

326326
case class Directive(
327327
name: String,
328-
arguments: Vector[Argument],
328+
arguments: Vector[Argument] = Vector.empty,
329329
comments: Vector[Comment] = Vector.empty,
330330
location: Option[AstLocation] = None)
331331
extends AstNode

modules/core/src/main/scala/sangria/schema/AstSchemaBuilder.scala

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -324,8 +324,8 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
324324
description = definition.flatMap(_.description.map(_.value)),
325325
directives = directives,
326326
astDirectives =
327-
definition.fold(Vector.empty[ast.Directive])(_.directives) ++ extensions.flatMap(
328-
_.directives),
327+
(definition.fold(Vector.empty[ast.Directive])(_.directives) ++ extensions.flatMap(
328+
_.directives)).asInstanceOf[Vector[ast.Directive with OnSchema]],
329329
astNodes = Vector(mat.document) ++ extensions ++ definition.toVector
330330
)
331331

@@ -346,7 +346,8 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
346346
directives = directives,
347347
description = originalSchema.description,
348348
validationRules = originalSchema.validationRules,
349-
astDirectives = originalSchema.astDirectives ++ extensions.flatMap(_.directives),
349+
astDirectives = (originalSchema.astDirectives ++ extensions.flatMap(_.directives))
350+
.asInstanceOf[Vector[ast.Directive with OnSchema]],
350351
astNodes = {
351352
val (docs, other) = originalSchema.astNodes.partition(_.isInstanceOf[ast.Document])
352353
val newDoc = ast.Document.merge(docs.asInstanceOf[Vector[ast.Document]] :+ mat.document)
@@ -374,7 +375,7 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
374375
interfaces = interfaces,
375376
instanceCheck =
376377
(value: Any, clazz: Class[_], _: ObjectType[Ctx, Any]) => fn(value, clazz),
377-
astDirectives = directives,
378+
astDirectives = directives.asInstanceOf[Vector[ast.Directive with OnObject]],
378379
astNodes = (definition +: extensions).toVector
379380
)
380381
case None =>
@@ -384,7 +385,7 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
384385
fieldsFn = fields,
385386
interfaces = interfaces,
386387
instanceCheck = ObjectType.defaultInstanceCheck[Ctx, Any],
387-
astDirectives = directives,
388+
astDirectives = directives.asInstanceOf[Vector[ast.Directive with OnObject]],
388389
astNodes = (definition +: extensions).toVector
389390
)
390391
}
@@ -404,15 +405,17 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
404405
existing.copy(
405406
fieldsFn = fields,
406407
interfaces = interfaces,
407-
astDirectives = existing.astDirectives ++ extensions.flatMap(_.directives),
408+
astDirectives = existing.astDirectives ++ extensions.flatMap(
409+
_.directives.asInstanceOf[Vector[ast.Directive with OnObject]]),
408410
astNodes = existing.astNodes ++ extensions,
409411
instanceCheck = (value: Any, clazz: Class[_], _: ObjectType[Ctx, Any]) => fn(value, clazz)
410412
)(ClassTag(existing.valClass))
411413
case None =>
412414
existing.copy(
413415
fieldsFn = fields,
414416
interfaces = interfaces,
415-
astDirectives = existing.astDirectives ++ extensions.flatMap(_.directives),
417+
astDirectives = existing.astDirectives ++ extensions.flatMap(
418+
_.directives.asInstanceOf[Vector[ast.Directive with OnObject]]),
416419
astNodes = existing.astNodes ++ extensions,
417420
instanceCheck =
418421
existing.instanceCheck.asInstanceOf[(Any, Class[_], ObjectType[Ctx, _]) => Boolean]
@@ -430,7 +433,8 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
430433
name = typeName(definition),
431434
description = typeDescription(definition),
432435
fieldsFn = fields,
433-
astDirectives = definition.directives ++ extensions.flatMap(_.directives),
436+
astDirectives = (definition.directives ++ extensions.flatMap(_.directives))
437+
.asInstanceOf[Vector[ast.Directive with OnInputObjectType]],
434438
astNodes = definition +: extensions
435439
))
436440

@@ -449,7 +453,7 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
449453
fieldsFn = fields,
450454
interfaces = Nil,
451455
manualPossibleTypes = () => Nil,
452-
astDirectives = directives,
456+
astDirectives = directives.asInstanceOf[Vector[ast.Directive with OnInterface]],
453457
astNodes = (definition +: extensions).toVector
454458
))
455459
}
@@ -464,7 +468,8 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
464468
fieldsFn = fields,
465469
manualPossibleTypes = () => Nil,
466470
interfaces = Nil,
467-
astDirectives = existing.astDirectives ++ extensions.flatMap(_.directives),
471+
astDirectives = (existing.astDirectives ++ extensions
472+
.flatMap(_.directives)).asInstanceOf[Vector[ast.Directive with OnInterface]],
468473
astNodes = existing.astNodes ++ extensions
469474
)
470475

@@ -479,7 +484,8 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
479484
name = typeName(definition),
480485
description = typeDescription(definition),
481486
types = types,
482-
astDirectives = definition.directives ++ extensions.flatMap(_.directives),
487+
astDirectives = (definition.directives ++ extensions.flatMap(_.directives))
488+
.asInstanceOf[Vector[ast.Directive with OnUnion]],
483489
astNodes = definition +: extensions
484490
))
485491

@@ -491,8 +497,10 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
491497
mat: AstSchemaMaterializer[Ctx]): UnionType[Ctx] =
492498
existing.copy(
493499
typesFn = () => types,
494-
astDirectives = existing.astDirectives ++ extensions.flatMap(_.directives),
495-
astNodes = existing.astNodes ++ extensions)
500+
astDirectives = (existing.astDirectives ++ extensions.flatMap(_.directives))
501+
.asInstanceOf[Vector[ast.Directive with OnUnion]],
502+
astNodes = existing.astNodes ++ extensions
503+
)
496504

497505
def extendScalarAlias[T, ST](
498506
origin: MatOrigin,
@@ -516,7 +524,8 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
516524
coerceInput = scalarCoerceInput(definition),
517525
complexity = scalarComplexity(definition),
518526
scalarInfo = scalarValueInfo(definition),
519-
astDirectives = definition.directives ++ extensions.flatMap(_.directives),
527+
astDirectives = (definition.directives ++ extensions.flatMap(_.directives))
528+
.asInstanceOf[Vector[ast.Directive with OnScalar]],
520529
astNodes = definition +: extensions
521530
))
522531

@@ -531,7 +540,8 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
531540
name = typeName(definition),
532541
description = typeDescription(definition),
533542
values = values,
534-
astDirectives = definition.directives ++ extensions.flatMap(_.directives),
543+
astDirectives = (definition.directives ++ extensions.flatMap(_.directives))
544+
.asInstanceOf[Vector[ast.Directive with OnEnumType]],
535545
astNodes = definition +: extensions
536546
))
537547

@@ -547,7 +557,7 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
547557
description = enumValueDescription(definition),
548558
value = enumValue(typeDefinition, definition),
549559
deprecationReason = enumValueDeprecationReason(definition),
550-
astDirectives = definition.directives,
560+
astDirectives = definition.directives.asInstanceOf[Vector[ast.Directive with OnEnumValue]],
551561
astNodes = Vector(definition)
552562
))
553563

@@ -570,7 +580,7 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
570580
deprecationReason = fieldDeprecationReason(definition),
571581
complexity = fieldComplexity(typeDefinition, definition),
572582
manualPossibleTypes = () => Nil,
573-
astDirectives = definition.directives,
583+
astDirectives = definition.directives.asInstanceOf[Vector[ast.Directive with OnField]],
574584
astNodes = Vector(definition)
575585
))
576586

@@ -655,7 +665,7 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
655665
description = inputFieldDescription(definition),
656666
fieldType = tpe,
657667
defaultValue = defaultValue,
658-
astDirectives = definition.directives,
668+
astDirectives = definition.directives.asInstanceOf[Vector[ast.Directive with OnInputField]],
659669
astNodes = Vector(definition)
660670
))
661671

@@ -683,7 +693,7 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
683693
argumentType = tpe,
684694
defaultValue = defaultValue,
685695
fromInput = argumentFromInput(typeDefinition, fieldDefinition, definition),
686-
astDirectives = definition.directives,
696+
astDirectives = definition.directives.asInstanceOf[Vector[ast.Directive with OnArgument]],
687697
astNodes = Vector(definition)
688698
))
689699

@@ -720,8 +730,10 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
720730
mat: AstSchemaMaterializer[Ctx]): InputObjectType[T] =
721731
existing.copy(
722732
fieldsFn = fields,
723-
astDirectives = existing.astDirectives ++ extensions.flatMap(_.directives),
724-
astNodes = existing.astNodes ++ extensions)
733+
astDirectives = (existing.astDirectives ++ extensions.flatMap(_.directives))
734+
.asInstanceOf[Vector[ast.Directive with OnInputObjectType]],
735+
astNodes = existing.astNodes ++ extensions
736+
)
725737

726738
def transformEnumType[T](
727739
origin: MatOrigin,
@@ -731,7 +743,9 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
731743
val dirs = existing.astDirectives ++ extensions.flatMap(_.directives)
732744

733745
if (dirs.nonEmpty)
734-
existing.copy(astDirectives = dirs, astNodes = existing.astNodes ++ extensions)
746+
existing.copy(
747+
astDirectives = dirs.asInstanceOf[Vector[ast.Directive with OnEnumType]],
748+
astNodes = existing.astNodes ++ extensions)
735749
else existing
736750
}
737751

@@ -743,7 +757,9 @@ class DefaultAstSchemaBuilder[Ctx] extends AstSchemaBuilder[Ctx] {
743757
val dirs = existing.astDirectives ++ extensions.flatMap(_.directives)
744758

745759
if (dirs.nonEmpty)
746-
existing.copy(astDirectives = dirs, astNodes = existing.astNodes ++ extensions)
760+
existing.copy(
761+
astDirectives = dirs.asInstanceOf[Vector[ast.Directive with OnScalar]],
762+
astNodes = existing.astNodes ++ extensions)
747763
else existing
748764
}
749765

modules/core/src/main/scala/sangria/schema/AstSchemaMaterializer.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,8 @@ class AstSchemaMaterializer[Ctx] private (
703703
def extendEnumType(origin: MatOrigin, tpe: EnumType[_]) = {
704704
val extensions = findEnumExtensions(tpe.name)
705705
val extraValues = extensions.flatMap(_.values)
706-
val extraDirs = extensions.flatMap(_.directives)
706+
val extraDirs =
707+
extensions.flatMap(_.directives).asInstanceOf[Vector[ast.Directive with OnEnumType]]
707708

708709
val ev = extraValues.flatMap(buildEnumValue(origin, Right(tpe), _, extensions))
709710

modules/core/src/main/scala/sangria/schema/ResolverBasedAstSchemaBuilder.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,8 @@ class ResolverBasedAstSchemaBuilder[Ctx](val resolvers: Seq[AstSchemaResolver[Ct
149149
description = definition.flatMap(_.description.map(_.value)),
150150
directives = directives,
151151
astDirectives =
152-
definition.fold(Vector.empty[ast.Directive])(_.directives) ++ extensions.flatMap(
153-
_.directives),
152+
(definition.fold(Vector.empty[ast.Directive])(_.directives) ++ extensions.flatMap(
153+
_.directives)).asInstanceOf[Vector[ast.Directive with OnSchema]],
154154
astNodes = Vector(mat.document) ++ extensions ++ definition.toVector,
155155
validationRules = SchemaValidationRule.default :+ new ResolvedDirectiveValidationRule(
156156
this.directives.filterNot(_.repeatable).map(_.name).toSet)

0 commit comments

Comments
 (0)