{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE TupleSections #-}
module Data.Morpheus.Validation.Query.Arguments
( validateArguments
) where
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 (incompatibleVariableType, undefinedVariable)
import Data.Morpheus.Rendering.RenderGQL (RenderGQL (..))
import Data.Morpheus.Types.Internal.AST.Operation (ValidVariables, Variable (..))
import Data.Morpheus.Types.Internal.AST.RawSelection (RawArgument (..), RawArguments, Reference (..))
import Data.Morpheus.Types.Internal.AST.Selection (Argument (..), ArgumentOrigin (..), Arguments)
import Data.Morpheus.Types.Internal.Base (EnhancedKey (..), Position)
import Data.Morpheus.Types.Internal.Data (DataArgument, DataField (..), DataField, DataTypeLib,
TypeAlias (..), isFieldNullable, isWeaker)
import Data.Morpheus.Types.Internal.Validation (Validation)
import Data.Morpheus.Types.Internal.Value (Value (Null))
import Data.Morpheus.Validation.Internal.Utils (checkForUnknownKeys, checkNameCollision, getInputType)
import Data.Morpheus.Validation.Query.Input.Object (validateInputValue)
import Data.Text (Text)
resolveArgumentVariables :: Text -> ValidVariables -> DataField -> RawArguments -> Validation Arguments
resolveArgumentVariables operatorName variables DataField {fieldName, fieldArgs} = mapM resolveVariable
where
resolveVariable :: (Text, RawArgument) -> Validation (Text, Argument)
resolveVariable (key, RawArgument argument) = pure (key, argument)
resolveVariable (key, VariableReference Reference {referenceName, referencePosition}) =
(key, ) . toArgument <$> lookupVar
where
toArgument argumentValue =
Argument {argumentValue, argumentOrigin = VARIABLE, argumentPosition = referencePosition}
lookupVar =
case lookup referenceName variables of
Nothing -> Left $ undefinedVariable operatorName referencePosition referenceName
Just Variable {variableValue, variableType, variableTypeWrappers} ->
case lookup key fieldArgs of
Nothing -> Left $ unknownArguments fieldName [EnhancedKey key referencePosition]
Just DataField {fieldType = fieldT@TypeAlias {aliasTyCon, aliasWrappers}} ->
if variableType == aliasTyCon && not (isWeaker variableTypeWrappers aliasWrappers)
then return variableValue
else Left $ incompatibleVariableType referenceName varSignature fieldSignature referencePosition
where varSignature = renderWrapped variableType variableTypeWrappers
fieldSignature = render fieldT
validateArgument :: DataTypeLib -> Position -> Arguments -> (Text, DataArgument) -> Validation (Text, Argument)
validateArgument lib fieldPosition requestArgs (key, argType@DataField {fieldType = TypeAlias { aliasTyCon
, aliasWrappers
}}) =
case lookup key requestArgs of
Nothing -> handleNullable
Just argument@Argument {argumentOrigin = VARIABLE} -> pure (key, argument)
Just Argument {argumentValue = Null} -> handleNullable
Just argument -> validateArgumentValue argument
where
handleNullable
| isFieldNullable argType =
pure (key, Argument {argumentValue = Null, argumentOrigin = INLINE, argumentPosition = fieldPosition})
| otherwise = Left $ undefinedArgument (EnhancedKey key fieldPosition)
validateArgumentValue :: Argument -> Validation (Text, Argument)
validateArgumentValue arg@Argument {argumentValue, argumentPosition} =
getInputType aliasTyCon lib (internalUnknownTypeMessage aliasTyCon) >>= checkType >> pure (key, arg)
where
checkType type' = handleInputError (validateInputValue lib [] aliasWrappers type' (key, argumentValue))
handleInputError :: InputValidation a -> Validation ()
handleInputError (Left err) = Left $ argumentGotInvalidValue key (inputErrorMessage err) argumentPosition
handleInputError _ = pure ()
validateArguments ::
DataTypeLib -> Text -> ValidVariables -> (Text, DataField) -> Position -> RawArguments -> Validation Arguments
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 -> Validation [(Text, DataField)]
checkForUnknownArguments args =
checkForUnknownKeys enhancedKeys fieldKeys argError >> checkNameCollision enhancedKeys argumentNameCollision >>
pure fieldArgs
where
argError = unknownArguments key
enhancedKeys = map argToKey args
argToKey (key', Argument {argumentPosition}) = EnhancedKey key' argumentPosition
fieldKeys = map fst fieldArgs