@@ -3,6 +3,8 @@ package caliban.transformers
3
3
import caliban .InputValue
4
4
import caliban .execution .Field
5
5
import caliban .introspection .adt ._
6
+ import caliban .parsing .adt .Directive
7
+ import caliban .schema .Annotations .GQLDirective
6
8
import caliban .schema .Step
7
9
import caliban .schema .Step .{ FunctionStep , MetadataFunctionStep , NullStep , ObjectStep }
8
10
@@ -19,7 +21,7 @@ abstract class Transformer[-R] { self =>
19
21
* Set of type names that this transformer applies to.
20
22
* Needed for applying optimizations when combining transformers.
21
23
*/
22
- protected val typeNames : collection.Set [String ]
24
+ protected def typeNames : collection.Set [String ]
23
25
24
26
protected def transformStep [R1 <: R ](step : ObjectStep [R1 ], field : Field ): ObjectStep [R1 ]
25
27
@@ -326,20 +328,87 @@ object Transformer {
326
328
}
327
329
}
328
330
331
+ object ExcludeDirectives {
332
+
333
+ /**
334
+ * A transformer that allows excluding fields and inputs with specific directives.
335
+ *
336
+ * {{{
337
+ * case object Experimental extends GQLDirective(Directive("experimental"))
338
+ * case object Internal extends GQLDirective(Directive("internal"))
339
+ *
340
+ * ExcludeDirectives(Experimental, Internal)
341
+ * }}}
342
+ */
343
+ def apply (directives : GQLDirective * ): Transformer [Any ] =
344
+ if (directives.isEmpty) Empty else new ExcludeDirectives (directives.map(_.directive).toSet.contains)
345
+
346
+ /**
347
+ * A transformer that allows excluding fields and inputs with specific directives based on a predicate.
348
+ */
349
+ def apply (predicate : Directive => Boolean ): Transformer [Any ] =
350
+ new ExcludeDirectives (predicate)
351
+
352
+ }
353
+
354
+ final private class ExcludeDirectives (predicate : Directive => Boolean ) extends Transformer [Any ] {
355
+ private val map : mutable.HashMap [String , Set [String ]] = mutable.HashMap .empty
356
+
357
+ private def hasMatchingDirectives (directives : Option [List [Directive ]]): Boolean =
358
+ directives match {
359
+ case None | Some (Nil ) => false
360
+ case Some (dirs) => dirs.exists(predicate)
361
+ }
362
+
363
+ private def shouldKeepType (tpe : __Type, field : __Field): Boolean = {
364
+ val matched = hasMatchingDirectives(field.directives)
365
+ if (matched) map.updateWith(tpe.name.getOrElse(" " )) {
366
+ case Some (set) => Some (set + field.name)
367
+ case None => Some (Set (field.name))
368
+ }
369
+ ! matched
370
+ }
371
+
372
+ val typeVisitor : TypeVisitor =
373
+ TypeVisitor .fields.filterWith((t, field) => shouldKeepType(t, field)) |+|
374
+ TypeVisitor .fields.modify { field =>
375
+ def loop (arg : __InputValue): Option [__InputValue] =
376
+ if (arg._type.isNullable && hasMatchingDirectives(arg.directives)) None
377
+ else {
378
+ lazy val newType = arg._type.mapInnerType { t =>
379
+ t.copy(inputFields = t.inputFields(_).map(_.flatMap(loop)))
380
+ }
381
+ Some (arg.copy(`type` = () => newType))
382
+ }
383
+
384
+ field.copy(args = field.args(_).flatMap(loop))
385
+ }
386
+
387
+ protected def typeNames : collection.Set [String ] = map.keySet
388
+
389
+ protected def transformStep [R ](step : ObjectStep [R ], field : Field ): ObjectStep [R ] =
390
+ map.getOrElse(step.name, null ) match {
391
+ case null => step
392
+ case excl => step.copy(fields = name => if (! excl(name)) step.fields(name) else NullStep )
393
+ }
394
+ }
395
+
329
396
final private class Combined [- R ](left : Transformer [R ], right : Transformer [R ]) extends Transformer [R ] {
330
397
val typeVisitor : TypeVisitor = left.typeVisitor |+| right.typeVisitor
331
398
332
- protected val typeNames : mutable.HashSet [String ] = {
399
+ protected def typeNames : mutable.HashSet [String ] = {
333
400
val set = mutable.HashSet .from(left.typeNames)
334
401
set ++= right.typeNames
335
402
set
336
403
}
337
404
405
+ private lazy val materializedTypeNames = typeNames
406
+
338
407
protected def transformStep [R1 <: R ](step : ObjectStep [R1 ], field : Field ): ObjectStep [R1 ] =
339
408
right.transformStep(left.transformStep(step, field), field)
340
409
341
410
override def apply [R1 <: R ](step : ObjectStep [R1 ], field : Field ): ObjectStep [R1 ] =
342
- if (typeNames (step.name)) transformStep(step, field) else step
411
+ if (materializedTypeNames (step.name)) transformStep(step, field) else step
343
412
}
344
413
345
414
private def mapFunctionStep [R ](step : Step [R ])(f : Map [String , InputValue ] => Map [String , InputValue ]): Step [R ] =
0 commit comments