morpheus-graphql-core-0.28.2: src/Data/Morpheus/Validation/Query/Variable.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Data.Morpheus.Validation.Query.Variable
( resolveOperationVariables,
)
where
import Control.Monad.Except (throwError)
import Data.Mergeable
import Data.Morpheus.Error.Variable (uninitializedVariable)
import Data.Morpheus.Internal.Utils
( selectOr,
)
import Data.Morpheus.Types.Internal.AST
( Argument (..),
DefaultValue,
Directive (..),
FieldName,
Fragment (..),
IN,
ObjectEntry (..),
Operation (..),
Position,
RAW,
RawValue,
Ref (..),
ResolvedValue,
Selection (..),
SelectionContent (..),
SelectionSet,
TypeDefinition,
TypeRef (..),
VALID,
ValidValue,
Value (..),
Variable (..),
VariableContent (..),
VariableDefinitions,
Variables,
isNullable,
)
import Data.Morpheus.Types.Internal.Config
( Config (..),
VALIDATION_MODE (..),
)
import Data.Morpheus.Types.Internal.Validation
( BaseValidator,
Constraint (..),
InputSource (..),
askFragments,
askTypeDefinitions,
checkUnused,
constraint,
selectKnown,
setPosition,
startInput,
withScope,
)
import Data.Morpheus.Validation.Internal.Value
( validateInputByType,
)
import Relude
class ExploreRefs a where
exploreRefs :: a -> [Ref FieldName]
instance ExploreRefs RawValue where
exploreRefs (VariableValue ref) = [ref]
exploreRefs (Object fields) = concatMap (exploreRefs . entryValue) fields
exploreRefs (List ls) = concatMap exploreRefs ls
exploreRefs _ = []
instance ExploreRefs (Directive RAW) where
exploreRefs Directive {directiveArgs} = concatMap exploreRefs directiveArgs
instance ExploreRefs (Argument RAW) where
exploreRefs = exploreRefs . argumentValue
mapSelection :: (Selection RAW -> BaseValidator [b]) -> SelectionSet RAW -> BaseValidator [b]
mapSelection f = fmap concat . traverse f
allVariableRefs :: [SelectionSet RAW] -> BaseValidator (HashMap FieldName [Position])
allVariableRefs = collect <=< fmap (map toEntry . concat) . traverse (mapSelection searchRefs)
where
toEntry (Ref x y) = (x, [y])
exploreSelectionContent :: SelectionContent RAW -> BaseValidator [Ref FieldName]
exploreSelectionContent SelectionField = pure []
exploreSelectionContent (SelectionSet selSet) = mapSelection searchRefs selSet
---------------------------------------
searchRefs :: Selection RAW -> BaseValidator [Ref FieldName]
searchRefs Selection {selectionArguments, selectionDirectives, selectionContent} =
((directiveRefs <> argumentRefs) <>) <$> exploreSelectionContent selectionContent
where
directiveRefs = concatMap exploreRefs selectionDirectives
argumentRefs = concatMap exploreRefs selectionArguments
searchRefs (InlineFragment Fragment {fragmentSelection, fragmentDirectives}) =
(concatMap exploreRefs fragmentDirectives <>)
<$> mapSelection searchRefs fragmentSelection
searchRefs (Spread directives reference) =
(concatMap exploreRefs directives <>)
<$> ( askFragments
>>= selectKnown reference
>>= mapSelection searchRefs
. fragmentSelection
)
resolveOperationVariables ::
Config ->
Variables ->
Operation RAW ->
BaseValidator (VariableDefinitions VALID)
resolveOperationVariables
Config {validationMode}
root
Operation
{ operationSelection,
operationArguments
} =
checkUnusedVariables
*> traverse (lookupAndValidateValueOnBody root validationMode) operationArguments
where
checkUnusedVariables :: BaseValidator ()
checkUnusedVariables = do
uses <- allVariableRefs [operationSelection]
checkUnused uses (toList operationArguments)
lookupAndValidateValueOnBody ::
Variables ->
VALIDATION_MODE ->
Variable RAW ->
BaseValidator (Variable VALID)
lookupAndValidateValueOnBody
bodyVariables
validationMode
var@Variable
{ variableName,
variableType = variableType@TypeRef {typeWrappers, typeConName},
variablePosition,
variableValue = DefaultValue defaultValue
} =
withScope (setPosition variablePosition)
$ toVariable
<$> ( askTypeDefinitions
>>= selectKnown (Ref typeConName variablePosition)
>>= constraint INPUT var
>>= checkType getVariable defaultValue
)
where
toVariable x = var {variableValue = ValidVariableValue x}
getVariable :: Maybe ResolvedValue
getVariable = selectOr Nothing Just variableName bodyVariables
------------------------------------------------------------------
-- checkType ::
checkType ::
Maybe ResolvedValue ->
DefaultValue ->
TypeDefinition IN VALID ->
BaseValidator ValidValue
checkType (Just variable) Nothing varType = validator varType False variable
checkType (Just variable) (Just defValue) varType =
validator varType True defValue *> validator varType False variable
checkType Nothing (Just defValue) varType = validator varType True defValue
checkType Nothing Nothing varType
| validationMode /= WITHOUT_VARIABLES && not (isNullable variableType) =
throwError $ uninitializedVariable var
| otherwise =
returnNull
where
returnNull :: BaseValidator ValidValue
returnNull = selectOr (pure Null) (validator varType False) variableName bodyVariables
-----------------------------------------------------------------------------------------------
validator :: TypeDefinition IN VALID -> Bool -> ResolvedValue -> BaseValidator ValidValue
validator varTypeDef isDefaultValue varValue =
startInput
(SourceVariable var isDefaultValue)
( validateInputByType
typeWrappers
varTypeDef
varValue
)