{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Data.Morpheus.Server
( gqlSocketApp
, gqlSocketMonadIOApp
, initGQLState
, GQLState
)
where
import Control.Exception ( finally )
import Control.Monad ( forever )
import Control.Monad.IO.Class ( MonadIO(liftIO) )
import Data.Text ( Text )
import Network.WebSockets ( ServerApp
, acceptRequestWith
, forkPingThread
, pendingRequest
, receiveData
, sendTextData
)
import Data.Morpheus.Execution.Server.Resolve
( RootResCon
, coreResolver
)
import Data.Morpheus.Execution.Subscription.Apollo
( SubAction(..)
, acceptApolloSubProtocol
, apolloFormat
, toApolloResponse
)
import Data.Morpheus.Execution.Subscription.ClientRegister
( GQLState
, addClientSubscription
, connectClient
, disconnectClient
, initGQLState
, publishUpdates
, removeClientSubscription
)
import Data.Morpheus.Types.Internal.Resolving
( GQLRootResolver(..)
, GQLChannel(..)
, ResponseEvent(..)
, ResponseStream
, runResultT
, Result(..)
)
import Data.Morpheus.Types.Internal.WebSocket
( GQLClient(..) )
import Data.Morpheus.Types.IO ( GQLResponse(..) )
import Data.Morpheus.Types.Internal.AST
( ValidValue )
handleSubscription
:: (Eq (StreamChannel e), GQLChannel e, MonadIO m)
=> GQLClient m e
-> GQLState m e
-> Text
-> ResponseStream e m ValidValue
-> m ()
handleSubscription GQLClient { clientConnection, clientID } state sessionId stream
= do
response <- runResultT stream
case response of
Success { events } -> mapM_ execute events
Failure errors -> liftIO $ sendTextData
clientConnection
(toApolloResponse sessionId $ Errors errors)
where
execute (Publish pub) = publishUpdates state pub
execute (Subscribe sub) = addClientSubscription clientID sub sessionId state
gqlSocketMonadIOApp
:: (RootResCon m e que mut sub, MonadIO m)
=> GQLRootResolver m e que mut sub
-> GQLState m e
-> (m () -> IO ())
-> ServerApp
gqlSocketMonadIOApp gqlRoot state f pending = do
connection <- acceptRequestWith pending
$ acceptApolloSubProtocol (pendingRequest pending)
forkPingThread connection 30
client <- connectClient connection state
finally (f $ queryHandler client) (disconnectClient client state)
where
queryHandler client = forever handleRequest
where
handleRequest = do
d <- liftIO $ receiveData (clientConnection client)
resolveMessage (apolloFormat d)
where
resolveMessage (SubError x) = liftIO $ print x
resolveMessage (AddSub sessionId request) =
handleSubscription client state sessionId (coreResolver gqlRoot request)
resolveMessage (RemoveSub sessionId) =
removeClientSubscription (clientID client) sessionId state
gqlSocketApp
:: (RootResCon IO e que mut sub)
=> GQLRootResolver IO e que mut sub
-> GQLState IO e
-> ServerApp
gqlSocketApp gqlRoot state = gqlSocketMonadIOApp gqlRoot state id