{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} -- | GraphQL Wai Server Applications 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) -- MORPHEUS 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 -- | Wai WebSocket Server App for GraphQL subscriptions 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