2020-07-20 21:29:12 +02:00
|
|
|
{- This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
|
|
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
|
|
obtain one at https://mozilla.org/MPL/2.0/. -}
|
|
|
|
|
|
|
|
{-# LANGUAGE ExplicitForAll #-}
|
|
|
|
|
2020-07-24 21:34:31 +02:00
|
|
|
-- | GraphQL validator.
|
2020-07-20 21:29:12 +02:00
|
|
|
module Language.GraphQL.Validate
|
|
|
|
( Error(..)
|
|
|
|
, document
|
|
|
|
, module Language.GraphQL.Validate.Rules
|
|
|
|
) where
|
|
|
|
|
2020-09-14 07:49:33 +02:00
|
|
|
import Control.Monad (join)
|
|
|
|
import Control.Monad.Trans.Class (MonadTrans(..))
|
|
|
|
import Control.Monad.Trans.Reader (runReaderT)
|
2020-07-20 21:29:12 +02:00
|
|
|
import Data.Sequence (Seq(..), (><), (|>))
|
|
|
|
import qualified Data.Sequence as Seq
|
|
|
|
import Language.GraphQL.AST.Document
|
2020-08-25 21:03:42 +02:00
|
|
|
import Language.GraphQL.Type.Internal
|
|
|
|
import Language.GraphQL.Type.Schema (Schema(..))
|
2020-07-20 21:29:12 +02:00
|
|
|
import Language.GraphQL.Validate.Rules
|
2020-08-25 21:03:42 +02:00
|
|
|
import Language.GraphQL.Validate.Validation
|
2020-07-20 21:29:12 +02:00
|
|
|
|
2020-07-24 21:34:31 +02:00
|
|
|
-- | Validates a document and returns a list of found errors. If the returned
|
|
|
|
-- list is empty, the document is valid.
|
2020-08-25 21:03:42 +02:00
|
|
|
document :: forall m. Schema m -> [Rule m] -> Document -> Seq Error
|
2020-07-20 21:29:12 +02:00
|
|
|
document schema' rules' document' =
|
2020-09-14 07:49:33 +02:00
|
|
|
runReaderT reader context
|
2020-07-20 21:29:12 +02:00
|
|
|
where
|
2020-08-25 21:03:42 +02:00
|
|
|
context = Validation
|
2020-07-20 21:29:12 +02:00
|
|
|
{ ast = document'
|
|
|
|
, schema = schema'
|
2020-08-25 21:03:42 +02:00
|
|
|
, types = collectReferencedTypes schema'
|
2020-07-20 21:29:12 +02:00
|
|
|
}
|
2020-09-14 07:49:33 +02:00
|
|
|
reader = do
|
|
|
|
rule' <- lift $ Seq.fromList rules'
|
|
|
|
join $ lift $ foldr (definition rule') Seq.empty document'
|
|
|
|
|
|
|
|
definition :: Rule m -> Definition -> Seq (RuleT m) -> Seq (RuleT m)
|
|
|
|
definition (DefinitionRule rule) definition' acc =
|
|
|
|
acc |> rule definition'
|
|
|
|
definition rule (ExecutableDefinition executableDefinition') acc =
|
|
|
|
acc >< executableDefinition rule executableDefinition'
|
|
|
|
definition _ _ acc = acc
|
|
|
|
|
|
|
|
executableDefinition :: Rule m -> ExecutableDefinition -> Seq (RuleT m)
|
|
|
|
executableDefinition rule (DefinitionOperation operation) =
|
|
|
|
operationDefinition rule operation
|
|
|
|
executableDefinition rule (DefinitionFragment fragment) =
|
|
|
|
fragmentDefinition rule fragment
|
|
|
|
|
|
|
|
operationDefinition :: Rule m -> OperationDefinition -> Seq (RuleT m)
|
|
|
|
operationDefinition (OperationDefinitionRule rule) operationDefinition' =
|
|
|
|
pure $ rule operationDefinition'
|
|
|
|
operationDefinition rule (SelectionSet selections _) =
|
|
|
|
selectionSet rule selections
|
|
|
|
operationDefinition rule (OperationDefinition _ _ _ _ selections _) =
|
|
|
|
selectionSet rule selections
|
|
|
|
|
|
|
|
fragmentDefinition :: Rule m -> FragmentDefinition -> Seq (RuleT m)
|
|
|
|
fragmentDefinition (FragmentDefinitionRule rule) fragmentDefinition' =
|
|
|
|
pure $ rule fragmentDefinition'
|
|
|
|
fragmentDefinition rule fragmentDefinition'@(FragmentDefinition _ _ _ selections _)
|
|
|
|
| FragmentRule definitionRule _ <- rule =
|
|
|
|
applyToChildren |> definitionRule fragmentDefinition'
|
|
|
|
| otherwise = applyToChildren
|
2020-09-07 22:01:49 +02:00
|
|
|
where
|
2020-09-14 07:49:33 +02:00
|
|
|
applyToChildren = selectionSet rule selections
|
2020-09-07 22:01:49 +02:00
|
|
|
|
2020-09-14 07:49:33 +02:00
|
|
|
selectionSet :: Traversable t => Rule m -> t Selection -> Seq (RuleT m)
|
|
|
|
selectionSet = foldMap . selection
|
2020-08-31 11:06:27 +02:00
|
|
|
|
2020-09-14 07:49:33 +02:00
|
|
|
selection :: Rule m -> Selection -> Seq (RuleT m)
|
|
|
|
selection rule selection'
|
|
|
|
| SelectionRule selectionRule <- rule =
|
|
|
|
applyToChildren |> selectionRule selection'
|
|
|
|
| otherwise = applyToChildren
|
2020-08-28 08:32:21 +02:00
|
|
|
where
|
2020-09-14 07:49:33 +02:00
|
|
|
applyToChildren =
|
|
|
|
case selection' of
|
|
|
|
FieldSelection field' -> field rule field'
|
|
|
|
InlineFragmentSelection inlineFragment' ->
|
|
|
|
inlineFragment rule inlineFragment'
|
|
|
|
FragmentSpreadSelection fragmentSpread' ->
|
|
|
|
pure $ fragmentSpread rule fragmentSpread'
|
|
|
|
|
|
|
|
field :: Rule m -> Field -> Seq (RuleT m)
|
|
|
|
field (FieldRule rule) field' = pure $ rule field'
|
|
|
|
field rule (Field _ _ _ _ selections _) = selectionSet rule selections
|
|
|
|
|
|
|
|
inlineFragment :: Rule m -> InlineFragment -> Seq (RuleT m)
|
|
|
|
inlineFragment (FragmentRule _ rule) inlineFragment' =
|
|
|
|
pure $ rule inlineFragment'
|
|
|
|
inlineFragment rule (InlineFragment _ _ selections _) =
|
|
|
|
selectionSet rule selections
|
|
|
|
|
|
|
|
fragmentSpread :: Rule m -> FragmentSpread -> RuleT m
|
|
|
|
fragmentSpread (FragmentSpreadRule rule) fragmentSpread' = rule fragmentSpread'
|
|
|
|
fragmentSpread _ _ = lift mempty
|