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-09-17 10:33:37 +02:00
|
|
|
{-# LANGUAGE LambdaCase #-}
|
2020-07-20 21:29:12 +02:00
|
|
|
|
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-09-18 07:32:58 +02:00
|
|
|
import Data.Foldable (toList)
|
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)
|
2020-09-17 10:33:37 +02:00
|
|
|
definition (DefinitionRule rule) definition' accumulator =
|
|
|
|
accumulator |> rule definition'
|
|
|
|
definition rule (ExecutableDefinition executableDefinition') accumulator =
|
|
|
|
accumulator >< executableDefinition rule executableDefinition'
|
|
|
|
definition rule (TypeSystemDefinition typeSystemDefinition' _) accumulator =
|
|
|
|
accumulator >< typeSystemDefinition rule typeSystemDefinition'
|
|
|
|
definition rule (TypeSystemExtension extension _) accumulator =
|
|
|
|
accumulator >< typeSystemExtension rule extension
|
|
|
|
|
|
|
|
typeSystemExtension :: Rule m -> TypeSystemExtension -> Seq (RuleT m)
|
|
|
|
typeSystemExtension rule = \case
|
|
|
|
SchemaExtension extension -> schemaExtension rule extension
|
|
|
|
TypeExtension extension -> typeExtension rule extension
|
|
|
|
|
|
|
|
typeExtension :: Rule m -> TypeExtension -> Seq (RuleT m)
|
|
|
|
typeExtension rule = \case
|
|
|
|
ScalarTypeExtension _ directives' -> directives rule directives'
|
|
|
|
ObjectTypeFieldsDefinitionExtension _ _ directives' fields ->
|
|
|
|
directives rule directives' >< foldMap (fieldDefinition rule) fields
|
|
|
|
ObjectTypeDirectivesExtension _ _ directives' -> directives rule directives'
|
|
|
|
ObjectTypeImplementsInterfacesExtension _ _ -> mempty
|
|
|
|
InterfaceTypeFieldsDefinitionExtension _ directives' fields ->
|
|
|
|
directives rule directives' >< foldMap (fieldDefinition rule) fields
|
|
|
|
InterfaceTypeDirectivesExtension _ directives' ->
|
|
|
|
directives rule directives'
|
|
|
|
UnionTypeUnionMemberTypesExtension _ directives' _ ->
|
|
|
|
directives rule directives'
|
|
|
|
UnionTypeDirectivesExtension _ directives' -> directives rule directives'
|
|
|
|
EnumTypeEnumValuesDefinitionExtension _ directives' values ->
|
|
|
|
directives rule directives' >< foldMap (enumValueDefinition rule) values
|
|
|
|
EnumTypeDirectivesExtension _ directives' -> directives rule directives'
|
|
|
|
InputObjectTypeInputFieldsDefinitionExtension _ directives' fields
|
|
|
|
-> directives rule directives'
|
|
|
|
>< foldMap (inputValueDefinition rule) fields
|
|
|
|
InputObjectTypeDirectivesExtension _ directives' ->
|
|
|
|
directives rule directives'
|
|
|
|
|
|
|
|
schemaExtension :: Rule m -> SchemaExtension -> Seq (RuleT m)
|
|
|
|
schemaExtension rule = \case
|
|
|
|
SchemaOperationExtension directives' _ -> directives rule directives'
|
|
|
|
SchemaDirectivesExtension directives' -> directives rule directives'
|
2020-09-14 07:49:33 +02:00
|
|
|
|
|
|
|
executableDefinition :: Rule m -> ExecutableDefinition -> Seq (RuleT m)
|
|
|
|
executableDefinition rule (DefinitionOperation operation) =
|
|
|
|
operationDefinition rule operation
|
|
|
|
executableDefinition rule (DefinitionFragment fragment) =
|
|
|
|
fragmentDefinition rule fragment
|
|
|
|
|
2020-09-17 10:33:37 +02:00
|
|
|
typeSystemDefinition :: Rule m -> TypeSystemDefinition -> Seq (RuleT m)
|
|
|
|
typeSystemDefinition rule = \case
|
|
|
|
SchemaDefinition directives' _ -> directives rule directives'
|
|
|
|
TypeDefinition typeDefinition' -> typeDefinition rule typeDefinition'
|
|
|
|
DirectiveDefinition _ _ arguments _ -> argumentsDefinition rule arguments
|
|
|
|
|
|
|
|
typeDefinition :: Rule m -> TypeDefinition -> Seq (RuleT m)
|
|
|
|
typeDefinition rule = \case
|
|
|
|
ScalarTypeDefinition _ _ directives' -> directives rule directives'
|
|
|
|
ObjectTypeDefinition _ _ _ directives' fields ->
|
|
|
|
directives rule directives' >< foldMap (fieldDefinition rule) fields
|
|
|
|
InterfaceTypeDefinition _ _ directives' fields ->
|
|
|
|
directives rule directives' >< foldMap (fieldDefinition rule) fields
|
|
|
|
UnionTypeDefinition _ _ directives' _ -> directives rule directives'
|
|
|
|
EnumTypeDefinition _ _ directives' values ->
|
|
|
|
directives rule directives' >< foldMap (enumValueDefinition rule) values
|
|
|
|
InputObjectTypeDefinition _ _ directives' fields
|
|
|
|
-> directives rule directives'
|
|
|
|
<> foldMap (inputValueDefinition rule) fields
|
|
|
|
|
|
|
|
enumValueDefinition :: Rule m -> EnumValueDefinition -> Seq (RuleT m)
|
|
|
|
enumValueDefinition rule (EnumValueDefinition _ _ directives') =
|
|
|
|
directives rule directives'
|
|
|
|
|
|
|
|
fieldDefinition :: Rule m -> FieldDefinition -> Seq (RuleT m)
|
|
|
|
fieldDefinition rule (FieldDefinition _ _ arguments _ directives') =
|
|
|
|
directives rule directives' >< argumentsDefinition rule arguments
|
|
|
|
|
|
|
|
argumentsDefinition :: Rule m -> ArgumentsDefinition -> Seq (RuleT m)
|
|
|
|
argumentsDefinition rule (ArgumentsDefinition definitions) =
|
|
|
|
foldMap (inputValueDefinition rule) definitions
|
|
|
|
|
|
|
|
inputValueDefinition :: Rule m -> InputValueDefinition -> Seq (RuleT m)
|
|
|
|
inputValueDefinition rule (InputValueDefinition _ _ _ _ directives') =
|
|
|
|
directives rule directives'
|
|
|
|
|
2020-09-14 07:49:33 +02:00
|
|
|
operationDefinition :: Rule m -> OperationDefinition -> Seq (RuleT m)
|
|
|
|
operationDefinition (OperationDefinitionRule rule) operationDefinition' =
|
|
|
|
pure $ rule operationDefinition'
|
|
|
|
operationDefinition rule (SelectionSet selections _) =
|
|
|
|
selectionSet rule selections
|
2020-09-17 10:33:37 +02:00
|
|
|
operationDefinition rule (OperationDefinition _ _ _ directives' selections _) =
|
|
|
|
selectionSet rule selections >< directives rule directives'
|
2020-09-14 07:49:33 +02:00
|
|
|
|
|
|
|
fragmentDefinition :: Rule m -> FragmentDefinition -> Seq (RuleT m)
|
|
|
|
fragmentDefinition (FragmentDefinitionRule rule) fragmentDefinition' =
|
|
|
|
pure $ rule fragmentDefinition'
|
2020-09-17 10:33:37 +02:00
|
|
|
fragmentDefinition rule fragmentDefinition'@(FragmentDefinition _ _ directives' selections _)
|
2020-09-14 07:49:33 +02:00
|
|
|
| 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-17 10:33:37 +02:00
|
|
|
>< directives rule directives'
|
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' ->
|
2020-09-17 10:33:37 +02:00
|
|
|
fragmentSpread rule fragmentSpread'
|
2020-09-14 07:49:33 +02:00
|
|
|
|
|
|
|
field :: Rule m -> Field -> Seq (RuleT m)
|
2020-09-17 10:33:37 +02:00
|
|
|
field rule field'@(Field _ _ _ directives' selections _)
|
|
|
|
| FieldRule fieldRule <- rule = applyToChildren |> fieldRule field'
|
|
|
|
| ArgumentsRule fieldRule _ <- rule = applyToChildren |> fieldRule field'
|
|
|
|
| otherwise = applyToChildren
|
|
|
|
where
|
|
|
|
applyToChildren = selectionSet rule selections >< directives rule directives'
|
2020-09-14 07:49:33 +02:00
|
|
|
|
|
|
|
inlineFragment :: Rule m -> InlineFragment -> Seq (RuleT m)
|
2020-09-17 10:33:37 +02:00
|
|
|
inlineFragment rule inlineFragment'@(InlineFragment _ directives' selections _)
|
|
|
|
| FragmentRule _ fragmentRule <- rule =
|
|
|
|
applyToChildren |> fragmentRule inlineFragment'
|
|
|
|
| otherwise = applyToChildren
|
|
|
|
where
|
|
|
|
applyToChildren = selectionSet rule selections
|
|
|
|
>< directives rule directives'
|
|
|
|
|
|
|
|
fragmentSpread :: Rule m -> FragmentSpread -> Seq (RuleT m)
|
|
|
|
fragmentSpread rule fragmentSpread'@(FragmentSpread _ directives' _)
|
|
|
|
| FragmentSpreadRule fragmentRule <- rule =
|
|
|
|
applyToChildren |> fragmentRule fragmentSpread'
|
|
|
|
| otherwise = applyToChildren
|
|
|
|
where
|
|
|
|
applyToChildren = directives rule directives'
|
|
|
|
|
|
|
|
directives :: Traversable t => Rule m -> t Directive -> Seq (RuleT m)
|
2020-09-18 07:32:58 +02:00
|
|
|
directives rule directives'
|
|
|
|
| DirectivesRule directivesRule <- rule =
|
|
|
|
applyToChildren |> directivesRule directiveList
|
|
|
|
| otherwise = applyToChildren
|
|
|
|
where
|
|
|
|
directiveList = toList directives'
|
|
|
|
applyToChildren = Seq.fromList $ fmap (directive rule) directiveList
|
2020-09-14 07:49:33 +02:00
|
|
|
|
2020-09-17 10:33:37 +02:00
|
|
|
directive :: Rule m -> Directive -> RuleT m
|
|
|
|
directive (ArgumentsRule _ rule) = rule
|
|
|
|
directive _ = lift . const mempty
|