{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Data.Morpheus.Types.Internal.Resolving.Resolver
( Event(..)
, GQLRootResolver(..)
, UnSubResolver
, Resolver
, MapStrategy(..)
, LiftOperation
, unsafeBind
, toResolver
, lift
, subscribe
, SubEvent
, GQLChannel(..)
, ResponseEvent(..)
, ResponseStream
, ObjectDeriving(..)
, Deriving(..)
, FieldRes
, WithOperation
, Context(..)
, unsafeInternalContext
, runResolverModel
, setTypeName
, ResolverModel(..)
, liftStateless
)
where
import Control.Monad.Fail (MonadFail(..))
import Control.Monad.Trans.Class ( MonadTrans(..))
import Control.Monad.IO.Class ( MonadIO(..) )
import Data.Maybe ( maybe )
import Data.Semigroup ( (<>)
, Semigroup(..)
)
import Control.Monad.Trans.Reader (ReaderT(..), ask,mapReaderT, withReaderT)
import Data.Text (pack)
import Data.Morpheus.Error.Internal ( internalResolvingError )
import Data.Morpheus.Error.Selection ( subfieldsNotSelected )
import Data.Morpheus.Types.Internal.AST.Selection
( Selection(..)
, SelectionContent(..)
, SelectionSet
, UnionTag(..)
, UnionSelection
, Operation(..)
)
import Data.Morpheus.Types.Internal.AST.Base
( Message
, Name
, OperationType
, QUERY
, MUTATION
, SUBSCRIPTION
, GQLErrors
, GQLError(..)
, VALID
, OperationType(..)
)
import Data.Morpheus.Types.Internal.AST.Data
( Schema
, Arguments
)
import Data.Morpheus.Types.Internal.AST.MergeSet
(toOrderedMap)
import Data.Morpheus.Types.Internal.Operation
( selectOr
, empty
, keyOf
, Merge(..)
)
import Data.Morpheus.Types.Internal.Resolving.Core
( Eventless
, Result(..)
, Failure(..)
, ResultT(..)
, cleanEvents
, mapEvent
, Event(..)
, Channel(..)
, StreamChannel
, GQLChannel(..)
, PushEvents(..)
, statelessToResultT
)
import Data.Morpheus.Types.Internal.AST.Value
( GQLValue(..)
, ValidValue
, ObjectEntry(..)
, Value(..)
, ScalarValue(..)
)
import Data.Morpheus.Types.IO ( renderResponse
, GQLResponse
)
type WithOperation (o :: OperationType) = LiftOperation o
type ResponseStream event (m :: * -> *) = ResultT (ResponseEvent event m) m
data ResponseEvent event (m :: * -> *)
= Publish event
| Subscribe (SubEvent event m)
type SubEvent event m = Event (Channel event) (event -> m GQLResponse)
data Context
= Context
{ currentSelection :: Selection VALID
, schema :: Schema
, operation :: Operation VALID
, currentTypeName :: Name
} deriving (Show)
newtype ResolverState event m a
= ResolverState
{
runResolverState :: ReaderT Context (ResultT event m) a
}
deriving
( Functor
, Applicative
, Monad
)
instance Monad m => MonadFail (ResolverState event m) where
fail = failure . pack
instance MonadTrans (ResolverState e) where
lift = ResolverState . lift . lift
instance (Monad m) => Failure Message (ResolverState e m) where
failure message = ResolverState $ do
selection <- currentSelection <$> ask
lift $ failure [resolverFailureMessage selection message]
instance (Monad m) => Failure GQLErrors (ResolverState e m) where
failure = ResolverState . lift . failure
instance (Monad m) => PushEvents e (ResolverState e m) where
pushEvents = ResolverState . lift . pushEvents
mapResolverState ::
( ReaderT Context (ResultT e m) a
-> ReaderT Context (ResultT e' m') a'
) -> ResolverState e m a
-> ResolverState e' m' a'
mapResolverState f (ResolverState x) = ResolverState (f x)
getState :: (Monad m) => ResolverState e m (Selection VALID)
getState = ResolverState $ currentSelection <$> ask
mapState :: (Context -> Context ) -> ResolverState e m a -> ResolverState e m a
mapState f = mapResolverState (withReaderT f)
clearStateResolverEvents :: (Functor m) => ResolverState e m a -> ResolverState e' m a
clearStateResolverEvents = mapResolverState (mapReaderT cleanEvents)
resolverFailureMessage :: Selection VALID -> Message -> GQLError
resolverFailureMessage Selection { selectionName, selectionPosition } message = GQLError
{ message = "Failure on Resolving Field \"" <> selectionName <> "\": " <> message
, locations = [selectionPosition]
}
data Resolver (o::OperationType) event (m :: * -> * ) value where
ResolverQ :: { runResolverQ :: ResolverState () m value } -> Resolver QUERY event m value
ResolverM :: { runResolverM :: ResolverState event m value } -> Resolver MUTATION event m value
ResolverS :: { runResolverS :: ResolverState (Channel event) m (ReaderT event (Resolver QUERY event m) value) } -> Resolver SUBSCRIPTION event m value
instance Show (Resolver o e m value) where
show ResolverQ {} = "Resolver QUERY e m a"
show ResolverM {} = "Resolver MUTATION e m a"
show ResolverS {} = "Resolver SUBSCRIPTION e m a"
deriving instance (Functor m) => Functor (Resolver o e m)
instance (LiftOperation o ,Monad m) => Applicative (Resolver o e m) where
pure = packResolver . pure
ResolverQ r1 <*> ResolverQ r2 = ResolverQ $ r1 <*> r2
ResolverM r1 <*> ResolverM r2 = ResolverM $ r1 <*> r2
ResolverS r1 <*> ResolverS r2 = ResolverS $ (<*>) <$> r1 <*> r2
instance (Monad m, LiftOperation o) => Monad (Resolver o e m) where
return = pure
(>>=) = unsafeBind
instance (MonadIO m) => MonadIO (Resolver QUERY e m) where
liftIO = lift . liftIO
instance (MonadIO m) => MonadIO (Resolver MUTATION e m) where
liftIO = lift . liftIO
instance MonadTrans (Resolver QUERY e) where
lift = packResolver . lift
instance MonadTrans (Resolver MUTATION e) where
lift = packResolver . lift
instance (LiftOperation o, Monad m) => Failure Message (Resolver o e m) where
failure = packResolver .failure
instance (LiftOperation o, Monad m) => Failure GQLErrors (Resolver o e m) where
failure = packResolver . failure
instance (Monad m) => PushEvents e (Resolver MUTATION e m) where
pushEvents = packResolver . pushEvents
liftStateless
:: ( LiftOperation o
, Monad m
)
=> Eventless a
-> Resolver o e m a
liftStateless
= packResolver
. ResolverState
. ReaderT
. const
. statelessToResultT
class LiftOperation (o::OperationType) where
packResolver :: Monad m => ResolverState e m a -> Resolver o e m a
withResolver :: Monad m => ResolverState e m a -> (a -> Resolver o e m b) -> Resolver o e m b
instance LiftOperation QUERY where
packResolver = ResolverQ . clearStateResolverEvents
withResolver ctxRes toRes = ResolverQ $ do
v <- clearStateResolverEvents ctxRes
runResolverQ $ toRes v
instance LiftOperation MUTATION where
packResolver = ResolverM
withResolver ctxRes toRes = ResolverM $ ctxRes >>= runResolverM . toRes
instance LiftOperation SUBSCRIPTION where
packResolver = ResolverS . pure . lift . packResolver
withResolver ctxRes toRes = ResolverS $ do
value <- clearStateResolverEvents ctxRes
runResolverS $ toRes value
mapResolverContext :: Monad m => (Context -> Context) -> Resolver o e m a -> Resolver o e m a
mapResolverContext f (ResolverQ res) = ResolverQ (mapState f res)
mapResolverContext f (ResolverM res) = ResolverM (mapState f res)
mapResolverContext f (ResolverS resM) = ResolverS $ do
res <- resM
pure $ ReaderT $ \e -> ResolverQ $ mapState f (runResolverQ (runReaderT res e))
setSelection :: Monad m => Selection VALID -> Resolver o e m a -> Resolver o e m a
setSelection currentSelection
= mapResolverContext (\ctx -> ctx { currentSelection })
setTypeName :: Monad m => Name -> Resolver o e m a -> Resolver o e m a
setTypeName currentTypeName
= mapResolverContext (\ctx -> ctx { currentTypeName } )
unsafeBind
:: forall o e m a b
. Monad m
=> Resolver o e m a
-> (a -> Resolver o e m b)
-> Resolver o e m b
unsafeBind (ResolverQ x) m2 = ResolverQ (x >>= runResolverQ . m2)
unsafeBind (ResolverM x) m2 = ResolverM (x >>= runResolverM . m2)
unsafeBind (ResolverS res) m2 = ResolverS $ do
(readResA :: ReaderT e (Resolver QUERY e m) a ) <- res
pure $ ReaderT $ \e -> ResolverQ $ do
let (resA :: Resolver QUERY e m a) = runReaderT readResA e
(valA :: a) <- runResolverQ resA
(readResB :: ReaderT e (Resolver QUERY e m) b) <- clearStateResolverEvents $ runResolverS (m2 valA)
runResolverQ $ runReaderT readResB e
subscribe
:: forall e m a
. ( PushEvents (Channel e) (ResolverState (Channel e) m)
, Monad m
)
=> [StreamChannel e]
-> Resolver QUERY e m (e -> Resolver QUERY e m a)
-> Resolver SUBSCRIPTION e m a
subscribe ch res = ResolverS $ do
pushEvents (map Channel ch :: [Channel e])
(eventRes :: e -> Resolver QUERY e m a) <- clearStateResolverEvents (runResolverQ res)
pure $ ReaderT eventRes
unsafeInternalContext :: (Monad m, LiftOperation o) => Resolver o e m Context
unsafeInternalContext = packResolver $ ResolverState ask
type family UnSubResolver (a :: * -> * ) :: (* -> *)
type instance UnSubResolver (Resolver SUBSCRIPTION e m) = Resolver QUERY e m
class MapStrategy
(from :: OperationType)
(to :: OperationType) where
mapStrategy
:: Monad m
=> Resolver from e m (Deriving from e m)
-> Resolver to e m (Deriving to e m)
instance MapStrategy o o where
mapStrategy = id
data Deriving (o :: OperationType) e (m :: * -> * )
= DerivingNull
| DerivingScalar ScalarValue
| DerivingEnum Name Name
| DerivingList [Deriving o e m]
| DerivingObject (ObjectDeriving o e m)
| DerivingUnion Name (Resolver o e m (Deriving o e m))
deriving (Show)
data ObjectDeriving o e m
= ObjectDeriving {
__typename :: Name,
objectFields :: [
( Name
, Resolver o e m (Deriving o e m)
)
]
} deriving (Show)
instance MapStrategy QUERY SUBSCRIPTION where
mapStrategy = ResolverS . pure . lift . fmap mapDeriving
mapDeriving
:: ( MapStrategy o o'
, Monad m
)
=> Deriving o e m
-> Deriving o' e m
mapDeriving DerivingNull = DerivingNull
mapDeriving (DerivingScalar x) = DerivingScalar x
mapDeriving (DerivingEnum typeName enum) = DerivingEnum typeName enum
mapDeriving (DerivingList x) = DerivingList $ map mapDeriving x
mapDeriving (DerivingObject x) = DerivingObject (mapObjectDeriving x)
mapDeriving (DerivingUnion name x) = DerivingUnion name (mapStrategy x)
mapObjectDeriving
:: ( MapStrategy o o'
, Monad m
)
=> ObjectDeriving o e m
-> ObjectDeriving o' e m
mapObjectDeriving (ObjectDeriving tyname x)
= ObjectDeriving tyname
$ map (mapEntry mapStrategy) x
mapEntry :: (a -> b) -> (Name, a) -> (Name, b)
mapEntry f (name,value) = (name, f value)
toResolver
:: forall o e m a b. (LiftOperation o, Monad m)
=> (Arguments VALID -> Eventless a)
-> ( a -> Resolver o e m b)
-> Resolver o e m b
toResolver toArgs = withResolver args
where
args :: ResolverState e m a
args = do
Selection { selectionArguments } <- getState
let resT = ResultT $ pure $ toArgs selectionArguments
ResolverState $ lift $ cleanEvents resT
type FieldRes o e m
= (Name, Resolver o e m (Deriving o e m))
instance Merge (Deriving o e m) where
merge p (DerivingObject x) (DerivingObject y)
= DerivingObject <$> merge p x y
merge _ _ _
= failure $ internalResolvingError "can't merge: incompatible resolvers"
instance Merge (ObjectDeriving o e m) where
merge _ (ObjectDeriving tyname x) (ObjectDeriving _ y)
= pure $ ObjectDeriving tyname (x <> y)
pickSelection :: Name -> UnionSelection -> SelectionSet VALID
pickSelection = selectOr empty unionTagSelection
withObject
:: (LiftOperation o, Monad m)
=> (SelectionSet VALID -> Resolver o e m value)
-> Selection VALID
-> Resolver o e m value
withObject f Selection { selectionName, selectionContent , selectionPosition } = checkContent selectionContent
where
checkContent (SelectionSet selection) = f selection
checkContent _ = failure (subfieldsNotSelected selectionName "" selectionPosition)
lookupRes
:: (LiftOperation o, Monad m)
=> Selection VALID
-> ObjectDeriving o e m
-> Resolver o e m ValidValue
lookupRes
Selection { selectionName }
| selectionName == "__typename"
= pure . Scalar . String . __typename
| otherwise
= maybe
(pure gqlNull)
(`unsafeBind` runDataResolver)
. lookup selectionName
. objectFields
resolveObject
:: forall o e m. (LiftOperation o , Monad m)
=> SelectionSet VALID
-> Deriving o e m
-> Resolver o e m ValidValue
resolveObject selectionSet (DerivingObject drv@ObjectDeriving { __typename }) =
Object . toOrderedMap <$> traverse resolver selectionSet
where
resolver :: Selection VALID -> Resolver o e m (ObjectEntry VALID)
resolver sel
= setSelection sel
$ setTypeName __typename
$ ObjectEntry (keyOf sel) <$> lookupRes sel drv
resolveObject _ _ =
failure $ internalResolvingError "expected object as resolver"
toEventResolver :: Monad m => ReaderT event (Resolver QUERY event m) ValidValue -> Context -> event -> m GQLResponse
toEventResolver (ReaderT subRes) sel event = do
value <- runResultT $ runReaderT (runResolverState $ runResolverQ (subRes event)) sel
pure $ renderResponse value
runDataResolver :: (Monad m, LiftOperation o) => Deriving o e m -> Resolver o e m ValidValue
runDataResolver = withResolver getState . __encode
where
__encode obj sel@Selection { selectionContent } = encodeNode obj selectionContent
where
encodeNode (DerivingList x) _ = List <$> traverse runDataResolver x
encodeNode objDrv@DerivingObject{} _ = withObject (`resolveObject` objDrv) sel
encodeNode (DerivingEnum _ enum) SelectionField = pure $ gqlString enum
encodeNode (DerivingEnum typename enum) unionSel@UnionSelection{}
= encodeNode (unionDrv (typename <> "EnumObject")) unionSel
where
unionDrv name
= DerivingUnion name
$ pure
$ DerivingObject
$ ObjectDeriving name [("enum", pure $ DerivingScalar $ String enum)]
encodeNode DerivingEnum {} _ =
failure ( "wrong selection on enum value" :: Message)
encodeNode (DerivingUnion typename unionRef) (UnionSelection selections)
= unionRef >>= resolveObject currentSelection
where currentSelection = pickSelection typename selections
encodeNode (DerivingUnion name _) _
= failure ("union Resolver \""<> name <> "\" should only recieve UnionSelection" :: Message)
encodeNode DerivingNull _ = pure Null
encodeNode (DerivingScalar x) SelectionField = pure $ Scalar x
encodeNode DerivingScalar {} _
= failure ("scalar Resolver should only recieve SelectionField" :: Message)
runResolver
:: Monad m
=> Resolver o event m ValidValue
-> Context
-> ResponseStream event m ValidValue
runResolver (ResolverQ resT) sel = cleanEvents $ (runReaderT $ runResolverState resT) sel
runResolver (ResolverM resT) sel = mapEvent Publish $ (runReaderT $ runResolverState resT) sel
runResolver (ResolverS resT) sel = ResultT $ do
(readResValue :: Result (Channel event1) (ReaderT event (Resolver QUERY event m) ValidValue)) <- runResultT $ (runReaderT $ runResolverState resT) sel
pure $ case readResValue of
Failure x -> Failure x
Success { warnings ,result , events = channels } -> do
let eventRes = toEventResolver result sel
Success {
events = [Subscribe $ Event channels eventRes],
warnings,
result = gqlNull
}
runRootDataResolver
:: (Monad m , LiftOperation o)
=> Eventless (Deriving o e m)
-> Context
-> ResponseStream e m (Value VALID)
runRootDataResolver
res
ctx@Context { operation = Operation { operationSelection } }
= do
root <- statelessToResultT res
runResolver (resolveObject operationSelection root) ctx
data GQLRootResolver (m :: * -> *) event (query :: (* -> *) -> * ) (mut :: (* -> *) -> * ) (sub :: (* -> *) -> * ) = GQLRootResolver
{ queryResolver :: query (Resolver QUERY event m)
, mutationResolver :: mut (Resolver MUTATION event m)
, subscriptionResolver :: sub (Resolver SUBSCRIPTION event m)
}
data ResolverModel e m
= ResolverModel
{ query :: Eventless (Deriving QUERY e m)
, mutation :: Eventless (Deriving MUTATION e m)
, subscription :: Eventless (Deriving SUBSCRIPTION e m)
}
runResolverModel :: Monad m => ResolverModel e m -> Context -> ResponseStream e m (Value VALID)
runResolverModel
ResolverModel
{ query
, mutation
, subscription
}
ctx@Context { operation = Operation { operationType} }
= selectByOperation operationType
where
selectByOperation Query
= runRootDataResolver query ctx
selectByOperation Mutation
= runRootDataResolver mutation ctx
selectByOperation Subscription
= runRootDataResolver subscription ctx