{-# LANGUAGE TupleSections #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NamedFieldPuns #-}
module Data.Morpheus.Validation.Query.Arguments
( validateArguments
)
where
import Data.Maybe ( maybe )
import Data.Morpheus.Error.Arguments ( argumentGotInvalidValue
, argumentNameCollision
, undefinedArgument
, unknownArguments
)
import Data.Morpheus.Error.Input ( InputValidation
, inputErrorMessage
)
import Data.Morpheus.Error.Internal ( internalUnknownTypeMessage )
import Data.Morpheus.Error.Variable ( undefinedVariable )
import Data.Morpheus.Types.Internal.AST
( ValidVariables
, Variable(..)
, Argument(..)
, RawArgument
, RawArguments
, ValidArgument
, ValidArguments
, Arguments
, Ref(..)
, Position
, DataArgument
, DataField(..)
, Schema
, TypeRef(..)
, isFieldNullable
, lookupInputType
, Value(..)
, Name
, RawValue
, ResolvedValue
, RESOLVED
, VALID
, checkForUnknownKeys
, checkNameCollision
)
import Data.Morpheus.Types.Internal.Resolving
( Validation
, Failure(..)
)
import Data.Morpheus.Validation.Internal.Value
( validateInputValue )
import Data.Text ( Text )
resolveObject :: Name -> ValidVariables -> RawValue -> Validation ResolvedValue
resolveObject operationName variables = resolve
where
resolve :: RawValue -> Validation ResolvedValue
resolve Null = pure Null
resolve (Scalar x ) = pure $ Scalar x
resolve (Enum x ) = pure $ Enum x
resolve (List x ) = List <$> traverse resolve x
resolve (Object obj) = Object <$> traverse mapSecond obj
where mapSecond (fName, y) = (fName, ) <$> resolve y
resolve (VariableValue ref) =
ResolvedVariable ref <$> variableByRef operationName variables ref
variableByRef :: Name -> ValidVariables -> Ref -> Validation (Variable VALID)
variableByRef operationName variables Ref { refName, refPosition } = maybe
variableError
pure
(lookup refName variables)
where
variableError = failure $ undefinedVariable operationName refPosition refName
resolveArgumentVariables
:: Name
-> ValidVariables
-> DataField
-> RawArguments
-> Validation (Arguments RESOLVED)
resolveArgumentVariables operationName variables DataField { fieldName, fieldArgs }
= mapM resolveVariable
where
resolveVariable :: (Text, RawArgument) -> Validation (Text, Argument RESOLVED)
resolveVariable (key, Argument val position) = case lookup key fieldArgs of
Nothing -> failure $ unknownArguments fieldName [Ref key position]
Just _ -> do
constValue <- resolveObject operationName variables val
pure (key, Argument constValue position)
validateArgument
:: Schema
-> Position
-> Arguments RESOLVED
-> (Text, DataArgument)
-> Validation (Text, ValidArgument)
validateArgument lib fieldPosition requestArgs (key, argType@DataField { fieldType = TypeRef { typeConName, typeWrappers } })
= case lookup key requestArgs of
Nothing -> handleNullable
Just Argument { argumentValue = Null } -> handleNullable
Just argument -> validateArgumentValue argument
where
handleNullable
| isFieldNullable argType
= pure
(key, Argument { argumentValue = Null, argumentPosition = fieldPosition })
| otherwise
= failure $ undefinedArgument (Ref key fieldPosition)
validateArgumentValue :: Argument RESOLVED -> Validation (Text, ValidArgument)
validateArgumentValue Argument { argumentValue = value, argumentPosition } =
do
datatype <- lookupInputType typeConName
lib
(internalUnknownTypeMessage typeConName)
argumentValue <- handleInputError
$ validateInputValue lib [] typeWrappers datatype (key, value)
pure (key, Argument { argumentValue, argumentPosition })
where
handleInputError :: InputValidation a -> Validation a
handleInputError (Left err) = failure $ case inputErrorMessage err of
Left errors -> errors
Right message -> argumentGotInvalidValue key message argumentPosition
handleInputError (Right x) = pure x
validateArguments
:: Schema
-> Text
-> ValidVariables
-> (Text, DataField)
-> Position
-> RawArguments
-> Validation ValidArguments
validateArguments typeLib operatorName variables (key, field@DataField { fieldArgs }) pos rawArgs
= do
args <- resolveArgumentVariables operatorName variables field rawArgs
dataArgs <- checkForUnknownArguments args
mapM (validateArgument typeLib pos args) dataArgs
where
checkForUnknownArguments
:: Arguments RESOLVED -> Validation [(Text, DataField)]
checkForUnknownArguments args =
checkForUnknownKeys enhancedKeys fieldKeys argError
>> checkNameCollision enhancedKeys argumentNameCollision
>> pure fieldArgs
where
argError = unknownArguments key
enhancedKeys = map argToKey args
argToKey :: (Name, Argument RESOLVED) -> Ref
argToKey (key', Argument { argumentPosition }) = Ref key' argumentPosition
fieldKeys = map fst fieldArgs