{-# 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
( DataField(..)
, DataType(..)
, DataObject
, DataTyCon(..)
, Key
, RawDataType(..)
, TypeAlias(..)
, isWeaker
, isWeaker
)
import Data.Morpheus.Types.Internal.Resolving
( Validation
, Failure(..)
)
validatePartialDocument :: [(Key, RawDataType)] -> Validation [(Key, DataType)]
validatePartialDocument lib = catMaybes <$> traverse validateType lib
where
validateType :: (Key, RawDataType) -> Validation (Maybe (Key, DataType))
validateType (name, FinalDataType x) = pure $ Just (name, x)
validateType (name, Implements interfaces object) =
asTuple name <$> object `mustImplement` interfaces
validateType _ = pure Nothing
asTuple name x = Just (name, x)
mustImplement :: DataObject -> [Key] -> Validation DataType
mustImplement object interfaceKey = do
interface <- traverse getInterfaceByKey interfaceKey
case concatMap (mustBeSubset object) interface of
[] -> pure $ DataObject object
errors -> failure $ partialImplements (typeName object) errors
mustBeSubset :: DataObject -> DataObject -> [(Key, Key, ImplementsError)]
mustBeSubset DataTyCon { typeData = objFields } DataTyCon { typeName, typeData = interfaceFields }
= concatMap checkField interfaceFields
where
checkField :: (Key, DataField) -> [(Key, Key, ImplementsError)]
checkField (key, DataField { fieldType = interfaceT@TypeAlias { aliasTyCon = interfaceTypeName, aliasWrappers = interfaceWrappers } })
= case lookup key objFields of
Just DataField { fieldType = objT@TypeAlias { aliasTyCon, aliasWrappers } }
| aliasTyCon == interfaceTypeName && not
(isWeaker aliasWrappers interfaceWrappers)
-> []
| otherwise
-> [ ( typeName
, key
, UnexpectedType { expectedType = render interfaceT
, foundType = render objT
}
)
]
Nothing -> [(typeName, key, UndefinedField)]
getInterfaceByKey :: Key -> Validation DataObject
getInterfaceByKey key = case lookup key lib of
Just (Interface x) -> pure x
_ -> failure $ unknownInterface key