summaryrefslogtreecommitdiff
path: root/src/Language/GraphQL/Validate.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Language/GraphQL/Validate.hs')
-rw-r--r--src/Language/GraphQL/Validate.hs200
1 files changed, 137 insertions, 63 deletions
diff --git a/src/Language/GraphQL/Validate.hs b/src/Language/GraphQL/Validate.hs
index 0fa04cb..eedad6c 100644
--- a/src/Language/GraphQL/Validate.hs
+++ b/src/Language/GraphQL/Validate.hs
@@ -3,11 +3,12 @@
obtain one at https://mozilla.org/MPL/2.0/. -}
{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- | GraphQL validator.
module Language.GraphQL.Validate
- ( Error(..)
+ ( Validation.Error(..)
, document
, module Language.GraphQL.Validate.Rules
) where
@@ -20,38 +21,100 @@ import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
import Data.Sequence (Seq(..), (><), (|>))
import qualified Data.Sequence as Seq
+import Language.GraphQL.AST.DirectiveLocation (DirectiveLocation(..))
+import qualified Language.GraphQL.AST.DirectiveLocation as DirectiveLocation
import Language.GraphQL.AST.Document
import Language.GraphQL.Type.Internal
+import qualified Language.GraphQL.Type.Definition as Definition
+import qualified Language.GraphQL.Type.In as In
import qualified Language.GraphQL.Type.Out as Out
-import Language.GraphQL.Type.Schema (Schema(..))
+import Language.GraphQL.Type.Schema (Schema)
import qualified Language.GraphQL.Type.Schema as Schema
import Language.GraphQL.Validate.Rules
-import Language.GraphQL.Validate.Validation
+import Language.GraphQL.Validate.Validation (Validation(Validation))
+import qualified Language.GraphQL.Validate.Validation as Validation
-type ApplyRule m a =
- HashMap Name (Schema.Type m) -> Rule m -> Maybe (Out.Type m) -> a -> Seq (RuleT m)
+type ApplySelectionRule m a
+ = HashMap Name (Schema.Type m)
+ -> Validation.Rule m
+ -> Maybe (Out.Type m)
+ -> a
+ -> Seq (Validation.RuleT m)
+
+type ApplyRule m a = Validation.Rule m -> a -> Seq (Validation.RuleT m)
-- | Validates a document and returns a list of found errors. If the returned
-- list is empty, the document is valid.
-document :: forall m. Schema m -> [Rule m] -> Document -> Seq Error
+document :: forall m
+ . Schema m
+ -> [Validation.Rule m]
+ -> Document
+ -> Seq Validation.Error
document schema' rules' document' =
runReaderT reader context
where
context = Validation
- { ast = document'
- , schema = schema'
- , types = collectReferencedTypes schema'
+ { Validation.ast = document'
+ , Validation.schema = schema'
+ , Validation.types = collectReferencedTypes schema'
+ , Validation.directives = allDirectives
}
+ allDirectives =
+ HashMap.union (Schema.directives schema') defaultDirectives
+ defaultDirectives = HashMap.fromList
+ [ ("skip", skipDirective)
+ , ("include", includeDirective)
+ , ("deprecated", deprecatedDirective)
+ ]
+ includeDirective =
+ Schema.Directive includeDescription skipIncludeLocations includeArguments
+ includeArguments = HashMap.singleton "if"
+ $ In.Argument (Just "Included when true.") ifType Nothing
+ includeDescription = Just
+ "Directs the executor to include this field or fragment only when the \
+ \`if` argument is true."
+ skipDirective =
+ Schema.Directive skipDescription skipIncludeLocations skipArguments
+ skipArguments = HashMap.singleton "if"
+ $ In.Argument (Just "skipped when true.") ifType Nothing
+ ifType = In.NonNullScalarType Definition.boolean
+ skipDescription = Just
+ "Directs the executor to skip this field or fragment when the `if` \
+ \argument is true."
+ skipIncludeLocations =
+ [ ExecutableDirectiveLocation DirectiveLocation.Field
+ , ExecutableDirectiveLocation DirectiveLocation.FragmentSpread
+ , ExecutableDirectiveLocation DirectiveLocation.InlineFragment
+ ]
+ deprecatedDirective =
+ Schema.Directive deprecatedDescription deprecatedLocations deprecatedArguments
+ reasonDescription = Just
+ "Explains why this element was deprecated, usually also including a \
+ \suggestion for how to access supported similar data. Formatted using \
+ \the Markdown syntax, as specified by \
+ \[CommonMark](https://commonmark.org/).'"
+ deprecatedArguments = HashMap.singleton "reason"
+ $ In.Argument reasonDescription reasonType
+ $ Just "No longer supported"
+ reasonType = In.NamedScalarType Definition.string
+ deprecatedDescription = Just
+ "Marks an element of a GraphQL schema as no longer supported."
+ deprecatedLocations =
+ [ TypeSystemDirectiveLocation DirectiveLocation.FieldDefinition
+ , TypeSystemDirectiveLocation DirectiveLocation.ArgumentDefinition
+ , TypeSystemDirectiveLocation DirectiveLocation.InputFieldDefinition
+ , TypeSystemDirectiveLocation DirectiveLocation.EnumValue
+ ]
reader = do
rule' <- lift $ Seq.fromList rules'
join $ lift $ foldr (definition rule' context) Seq.empty document'
-definition :: Rule m
+definition :: Validation.Rule m
-> Validation m
-> Definition
- -> Seq (RuleT m)
- -> Seq (RuleT m)
-definition (DefinitionRule rule) _ definition' accumulator =
+ -> Seq (Validation.RuleT m)
+ -> Seq (Validation.RuleT m)
+definition (Validation.DefinitionRule rule) _ definition' accumulator =
accumulator |> rule definition'
definition rule context (ExecutableDefinition definition') accumulator =
accumulator >< executableDefinition rule context definition'
@@ -60,12 +123,12 @@ definition rule _ (TypeSystemDefinition typeSystemDefinition' _) accumulator =
definition rule _ (TypeSystemExtension extension _) accumulator =
accumulator >< typeSystemExtension rule extension
-typeSystemExtension :: Rule m -> TypeSystemExtension -> Seq (RuleT m)
+typeSystemExtension :: forall m. ApplyRule m TypeSystemExtension
typeSystemExtension rule = \case
SchemaExtension extension -> schemaExtension rule extension
TypeExtension extension -> typeExtension rule extension
-typeExtension :: Rule m -> TypeExtension -> Seq (RuleT m)
+typeExtension :: forall m. ApplyRule m TypeExtension
typeExtension rule = \case
ScalarTypeExtension _ directives' -> directives rule directives'
ObjectTypeFieldsDefinitionExtension _ _ directives' fields ->
@@ -88,27 +151,28 @@ typeExtension rule = \case
InputObjectTypeDirectivesExtension _ directives' ->
directives rule directives'
-schemaExtension :: Rule m -> SchemaExtension -> Seq (RuleT m)
+schemaExtension :: forall m. ApplyRule m SchemaExtension
schemaExtension rule = \case
SchemaOperationExtension directives' _ -> directives rule directives'
SchemaDirectivesExtension directives' -> directives rule directives'
-executableDefinition :: Rule m
+executableDefinition :: forall m
+ . Validation.Rule m
-> Validation m
-> ExecutableDefinition
- -> Seq (RuleT m)
+ -> Seq (Validation.RuleT m)
executableDefinition rule context (DefinitionOperation operation) =
operationDefinition rule context operation
executableDefinition rule context (DefinitionFragment fragment) =
fragmentDefinition rule context fragment
-typeSystemDefinition :: Rule m -> TypeSystemDefinition -> Seq (RuleT m)
+typeSystemDefinition :: forall m. ApplyRule m TypeSystemDefinition
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 :: forall m. ApplyRule m TypeDefinition
typeDefinition rule = \case
ScalarTypeDefinition _ _ directives' -> directives rule directives'
ObjectTypeDefinition _ _ _ directives' fields ->
@@ -122,30 +186,31 @@ typeDefinition rule = \case
-> directives rule directives'
<> foldMap (inputValueDefinition rule) fields
-enumValueDefinition :: Rule m -> EnumValueDefinition -> Seq (RuleT m)
+enumValueDefinition :: forall m. ApplyRule m EnumValueDefinition
enumValueDefinition rule (EnumValueDefinition _ _ directives') =
directives rule directives'
-fieldDefinition :: Rule m -> FieldDefinition -> Seq (RuleT m)
+fieldDefinition :: forall m. ApplyRule m FieldDefinition
fieldDefinition rule (FieldDefinition _ _ arguments' _ directives') =
directives rule directives' >< argumentsDefinition rule arguments'
-argumentsDefinition :: Rule m -> ArgumentsDefinition -> Seq (RuleT m)
+argumentsDefinition :: forall m. ApplyRule m ArgumentsDefinition
argumentsDefinition rule (ArgumentsDefinition definitions) =
foldMap (inputValueDefinition rule) definitions
-inputValueDefinition :: Rule m -> InputValueDefinition -> Seq (RuleT m)
+inputValueDefinition :: forall m. ApplyRule m InputValueDefinition
inputValueDefinition rule (InputValueDefinition _ _ _ _ directives') =
directives rule directives'
-operationDefinition :: Rule m
+operationDefinition :: forall m
+ . Validation.Rule m
-> Validation m
-> OperationDefinition
- -> Seq (RuleT m)
+ -> Seq (Validation.RuleT m)
operationDefinition rule context operation
- | OperationDefinitionRule operationRule <- rule =
+ | Validation.OperationDefinitionRule operationRule <- rule =
pure $ operationRule operation
- | VariablesRule variablesRule <- rule
+ | Validation.VariablesRule variablesRule <- rule
, OperationDefinition _ _ variables _ _ _ <- operation
= Seq.fromList (variableDefinition rule <$> variables)
|> variablesRule variables
@@ -155,11 +220,13 @@ operationDefinition rule context operation
= selectionSet types' rule (getRootType operationType) selections
>< directives rule directives'
where
- types' = types context
- getRootType Query = Just $ Out.NamedObjectType $ query $ schema context
- getRootType Mutation = Out.NamedObjectType <$> mutation (schema context)
+ types' = Validation.types context
+ getRootType Query =
+ Just $ Out.NamedObjectType $ Schema.query $ Validation.schema context
+ getRootType Mutation =
+ Out.NamedObjectType <$> Schema.mutation (Validation.schema context)
getRootType Subscription =
- Out.NamedObjectType <$> subscription (schema context)
+ Out.NamedObjectType <$> Schema.subscription (Validation.schema context)
typeToOut :: forall m. Schema.Type m -> Maybe (Out.Type m)
typeToOut (Schema.ObjectType objectType) =
@@ -171,27 +238,30 @@ typeToOut (Schema.EnumType enumType) = Just $ Out.NamedEnumType enumType
typeToOut (Schema.ScalarType scalarType) = Just $ Out.NamedScalarType scalarType
typeToOut _ = Nothing
-variableDefinition :: Rule m -> VariableDefinition -> RuleT m
-variableDefinition (ValueRule _ rule) (VariableDefinition _ _ value _) =
+variableDefinition :: forall m
+ . Validation.Rule m
+ -> VariableDefinition
+ -> Validation.RuleT m
+variableDefinition (Validation.ValueRule _ rule) (VariableDefinition _ _ value _) =
maybe (lift mempty) rule value
variableDefinition _ _ = lift mempty
fragmentDefinition :: forall m
- . Rule m
+ . Validation.Rule m
-> Validation m
-> FragmentDefinition
- -> Seq (RuleT m)
-fragmentDefinition (FragmentDefinitionRule rule) _ definition' =
+ -> Seq (Validation.RuleT m)
+fragmentDefinition (Validation.FragmentDefinitionRule rule) _ definition' =
pure $ rule definition'
fragmentDefinition rule context definition'
| FragmentDefinition _ typeCondition directives' selections _ <- definition'
- , FragmentRule definitionRule _ <- rule
+ , Validation.FragmentRule definitionRule _ <- rule
= applyToChildren typeCondition directives' selections
|> definitionRule definition'
| FragmentDefinition _ typeCondition directives' selections _ <- definition'
= applyToChildren typeCondition directives' selections
where
- types' = types context
+ types' = Validation.types context
applyToChildren typeCondition directives' selections
= selectionSet types' rule (lookupType' typeCondition) selections
>< directives rule directives'
@@ -204,12 +274,12 @@ lookupType :: forall m
lookupType typeCondition types' = HashMap.lookup typeCondition types'
>>= typeToOut
-selectionSet :: Traversable t => forall m. ApplyRule m (t Selection)
+selectionSet :: Traversable t => forall m. ApplySelectionRule m (t Selection)
selectionSet types' rule = foldMap . selection types' rule
-selection :: forall m. ApplyRule m Selection
+selection :: forall m. ApplySelectionRule m Selection
selection types' rule objectType selection'
- | SelectionRule selectionRule <- rule =
+ | Validation.SelectionRule selectionRule <- rule =
applyToChildren |> selectionRule objectType selection'
| otherwise = applyToChildren
where
@@ -221,33 +291,37 @@ selection types' rule objectType selection'
FragmentSpreadSelection fragmentSpread' ->
fragmentSpread rule fragmentSpread'
-field :: forall m. ApplyRule m Field
+field :: forall m. ApplySelectionRule m Field
field types' rule objectType field' = go field'
where
- go (Field _ fieldName arguments' directives' selections _)
- | ArgumentsRule fieldRule _ <- rule
- = applyToChildren fieldName arguments' directives' selections
- |> fieldRule field'
- | otherwise =
- applyToChildren fieldName arguments' directives' selections
- applyToChildren fieldName arguments' directives' selections =
- let child = objectType >>= lookupTypeField fieldName
- in selectionSet types' rule child selections
+ go (Field _ fieldName _ _ _ _)
+ | Validation.FieldRule fieldRule <- rule =
+ applyToChildren fieldName |> fieldRule objectType field'
+ | Validation.ArgumentsRule argumentsRule _ <- rule =
+ applyToChildren fieldName |> argumentsRule objectType field'
+ | otherwise = applyToChildren fieldName
+ typeFieldType (Out.Field _ type' _) = type'
+ applyToChildren fieldName =
+ let Field _ _ arguments' directives' selections _ = field'
+ fieldType = objectType
+ >>= fmap typeFieldType . lookupTypeField fieldName
+ in selectionSet types' rule fieldType selections
>< directives rule directives'
>< arguments rule arguments'
-arguments :: Rule m -> [Argument] -> Seq (RuleT m)
+arguments :: forall m. ApplyRule m [Argument]
arguments = (.) Seq.fromList . fmap . argument
-argument :: Rule m -> Argument -> RuleT m
-argument (ValueRule rule _) (Argument _ (Node value _) _) = rule value
+argument :: forall m. Validation.Rule m -> Argument -> Validation.RuleT m
+argument (Validation.ValueRule rule _) (Argument _ (Node value _) _) =
+ rule value
argument _ _ = lift mempty
-inlineFragment :: forall m. ApplyRule m InlineFragment
+inlineFragment :: forall m. ApplySelectionRule m InlineFragment
inlineFragment types' rule objectType inlineFragment' = go inlineFragment'
where
go (InlineFragment optionalType directives' selections _)
- | FragmentRule _ fragmentRule <- rule
+ | Validation.FragmentRule _ fragmentRule <- rule
= applyToChildren (refineTarget optionalType) directives' selections
|> fragmentRule inlineFragment'
| otherwise = applyToChildren (refineTarget optionalType) directives' selections
@@ -257,24 +331,24 @@ inlineFragment types' rule objectType inlineFragment' = go inlineFragment'
= selectionSet types' rule objectType' selections
>< directives rule directives'
-fragmentSpread :: Rule m -> FragmentSpread -> Seq (RuleT m)
+fragmentSpread :: forall m. ApplyRule m FragmentSpread
fragmentSpread rule fragmentSpread'@(FragmentSpread _ directives' _)
- | FragmentSpreadRule fragmentRule <- rule =
+ | Validation.FragmentSpreadRule fragmentRule <- rule =
applyToChildren |> fragmentRule fragmentSpread'
| otherwise = applyToChildren
where
applyToChildren = directives rule directives'
-directives :: Traversable t => Rule m -> t Directive -> Seq (RuleT m)
+directives :: Traversable t => forall m. ApplyRule m (t Directive)
directives rule directives'
- | DirectivesRule directivesRule <- rule =
+ | Validation.DirectivesRule directivesRule <- rule =
applyToChildren |> directivesRule directiveList
| otherwise = applyToChildren
where
directiveList = toList directives'
applyToChildren = foldMap (directive rule) directiveList
-directive :: Rule m -> Directive -> Seq (RuleT m)
-directive (ArgumentsRule _ argumentsRule) directive' =
+directive :: forall m. ApplyRule m Directive
+directive (Validation.ArgumentsRule _ argumentsRule) directive' =
pure $ argumentsRule directive'
directive rule (Directive _ arguments' _) = arguments rule arguments'