{-# 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
, GResolver(..)
, Encode(..)
, encodeQuery
, encodeOperation
, ObjectResolvers(..)
, OBJ_RES
) where
import Control.Monad ((>=>))
import Control.Monad.Except (liftEither, runExceptT, withExceptT)
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 (pack)
import GHC.Generics
import Data.Morpheus.Error.Internal (internalErrorT)
import Data.Morpheus.Error.Selection (resolverError, subfieldsNotSelected)
import Data.Morpheus.Execution.Server.Decode (DecodeObject, decodeArguments)
import Data.Morpheus.Execution.Server.Generics.EnumRep (EnumRep (..))
import Data.Morpheus.Kind (Context (..), ENUM, GQL_KIND, OBJECT, SCALAR, UNION,
VContext (..))
import Data.Morpheus.Types.Custom (MapKind, Pair (..), mapKindFromList)
import Data.Morpheus.Types.GQLScalar (GQLScalar (..))
import Data.Morpheus.Types.GQLType (GQLType (CUSTOM, 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 (Key)
import Data.Morpheus.Types.Internal.Stream (PublishStream, StreamT (..), SubscribeStream,
initExceptStream, injectEvents)
import Data.Morpheus.Types.Internal.Validation (GQLErrors, ResolveT, failResolveT)
import Data.Morpheus.Types.Internal.Value (GQLValue (..), Value (..))
import Data.Morpheus.Types.Resolver (Event (..), Resolver, SubResolveT, SubResolver (..))
class Encode resolver value where
encode :: resolver -> (Key, Selection) -> value
instance {-# OVERLAPPABLE #-} EncodeKind (KIND a) a res => Encode a res where
encode resolver = encodeKind (VContext resolver :: VContext (KIND a) a)
instance (GQLValue value, Encode a value) => Encode (Maybe a) value where
encode Nothing = const gqlNull
encode (Just value) = encode value
instance Encode (Pair k v) value => Encode (k, v) value where
encode (key, value) = encode (Pair key value)
instance Encode [a] value => Encode (Set a) value where
encode = encode . S.toList
instance (Eq k, Monad m, Encode (MapKind k v (Resolver m)) (ResolveT m value)) =>
Encode (Map k v) (ResolveT m value) where
encode value = encode ((mapKindFromList $ M.toList value) :: MapKind k v (Resolver m))
instance (Monad m, GQLValue value, Encode a (m value)) => Encode [a] (m value) where
encode list query = gqlList <$> traverse (`encode` query) list
instance (DecodeObject a, Monad m, Encode b (ResolveT m value)) => Encode (a -> b) (ResolveT m value) where
encode resolver selection = decodeArgs selection >>= (`encode` selection) . resolver
where
decodeArgs :: (Key, Selection) -> ResolveT m a
decodeArgs = liftEither . decodeArguments . selectionArguments . snd
instance (Monad m, Encode a (ResolveT m value)) => Encode (Either String a) (ResolveT m value) where
encode resolver = (`encodeResolver` liftEither resolver)
instance (Monad m, Encode b (ResolveT m value)) => Encode (Resolver m b) (ResolveT m value) where
encode = flip encodeResolver
instance (Monad m, Encode b (ResolveT m value)) => Encode (Resolver m b) (ResolveT (StreamT m c) value) where
encode resolver = injectEvents [] . encode resolver
instance (Monad m, Encode b (ResolveT m Value)) => Encode (SubResolver m e c b) (SubResolveT m e c Value) where
encode resolver selection = handleResolver resolver
where
handleResolver SubResolver {subChannels, subResolver} =
initExceptStream [subChannels] (encodeResolver selection . subResolver)
class EncodeKind (kind :: GQL_KIND) a value where
encodeKind :: VContext kind a -> (Key, Selection) -> value
instance (GQLScalar a, GQLValue value) => EncodeKind SCALAR a value where
encodeKind = pure . gqlScalar . serialize . unVContext
instance (Generic a, EnumRep (Rep a), GQLValue value) => EncodeKind ENUM a value where
encodeKind = pure . gqlString . encodeRep . from . unVContext
instance (Monad m, EncodeCon m a value, GQLValue value) => EncodeKind OBJECT a (ResolveT m value) where
encodeKind (VContext value) (_, Selection {selectionRec = SelectionSet selection}) =
resolveFields selection (__typenameResolver : objectResolvers (Proxy :: Proxy (CUSTOM a)) value)
where
__typenameResolver = ("__typename", const $ pure $ gqlString $ __typeName (Proxy @a))
encodeKind _ (key, Selection {selectionPosition}) = failResolveT $ subfieldsNotSelected key "" selectionPosition
instance (Monad m, GQL_RES a, GResolver UNION (Rep a) (ResolveT m value)) => EncodeKind UNION a (ResolveT m value) where
encodeKind (VContext value) (key, sel@Selection {selectionRec = UnionSelection selections}) =
resolver (key, sel {selectionRec = SelectionSet lookupSelection})
where
lookupSelection = fromMaybe [] $ lookup typeName selections
(typeName, resolver) = unionResolver value
encodeKind _ _ = internalErrorT "union Resolver only should recieve UnionSelection"
type GQL_RES a = (Generic a, GQLType a)
type EncodeOperator m a value = Resolver m a -> ValidOperation -> m (Either GQLErrors value)
type OBJ_RES m a value = ObjectResolvers (CUSTOM a) a (ResolveT m value)
type EncodeCon m a value = (GQL_RES a, OBJ_RES m a value)
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)
type FieldRes m value = (Key, (Key, Selection) -> ResolveT m value)
type family GRes (kind :: GQL_KIND) value :: *
type instance GRes OBJECT v = [(Key, (Key, Selection) -> v)]
type instance GRes UNION v = (Key, (Key, Selection) -> v)
class ObjectResolvers (custom :: Bool) a value where
objectResolvers :: Proxy custom -> a -> [(Key, (Key, Selection) -> value)]
instance (Generic a, GResolver OBJECT (Rep a) value) => ObjectResolvers 'False a value where
objectResolvers _ = getResolvers (Context :: Context OBJECT value) . from
unionResolver :: (Generic a, GResolver UNION (Rep a) value) => a -> (Key, (Key, Selection) -> value)
unionResolver = getResolvers (Context :: Context UNION value) . from
class GResolver (kind :: GQL_KIND) f value where
getResolvers :: Context kind value -> f a -> GRes kind value
instance GResolver kind f value => GResolver kind (M1 D c f) value where
getResolvers context (M1 src) = getResolvers context src
instance GResolver kind f value => GResolver kind (M1 C c f) value where
getResolvers context (M1 src) = getResolvers context src
instance GResolver OBJECT U1 value where
getResolvers _ _ = []
instance (Selector s, GQLType a, Encode a value) => GResolver OBJECT (M1 S s (K1 s2 a)) value where
getResolvers _ m@(M1 (K1 src)) = [(pack (selName m), encode src)]
instance (GResolver OBJECT f value, GResolver OBJECT g value) => GResolver OBJECT (f :*: g) value where
getResolvers context (a :*: b) = getResolvers context a ++ getResolvers context b
instance (Selector s, GQLType a, Encode a value) => GResolver UNION (M1 S s (K1 s2 a)) value where
getResolvers _ (M1 (K1 src)) = (__typeName (Proxy @a), encode src)
instance (GResolver UNION a value, GResolver UNION b value) => GResolver UNION (a :+: b) value where
getResolvers context (L1 x) = getResolvers context x
getResolvers context (R1 x) = getResolvers context x
encodeQuery ::
forall m a schema. (GQL_RES a, GQL_RES schema, Monad m, EncodeCon m schema Value, EncodeCon m a Value)
=> schema
-> EncodeOperator m a Value
encodeQuery schema = encodeOperationWith (objectResolvers (Proxy :: Proxy (CUSTOM schema)) schema)
encodeOperation :: (Monad m, GQL_RES a, EncodeCon m a value, GQLValue value) => EncodeOperator m a value
encodeOperation = encodeOperationWith []
encodeOperationWith ::
forall m a value. (Monad m, GQL_RES a, GQLValue value, EncodeCon m a value)
=> [FieldRes m value]
-> EncodeOperator m a value
encodeOperationWith externalRes rootResolver Operation {operationSelection, operationPosition, operationName} =
runExceptT $
operationResolveT >>=
resolveFields operationSelection . (++) externalRes . objectResolvers (Proxy :: Proxy (CUSTOM a))
where
operationResolveT = withExceptT (resolverError operationPosition operationName) rootResolver
encodeResolver :: (Monad m, Encode a (ResolveT m res)) => (Key, Selection) -> Resolver m a -> ResolveT m res
encodeResolver selection@(fieldName, Selection {selectionPosition}) =
withExceptT (resolverError selectionPosition fieldName) >=> (`encode` selection)
resolveFields :: (Monad m, GQLValue a) => SelectionSet -> [FieldRes m a] -> ResolveT m a
resolveFields selectionSet resolvers = gqlObject <$> traverse selectResolver selectionSet
where
selectResolver (key, selection) =
(key, ) <$>
case selectionRec selection of
SelectionAlias name selectionRec -> lookupRes name (selection {selectionRec})
_ -> lookupRes key selection
where
lookupRes resKey sel = (fromMaybe (const $ return gqlNull) $ lookup resKey resolvers) (key, sel)