morpheus-graphql-core-0.18.0: src/Data/Morpheus/Validation/Query/FragmentPreconditions.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Data.Morpheus.Validation.Query.FragmentPreconditions
( checkFragmentPreconditions,
)
where
import Control.Monad.Except (throwError)
import Data.Mergeable
import Data.Morpheus.Error.Fragment
( cannotSpreadWithinItself,
)
import Data.Morpheus.Internal.Graph
( Edges,
Graph,
Node,
cycleChecking,
)
import Data.Morpheus.Internal.Utils
( selectOr,
)
import Data.Morpheus.Types.Internal.AST
( Fragment (..),
FragmentName,
Fragments,
RAW,
Ref (..),
Selection (..),
SelectionContent (..),
SelectionSet,
)
import Data.Morpheus.Types.Internal.Validation
( BaseValidator,
askFragments,
checkUnused,
)
import Relude
checkUnusedFragments :: SelectionSet RAW -> BaseValidator ()
checkUnusedFragments selectionSet = do
fragments <- askFragments
usages <- usedFragments fragments selectionSet
checkUnused usages fragments
usedFragments :: Fragments RAW -> SelectionSet RAW -> BaseValidator (HashMap FragmentName [Node FragmentName])
usedFragments fragments = collect . map toEntry . concatMap findAllUses . toList
where
toEntry (Ref x y) = (x, [Ref x y])
findUsesSelectionContent :: SelectionContent RAW -> [Node FragmentName]
findUsesSelectionContent (SelectionSet selectionSet) =
concatMap findAllUses selectionSet
findUsesSelectionContent SelectionField = []
findAllUses :: Selection RAW -> [Node FragmentName]
findAllUses Selection {selectionContent} =
findUsesSelectionContent selectionContent
findAllUses (InlineFragment Fragment {fragmentSelection}) =
concatMap findAllUses fragmentSelection
findAllUses (Spread _ Ref {refName, refPosition}) =
[Ref refName refPosition] <> searchInFragment
where
searchInFragment =
selectOr
[]
(concatMap findAllUses . fragmentSelection)
refName
fragments
checkFragmentPreconditions :: SelectionSet RAW -> BaseValidator ()
checkFragmentPreconditions selection =
(exploreSpreads >>= cycleChecking (throwError . cannotSpreadWithinItself))
*> checkUnusedFragments selection
exploreSpreads :: BaseValidator (Graph FragmentName)
exploreSpreads = fmap exploreFragmentSpreads . toList <$> askFragments
exploreFragmentSpreads :: Fragment RAW -> Edges FragmentName
exploreFragmentSpreads Fragment {fragmentName, fragmentSelection, fragmentPosition} =
(Ref fragmentName fragmentPosition, concatMap scanSpread fragmentSelection)
class ScanSpread a where
scanSpread :: a -> [Node FragmentName]
instance ScanSpread (Selection RAW) where
scanSpread Selection {selectionContent} =
scanSpread selectionContent
scanSpread (InlineFragment Fragment {fragmentSelection}) =
concatMap scanSpread fragmentSelection
scanSpread (Spread _ Ref {refName, refPosition}) =
[Ref refName refPosition]
instance ScanSpread (SelectionContent RAW) where
scanSpread SelectionField = []
scanSpread (SelectionSet selectionSet) =
concatMap scanSpread selectionSet