{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Morpheus.Execution.Server.Encode
( EncodeCon
, EncodeMutCon
, EncodeSubCon
, encodeQuery
, encodeMut
, encodeSub
) where
import Control.Monad ((>=>))
import Control.Monad.Trans.Except
import Data.Map (Map)
import qualified Data.Map as M (toList)
import Data.Maybe (fromMaybe)
import Data.Proxy (Proxy (..))
import Data.Set (Set)
import qualified Data.Set as S (toList)
import Data.Text (Text, pack)
import Data.Typeable (Typeable)
import GHC.Generics
import Data.Morpheus.Error.Internal (internalErrorT)
import Data.Morpheus.Error.Selection (fieldNotResolved, subfieldsNotSelected)
import Data.Morpheus.Execution.Server.Decode (ArgumentsConstraint, decodeArguments)
import Data.Morpheus.Execution.Server.Generics.EnumRep (EnumRep (..))
import Data.Morpheus.Kind (ENUM, GQL_KIND, OBJECT, SCALAR, UNION, WRAPPER)
import Data.Morpheus.Types.Custom (MapKind, Pair (..), mapKindFromList)
import Data.Morpheus.Types.GQLScalar (GQLScalar (..))
import Data.Morpheus.Types.GQLType (GQLType (KIND, __typeName))
import Data.Morpheus.Types.Internal.AST.Operation (Operation (..), ValidOperation)
import Data.Morpheus.Types.Internal.AST.Selection (Selection (..), SelectionRec (..), SelectionSet)
import Data.Morpheus.Types.Internal.Base (Position)
import Data.Morpheus.Types.Internal.Stream (PublishStream, StreamState (..), StreamT (..),
SubscribeStream)
import Data.Morpheus.Types.Internal.Validation (GQLErrors, ResolveT, failResolveT)
import Data.Morpheus.Types.Internal.Value (ScalarValue (..), Value (..))
import Data.Morpheus.Types.Resolver (Event (..), Resolver, SubResolveT, SubResolver)
type EncodeOperator m a value
= Resolver m a -> ValidOperation -> m (Either GQLErrors value)
type EncodeCon m a v
= (Generic a, Typeable a, ObjectFieldResolvers (Rep a) (ResolveT m v))
type EncodeMutCon m event con mut
= EncodeCon (PublishStream m event con) mut Value
type EncodeSubCon m event con sub
= EncodeCon (SubscribeStream m event) sub (Event event con -> ResolveT m Value)
encodeQuery ::
(Monad m, EncodeCon m schema Value, EncodeCon m a Value)
=> schema
-> EncodeOperator m a Value
encodeQuery types rootResolver operator@Operation {operationSelection} =
runExceptT
(fmap resolversBy (operatorToResolveT operator rootResolver) >>=
resolveBySelection operationSelection . (++) (resolversBy types))
encodeMut :: (Monad m, EncodeCon m a Value) => EncodeOperator m a Value
encodeMut = encodeOperator resolveBySelection
encodeSub ::
(Monad m, EncodeSubCon m event con a)
=> EncodeOperator (SubscribeStream m event) a (Event event con -> ResolveT m Value)
encodeSub = encodeOperator (flip resolveSelection)
where
resolveSelection resolvers =
fmap toObj . mapM (selectResolver (const $ pure Null) resolvers)
where
toObj pairs args =
Object <$> mapM (\(key, valFunc) -> (key, ) <$> valFunc args) pairs
class ObjectFieldResolvers f o where
objectFieldResolvers :: f a -> [(Text, (Text, Selection) -> o)]
instance ObjectFieldResolvers U1 res where
objectFieldResolvers _ = []
instance (Selector s, Encoder a (KIND a) res) =>
ObjectFieldResolvers (M1 S s (K1 s2 a)) res where
objectFieldResolvers m@(M1 (K1 src)) = [(pack $ selName m, encode src)]
instance ObjectFieldResolvers f res => ObjectFieldResolvers (M1 D c f) res where
objectFieldResolvers (M1 src) = objectFieldResolvers src
instance ObjectFieldResolvers f res => ObjectFieldResolvers (M1 C c f) res where
objectFieldResolvers (M1 src) = objectFieldResolvers src
instance (ObjectFieldResolvers f res, ObjectFieldResolvers g res) =>
ObjectFieldResolvers (f :*: g) res where
objectFieldResolvers (a :*: b) =
objectFieldResolvers a ++ objectFieldResolvers b
class UnionResolvers f result where
unionResolvers :: f a -> (Text, (Text, Selection) -> result)
instance UnionResolvers f res => UnionResolvers (M1 S s f) res where
unionResolvers (M1 x) = unionResolvers x
instance UnionResolvers f res => UnionResolvers (M1 D c f) res where
unionResolvers (M1 x) = unionResolvers x
instance UnionResolvers f res => UnionResolvers (M1 C c f) res where
unionResolvers (M1 x) = unionResolvers x
instance (UnionResolvers a res, UnionResolvers b res) =>
UnionResolvers (a :+: b) res where
unionResolvers (L1 x) = unionResolvers x
unionResolvers (R1 x) = unionResolvers x
type ObjectConstraint a m
= ( Monad m
, Generic a
, GQLType a
, ObjectFieldResolvers (Rep a) (ResolveT m Value))
type UnionConstraint a m
= (Monad m, Generic a, GQLType a, UnionResolvers (Rep a) (ResolveT m Value))
type EnumConstraint a = (Generic a, EnumRep (Rep a))
newtype WithGQLKind a (b :: GQL_KIND) =
WithGQLKind
{ resolverValue :: a
}
type GQLKindOf a = WithGQLKind a (KIND a)
encode ::
forall a result. Encoder a (KIND a) result
=> a
-> (Text, Selection)
-> result
encode resolver = __encode (WithGQLKind resolver :: GQLKindOf a)
class Encoder a kind result where
__encode :: WithGQLKind a kind -> (Text, Selection) -> result
type ResValue m = (ResolveT m Value)
instance (GQLScalar a, Monad m) => Encoder a SCALAR (ResValue m) where
__encode = pure . pure . Scalar . serialize . resolverValue
instance (EnumConstraint a, Monad m) => Encoder a ENUM (ResValue m) where
__encode = pure . pure . Scalar . String . encodeRep . from . resolverValue
instance ObjectConstraint a m => Encoder a OBJECT (ResValue m) where
__encode (WithGQLKind value) (_, Selection {selectionRec = SelectionSet selection'}) =
resolveBySelection selection' (__typenameResolver : resolversBy value)
where
__typenameResolver =
("__typename", const $ return $ Scalar $ String $ __typeName (Proxy @a))
__encode _ (key, Selection {selectionPosition}) =
failResolveT $ subfieldsNotSelected key "" selectionPosition
instance UnionConstraint a m => Encoder a UNION (ResValue m) where
__encode (WithGQLKind value) (key', sel@Selection {selectionRec = UnionSelection selections'}) =
resolver (key', sel {selectionRec = SelectionSet lookupSelection})
where
lookupSelection :: SelectionSet
lookupSelection = fromMaybe [] $ lookup typeName selections'
(typeName, resolver) = unionResolvers (from value)
__encode _ _ =
internalErrorT "union Resolver only should recieve UnionSelection"
instance (GQLType a, Encoder a (KIND a) result) =>
UnionResolvers (K1 s a) result where
unionResolvers (K1 src) = (__typeName (Proxy @a), encode src)
instance (ArgumentsConstraint a, Monad m, Encoder b (KIND b) (ResValue m)) =>
Encoder (a -> Resolver m b) WRAPPER (ResValue m) where
__encode (WithGQLKind resolver) selection =
decodeArgs selection >>= encodeResolver selection . resolver
instance (Monad m, Encoder a (KIND a) (ResValue m), ArgumentsConstraint p) =>
Encoder (p -> Either String a) WRAPPER (ResValue m) where
__encode (WithGQLKind resolver) selection =
decodeArgs selection >>=
encodeResolver selection . (ExceptT . pure . resolver)
instance (ArgumentsConstraint a, Monad m, Encoder b (KIND b) (ResValue m)) =>
Encoder (a -> Resolver m b) WRAPPER (ResValue (StreamT m c)) where
__encode resolver selection =
ExceptT $
StreamT $ StreamState [] <$> runExceptT (__encode resolver selection)
instance (ArgumentsConstraint a, Monad m, Encoder b (KIND b) (ResValue m)) =>
Encoder (a -> SubResolver m e c b) WRAPPER (SubResolveT m e c Value) where
__encode (WithGQLKind resolver) selection =
decodeArgs selection >>= handleResolver . resolver
where
handleResolver Event {channels, content} =
ExceptT $
StreamT $
pure $
StreamState [channels] (Right $ encodeResolver selection . content)
instance (Monad m, Encoder a (KIND a) (ResValue m)) =>
Encoder (Maybe a) WRAPPER (ResValue m) where
__encode (WithGQLKind Nothing) = const $ pure Null
__encode (WithGQLKind (Just value)) = encode value
instance (Monad m, Encoder a (KIND a) (ResValue m)) =>
Encoder [a] WRAPPER (ResValue m) where
__encode (WithGQLKind list) query =
List <$> mapM (`__encode` query) (map WithGQLKind list :: [GQLKindOf a])
instance Encoder (Pair k v) OBJECT (ResValue m) =>
Encoder (k, v) WRAPPER (ResValue m) where
__encode (WithGQLKind (key, value)) = encode (Pair key value)
instance Encoder [a] WRAPPER result => Encoder (Set a) WRAPPER result where
__encode (WithGQLKind dataSet) = encode (S.toList dataSet)
instance (Eq k, Monad m, Encoder (MapKind k v (Resolver m)) OBJECT (ResValue m)) =>
Encoder (Map k v) WRAPPER (ResValue m) where
__encode (WithGQLKind value) =
encode ((mapKindFromList $ M.toList value) :: MapKind k v (Resolver m))
type ResolveSel result
= SelectionSet -> [(Text, (Text, Selection) -> result)] -> result
resolverToResolveT ::
Monad m => Position -> Text -> Resolver m a -> ResolveT m a
resolverToResolveT pos name = ExceptT . toResolveM
where
toResolveM :: Monad m => Resolver m a -> m (Either GQLErrors a)
toResolveM resolver = runExceptT resolver >>= runExceptT . liftEither
where
liftEither :: Monad m => Either String a -> ResolveT m a
liftEither (Left message) =
failResolveT $ fieldNotResolved pos name (pack message)
liftEither (Right value) = pure value
encodeResolver ::
(Monad m, Encoder a (KIND a) (ResValue m))
=> (Text, Selection)
-> Resolver m a
-> ResValue m
encodeResolver selection@(fieldName, Selection {selectionPosition}) =
resolverToResolveT selectionPosition fieldName >=> (`encode` selection)
decodeArgs ::
(Monad m, ArgumentsConstraint a) => (Text, Selection) -> ResolveT m a
decodeArgs (_, Selection {selectionArguments}) =
ExceptT $ pure $ decodeArguments selectionArguments
operatorToResolveT :: Monad m => ValidOperation -> Resolver m a -> ResolveT m a
operatorToResolveT Operation {operationPosition, operationName} =
resolverToResolveT operationPosition operationName
encodeOperator ::
(Monad m, EncodeCon m a v)
=> ResolveSel (ResolveT m v)
-> EncodeOperator m a v
encodeOperator resSel rootResolver operation@Operation {operationSelection} =
runExceptT
(operatorToResolveT operation rootResolver >>=
resSel operationSelection . resolversBy)
resolveBySelection :: Monad m => ResolveSel (ResolveT m Value)
resolveBySelection selection resolvers =
Object <$> mapM (selectResolver Null resolvers) selection
selectResolver ::
Monad m
=> a
-> [(Text, (Text, Selection) -> m a)]
-> (Text, Selection)
-> m (Text, a)
selectResolver defaultValue resolvers (key, selection) =
case selectionRec selection of
SelectionAlias name selectionRec ->
unwrapMonadTuple (key, lookupResolver name (selection {selectionRec}))
_ -> unwrapMonadTuple (key, lookupResolver key selection)
where
unwrapMonadTuple :: Monad m => (Text, m a) -> m (Text, a)
unwrapMonadTuple (text, ioa) = ioa >>= \x -> pure (text, x)
lookupResolver resolverKey sel =
(fromMaybe (const $ return $defaultValue) $ lookup resolverKey resolvers)
(key, sel)
resolversBy ::
(Generic a, ObjectFieldResolvers (Rep a) result)
=> a
-> [(Text, (Text, Selection) -> result)]
resolversBy = objectFieldResolvers . from