{-# LANGUAGE NamedFieldPuns #-} module Data.Morpheus.Validation.Document.Validation ( validatePartialDocument ) where import Data.Maybe -- -- Morpheus 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