packages feed

morpheus-graphql-app 0.27.0 → 0.27.1

raw patch · 25 files changed

+774/−561 lines, 25 filesdep ~textPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: text

API changes (from Hackage documentation)

- Data.Morpheus.App.Internal.Resolving: getChannels :: EventHandler e => e -> [Channel e]
- Data.Morpheus.App.Internal.Resolving: lift :: (MonadTrans t, Monad m) => m a -> t m a
- Data.Morpheus.App.Internal.Resolving: liftResolverState :: (LiftOperation o, Monad m) => ResolverState a -> Resolver o e m a
- Data.Morpheus.Types.GQLWrapper: instance Data.Morpheus.Types.GQLWrapper.EncodeWrapper Data.Morpheus.App.Internal.Resolving.Resolver.SubscriptionField
+ Data.Morpheus.App.Internal.Resolving: class (MonadResolver m, MonadIO m) => MonadIOResolver (m :: Type -> Type)
+ Data.Morpheus.App.Internal.Resolving: class (Monad m, MonadReader ResolverContext m, MonadFail m, MonadError GQLError m, Monad (MonadParam m)) => MonadResolver (m :: Type -> Type) where {
+ Data.Morpheus.App.Internal.Resolving: liftState :: MonadResolver m => ResolverState a -> m a
+ Data.Morpheus.App.Internal.Resolving: publish :: (MonadResolver m, MonadOperation m ~ MUTATION) => [MonadEvent m] -> m ()
+ Data.Morpheus.App.Internal.Resolving: runResolver :: MonadResolver m => Maybe (Selection VALID -> ResolverState (Channel (MonadEvent m))) -> m ValidValue -> ResolverContext -> ResponseStream (MonadEvent m) (MonadParam m) ValidValue
+ Data.Morpheus.App.Internal.Resolving: type MonadEvent m :: Type;
+ Data.Morpheus.App.Internal.Resolving: type MonadMutation m :: (Type -> Type);
+ Data.Morpheus.App.Internal.Resolving: type MonadOperation m :: OperationType;
+ Data.Morpheus.App.Internal.Resolving: type MonadParam m :: (Type -> Type);
+ Data.Morpheus.App.Internal.Resolving: type MonadQuery m :: (Type -> Type);
+ Data.Morpheus.App.Internal.Resolving: type MonadSubscription m :: (Type -> Type);
+ Data.Morpheus.Types.GQLWrapper: instance Data.Morpheus.Types.GQLWrapper.EncodeWrapper Data.Morpheus.App.Internal.Resolving.MonadResolver.SubscriptionField
+ Data.Morpheus.Types.GQLWrapper: instance Data.Morpheus.Types.GQLWrapper.EncodeWrapperValue Data.Sequence.Internal.Seq
+ Data.Morpheus.Types.GQLWrapper: instance Data.Morpheus.Types.GQLWrapper.EncodeWrapperValue Data.Set.Internal.Set
+ Data.Morpheus.Types.GQLWrapper: instance Data.Morpheus.Types.GQLWrapper.EncodeWrapperValue Data.Vector.Vector
+ Data.Morpheus.Types.GQLWrapper: instance Data.Morpheus.Types.GQLWrapper.EncodeWrapperValue GHC.Base.NonEmpty
- Data.Morpheus.App.Internal.Resolving: [SubscriptionField] :: (forall e m v. a ~ Resolver SUBSCRIPTION e m v => Channel e) -> a -> SubscriptionField a
+ Data.Morpheus.App.Internal.Resolving: [SubscriptionField] :: (forall m v. (a ~ m v, MonadResolver m, MonadOperation m ~ SUBSCRIPTION) => Channel (MonadEvent m)) -> a -> SubscriptionField a
- Data.Morpheus.App.Internal.Resolving: getArguments :: (LiftOperation o, Monad m) => Resolver o e m (Arguments VALID)
+ Data.Morpheus.App.Internal.Resolving: getArguments :: MonadResolver m => m (Arguments VALID)
- Data.Morpheus.App.Internal.Resolving: subscribe :: Monad m => Channel e -> Resolver QUERY e m (e -> Resolver SUBSCRIPTION e m a) -> SubscriptionField (Resolver SUBSCRIPTION e m a)
+ Data.Morpheus.App.Internal.Resolving: subscribe :: (MonadResolver m, MonadOperation m ~ SUBSCRIPTION) => Channel (MonadEvent m) -> MonadQuery m (MonadEvent m -> m a) -> SubscriptionField (m a)
- Data.Morpheus.App.Internal.Resolving: type Channel e;
+ Data.Morpheus.App.Internal.Resolving: type Channel e :: Type;
- Data.Morpheus.App.Internal.Resolving: withArguments :: (LiftOperation o, Monad m) => (Arguments VALID -> Resolver o e m a) -> Resolver o e m a
+ Data.Morpheus.App.Internal.Resolving: withArguments :: MonadResolver m => (Arguments VALID -> m a) -> m a
- Data.Morpheus.App.NamedResolvers: data ResultBuilder o e m
+ Data.Morpheus.App.NamedResolvers: data ResultBuilder m
- Data.Morpheus.App.NamedResolvers: getArgument :: (LiftOperation o, Monad m) => FieldName -> Resolver o e m (Value VALID)
+ Data.Morpheus.App.NamedResolvers: getArgument :: MonadResolver m => FieldName -> m (Value VALID)
- Data.Morpheus.App.NamedResolvers: nullRes :: (LiftOperation o, Monad m) => Resolver o e m (ResultBuilder o e m)
+ Data.Morpheus.App.NamedResolvers: nullRes :: MonadResolver m => m (ResultBuilder m)
- Data.Morpheus.App.NamedResolvers: object :: (LiftOperation o, Monad m) => [(FieldName, Resolver o e m (ResolverValue (Resolver o e m)))] -> Resolver o e m (ResultBuilder o e m)
+ Data.Morpheus.App.NamedResolvers: object :: MonadResolver m => [(FieldName, m (ResolverValue m))] -> m (ResultBuilder m)
- Data.Morpheus.App.NamedResolvers: queryResolvers :: Monad m => [(TypeName, NamedResolverFunction QUERY e m)] -> RootResolverValue e m
+ Data.Morpheus.App.NamedResolvers: queryResolvers :: Monad m => [(TypeName, NamedFunction (Resolver QUERY e m))] -> RootResolverValue e m
- Data.Morpheus.App.NamedResolvers: type NamedResolverFunction o e m = [ValidValue] -> Resolver o e m [ResultBuilder o e m]
+ Data.Morpheus.App.NamedResolvers: type NamedResolverFunction o e m = NamedFunction (Resolver o e m)
- Data.Morpheus.App.NamedResolvers: variant :: (LiftOperation o, Monad m) => TypeName -> ValidValue -> Resolver o e m (ResultBuilder o e m)
+ Data.Morpheus.App.NamedResolvers: variant :: MonadResolver m => TypeName -> ValidValue -> m (ResultBuilder m)

Files

morpheus-graphql-app.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           morpheus-graphql-app-version:        0.27.0+version:        0.27.1 synopsis:       Morpheus GraphQL App description:    Build GraphQL APIs with your favourite functional language! category:       web, graphql@@ -46,6 +46,9 @@     test/batching/objects-fields/query.gql     test/batching/objects-lists-fields/query.gql     test/batching/schema.gql+    test/execution/many/query.gql+    test/execution/schema.gql+    test/execution/single/query.gql     test/merge/schema/query-subscription-mutation/app-1.gql     test/merge/schema/query-subscription-mutation/app-2.gql     test/merge/schema/query-subscription-mutation/mutation/query.gql@@ -96,6 +99,8 @@     test/batching/objects-fields/response.json     test/batching/objects-lists-fields/batching.json     test/batching/objects-lists-fields/response.json+    test/execution/many/response.json+    test/execution/single/response.json     test/merge/schema/query-subscription-mutation/app-1.json     test/merge/schema/query-subscription-mutation/app-2.json     test/merge/schema/query-subscription-mutation/mutation/response.json@@ -128,7 +133,10 @@       Data.Morpheus.Types.GQLWrapper   other-modules:       Data.Morpheus.App.Internal.Resolving.Batching+      Data.Morpheus.App.Internal.Resolving.Cache       Data.Morpheus.App.Internal.Resolving.Event+      Data.Morpheus.App.Internal.Resolving.MonadResolver+      Data.Morpheus.App.Internal.Resolving.Refs       Data.Morpheus.App.Internal.Resolving.Resolver       Data.Morpheus.App.Internal.Resolving.ResolverState       Data.Morpheus.App.Internal.Resolving.ResolveValue@@ -155,7 +163,7 @@     , relude >=0.3.0 && <2.0.0     , scientific >=0.3.6.2 && <0.4.0     , template-haskell >=2.0.0 && <3.0.0-    , text >=1.2.3 && <1.3.0+    , text >=1.2.3 && <2.1.0     , th-lift-instances >=0.1.1 && <0.3.0     , transformers >=0.3.0 && <0.6.0     , unordered-containers >=0.2.8 && <0.3.0@@ -168,6 +176,7 @@   other-modules:       APIConstraints       Batching+      Execution       NamedResolvers       Paths_morpheus_graphql_app   hs-source-dirs:@@ -190,7 +199,7 @@     , tasty >=0.1.0 && <1.5.0     , tasty-hunit >=0.1.0 && <1.0.0     , template-haskell >=2.0.0 && <3.0.0-    , text >=1.2.3 && <1.3.0+    , text >=1.2.3 && <2.1.0     , th-lift-instances >=0.1.1 && <0.3.0     , transformers >=0.3.0 && <0.6.0     , unordered-containers >=0.2.8 && <0.3.0
src/Data/Morpheus/App/Internal/Resolving.hs view
@@ -4,7 +4,6 @@   ( Resolver,     LiftOperation,     runRootResolverValue,-    lift,     ResponseEvent (..),     ResponseStream,     cleanEvents,@@ -13,12 +12,10 @@     ObjectTypeResolver (..),     WithOperation,     PushEvents (..),-    subscribe,     ResolverContext (..),     RootResolverValue (..),     resultOr,     withArguments,-    -- Dynamic Resolver     mkBoolean,     mkFloat,     mkInt,@@ -30,9 +27,9 @@     mkUnion,     mkObject,     SubscriptionField (..),-    getArguments,     ResolverState,-    liftResolverState,+    MonadResolver (..),+    MonadIOResolver,     ResolverEntry,     sortErrors,     EventHandler (..),@@ -45,6 +42,7 @@ where  import Data.Morpheus.App.Internal.Resolving.Event+import Data.Morpheus.App.Internal.Resolving.MonadResolver import Data.Morpheus.App.Internal.Resolving.Resolver import Data.Morpheus.App.Internal.Resolving.ResolverState import Data.Morpheus.App.Internal.Resolving.RootResolverValue
src/Data/Morpheus/App/Internal/Resolving/Batching.hs view
@@ -1,11 +1,11 @@ {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-}@@ -17,23 +17,41 @@ {-# LANGUAGE NoImplicitPrelude #-}  module Data.Morpheus.App.Internal.Resolving.Batching-  ( CacheKey (..),-    LocalCache,-    useCached,-    buildCacheWith,-    ResolverMapContext (..),-    ResolverMapT (..),-    runResMapT,+  ( ResolverMapT (..),+    SelectionRef,+    runBatchedT,+    MonadBatching (..),   ) where  import Control.Monad.Except (MonadError (throwError)) import Data.ByteString.Lazy.Char8 (unpack)-import qualified Data.HashMap.Lazy as HM-import Data.Morpheus.App.Internal.Resolving.ResolverState (config)-import Data.Morpheus.App.Internal.Resolving.Types (ResolverMap)+import Data.HashMap.Lazy (keys)+import Data.Morpheus.App.Internal.Resolving.Cache+  ( CacheKey (..),+    CacheT,+    CacheValue (..),+    cacheResolverValues,+    cacheValue,+    isCached,+    printSelectionKey,+    useCached,+    withDebug,+  )+import Data.Morpheus.App.Internal.Resolving.Refs (scanRefs)+import Data.Morpheus.App.Internal.Resolving.ResolverState (ResolverContext)+import Data.Morpheus.App.Internal.Resolving.Types+  ( NamedResolver (..),+    NamedResolverResult (..),+    ResolverMap,+  ) import Data.Morpheus.App.Internal.Resolving.Utils-import Data.Morpheus.Core (Config (..), RenderGQL, render)+  ( NamedResolverRef (..),+    ResolverMonad,+    ResolverValue (ResEnum, ResNull, ResObject, ResRef, ResScalar),+  )+import Data.Morpheus.Core (render)+import Data.Morpheus.Internal.Utils (Empty (empty), IsMap (..), selectOr) import Data.Morpheus.Types.Internal.AST   ( GQLError,     Msg (..),@@ -43,33 +61,8 @@     ValidValue,     internal,   )-import Debug.Trace (trace) import GHC.Show (Show (show))-import Relude hiding (show, trace)--type LocalCache = HashMap CacheKey ValidValue--useCached :: (Eq k, Show k, Hashable k, MonadError GQLError f) => HashMap k a -> k -> f a-useCached mp v = case HM.lookup v mp of-  Just x -> pure x-  Nothing -> throwError (internal $ "cache value could not found for key" <> msg (show v :: String))--dumpCache :: Bool -> LocalCache -> LocalCache-dumpCache enabled cache-  | null cache || not enabled = cache-  | otherwise = trace ("\nCACHE:\n" <> printCache cache) cache--printCache :: LocalCache -> [Char]-printCache cache = intercalate "\n" (map printKeyValue $ HM.toList cache) <> "\n"-  where-    printKeyValue (key, v) = " " <> show key <> ": " <> unpack (render v)--printSel :: RenderGQL a => a -> [Char]-printSel sel = map replace $ filter ignoreSpaces $ unpack (render sel)-  where-    ignoreSpaces x = x /= ' '-    replace '\n' = ' '-    replace x = x+import Relude hiding (empty, show)  data BatchEntry = BatchEntry   { batchedSelection :: SelectionContent VALID,@@ -78,74 +71,107 @@   }  instance Show BatchEntry where-  show BatchEntry {..} = printSel batchedSelection <> ":" <> toString batchedType <> ":" <> show (map (unpack . render) batchedArguments)--data CacheKey = CacheKey-  { cachedSel :: SelectionContent VALID,-    cachedTypeName :: TypeName,-    cachedArg :: ValidValue-  }-  deriving (Eq, Generic)--instance Show CacheKey where-  show (CacheKey sel typename dep) = printSel sel <> ":" <> toString typename <> ":" <> unpack (render dep)+  show BatchEntry {..} =+    "\nBATCH("+      <> toString batchedType+      <> "):"+      <> "\n  sel:"+      <> printSelectionKey batchedSelection+      <> "\n  dep:"+      <> show (map (unpack . render) batchedArguments) -instance Hashable CacheKey where-  hashWithSalt s (CacheKey sel tyName arg) = hashWithSalt s (sel, tyName, render arg)+type SelectionRef = (SelectionContent VALID, NamedResolverRef)  uniq :: (Eq a, Hashable a) => [a] -> [a]-uniq = HM.keys . HM.fromList . map (,True)+uniq = keys . unsafeFromList . map (,True) -buildBatches :: [(SelectionContent VALID, NamedResolverRef)] -> [BatchEntry]+buildBatches :: [SelectionRef] -> [BatchEntry] buildBatches inputs =   let entityTypes = uniq $ map (second resolverTypeName) inputs    in mapMaybe (selectByEntity inputs) entityTypes -selectByEntity :: [(SelectionContent VALID, NamedResolverRef)] -> (SelectionContent VALID, TypeName) -> Maybe BatchEntry-selectByEntity inputs (tSel, tName) = case filter areEq inputs of+selectByEntity :: [SelectionRef] -> (SelectionContent VALID, TypeName) -> Maybe BatchEntry+selectByEntity inputs (tSel, tName) = case gerArgs (filter areEq inputs) of   [] -> Nothing-  xs -> Just $ BatchEntry tSel tName (uniq $ concatMap (resolverArgument . snd) xs)+  args -> Just (BatchEntry tSel tName args)+    where+   where+    gerArgs = uniq . concatMap (resolverArgument . snd)     areEq (sel, v) = sel == tSel && tName == resolverTypeName v -type ResolverFun m = NamedResolverRef -> SelectionContent VALID -> m [ValidValue]--resolveBatched :: Monad m => ResolverFun m -> BatchEntry -> m LocalCache-resolveBatched f (BatchEntry sel name deps) = do-  res <- f (NamedResolverRef name deps) sel-  let keys = map (CacheKey sel name) deps-  let entries = zip keys res-  pure $ HM.fromList entries--updateCache :: (ResolverMonad m, Traversable t) => ResolverFun m -> LocalCache -> t BatchEntry -> m LocalCache-updateCache f cache entries = do-  caches <- traverse (resolveBatched f) entries-  let newCache = foldr (<>) cache caches-  enabled <- asks (debug . config)-  pure $ dumpCache enabled newCache--buildCacheWith :: ResolverMonad m => ResolverFun m -> LocalCache -> [(SelectionContent VALID, NamedResolverRef)] -> m LocalCache-buildCacheWith f cache entries = updateCache f cache (buildBatches entries)--data ResolverMapContext m = ResolverMapContext-  { localCache :: LocalCache,-    resolverMap :: ResolverMap m-  }- newtype ResolverMapT m a = ResolverMapT-  { _runResMapT :: ReaderT (ResolverMapContext m) m a+  { _runResMapT :: ReaderT (ResolverMap m) (CacheT m) a   }   deriving     ( Functor,       Applicative,-      Monad,-      MonadReader (ResolverMapContext m)+      Monad     ) +instance (MonadReader ResolverContext m) => MonadReader ResolverContext (ResolverMapT m) where+  ask = ResolverMapT (lift ask)+  local f (ResolverMapT m) = ResolverMapT (ReaderT (local f . runReaderT m))+ instance MonadTrans ResolverMapT where-  lift = ResolverMapT . lift+  lift = ResolverMapT . lift . lift  deriving instance MonadError GQLError m => MonadError GQLError (ResolverMapT m) -runResMapT :: ResolverMapT m a -> ResolverMapContext m -> m a-runResMapT (ResolverMapT x) = runReaderT x+runBatchedT :: Monad m => ResolverMapT m a -> ResolverMap m -> m a+runBatchedT (ResolverMapT m) rmap = fst <$> runStateT (runReaderT m rmap) empty++toKeys :: BatchEntry -> [CacheKey]+toKeys (BatchEntry sel name deps) = map (CacheKey sel name) deps++inCache :: Monad m => CacheT m a -> ResolverMapT m a+inCache = ResolverMapT . lift++class MonadTrans t => MonadBatching t where+  resolveRef :: ResolverMonad m => SelectionContent VALID -> NamedResolverRef -> t m (CacheKey, CacheValue m)+  storeValue :: ResolverMonad m => CacheKey -> ValidValue -> t m ValidValue++instance MonadBatching IdentityT where+  resolveRef _ _ = throwError $ internal "batching is only allowed with named resolvers"+  storeValue _ _ = throwError $ internal "batching is only allowed with named resolvers"++instance MonadBatching ResolverMapT where+  resolveRef sel (NamedResolverRef typename [arg]) = do+    let key = CacheKey sel typename arg+    alreadyCached <- inCache (isCached key)+    if alreadyCached+      then pure ()+      else prefetch (BatchEntry sel typename [arg])+    inCache $ do+      value <- useCached key+      pure (key, value)+  resolveRef _ ref = throwError (internal ("expected only one resolved value for " <> msg (show ref :: String)))+  storeValue key = inCache . cacheValue key++prefetch :: ResolverMonad m => BatchEntry -> ResolverMapT m ()+prefetch batch = do+  value <- run batch+  batches <- buildBatches . concat <$> traverse (lift . scanRefs (batchedSelection batch)) value+  resolvedEntries <- traverse (\b -> (b,) <$> run b) batches+  let caches = foldMap zipCaches $ (batch, value) : resolvedEntries+  inCache $ cacheResolverValues caches+  where+    zipCaches (b, res) = zip (toKeys b) res+    run = withDebug >=> runBatch++runBatch :: (MonadError GQLError m, MonadReader ResolverContext m) => BatchEntry -> ResolverMapT m [ResolverValue m]+runBatch (BatchEntry _ name deps)+  | null deps = pure []+  | otherwise = do+      resolvers <- ResolverMapT ask+      NamedResolver {resolverFun} <- lift (selectOr notFound pure name resolvers)+      map (toResolverValue name) <$> lift (resolverFun deps)+  where+    notFound = throwError ("resolver type " <> msg name <> "can't found")++toResolverValue :: (Monad m) => TypeName -> NamedResolverResult m -> ResolverValue m+toResolverValue typeName (NamedObjectResolver res) = ResObject (Just typeName) res+toResolverValue _ (NamedUnionResolver unionRef) = ResRef $ pure unionRef+toResolverValue _ (NamedEnumResolver value) = ResEnum value+toResolverValue _ NamedNullResolver = ResNull+toResolverValue _ (NamedScalarResolver v) = ResScalar v
+ src/Data/Morpheus/App/Internal/Resolving/Cache.hs view
@@ -0,0 +1,114 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.App.Internal.Resolving.Cache+  ( CacheKey (..),+    CacheStore (..),+    printSelectionKey,+    useCached,+    isCached,+    withDebug,+    cacheResolverValues,+    cacheValue,+    CacheValue (..),+    CacheT,+  )+where++import Control.Monad.Except+import Data.ByteString.Lazy.Char8 (unpack)+import qualified Data.HashMap.Lazy as HM+import Data.Morpheus.App.Internal.Resolving.ResolverState+import Data.Morpheus.App.Internal.Resolving.Types (ResolverValue)+import Data.Morpheus.App.Internal.Resolving.Utils (ResolverMonad)+import Data.Morpheus.Core (Config (debug), RenderGQL, render)+import Data.Morpheus.Internal.Utils+  ( Empty (..),+    IsMap (..),+  )+import Data.Morpheus.Types.Internal.AST+  ( Msg (msg),+    SelectionContent,+    TypeName,+    VALID,+    ValidValue,+    internal,+  )+import Debug.Trace (trace)+import Relude hiding (Show, empty, show, trace)+import Prelude (Show (show))++type CacheT m = (StateT (CacheStore m) m)++printSelectionKey :: RenderGQL a => a -> String+printSelectionKey sel = map replace $ filter ignoreSpaces $ unpack (render sel)+  where+    ignoreSpaces x = x /= ' '+    replace '\n' = ' '+    replace x = x++data CacheKey = CacheKey+  { cachedSel :: SelectionContent VALID,+    cachedTypeName :: TypeName,+    cachedArg :: ValidValue+  }+  deriving (Eq, Generic)++data CacheValue m+  = CachedValue ValidValue+  | CachedResolver (ResolverValue m)++instance Show (CacheValue m) where+  show (CachedValue v) = unpack (render v)+  show (CachedResolver v) = show v++instance Show CacheKey where+  show (CacheKey sel typename dep) = printSelectionKey sel <> ":" <> toString typename <> ":" <> unpack (render dep)++instance Hashable CacheKey where+  hashWithSalt s (CacheKey sel tyName arg) = hashWithSalt s (sel, tyName, render arg)++newtype CacheStore m = CacheStore {_unpackStore :: HashMap CacheKey (CacheValue m)}++instance Show (CacheStore m) where+  show (CacheStore cache) = "\nCACHE:\n" <> intercalate "\n" (map printKeyValue $ toAssoc cache) <> "\n"+    where+      printKeyValue (key, v) = " " <> show key <> ": " <> show v++instance Empty (CacheStore m) where+  empty = CacheStore empty++cacheResolverValues :: ResolverMonad m => [(CacheKey, ResolverValue m)] -> CacheT m ()+cacheResolverValues pres = do+  CacheStore oldCache <- get+  let updates = unsafeFromList (map (second CachedResolver) pres)+  cache <- labeledDebug "\nUPDATE|>" $ CacheStore $ updates <> oldCache+  modify (const cache)++useCached :: ResolverMonad m => CacheKey -> CacheT m (CacheValue m)+useCached v = do+  cache <- get >>= labeledDebug "\nUSE|>"+  case lookup v (_unpackStore cache) of+    Just x -> pure x+    Nothing -> throwError (internal $ "cache value could not found for key" <> msg (show v :: String))++isCached :: Monad m => CacheKey -> CacheT m Bool+isCached key = isJust . lookup key . _unpackStore <$> get++setValue :: (CacheKey, ValidValue) -> CacheStore m -> CacheStore m+setValue (key, value) = CacheStore . HM.insert key (CachedValue value) . _unpackStore++labeledDebug :: (Show a, MonadReader ResolverContext m) => String -> a -> m a+labeledDebug label v = showValue <$> asks (debug . config)+  where+    showValue enabled+      | enabled = trace (label <> show v) v+      | otherwise = v++withDebug :: (Show a, MonadReader ResolverContext m) => a -> m a+withDebug = labeledDebug ""++cacheValue :: Monad m => CacheKey -> ValidValue -> CacheT m ValidValue+cacheValue key value = modify (setValue (key, value)) $> value
src/Data/Morpheus/App/Internal/Resolving/Event.hs view
@@ -15,8 +15,7 @@   )  class EventHandler e where-  type Channel e-  getChannels :: e -> [Channel e]+  type Channel e :: Type  data ResponseEvent event (m :: Type -> Type)   = Publish event
+ src/Data/Morpheus/App/Internal/Resolving/MonadResolver.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE ConstrainedClassMethods #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.App.Internal.Resolving.MonadResolver+  ( MonadResolver (..),+    MonadIOResolver,+    SubscriptionField (..),+    ResolverContext (..),+    withArguments,+    getArgument,+  )+where++import Control.Monad.Except (MonadError)+import Data.Morpheus.App.Internal.Resolving.Event+  ( EventHandler (..),+    ResponseEvent,+  )+import Data.Morpheus.App.Internal.Resolving.ResolverState+  ( ResolverContext (..),+    ResolverState,+  )+import Data.Morpheus.Internal.Ext (ResultT)+import Data.Morpheus.Internal.Utils (selectOr)+import Data.Morpheus.Types.Internal.AST+  ( Argument (..),+    Arguments,+    FieldName,+    GQLError,+    MUTATION,+    OperationType,+    SUBSCRIPTION,+    Selection,+    VALID,+    ValidValue,+    Value (..),+  )+import Relude++class (MonadResolver m, MonadIO m) => MonadIOResolver (m :: Type -> Type)++class+  ( Monad m,+    MonadReader ResolverContext m,+    MonadFail m,+    MonadError GQLError m,+    Monad (MonadParam m)+  ) =>+  MonadResolver (m :: Type -> Type)+  where+  type MonadOperation m :: OperationType+  type MonadEvent m :: Type+  type MonadQuery m :: (Type -> Type)+  type MonadMutation m :: (Type -> Type)+  type MonadSubscription m :: (Type -> Type)+  type MonadParam m :: (Type -> Type)+  liftState :: ResolverState a -> m a+  getArguments :: m (Arguments VALID)+  subscribe :: (MonadOperation m ~ SUBSCRIPTION) => Channel (MonadEvent m) -> MonadQuery m (MonadEvent m -> m a) -> SubscriptionField (m a)+  publish :: (MonadOperation m ~ MUTATION) => [MonadEvent m] -> m ()+  runResolver ::+    Maybe (Selection VALID -> ResolverState (Channel (MonadEvent m))) ->+    m ValidValue ->+    ResolverContext ->+    ResponseStream (MonadEvent m) (MonadParam m) ValidValue++data SubscriptionField (a :: Type) where+  SubscriptionField ::+    { channel :: forall m v. (a ~ m v, MonadResolver m, MonadOperation m ~ SUBSCRIPTION) => Channel (MonadEvent m),+      unSubscribe :: a+    } ->+    SubscriptionField a++withArguments :: (MonadResolver m) => (Arguments VALID -> m a) -> m a+withArguments = (getArguments >>=)++getArgument :: (MonadResolver m) => FieldName -> m (Value VALID)+getArgument name = selectOr Null argumentValue name <$> getArguments++type ResponseStream event (m :: Type -> Type) = ResultT (ResponseEvent event m) m
+ src/Data/Morpheus/App/Internal/Resolving/Refs.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.App.Internal.Resolving.Refs+  ( scanRefs,+  )+where++import Data.Morpheus.App.Internal.Resolving.ResolverState+  ( inSelectionField,+  )+import Data.Morpheus.App.Internal.Resolving.Types (NamedResolverRef, ObjectTypeResolver (..), ResolverValue (..))+import Data.Morpheus.App.Internal.Resolving.Utils (ResolverMonad, withField, withObject)+import Data.Morpheus.Types.Internal.AST+  ( Selection (..),+    SelectionContent (..),+    SelectionSet,+    VALID,+  )+import Relude hiding (empty)++type SelectionRef = (SelectionContent VALID, NamedResolverRef)++scanRefs :: (ResolverMonad m) => SelectionContent VALID -> ResolverValue m -> m [SelectionRef]+scanRefs sel (ResList xs) = concat <$> traverse (scanRefs sel) xs+scanRefs sel (ResLazy x) = x >>= scanRefs sel+scanRefs sel (ResObject tyName obj) = withObject tyName (objectRefs obj) sel+scanRefs sel (ResRef ref) = pure . (sel,) <$> ref+scanRefs _ ResEnum {} = pure []+scanRefs _ ResNull = pure []+scanRefs _ ResScalar {} = pure []++objectRefs :: (ResolverMonad m) => ObjectTypeResolver m -> Maybe (SelectionSet VALID) -> m [SelectionRef]+objectRefs _ Nothing = pure []+objectRefs obj (Just sel) = concat <$> traverse (fieldRefs obj) (toList sel)++fieldRefs :: (ResolverMonad m) => ObjectTypeResolver m -> Selection VALID -> m [SelectionRef]+fieldRefs obj selection@Selection {..}+  | selectionName == "__typename" = pure []+  | otherwise = inSelectionField selection $ do+      resValue <- withField [] (fmap pure) selectionName obj+      concat <$> traverse (scanRefs selectionContent) resValue
src/Data/Morpheus/App/Internal/Resolving/ResolveValue.hs view
@@ -2,61 +2,48 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE NoImplicitPrelude #-}  module Data.Morpheus.App.Internal.Resolving.ResolveValue-  ( resolveRef,-    resolveObject,-    ResolverMapContext (..),+  ( resolvePlainRoot,+    resolveNamedRoot,   ) where  import Control.Monad.Except (MonadError (throwError))-import qualified Data.HashMap.Lazy as HM import Data.Morpheus.App.Internal.Resolving.Batching-  ( CacheKey (..),-    ResolverMapContext (..),-    ResolverMapT,-    buildCacheWith,-    runResMapT,-    useCached,+  ( MonadBatching (..),+    runBatchedT,   )+import Data.Morpheus.App.Internal.Resolving.Cache (CacheValue (..))+import Data.Morpheus.App.Internal.Resolving.MonadResolver (MonadResolver) import Data.Morpheus.App.Internal.Resolving.ResolverState   ( ResolverContext (..),-    askFieldTypeName,-    updateCurrentType,+    inSelectionField,   ) import Data.Morpheus.App.Internal.Resolving.Types-  ( NamedResolver (..),-    NamedResolverRef (..),-    NamedResolverResult (..),-    ObjectTypeResolver (..),-    ResolverMap,-    ResolverValue (..),+  ( ResolverMap,     mkEnum,+    mkNull,+    mkString,     mkUnion,   )-import Data.Morpheus.Error (subfieldsNotSelected)+import Data.Morpheus.App.Internal.Resolving.Utils import Data.Morpheus.Internal.Utils   ( KeyOf (keyOf),     empty,-    selectOr,     traverseCollection,-    (<:>),   ) import Data.Morpheus.Types.Internal.AST-  ( GQLError,-    Msg (msg),-    ObjectEntry (ObjectEntry),+  ( ObjectEntry (ObjectEntry),     ScalarValue (..),     Selection (..),     SelectionContent (..),     SelectionSet,     TypeDefinition (..),     TypeName,-    UnionTag (unionTagSelection),     VALID,     ValidValue,     Value (..),@@ -67,204 +54,47 @@   ) import Relude hiding (empty) -scanRefs :: (MonadError GQLError m, MonadReader ResolverContext m) => SelectionContent VALID -> ResolverValue m -> m [(SelectionContent VALID, NamedResolverRef)]-scanRefs sel (ResList xs) = concat <$> traverse (scanRefs sel) xs-scanRefs sel (ResLazy x) = x >>= scanRefs sel-scanRefs sel (ResObject tyName obj) = withObject tyName (objectRefs obj) sel-scanRefs sel (ResRef ref) = pure . (sel,) <$> ref-scanRefs _ ResEnum {} = pure []-scanRefs _ ResNull = pure []-scanRefs _ ResScalar {} = pure []--objectRefs ::-  ( MonadError GQLError m,-    MonadReader ResolverContext m-  ) =>-  ObjectTypeResolver m ->-  Maybe (SelectionSet VALID) ->-  m [(SelectionContent VALID, NamedResolverRef)]-objectRefs _ Nothing = pure []-objectRefs dr (Just sel) = concat <$> traverse (fieldRefs dr) (toList sel)--fieldRefs ::-  (MonadError GQLError m, MonadReader ResolverContext m) =>-  ObjectTypeResolver m ->-  Selection VALID ->-  m [(SelectionContent VALID, NamedResolverRef)]-fieldRefs ObjectTypeResolver {..} currentSelection@Selection {..}-  | selectionName == "__typename" = pure []-  | otherwise = do-      t <- askFieldTypeName selectionName-      updateCurrentType t $-        local (\ctx -> ctx {currentSelection}) $ do-          x <- maybe (pure []) (fmap pure) (HM.lookup selectionName objectFields)-          concat <$> traverse (scanRefs selectionContent) x--resolveSelection ::-  ( Monad m,-    MonadReader ResolverContext m,-    MonadError GQLError m-  ) =>-  ResolverValue m ->-  SelectionContent VALID ->-  ResolverMapT m ValidValue-resolveSelection res selection = do-  ctx <- ask-  newRmap <- lift (scanRefs selection res >>= buildCache ctx)-  local (const newRmap) (__resolveSelection res selection)--buildCache :: (MonadError GQLError m, MonadReader ResolverContext m) => ResolverMapContext m -> [(SelectionContent VALID, NamedResolverRef)] -> m (ResolverMapContext m)-buildCache ctx@(ResolverMapContext cache rmap) entries = (`ResolverMapContext` rmap) <$> buildCacheWith (resolveRefsCached ctx) cache entries--__resolveSelection ::-  ( Monad m,-    MonadReader ResolverContext m,-    MonadError GQLError m-  ) =>-  ResolverValue m ->-  SelectionContent VALID ->-  ResolverMapT m ValidValue-__resolveSelection (ResLazy x) selection = lift x >>= (`resolveSelection` selection)-__resolveSelection (ResList xs) selection = List <$> traverse (`resolveSelection` selection) xs-__resolveSelection (ResObject tyName obj) sel = do-  ctx <- ask-  lift $ withObject tyName (resolveObject ctx obj) sel-__resolveSelection (ResEnum name) SelectionField = pure $ Scalar $ String $ unpackName name-__resolveSelection (ResEnum name) unionSel@UnionSelection {} = resolveSelection (mkUnion name [(unitFieldName, pure $ mkEnum unitTypeName)]) unionSel-__resolveSelection ResEnum {} _ = throwError (internal "wrong selection on enum value")-__resolveSelection ResNull _ = pure Null-__resolveSelection (ResScalar x) SelectionField = pure $ Scalar x-__resolveSelection ResScalar {} _ = throwError (internal "scalar Resolver should only receive SelectionField")-__resolveSelection (ResRef ref) sel = do-  ctx <- ask-  lift (ref >>= flip (resolveRef ctx) sel)--withObject ::-  ( MonadError GQLError m,-    MonadReader ResolverContext m-  ) =>-  Maybe TypeName ->-  (Maybe (SelectionSet VALID) -> m value) ->-  SelectionContent VALID ->-  m value-withObject __typename f = updateCurrentType __typename . checkContent-  where-    checkContent (SelectionSet selection) = f (Just selection)-    checkContent (UnionSelection interface unionSel) = do-      typename <- asks (typeName . currentType)-      selection <- selectOr (pure interface) (fx interface) typename unionSel-      f selection-      where-        fx (Just x) y = Just <$> (x <:> unionTagSelection y)-        fx Nothing y = pure $ Just $ unionTagSelection y-    checkContent SelectionField = noEmptySelection--noEmptySelection :: (MonadError GQLError m, MonadReader ResolverContext m) => m value-noEmptySelection = do-  sel <- asks currentSelection-  throwError $ subfieldsNotSelected (selectionName sel) "" (selectionPosition sel)--resolveRef ::-  ( MonadError GQLError m,-    MonadReader ResolverContext m-  ) =>-  ResolverMapContext m ->-  NamedResolverRef ->-  SelectionContent VALID ->-  m ValidValue-resolveRef rmap ref selection = resolveRefsCached rmap ref selection >>= toOne--toOne :: (MonadError GQLError f, Show a) => [a] -> f a-toOne [x] = pure x-toOne x = throwError (internal ("expected only one resolved value for " <> msg (show x :: String)))--resolveRefsCached ::-  ( MonadError GQLError m,-    MonadReader ResolverContext m-  ) =>-  ResolverMapContext m ->-  NamedResolverRef ->-  SelectionContent VALID ->-  m [ValidValue]-resolveRefsCached ctx (NamedResolverRef name args) selection = do-  let keys = map (CacheKey selection name) args-  let cached = map resolveCached keys-  let cachedMap = HM.fromList (mapMaybe unp cached)-  notCachedMap <- runResMapT (resolveUncached name selection $ map fst $ filter (isNothing . snd) cached) ctx-  traverse (useCached (cachedMap <> notCachedMap)) args-  where-    unp (_, Nothing) = Nothing-    unp (x, Just y) = Just (x, y)-    resolveCached key = (cachedArg key, HM.lookup key $ localCache ctx)+-- UNCACHED+resolvePlainRoot :: MonadResolver m => ObjectTypeResolver m -> SelectionSet VALID -> m ValidValue+resolvePlainRoot resolver selection = do+  name <- asks (typeName . currentType)+  runIdentityT (resolveSelection (SelectionSet selection) (ResObject (Just name) resolver)) -processResult ::-  (MonadError GQLError m, MonadReader ResolverContext m) =>-  TypeName ->-  SelectionContent VALID ->-  NamedResolverResult m ->-  ResolverMapT m ValidValue-processResult typename selection (NamedObjectResolver res) = do-  ctx <- ask-  lift $ withObject (Just typename) (resolveObject ctx res) selection-processResult _ selection (NamedUnionResolver unionRef) = resolveSelection (ResRef $ pure unionRef) selection-processResult _ selection (NamedEnumResolver value) = resolveSelection (ResEnum value) selection-processResult _ selection NamedNullResolver = resolveSelection ResNull selection-processResult _ selection (NamedScalarResolver v) = resolveSelection (ResScalar v) selection+-- CACHED+resolveNamedRoot :: MonadResolver m => TypeName -> ResolverMap m -> SelectionSet VALID -> m ValidValue+resolveNamedRoot typeName resolvers selection =+  runBatchedT+    (resolveSelection (SelectionSet selection) (ResRef $ pure (NamedResolverRef typeName ["ROOT"])))+    resolvers -resolveUncached ::-  ( MonadError GQLError m,-    MonadReader ResolverContext m-  ) =>-  TypeName ->-  SelectionContent VALID ->-  [ValidValue] ->-  ResolverMapT m (HashMap ValidValue ValidValue)-resolveUncached _ _ [] = pure empty-resolveUncached typename selection xs = do-  rmap <- asks resolverMap-  vs <- lift (getNamedResolverBy (NamedResolverRef typename xs) rmap) >>= traverse (processResult typename selection)-  pure $ HM.fromList (zip xs vs)+-- RESOLVING -getNamedResolverBy ::-  (MonadError GQLError m) =>-  NamedResolverRef ->-  ResolverMap m ->-  m [NamedResolverResult m]-getNamedResolverBy NamedResolverRef {..} = selectOr cantFoundError ((resolverArgument &) . resolverFun) resolverTypeName+resolveSelection :: (ResolverMonad (t m), MonadBatching t, MonadResolver m) => SelectionContent VALID -> ResolverValue m -> t m ValidValue+resolveSelection selection (ResLazy x) = lift x >>= resolveSelection selection+resolveSelection selection (ResList xs) = List <$> traverse (resolveSelection selection) xs+resolveSelection SelectionField (ResEnum name) = pure $ Scalar $ String $ unpackName name+resolveSelection selection@UnionSelection {} (ResEnum name) = resolveSelection selection (mkUnion name [(unitFieldName, pure $ mkEnum unitTypeName)])+resolveSelection _ ResEnum {} = throwError (internal "wrong selection on enum value")+resolveSelection _ ResNull = pure Null+resolveSelection SelectionField (ResScalar x) = pure $ Scalar x+resolveSelection _ ResScalar {} = throwError (internal "scalar resolver should only receive SelectionField")+resolveSelection selection (ResObject typeName obj) = withObject typeName (mapSelectionSet resolveField) selection   where-    cantFoundError = throwError ("Resolver Type " <> msg resolverTypeName <> "can't found")+    resolveField s = lift (toResolverValue obj s) >>= resolveSelection (selectionContent s)+resolveSelection selection (ResRef mRef) = do+  (key, value) <- resolveRef selection =<< lift mRef+  case value of+    (CachedValue v) -> pure v+    (CachedResolver v) -> resolveSelection selection v >>= storeValue key -resolveObject ::-  ( MonadReader ResolverContext m,-    MonadError GQLError m-  ) =>-  ResolverMapContext m ->+toResolverValue ::+  (MonadResolver m) =>   ObjectTypeResolver m ->-  Maybe (SelectionSet VALID) ->-  m ValidValue-resolveObject rmap drv sel = do-  newCache <- objectRefs drv sel >>= buildCache rmap-  Object <$> maybe (pure empty) (traverseCollection (resolver newCache)) sel-  where-    resolver cacheCTX currentSelection = do-      t <- askFieldTypeName (selectionName currentSelection)-      updateCurrentType t $-        local (\ctx -> ctx {currentSelection}) $-          ObjectEntry (keyOf currentSelection)-            <$> runResMapT (runFieldResolver currentSelection drv) cacheCTX--runFieldResolver ::-  ( Monad m,-    MonadReader ResolverContext m,-    MonadError GQLError m-  ) =>   Selection VALID ->-  ObjectTypeResolver m ->-  ResolverMapT m ValidValue-runFieldResolver Selection {selectionName, selectionContent}-  | selectionName == "__typename" =-      const (Scalar . String . unpackName <$> lift (asks (typeName . currentType)))-  | otherwise =-      maybe (pure Null) (lift >=> (`resolveSelection` selectionContent))-        . HM.lookup selectionName-        . objectFields+  m (ResolverValue m)+toResolverValue obj Selection {selectionName}+  | selectionName == "__typename" = mkString . unpackName <$> asks (typeName . currentType)+  | otherwise = withField mkNull id selectionName obj++mapSelectionSet :: (ResolverMonad m) => (Selection VALID -> m ValidValue) -> Maybe (SelectionSet VALID) -> m ValidValue+mapSelectionSet f = fmap Object . maybe (pure empty) (traverseCollection (\sel -> ObjectEntry (keyOf sel) <$> inSelectionField sel (f sel)))
src/Data/Morpheus/App/Internal/Resolving/Resolver.hs view
@@ -19,18 +19,10 @@ module Data.Morpheus.App.Internal.Resolving.Resolver   ( Resolver,     LiftOperation,-    lift,-    subscribe,     ResponseEvent (..),     ResponseStream,     WithOperation,-    ResolverContext (..),-    withArguments,-    getArguments,     SubscriptionField (..),-    liftResolverState,-    runResolver,-    getArgument,   ) where @@ -40,6 +32,11 @@   ( EventHandler (..),     ResponseEvent (..),   )+import Data.Morpheus.App.Internal.Resolving.MonadResolver+  ( MonadIOResolver,+    MonadResolver (..),+    SubscriptionField (..),+  ) import Data.Morpheus.App.Internal.Resolving.ResolverState   ( ResolverContext (..),     ResolverState,@@ -59,16 +56,12 @@     cleanEvents,     mapEvent,   )-import Data.Morpheus.Internal.Utils (selectOr) import Data.Morpheus.Types.IO   ( GQLResponse,     renderResponse,   ) import Data.Morpheus.Types.Internal.AST-  ( Argument (argumentValue),-    Arguments,-    FieldName,-    GQLError,+  ( GQLError,     MUTATION,     OperationType (..),     QUERY,@@ -90,22 +83,36 @@  type ResponseStream event (m :: Type -> Type) = ResultT (ResponseEvent event m) m -data SubscriptionField (a :: Type) where-  SubscriptionField ::-    { channel :: forall e m v. a ~ Resolver SUBSCRIPTION e m v => Channel e,-      unSubscribe :: a-    } ->-    SubscriptionField a------- GraphQL Field Resolver---+-- GraphQL resolver --------------------------------------------------------------- data Resolver (o :: OperationType) event (m :: Type -> Type) value where   ResolverQ :: {runResolverQ :: ResolverStateT () m value} -> Resolver QUERY event m value   ResolverM :: {runResolverM :: ResolverStateT event m value} -> Resolver MUTATION event m value   ResolverS :: {runResolverS :: ResolverStateT () m (SubEventRes event m value)} -> Resolver SUBSCRIPTION event m value +instance (LiftOperation o, Monad m, MonadIO m) => MonadIOResolver (Resolver o e m)++instance (LiftOperation o, Monad m) => MonadResolver (Resolver o e m) where+  type MonadOperation (Resolver o e m) = o+  type MonadEvent (Resolver o e m) = e+  type MonadQuery (Resolver o e m) = (Resolver QUERY e m)+  type MonadMutation (Resolver o e m) = (Resolver MUTATION e m)+  type MonadSubscription (Resolver o e m) = (Resolver SUBSCRIPTION e m)+  type MonadParam (Resolver o e m) = m+  getArguments = asks (selectionArguments . currentSelection)+  liftState = packResolver . toResolverStateT+  subscribe ch res = SubscriptionField ch (ResolverS (runSubscription <$> runResolverQ res))+    where+      runSubscription f = join (ReaderT (runResolverS . f))+  publish = pushEvents+  runResolver _ (ResolverQ resT) sel = cleanEvents $ runResolverStateT resT sel+  runResolver _ (ResolverM resT) sel = mapEvent Publish $ runResolverStateT resT sel+  runResolver toChannel (ResolverS resT) ctx = ResultT $ do+    readResValue <- runResolverStateValueM resT ctx+    pure $ case readResValue >>= subscriptionEvents ctx toChannel . toEventResolver ctx of+      Failure x -> Failure x+      Success {warnings, result} -> Success {warnings, result = ([result], Null)}+ type SubEventRes event m value = ReaderT event (ResolverStateT () m) value  instance Show (Resolver o e m value) where@@ -170,9 +177,6 @@   local f (ResolverM res) = ResolverM (local f res)   local f (ResolverS resM) = ResolverS $ mapReaderT (local f) <$> resM -liftResolverState :: (LiftOperation o, Monad m) => ResolverState a -> Resolver o e m a-liftResolverState = packResolver . toResolverStateT- class LiftOperation (o :: OperationType) where   packResolver :: Monad m => ResolverStateT e m a -> Resolver o e m a @@ -184,54 +188,6 @@  instance LiftOperation SUBSCRIPTION where   packResolver = ResolverS . pure . lift . clearStateResolverEvents--subscribe ::-  (Monad m) =>-  Channel e ->-  Resolver QUERY e m (e -> Resolver SUBSCRIPTION e m a) ->-  SubscriptionField (Resolver SUBSCRIPTION e m a)-subscribe ch res =-  SubscriptionField ch $-    ResolverS $-      fromSub <$> runResolverQ res-  where-    fromSub :: Monad m => (e -> Resolver SUBSCRIPTION e m a) -> ReaderT e (ResolverStateT () m) a-    fromSub f = join (ReaderT (runResolverS . f))--withArguments ::-  (LiftOperation o, Monad m) =>-  (Arguments VALID -> Resolver o e m a) ->-  Resolver o e m a-withArguments = (getArguments >>=)--getArguments ::-  (LiftOperation o, Monad m) =>-  Resolver o e m (Arguments VALID)-getArguments = asks (selectionArguments . currentSelection)--getArgument ::-  (LiftOperation o, Monad m) =>-  FieldName ->-  Resolver o e m (Value VALID)-getArgument name = selectOr Null argumentValue name <$> getArguments--runResolver ::-  Monad m =>-  Maybe (Selection VALID -> ResolverState (Channel event)) ->-  Resolver o event m ValidValue ->-  ResolverContext ->-  ResponseStream event m ValidValue-runResolver _ (ResolverQ resT) sel = cleanEvents $ runResolverStateT resT sel-runResolver _ (ResolverM resT) sel = mapEvent Publish $ runResolverStateT resT sel-runResolver toChannel (ResolverS resT) ctx = ResultT $ do-  readResValue <- runResolverStateValueM resT ctx-  pure $ case readResValue >>= subscriptionEvents ctx toChannel . toEventResolver ctx of-    Failure x -> Failure x-    Success {warnings, result} ->-      Success-        { warnings,-          result = ([result], Null)-        }  toEventResolver :: Monad m => ResolverContext -> SubEventRes event m ValidValue -> (event -> m GQLResponse) toEventResolver sel (ReaderT subRes) event = renderResponse <$> runResolverStateValueM (subRes event) sel
src/Data/Morpheus/App/Internal/Resolving/ResolverState.hs view
@@ -28,6 +28,8 @@     runResolverStateValueM,     updateCurrentType,     askFieldTypeName,+    inField,+    inSelectionField,   ) where @@ -106,6 +108,16 @@   (DataInterface fs) -> selectOr Nothing (Just . typeConName . fieldType) name fs   _ -> Nothing +inSelectionField :: (MonadReader ResolverContext m, MonadError GQLError m) => Selection VALID -> m b -> m b+inSelectionField selection m = do+  inField (selectionName selection) $+    local (\ctx -> ctx {currentSelection = selection}) m++inField :: (MonadReader ResolverContext m, MonadError GQLError m) => FieldName -> m b -> m b+inField fieldName m = do+  fieldType <- askFieldTypeName fieldName+  updateCurrentType fieldType m+ askFieldTypeName :: MonadReader ResolverContext m => FieldName -> m (Maybe TypeName) askFieldTypeName name = asks (fieldTypeName name . currentType) @@ -123,7 +135,7 @@ runResolverState :: ResolverState a -> ResolverContext -> GQLResult a runResolverState res = fmap snd . runIdentity . runResolverStateM res --- Resolver Internal State+-- internal resolver state newtype ResolverStateT event m a = ResolverStateT   { _runResolverStateT :: ReaderT ResolverContext (ResultT event m) a   }
src/Data/Morpheus/App/Internal/Resolving/RootResolverValue.hs view
@@ -1,6 +1,10 @@ {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE NoImplicitPrelude #-}  module Data.Morpheus.App.Internal.Resolving.RootResolverValue@@ -9,22 +13,20 @@   ) where -import Control.Monad.Except (MonadError, throwError)-import qualified Data.Aeson as A+import Control.Monad.Except (throwError)+import Data.Aeson (FromJSON (..))+import Data.HashMap.Strict (adjust) import Data.Morpheus.App.Internal.Resolving.Event   ( EventHandler (..),   )+import Data.Morpheus.App.Internal.Resolving.MonadResolver import Data.Morpheus.App.Internal.Resolving.ResolveValue import Data.Morpheus.App.Internal.Resolving.Resolver-  ( LiftOperation,-    Resolver,+  ( Resolver,     ResponseStream,-    runResolver,   ) import Data.Morpheus.App.Internal.Resolving.ResolverState-  ( ResolverContext (..),-    ResolverState,-    runResolverStateT,+  ( ResolverState,     toResolverStateT,   ) import Data.Morpheus.App.Internal.Resolving.SchemaAPI (schemaAPI)@@ -32,25 +34,20 @@ import Data.Morpheus.App.Internal.Resolving.Utils   ( lookupResJSON,   )-import Data.Morpheus.Internal.Ext (merge)-import Data.Morpheus.Internal.Utils-  ( empty,-  ) import Data.Morpheus.Types.Internal.AST-  ( GQLError,-    MUTATION,+  ( MUTATION,     Operation (..),     OperationType (..),     QUERY,     SUBSCRIPTION,+    Schema (..),     Selection,-    SelectionContent (SelectionSet),     SelectionSet,+    TypeDefinition (typeName),+    TypeName,     VALID,     ValidValue,     Value (..),-    internal,-    splitSystemSelection,   ) import Relude hiding   ( Show,@@ -68,7 +65,7 @@   | NamedResolversValue       {queryResolverMap :: ResolverMap (Resolver QUERY e m)} -instance Monad m => A.FromJSON (RootResolverValue e m) where+instance Monad m => FromJSON (RootResolverValue e m) where   parseJSON res =     pure       RootResolverValue@@ -78,21 +75,10 @@           channelMap = Nothing         } -runRootDataResolver ::-  (Monad m, LiftOperation o) =>-  Maybe (Selection VALID -> ResolverState (Channel e)) ->-  ResolverState (ObjectTypeResolver (Resolver o e m)) ->-  ResolverContext ->-  SelectionSet VALID ->-  ResponseStream e m (Value VALID)-runRootDataResolver-  channels-  res-  ctx-  selection =-    do-      root <- runResolverStateT (toResolverStateT res) ctx-      runResolver channels (resolveObject (ResolverMapContext mempty mempty) root (Just selection)) ctx+rootResolver :: (MonadResolver m) => ResolverState (ObjectTypeResolver m) -> SelectionSet VALID -> m ValidValue+rootResolver res selection = do+  root <- liftState (toResolverStateT res)+  resolvePlainRoot root selection  runRootResolverValue :: Monad m => RootResolverValue e m -> ResolverContext -> ResponseStream e m (Value VALID) runRootResolverValue@@ -102,37 +88,34 @@       subscriptionResolver,       channelMap     }-  ctx@ResolverContext {operation = Operation {operationType, operationSelection}} =+  ctx@ResolverContext {operation = Operation {..}, ..} =     selectByOperation operationType     where       selectByOperation OPERATION_QUERY =-        withIntrospection (runRootDataResolver channelMap queryResolver ctx) ctx+        runResolver channelMap (rootResolver (withIntroFields schema <$> queryResolver) operationSelection) ctx       selectByOperation OPERATION_MUTATION =-        runRootDataResolver channelMap mutationResolver ctx operationSelection+        runResolver channelMap (rootResolver mutationResolver operationSelection) ctx       selectByOperation OPERATION_SUBSCRIPTION =-        runRootDataResolver channelMap subscriptionResolver ctx operationSelection+        runResolver channelMap (rootResolver subscriptionResolver operationSelection) ctx runRootResolverValue   NamedResolversValue {queryResolverMap}-  ctx@ResolverContext {operation = Operation {operationType}} =+  ctx@ResolverContext {operation = Operation {..}} =     selectByOperation operationType     where-      selectByOperation OPERATION_QUERY = withIntrospection (\sel -> runResolver Nothing (resolvedValue sel) ctx) ctx+      selectByOperation OPERATION_QUERY = runResolver Nothing queryResolver ctx         where-          resolvedValue selection = resolveRef (ResolverMapContext empty queryResolverMap) (NamedResolverRef "Query" ["ROOT"]) (SelectionSet selection)+          queryResolver = do+            name <- asks (typeName . query . schema)+            resolveNamedRoot name (withNamedIntroFields name ctx queryResolverMap) operationSelection       selectByOperation _ = throwError "mutation and subscription is not supported for namedResolvers" -withIntrospection :: Monad m => (SelectionSet VALID -> ResponseStream event m ValidValue) -> ResolverContext -> ResponseStream event m ValidValue-withIntrospection f ctx@ResolverContext {operation} = case splitSystemSelection (operationSelection operation) of-  (Nothing, _) -> f (operationSelection operation)-  (Just intro, Nothing) -> introspection intro ctx-  (Just intro, Just selection) -> do-    x <- f selection-    y <- introspection intro ctx-    mergeRoot y x--introspection :: Monad m => SelectionSet VALID -> ResolverContext -> ResponseStream event m ValidValue-introspection selection ctx@ResolverContext {schema} = runResolver Nothing (resolveObject (ResolverMapContext mempty mempty) (schemaAPI schema) (Just selection)) ctx+withNamedIntroFields :: (MonadResolver m, MonadOperation m ~ QUERY) => TypeName -> ResolverContext -> ResolverMap m -> ResolverMap m+withNamedIntroFields queryName ResolverContext {..} = adjust updateNamed queryName+  where+    updateNamed NamedResolver {..} = NamedResolver {resolverFun = const (updateResult <$> resolverFun ["ROOT"]), ..}+      where+        updateResult [NamedObjectResolver obj] = [NamedObjectResolver (withIntroFields schema obj)]+        updateResult value = value -mergeRoot :: MonadError GQLError m => ValidValue -> ValidValue -> m ValidValue-mergeRoot (Object x) (Object y) = Object <$> merge x y-mergeRoot _ _ = throwError (internal "can't merge non object types")+withIntroFields :: (MonadResolver m, MonadOperation m ~ QUERY) => Schema VALID -> ObjectTypeResolver m -> ObjectTypeResolver m+withIntroFields schema (ObjectTypeResolver fields) = ObjectTypeResolver (fields <> objectFields (schemaAPI schema))
src/Data/Morpheus/App/Internal/Resolving/SchemaAPI.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE NoImplicitPrelude #-}  module Data.Morpheus.App.Internal.Resolving.SchemaAPI@@ -9,7 +10,10 @@   ) where -import Data.Morpheus.App.Internal.Resolving.Resolver (Resolver, withArguments)+import Data.Morpheus.App.Internal.Resolving.MonadResolver+  ( MonadResolver (..),+    withArguments,+  ) import Data.Morpheus.App.Internal.Resolving.Types   ( ObjectTypeResolver (..),     ResolverValue,@@ -18,8 +22,7 @@     mkObject,   ) import Data.Morpheus.App.RenderIntrospection-  ( WithSchema,-    createObjectType,+  ( createObjectType,     render,   ) import Data.Morpheus.Internal.Utils@@ -44,25 +47,25 @@ import Relude hiding (empty) import qualified Relude as HM -resolveTypes :: (Monad m, WithSchema m) => Schema VALID -> m (ResolverValue m)+resolveTypes :: MonadResolver m => Schema VALID -> m (ResolverValue m) resolveTypes schema = mkList <$> traverse render (toList $ typeDefinitions schema)  renderOperation ::-  (Monad m, WithSchema m) =>+  MonadResolver m =>   Maybe (TypeDefinition OBJECT VALID) ->   m (ResolverValue m) renderOperation (Just TypeDefinition {typeName}) = pure $ createObjectType typeName Nothing [] empty renderOperation Nothing = pure mkNull  findType ::-  (Monad m, WithSchema m) =>+  MonadResolver m =>   TypeName ->   Schema VALID ->   m (ResolverValue m) findType name = selectOr (pure mkNull) render name . typeDefinitions  schemaResolver ::-  (Monad m, WithSchema m) =>+  MonadResolver m =>   Schema VALID ->   m (ResolverValue m) schemaResolver schema@Schema {query, mutation, subscription, directiveDefinitions} =@@ -76,7 +79,12 @@         ("directives", render $ sortWith directiveDefinitionName $ toList directiveDefinitions)       ] -schemaAPI :: Monad m => Schema VALID -> ObjectTypeResolver (Resolver QUERY e m)+schemaAPI ::+  ( MonadOperation m ~ QUERY,+    MonadResolver m+  ) =>+  Schema VALID ->+  ObjectTypeResolver m schemaAPI schema =   ObjectTypeResolver     ( HM.fromList
src/Data/Morpheus/App/Internal/Resolving/Types.hs view
@@ -37,7 +37,7 @@ import Control.Monad.Except (MonadError (throwError)) import qualified Data.HashMap.Lazy as HM import Data.Morpheus.Internal.Ext (Merge (..))-import Data.Morpheus.Internal.Utils (KeyOf (keyOf))+import Data.Morpheus.Internal.Utils (IsMap (toAssoc), KeyOf (keyOf)) import Data.Morpheus.Types.Internal.AST   ( FieldName,     GQLError,@@ -69,7 +69,9 @@   }  instance Show (ObjectTypeResolver m) where-  show _ = "ObjectTypeResolver {}"+  show ObjectTypeResolver {..} = "ObjectTypeResolver { " <> intercalate "," (map showField (toAssoc objectFields)) <> " }"+    where+      showField (name, _) = show name <> " = " <> "ResolverValue m"  data NamedResolverRef = NamedResolverRef   { resolverTypeName :: TypeName,
src/Data/Morpheus/App/Internal/Resolving/Utils.hs view
@@ -7,6 +7,7 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE NoImplicitPrelude #-} @@ -18,29 +19,39 @@     lookupResJSON,     mkValue,     ResolverMonad,+    withField,+    withObject,   ) where  import Control.Monad.Except (MonadError (throwError))-import qualified Data.Aeson as A+import Data.Aeson (Value (..)) import Data.Morpheus.App.Internal.Resolving.ResolverState   ( ResolverContext (..),+    updateCurrentType,   ) import Data.Morpheus.App.Internal.Resolving.Types   ( NamedResolverRef (..),     ObjectTypeResolver (..),     ResolverValue (..),+    mkBoolean,     mkList,     mkNull,     mkObjectMaybe,+    mkString,   )-import Data.Morpheus.Internal.Utils (selectOr, toAssoc)-import qualified Data.Morpheus.Internal.Utils as U+import Data.Morpheus.Error (subfieldsNotSelected)+import Data.Morpheus.Internal.Utils (IsMap (..), selectOr, toAssoc, (<:>)) import Data.Morpheus.Types.Internal.AST   ( FieldName,     GQLError,-    ScalarValue (..),+    Selection (..),+    SelectionContent (..),+    SelectionSet,+    TypeDefinition (..),     TypeName,+    UnionTag (..),+    VALID,     decodeScientific,     internal,     packName,@@ -48,7 +59,6 @@   ) import Data.Morpheus.Types.SelectionTree (SelectionTree (..)) import Data.Text (breakOnEnd, splitOn)-import qualified Data.Vector as V import Relude hiding (break)  type ResolverMonad m = (MonadError GQLError m, MonadReader ResolverContext m)@@ -56,9 +66,9 @@ lookupResJSON ::   (ResolverMonad f, MonadReader ResolverContext m) =>   FieldName ->-  A.Value ->+  Value ->   f (ObjectTypeResolver m)-lookupResJSON name (A.Object fields) =+lookupResJSON name (Object fields) =   selectOr     mkEmptyObject     (requireObject <=< mkValue)@@ -73,21 +83,21 @@   ( MonadReader ResolverContext f,     MonadReader ResolverContext m   ) =>-  A.Value ->+  Value ->   f (ResolverValue m)-mkValue (A.Object v) = pure $ mkObjectMaybe typename fields+mkValue (Object v) = pure $ mkObjectMaybe typename fields   where-    typename = U.lookup "__typename" v >>= unpackJSONName+    typename = lookup "__typename" v >>= unpackJSONName     fields = map (bimap packName mkValue) (toAssoc v)-mkValue (A.Array ls) = mkList <$> traverse mkValue (V.toList ls)-mkValue A.Null = pure mkNull-mkValue (A.Number x) = pure $ ResScalar (decodeScientific x)-mkValue (A.String txt) = case withSelf txt of+mkValue (Array ls) = mkList <$> traverse mkValue (toList ls)+mkValue Null = pure mkNull+mkValue (Number x) = pure $ ResScalar (decodeScientific x)+mkValue (String txt) = case withSelf txt of   ARG name -> do     sel <- asks currentSelection-    mkValue (fromMaybe A.Null (getArgument name sel))-  NoAPI v -> pure $ ResScalar (String v)-mkValue (A.Bool x) = pure $ ResScalar (Boolean x)+    mkValue (fromMaybe Null (getArgument name sel))+  NoAPI v -> pure $ mkString v+mkValue (Bool x) = pure $ mkBoolean x  data SelfAPI   = ARG Text@@ -104,6 +114,29 @@ requireObject (ResObject _ x) = pure x requireObject _ = throwError (internal "resolver must be an object") -unpackJSONName :: A.Value -> Maybe TypeName-unpackJSONName (A.String x) = Just (packName x)+unpackJSONName :: Value -> Maybe TypeName+unpackJSONName (String x) = Just (packName x) unpackJSONName _ = Nothing++withField :: Monad m' => a -> (m (ResolverValue m) -> m' a) -> FieldName -> ObjectTypeResolver m -> m' a+withField fb suc selectionName ObjectTypeResolver {..} = maybe (pure fb) suc (lookup selectionName objectFields)++withObject ::+  (ResolverMonad m) =>+  Maybe TypeName ->+  (Maybe (SelectionSet VALID) -> m value) ->+  SelectionContent VALID ->+  m value+withObject __typename f = updateCurrentType __typename . checkContent+  where+    checkContent (SelectionSet selection) = f (Just selection)+    checkContent (UnionSelection interface unionSel) = do+      typename <- asks (typeName . currentType)+      selection <- selectOr (pure interface) (fx interface) typename unionSel+      f selection+      where+        fx (Just x) y = Just <$> (x <:> unionTagSelection y)+        fx Nothing y = pure $ Just $ unionTagSelection y+    checkContent SelectionField = do+      sel <- asks currentSelection+      throwError $ subfieldsNotSelected (selectionName sel) "" (selectionPosition sel)
src/Data/Morpheus/App/NamedResolvers.hs view
@@ -1,3 +1,6 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE RankNTypes #-}+ module Data.Morpheus.App.NamedResolvers   ( ref,     object,@@ -15,7 +18,11 @@ where  import qualified Data.HashMap.Lazy as HM-import Data.Morpheus.App.Internal.Resolving.Resolver (LiftOperation, Resolver, getArgument)+import Data.Morpheus.App.Internal.Resolving.MonadResolver+  ( MonadResolver,+    getArgument,+  )+import Data.Morpheus.App.Internal.Resolving.Resolver (Resolver) import Data.Morpheus.App.Internal.Resolving.RootResolverValue (RootResolverValue (..)) import Data.Morpheus.App.Internal.Resolving.Types   ( NamedResolver (..),@@ -49,31 +56,33 @@ refs :: Applicative m => TypeName -> [ValidValue] -> ResolverValue m refs typeName = mkList . map (ref typeName) -type NamedResolverFunction o e m = [ValidValue] -> Resolver o e m [ResultBuilder o e m]+type NamedResolverFunction o e m = NamedFunction (Resolver o e m) +type NamedFunction m = [ValidValue] -> m [ResultBuilder m]+ -- types-object :: (LiftOperation o, Monad m) => [(FieldName, Resolver o e m (ResolverValue (Resolver o e m)))] -> Resolver o e m (ResultBuilder o e m)+object :: (MonadResolver m) => [(FieldName, m (ResolverValue m))] -> m (ResultBuilder m) object = pure . Object -variant :: (LiftOperation o, Monad m) => TypeName -> ValidValue -> Resolver o e m (ResultBuilder o e m)+variant :: (MonadResolver m) => TypeName -> ValidValue -> m (ResultBuilder m) variant tName = pure . Union tName -nullRes :: (LiftOperation o, Monad m) => Resolver o e m (ResultBuilder o e m)+nullRes :: (MonadResolver m) => m (ResultBuilder m) nullRes = pure Null -queryResolvers :: Monad m => [(TypeName, NamedResolverFunction QUERY e m)] -> RootResolverValue e m+queryResolvers :: Monad m => [(TypeName, NamedFunction (Resolver QUERY e m))] -> RootResolverValue e m queryResolvers = NamedResolversValue . mkResolverMap  -- INTERNAL-data ResultBuilder o e m-  = Object [(FieldName, Resolver o e m (ResolverValue (Resolver o e m)))]+data ResultBuilder m+  = Object [(FieldName, m (ResolverValue m))]   | Union TypeName ValidValue   | Null -mkResolverMap :: (LiftOperation o, Monad m) => [(TypeName, NamedResolverFunction o e m)] -> ResolverMap (Resolver o e m)+mkResolverMap :: MonadResolver m => [(TypeName, NamedFunction m)] -> ResolverMap m mkResolverMap = HM.fromList . map packRes   where-    packRes :: (LiftOperation o, Monad m) => (TypeName, NamedResolverFunction o e m) -> (TypeName, NamedResolver (Resolver o e m))+    packRes :: MonadResolver m => (TypeName, NamedFunction m) -> (TypeName, NamedResolver m)     packRes (typeName, f) = (typeName, NamedResolver typeName (fmap (map mapValue) . f))       where         mapValue (Object x) = NamedObjectResolver (ObjectTypeResolver $ HM.fromList x)
src/Data/Morpheus/App/RenderIntrospection.hs view
@@ -11,13 +11,12 @@ module Data.Morpheus.App.RenderIntrospection   ( render,     createObjectType,-    WithSchema,   ) where  import Control.Monad.Except (MonadError (throwError))-import Data.Morpheus.App.Internal.Resolving.Resolver-  ( Resolver,+import Data.Morpheus.App.Internal.Resolving.MonadResolver+  ( MonadResolver,     ResolverContext (..),   ) import Data.Morpheus.App.Internal.Resolving.Types@@ -46,12 +45,9 @@     FieldDefinition (..),     FieldName,     FieldsDefinition,-    GQLError,     IN,     Msg (msg),     OUT,-    QUERY,-    Schema,     TRUE,     TypeContent (..),     TypeDefinition (..),@@ -76,28 +72,14 @@ import Data.Text (pack) import Relude -class-  ( Monad m,-    MonadError GQLError m-  ) =>-  WithSchema m-  where-  getSchema :: m (Schema VALID)--instance Monad m => WithSchema (Resolver QUERY e m) where-  getSchema = schema <$> ask--selectType ::-  WithSchema m =>-  TypeName ->-  m (TypeDefinition ANY VALID)+selectType :: MonadResolver m => TypeName -> m (TypeDefinition ANY VALID) selectType name =-  getSchema+  asks schema     >>= selectBy (internal $ "INTROSPECTION Type not Found: \"" <> msg name <> "\"") name       . typeDefinitions  class RenderIntrospection a where-  render :: (Monad m, WithSchema m) => a -> m (ResolverValue m)+  render :: (MonadResolver m) => a -> m (ResolverValue m)  instance RenderIntrospection TypeName where   render = pure . mkString . unpackName@@ -150,19 +132,12 @@       } = pure $ renderContent typeContent       where         __type ::-          ( Monad m,-            WithSchema m-          ) =>+          MonadResolver m =>           TypeKind ->           [(FieldName, m (ResolverValue m))] ->           ResolverValue m         __type kind = mkType kind typeName typeDescription-        renderContent ::-          ( Monad m,-            WithSchema m-          ) =>-          TypeContent bool a VALID ->-          ResolverValue m+        renderContent :: MonadResolver m => TypeContent bool a VALID -> ResolverValue m         renderContent DataScalar {} = __type KIND_SCALAR []         renderContent (DataEnum enums) = __type KIND_ENUM [("enumValues", render enums)]         renderContent (DataInputObject inputFields) =@@ -212,10 +187,7 @@   render Null = pure mkNull   render x = pure $ mkString $ fromLBS $ GQL.render x -instance-  RenderIntrospection-    (FieldDefinition OUT VALID)-  where+instance RenderIntrospection (FieldDefinition OUT VALID) where   render FieldDefinition {..} =     pure $       mkObject "__Field" $@@ -255,7 +227,7 @@ instance RenderIntrospection TypeRef where   render TypeRef {typeConName, typeWrappers} = renderWrapper typeWrappers     where-      renderWrapper :: (Monad m, WithSchema m) => TypeWrapper -> m (ResolverValue m)+      renderWrapper :: (Monad m, MonadResolver m) => TypeWrapper -> m (ResolverValue m)       renderWrapper (TypeList nextWrapper isNonNull) =         pure $           withNonNull isNonNull $@@ -270,9 +242,7 @@           pure $ mkType kind typeConName Nothing []  withNonNull ::-  ( Monad m,-    WithSchema m-  ) =>+  MonadResolver m =>   Bool ->   ResolverValue m ->   ResolverValue m@@ -285,19 +255,13 @@ withNonNull False contentType = contentType  renderPossibleTypes ::-  (Monad m, WithSchema m) =>+  MonadResolver m =>   TypeName ->   m (ResolverValue m)-renderPossibleTypes name =-  mkList-    <$> ( getSchema-            >>= traverse render . possibleInterfaceTypes name-        )+renderPossibleTypes name = mkList <$> (asks schema >>= traverse render . possibleInterfaceTypes name)  renderDeprecated ::-  ( Monad m,-    WithSchema m-  ) =>+  MonadResolver m =>   Directives s ->   [(FieldName, m (ResolverValue m))] renderDeprecated dirs =@@ -306,17 +270,14 @@   ]  description ::-  ( Monad m,-    WithSchema m-  ) =>+  MonadResolver m =>   Maybe Description ->   (FieldName, m (ResolverValue m)) description = ("description",) . render  mkType ::   ( RenderIntrospection name,-    Monad m,-    WithSchema m+    MonadResolver m   ) =>   TypeKind ->   name ->@@ -334,7 +295,7 @@     )  createObjectType ::-  (Monad m, WithSchema m) =>+  (MonadResolver m) =>   TypeName ->   Maybe Description ->   [TypeName] ->@@ -344,7 +305,7 @@   mkType (KIND_OBJECT Nothing) name desc [("fields", render fields), ("interfaces", mkList <$> traverse implementedInterface interfaces)]  implementedInterface ::-  (Monad m, WithSchema m) =>+  (MonadResolver m) =>   TypeName ->   m (ResolverValue m) implementedInterface name =@@ -356,27 +317,26 @@  renderName ::   ( RenderIntrospection name,-    Monad m,-    WithSchema m+    MonadResolver m   ) =>   name ->   (FieldName, m (ResolverValue m)) renderName = ("name",) . render  renderKind ::-  (Monad m, WithSchema m) =>+  MonadResolver m =>   TypeKind ->   (FieldName, m (ResolverValue m)) renderKind = ("kind",) . render  type' ::-  (Monad m, WithSchema m) =>+  MonadResolver m =>   TypeRef ->   (FieldName, m (ResolverValue m)) type' = ("type",) . render  defaultValue ::-  (Monad m, WithSchema m) =>+  MonadResolver m =>   Maybe (FieldContent TRUE IN VALID) ->   ( FieldName,     m (ResolverValue m)
src/Data/Morpheus/Types/GQLWrapper.hs view
@@ -38,15 +38,8 @@     wrapper a ->     m (ResolverValue m) -withList ::-  ( EncodeWrapper f,-    Monad m-  ) =>-  (a -> f b) ->-  (b -> m (ResolverValue m)) ->-  a ->-  m (ResolverValue m)-withList f encodeValue = encodeWrapper encodeValue . f+withList :: (Monad m, Foldable f) => (a -> m (ResolverValue m)) -> f a -> m (ResolverValue m)+withList encodeValue = encodeWrapper encodeValue . toList  instance EncodeWrapper Maybe where   encodeWrapper = maybe (pure mkNull)@@ -55,16 +48,16 @@   encodeWrapper encodeValue = fmap mkList . traverse encodeValue  instance EncodeWrapper NonEmpty where-  encodeWrapper = withList toList+  encodeWrapper = withList  instance EncodeWrapper Seq where-  encodeWrapper = withList toList+  encodeWrapper = withList  instance EncodeWrapper Vector where-  encodeWrapper = withList toList+  encodeWrapper = withList  instance EncodeWrapper Set where-  encodeWrapper = withList toList+  encodeWrapper = withList  instance EncodeWrapper SubscriptionField where   encodeWrapper encode (SubscriptionField _ res) = encode res@@ -143,3 +136,15 @@  instance EncodeWrapperValue [] where   encodeWrapperValue f xs = List <$> traverse f xs++instance EncodeWrapperValue Set where+  encodeWrapperValue f = encodeWrapperValue f . toList++instance EncodeWrapperValue NonEmpty where+  encodeWrapperValue f = encodeWrapperValue f . toList++instance EncodeWrapperValue Seq where+  encodeWrapperValue f = encodeWrapperValue f . toList++instance EncodeWrapperValue Vector where+  encodeWrapperValue f = encodeWrapperValue f . toList
test/Batching.hs view
@@ -43,6 +43,8 @@     TypeName,     VALID,     ValidValue,+    Value (..),+    msg,     unpackName,   ) import Relude hiding (ByteString, fromList)@@ -65,7 +67,7 @@ require :: Monad m => NamedResolverFunction QUERY e m -> BatchedValues -> NamedResolverFunction QUERY e m require f req args   | fromList args == req = f args-  | otherwise = throwError ("was not batched! expected: " <> show req <> "got: " <> show args)+  | otherwise = throwError ("was not batched! expected: " <> msg (List $ toList req) <> "got: " <> msg (List args))  gods :: [ValidValue] gods = ["poseidon", "morpheus", "zeus"]
+ test/Execution.hs view
@@ -0,0 +1,95 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Execution+  ( runExecutionTest,+  )+where++import Control.Monad.Except (MonadError (throwError))+import Data.ByteString.Lazy.Char8 (readFile)+import Data.Morpheus.App (mkApp, runApp)+import Data.Morpheus.App.Internal.Resolving+  ( ObjectTypeResolver (..),+    Resolver,+    ResolverValue,+    RootResolverValue (..),+    mkObject,+    resultOr,+  )+import Data.Morpheus.App.NamedResolvers+  ( getArgument,+    list,+  )+import Data.Morpheus.Core+  ( parseSchema,+  )+import Data.Morpheus.Types.IO+  ( GQLRequest (..),+    GQLResponse,+  )+import Data.Morpheus.Types.Internal.AST+  ( Msg (..),+    QUERY,+    Schema,+    VALID,+    ValidValue,+  )+import Relude hiding (ByteString, readFile)+import Test.Morpheus+  ( FileUrl,+    file,+    testApi,+  )+import Test.Tasty+  ( TestTree,+  )++type ExecState m = (StateT Int m)++type ResQ m = Resolver QUERY () (ExecState m)++getName :: ValidValue -> ResolverValue m+getName "zeus" = "Zeus"+getName "morpheus" = "Morpheus"+getName "poseidon" = "Zeus"+getName "cronos" = "Cronos"+getName _ = ""++restrictExecutions :: Monad m => Int -> ResQ m ()+restrictExecutions expected = do+  count <- lift get+  if expected == count then pure () else throwError ("unexpected execution count. expected " <> msg expected <> " but got " <> msg count <> ".")++deityResolver :: Monad m => ValidValue -> ResQ m (ResolverValue (ResQ m))+deityResolver name = do+  lift (modify (+ 1))+  pure $ mkObject "Deity" [("name", pure $ getName name)]++resolvers :: Monad m => RootResolverValue () (ExecState m)+resolvers =+  RootResolverValue+    { queryResolver =+        pure+          ( ObjectTypeResolver $+              fromList+                [ ("deity", (getArgument "id" >>= deityResolver) <* restrictExecutions 1),+                  ("deities", (list <$> traverse deityResolver ["zeus", "morpheus"]) <* restrictExecutions 2)+                ]+          ),+      mutationResolver = pure (ObjectTypeResolver mempty),+      subscriptionResolver = pure (ObjectTypeResolver mempty),+      channelMap = Nothing+    }++getSchema :: FileUrl -> IO (Schema VALID)+getSchema url = readFile (toString url) >>= resultOr (fail . show) pure . parseSchema++runExecutionTest :: FileUrl -> FileUrl -> TestTree+runExecutionTest url = testApi api+  where+    api :: GQLRequest -> IO GQLResponse+    api req = do+      schemaDeities <- getSchema (file url "schema.gql")+      (response, _) <- runStateT (runApp (mkApp schemaDeities resolvers) req) 0+      pure response
test/Spec.hs view
@@ -25,6 +25,7 @@   ( GQLRequest (..),     GQLResponse,   )+import Execution (runExecutionTest) import NamedResolvers (runNamedResolversTest) import Relude hiding (ByteString) import Test.Morpheus@@ -66,5 +67,6 @@       deepScan runApiTest (mkUrl "api"),       deepScan (map . runNamedResolversTest) (mkUrl "named-resolvers"),       deepScan (map . runAPIConstraints) (mkUrl "api-constraints"),-      deepScan (map . runBatchingTest) (mkUrl "batching")+      deepScan (map . runBatchingTest) (mkUrl "batching"),+      deepScan (map . runExecutionTest) (mkUrl "execution")     ]
+ test/execution/many/query.gql view
@@ -0,0 +1,5 @@+query {+  deities {+    name+  }+}
+ test/execution/many/response.json view
@@ -0,0 +1,12 @@+{+  "data": {+    "deities": [+      {+        "name": "Zeus"+      },+      {+        "name": "Morpheus"+      }+    ]+  }+}
+ test/execution/schema.gql view
@@ -0,0 +1,8 @@+type Deity {+  name: String!+}++type Query {+  deities: [Deity!]!+  deity(id: ID): Deity+}
+ test/execution/single/query.gql view
@@ -0,0 +1,5 @@+query {+  deity(id:"morpheus") {+    name+  }+}
+ test/execution/single/response.json view
@@ -0,0 +1,7 @@+{+  "data": {+    "deity": {+      "name": "Morpheus"+    }+  }+}