{-# LANGUAGE NamedFieldPuns #-}
module Data.Morpheus.Validation.Document.Validation
( validatePartialDocument
)
where
import Data.Maybe
import Data.Morpheus.Error.Document.Interface
( ImplementsError(..)
, partialImplements
, unknownInterface
)
import Data.Morpheus.Rendering.RenderGQL
( RenderGQL(..) )
import Data.Morpheus.Types.Internal.AST
( Name
, FieldDefinition(..)
, TypeDefinition(..)
, FieldsDefinition(..)
, TypeContent(..)
, TypeRef(..)
, isWeaker
, lookupWith
)
import Data.Morpheus.Types.Internal.Operation
( Selectable(..)
, Listable(..)
)
import Data.Morpheus.Types.Internal.Resolving
( Eventless
, Failure(..)
)
validatePartialDocument :: [TypeDefinition] -> Eventless [TypeDefinition]
validatePartialDocument lib = catMaybes <$> traverse validateType lib
where
validateType :: TypeDefinition -> Eventless (Maybe TypeDefinition)
validateType dt@TypeDefinition { typeName , typeContent = DataObject { objectImplements , objectFields} } = do
interface <- traverse getInterfaceByKey objectImplements
case concatMap (mustBeSubset objectFields) interface of
[] -> pure (Just dt)
errors -> failure $ partialImplements typeName errors
validateType TypeDefinition { typeContent = DataInterface {}} = pure Nothing
validateType x = pure (Just x)
mustBeSubset
:: FieldsDefinition -> (Name, FieldsDefinition) -> [(Name, Name, ImplementsError)]
mustBeSubset objFields (typeName, fields) = concatMap checkField (toList fields)
where
checkField :: FieldDefinition -> [(Name, Name, ImplementsError)]
checkField FieldDefinition { fieldName, fieldType = interfaceT@TypeRef { typeConName = interfaceTypeName, typeWrappers = interfaceWrappers } }
= selectOr err checkTypeEq fieldName objFields
where
err = [(typeName, fieldName, UndefinedField)]
checkTypeEq FieldDefinition { fieldType = objT@TypeRef { typeConName, typeWrappers } }
| typeConName == interfaceTypeName && not (isWeaker typeWrappers interfaceWrappers)
= []
| otherwise
= [ ( typeName , fieldName
, UnexpectedType { expectedType = render interfaceT
, foundType = render objT
}
)
]
getInterfaceByKey :: Name -> Eventless (Name, FieldsDefinition)
getInterfaceByKey interfaceName = case lookupWith typeName interfaceName lib of
Just TypeDefinition { typeContent = DataInterface { interfaceFields } } -> pure (interfaceName,interfaceFields)
_ -> failure $ unknownInterface interfaceName