{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}

{-|
Module      :  Data.GraphQL.Monad.Class
Maintainer  :  Brandon Chinn <brandonchinn178@gmail.com>
Stability   :  experimental
Portability :  portable

Defines the 'MonadGraphQLQuery' type class, which defines how GraphQL queries should be run.
-}
module Data.GraphQL.Monad.Class (
  MonadGraphQLQuery (..),
  runQuery,
) where

import Control.Exception (throwIO)
import Control.Monad.IO.Class (MonadIO (..))
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Except (ExceptT)
import Control.Monad.Trans.Identity (IdentityT)
import Control.Monad.Trans.Maybe (MaybeT)
import qualified Control.Monad.Trans.RWS.Lazy as Lazy
import qualified Control.Monad.Trans.RWS.Strict as Strict
import Control.Monad.Trans.Reader (ReaderT)
import qualified Control.Monad.Trans.State.Lazy as Lazy
import qualified Control.Monad.Trans.State.Strict as Strict
import qualified Control.Monad.Trans.Writer.Lazy as Lazy
import qualified Control.Monad.Trans.Writer.Strict as Strict
import Data.Aeson.Schema (Object)
import Data.Maybe (fromJust)

import Data.GraphQL.Error (GraphQLException (..))
import Data.GraphQL.Query (GraphQLQuery (..))
import Data.GraphQL.Result (GraphQLResult, getErrors, getResult)

-- | A type class for monads that can run GraphQL queries.
class (Monad m) => MonadGraphQLQuery m where
  -- | Run the given query and return the 'GraphQLResult'.
  runQuerySafe ::
    (GraphQLQuery query, schema ~ ResultSchema query) =>
    query
    -> m (GraphQLResult (Object schema))

-- | Run the given query and returns the result, erroring if the query returned errors.
runQuery ::
  (MonadIO m, MonadGraphQLQuery m, GraphQLQuery query, schema ~ ResultSchema query) =>
  query
  -> m (Object schema)
runQuery :: forall (m :: * -> *) query (schema :: Schema).
(MonadIO m, MonadGraphQLQuery m, GraphQLQuery query,
 schema ~ ResultSchema query) =>
query -> m (Object schema)
runQuery query
query = do
  GraphQLResult (Object schema)
result <- query -> m (GraphQLResult (Object schema))
forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
forall (m :: * -> *) query (schema :: Schema).
(MonadGraphQLQuery m, GraphQLQuery query,
 schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
runQuerySafe query
query
  case GraphQLResult (Object schema) -> [GraphQLError]
forall r. GraphQLResult r -> [GraphQLError]
getErrors GraphQLResult (Object schema)
result of
    [] -> Object schema -> m (Object schema)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Object schema -> m (Object schema))
-> Object schema -> m (Object schema)
forall a b. (a -> b) -> a -> b
$ Maybe (Object schema) -> Object schema
forall a. HasCallStack => Maybe a -> a
fromJust (Maybe (Object schema) -> Object schema)
-> Maybe (Object schema) -> Object schema
forall a b. (a -> b) -> a -> b
$ GraphQLResult (Object schema) -> Maybe (Object schema)
forall r. GraphQLResult r -> Maybe r
getResult GraphQLResult (Object schema)
result
    [GraphQLError]
errors -> IO (Object schema) -> m (Object schema)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Object schema) -> m (Object schema))
-> IO (Object schema) -> m (Object schema)
forall a b. (a -> b) -> a -> b
$ GraphQLException -> IO (Object schema)
forall e a. Exception e => e -> IO a
throwIO (GraphQLException -> IO (Object schema))
-> GraphQLException -> IO (Object schema)
forall a b. (a -> b) -> a -> b
$ [GraphQLError] -> GraphQLException
GraphQLException [GraphQLError]
errors

{- Instances for common monad transformers -}

instance (MonadGraphQLQuery m) => MonadGraphQLQuery (ReaderT r m) where
  runQuerySafe :: forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> ReaderT r m (GraphQLResult (Object schema))
runQuerySafe = m (GraphQLResult (Object schema))
-> ReaderT r m (GraphQLResult (Object schema))
forall (m :: * -> *) a. Monad m => m a -> ReaderT r m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (GraphQLResult (Object schema))
 -> ReaderT r m (GraphQLResult (Object schema)))
-> (query -> m (GraphQLResult (Object schema)))
-> query
-> ReaderT r m (GraphQLResult (Object schema))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. query -> m (GraphQLResult (Object schema))
forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
forall (m :: * -> *) query (schema :: Schema).
(MonadGraphQLQuery m, GraphQLQuery query,
 schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
runQuerySafe

instance (MonadGraphQLQuery m) => MonadGraphQLQuery (ExceptT e m) where
  runQuerySafe :: forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> ExceptT e m (GraphQLResult (Object schema))
runQuerySafe = m (GraphQLResult (Object schema))
-> ExceptT e m (GraphQLResult (Object schema))
forall (m :: * -> *) a. Monad m => m a -> ExceptT e m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (GraphQLResult (Object schema))
 -> ExceptT e m (GraphQLResult (Object schema)))
-> (query -> m (GraphQLResult (Object schema)))
-> query
-> ExceptT e m (GraphQLResult (Object schema))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. query -> m (GraphQLResult (Object schema))
forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
forall (m :: * -> *) query (schema :: Schema).
(MonadGraphQLQuery m, GraphQLQuery query,
 schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
runQuerySafe

instance (MonadGraphQLQuery m) => MonadGraphQLQuery (IdentityT m) where
  runQuerySafe :: forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> IdentityT m (GraphQLResult (Object schema))
runQuerySafe = m (GraphQLResult (Object schema))
-> IdentityT m (GraphQLResult (Object schema))
forall (m :: * -> *) a. Monad m => m a -> IdentityT m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (GraphQLResult (Object schema))
 -> IdentityT m (GraphQLResult (Object schema)))
-> (query -> m (GraphQLResult (Object schema)))
-> query
-> IdentityT m (GraphQLResult (Object schema))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. query -> m (GraphQLResult (Object schema))
forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
forall (m :: * -> *) query (schema :: Schema).
(MonadGraphQLQuery m, GraphQLQuery query,
 schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
runQuerySafe

instance (MonadGraphQLQuery m) => MonadGraphQLQuery (MaybeT m) where
  runQuerySafe :: forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> MaybeT m (GraphQLResult (Object schema))
runQuerySafe = m (GraphQLResult (Object schema))
-> MaybeT m (GraphQLResult (Object schema))
forall (m :: * -> *) a. Monad m => m a -> MaybeT m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (GraphQLResult (Object schema))
 -> MaybeT m (GraphQLResult (Object schema)))
-> (query -> m (GraphQLResult (Object schema)))
-> query
-> MaybeT m (GraphQLResult (Object schema))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. query -> m (GraphQLResult (Object schema))
forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
forall (m :: * -> *) query (schema :: Schema).
(MonadGraphQLQuery m, GraphQLQuery query,
 schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
runQuerySafe

instance (Monoid w, MonadGraphQLQuery m) => MonadGraphQLQuery (Lazy.RWST r w s m) where
  runQuerySafe :: forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> RWST r w s m (GraphQLResult (Object schema))
runQuerySafe = m (GraphQLResult (Object schema))
-> RWST r w s m (GraphQLResult (Object schema))
forall (m :: * -> *) a. Monad m => m a -> RWST r w s m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (GraphQLResult (Object schema))
 -> RWST r w s m (GraphQLResult (Object schema)))
-> (query -> m (GraphQLResult (Object schema)))
-> query
-> RWST r w s m (GraphQLResult (Object schema))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. query -> m (GraphQLResult (Object schema))
forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
forall (m :: * -> *) query (schema :: Schema).
(MonadGraphQLQuery m, GraphQLQuery query,
 schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
runQuerySafe

instance (Monoid w, MonadGraphQLQuery m) => MonadGraphQLQuery (Strict.RWST r w s m) where
  runQuerySafe :: forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> RWST r w s m (GraphQLResult (Object schema))
runQuerySafe = m (GraphQLResult (Object schema))
-> RWST r w s m (GraphQLResult (Object schema))
forall (m :: * -> *) a. Monad m => m a -> RWST r w s m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (GraphQLResult (Object schema))
 -> RWST r w s m (GraphQLResult (Object schema)))
-> (query -> m (GraphQLResult (Object schema)))
-> query
-> RWST r w s m (GraphQLResult (Object schema))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. query -> m (GraphQLResult (Object schema))
forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
forall (m :: * -> *) query (schema :: Schema).
(MonadGraphQLQuery m, GraphQLQuery query,
 schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
runQuerySafe

instance (MonadGraphQLQuery m) => MonadGraphQLQuery (Lazy.StateT s m) where
  runQuerySafe :: forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> StateT s m (GraphQLResult (Object schema))
runQuerySafe = m (GraphQLResult (Object schema))
-> StateT s m (GraphQLResult (Object schema))
forall (m :: * -> *) a. Monad m => m a -> StateT s m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (GraphQLResult (Object schema))
 -> StateT s m (GraphQLResult (Object schema)))
-> (query -> m (GraphQLResult (Object schema)))
-> query
-> StateT s m (GraphQLResult (Object schema))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. query -> m (GraphQLResult (Object schema))
forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
forall (m :: * -> *) query (schema :: Schema).
(MonadGraphQLQuery m, GraphQLQuery query,
 schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
runQuerySafe

instance (MonadGraphQLQuery m) => MonadGraphQLQuery (Strict.StateT s m) where
  runQuerySafe :: forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> StateT s m (GraphQLResult (Object schema))
runQuerySafe = m (GraphQLResult (Object schema))
-> StateT s m (GraphQLResult (Object schema))
forall (m :: * -> *) a. Monad m => m a -> StateT s m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (GraphQLResult (Object schema))
 -> StateT s m (GraphQLResult (Object schema)))
-> (query -> m (GraphQLResult (Object schema)))
-> query
-> StateT s m (GraphQLResult (Object schema))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. query -> m (GraphQLResult (Object schema))
forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
forall (m :: * -> *) query (schema :: Schema).
(MonadGraphQLQuery m, GraphQLQuery query,
 schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
runQuerySafe

instance (Monoid w, MonadGraphQLQuery m) => MonadGraphQLQuery (Lazy.WriterT w m) where
  runQuerySafe :: forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> WriterT w m (GraphQLResult (Object schema))
runQuerySafe = m (GraphQLResult (Object schema))
-> WriterT w m (GraphQLResult (Object schema))
forall (m :: * -> *) a. Monad m => m a -> WriterT w m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (GraphQLResult (Object schema))
 -> WriterT w m (GraphQLResult (Object schema)))
-> (query -> m (GraphQLResult (Object schema)))
-> query
-> WriterT w m (GraphQLResult (Object schema))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. query -> m (GraphQLResult (Object schema))
forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
forall (m :: * -> *) query (schema :: Schema).
(MonadGraphQLQuery m, GraphQLQuery query,
 schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
runQuerySafe

instance (Monoid w, MonadGraphQLQuery m) => MonadGraphQLQuery (Strict.WriterT w m) where
  runQuerySafe :: forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> WriterT w m (GraphQLResult (Object schema))
runQuerySafe = m (GraphQLResult (Object schema))
-> WriterT w m (GraphQLResult (Object schema))
forall (m :: * -> *) a. Monad m => m a -> WriterT w m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (GraphQLResult (Object schema))
 -> WriterT w m (GraphQLResult (Object schema)))
-> (query -> m (GraphQLResult (Object schema)))
-> query
-> WriterT w m (GraphQLResult (Object schema))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. query -> m (GraphQLResult (Object schema))
forall query (schema :: Schema).
(GraphQLQuery query, schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
forall (m :: * -> *) query (schema :: Schema).
(MonadGraphQLQuery m, GraphQLQuery query,
 schema ~ ResultSchema query) =>
query -> m (GraphQLResult (Object schema))
runQuerySafe