All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sangria.validation.rules.NoFragmentCycles.scala Maven / Gradle / Ivy

package sangria.validation.rules

import sangria.ast
import sangria.ast.AstVisitorCommand
import sangria.ast.AstVisitorCommand._
import sangria.validation._

import scala.collection.mutable.{Set ⇒ MutableSet, Map ⇒ MutableMap, Stack ⇒ MutableStack}
import scala.language.postfixOps

class NoFragmentCycles extends ValidationRule {
   override def visitor(ctx: ValidationContext) = new AstValidatingVisitor {
     val visitedFrags = MutableSet[String]()
     val spreadPath = MutableStack[ast.FragmentSpread]()
     val spreadPathIndexByName = MutableMap[String, Int]()

     def detectCycleRecursive(fragmentDef: ast.FragmentDefinition): Vector[Violation] = {
       visitedFrags += fragmentDef.name

       val spreadNodes = ctx.getFragmentSpreads(fragmentDef)

       if (spreadNodes.nonEmpty) {
         var errors: Vector[Violation] = Vector.empty

         spreadPathIndexByName(fragmentDef.name) = spreadPath.size

         spreadNodes.foreach { spreadNode ⇒
           spreadPathIndexByName.get(spreadNode.name) match {
             case None ⇒
               spreadPath.push(spreadNode)

               if (!visitedFrags.contains(spreadNode.name)) {
                 ctx.fragments.get(spreadNode.name) match {
                   case Some(frag) ⇒ errors = errors ++ detectCycleRecursive(frag)
                   case _ ⇒ // do nothing
                 }
               }

               spreadPath.pop()

             case Some(cycleIndex) ⇒
               val cyclePath = spreadPath.toList.reverse.slice(cycleIndex, spreadPath.size)

               errors = errors :+ CycleErrorViolation(
                 spreadNode.name,
                 cyclePath map (_.name),
                 ctx.sourceMapper,
                 (cyclePath :+ spreadNode).flatMap(_.position))
           }
         }

         spreadPathIndexByName.remove(fragmentDef.name)

         errors
       } else Vector.empty
     }

     override val onEnter: ValidationVisit = {
       case fragmentDef @ ast.FragmentDefinition(fragmentName, _, _, _, _, _) ⇒
         if (visitedFrags.contains(fragmentName)) Right(Skip)
         else {
           val errors = detectCycleRecursive(fragmentDef)

           if (errors.nonEmpty) Left(errors)
           else Right(Continue)
         }

       case _: ast.OperationDefinition ⇒ Right(Skip)
     }
   }
 }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy