{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Data.Morpheus.Server
( gqlSocketApp
, initGQLState
, GQLState
) where
import Control.Exception (finally)
import Control.Monad (forever)
import Data.Text (Text)
import Network.WebSockets (ServerApp, acceptRequestWith, forkPingThread,
pendingRequest, receiveData, sendTextData)
import Data.Morpheus.Execution.Server.Resolve (RootResCon, streamResolver)
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.Stream (ResponseEvent (..), ResponseStream, closeStream)
import Data.Morpheus.Types.Internal.WebSocket (GQLClient (..))
import Data.Morpheus.Types.IO (GQLResponse (..))
import Data.Morpheus.Types.Resolver (GQLRootResolver (..))
handleSubscription ::
Eq e
=> GQLClient IO e c
-> GQLState IO e c
-> Text
-> ResponseStream IO e c GQLResponse
-> IO ()
handleSubscription GQLClient {clientConnection, clientID} state sessionId stream = do
(actions, response) <- closeStream stream
case response of
Data _ -> mapM_ execute actions
Errors _ ->
sendTextData clientConnection (toApolloResponse sessionId response)
where
execute (Publish pub) = publishUpdates state pub
execute (Subscribe sub) = addClientSubscription clientID sub sessionId state
gqlSocketApp ::
RootResCon IO e c que mut sub
=> GQLRootResolver IO e c que mut sub
-> GQLState IO e c
-> ServerApp
gqlSocketApp gqlRoot state pending = do
connection <-
acceptRequestWith pending $ acceptApolloSubProtocol (pendingRequest pending)
forkPingThread connection 30
client <- connectClient connection state
finally (queryHandler client) (disconnectClient client state)
where
queryHandler client = forever handleRequest
where
handleRequest =
receiveData (clientConnection client) >>=
resolveMessage . apolloFormat
where
resolveMessage (SubError x) = print x
resolveMessage (AddSub sessionId request) =
handleSubscription
client
state
sessionId
(streamResolver gqlRoot request)
resolveMessage (RemoveSub sessionId) =
removeClientSubscription (clientID client) sessionId state