{-# 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
, DataTypeContent(..)
, Name
, Key
, RawDataType(..)
, TypeRef(..)
, DataFingerprint(..)
, Meta
, 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 { implementsName, implementsInterfaces, implementsMeta, implementsContent })
= asTuple name
<$> (implementsName, implementsMeta, implementsContent)
`mustImplement` implementsInterfaces
validateType _ = pure Nothing
asTuple name x = Just (name, x)
mustImplement :: (Name, Maybe Meta, DataObject) -> [Key] -> Validation DataType
mustImplement (typeName, typeMeta, object) interfaceKey = do
interface <- traverse getInterfaceByKey interfaceKey
case concatMap (mustBeSubset object) interface of
[] -> pure $ DataType { typeName
, typeFingerprint = DataFingerprint typeName []
, typeMeta
, typeContent = DataObject object
}
errors -> failure $ partialImplements typeName errors
mustBeSubset
:: DataObject -> (Name, DataObject) -> [(Key, Key, ImplementsError)]
mustBeSubset objFields (typeName, interfaceFields) = concatMap
checkField
interfaceFields
where
checkField :: (Key, DataField) -> [(Key, Key, ImplementsError)]
checkField (key, DataField { fieldType = interfaceT@TypeRef { typeConName = interfaceTypeName, typeWrappers = interfaceWrappers } })
= case lookup key objFields of
Just DataField { fieldType = objT@TypeRef { typeConName, typeWrappers } }
| typeConName == interfaceTypeName && not
(isWeaker typeWrappers interfaceWrappers)
-> []
| otherwise
-> [ ( typeName
, key
, UnexpectedType { expectedType = render interfaceT
, foundType = render objT
}
)
]
Nothing -> [(typeName, key, UndefinedField)]
getInterfaceByKey :: Key -> Validation (Name,DataObject)
getInterfaceByKey key = case lookup key lib of
Just Interface { interfaceContent } -> pure (key,interfaceContent)
_ -> failure $ unknownInterface key