{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} module Data.Morpheus.Resolve.Resolve ( resolve , resolveByteString , resolveStreamByteString , resolveStream , packStream ) where import Control.Monad.Trans.Except (ExceptT(..), runExceptT) import Data.Aeson (eitherDecode, encode) import Data.ByteString.Lazy.Char8 (ByteString) import Data.Morpheus.Error.Utils (badRequestError, renderErrors) import Data.Morpheus.Parser.Parser (parseGQL) import Data.Morpheus.Resolve.Operator (RootResCon, effectEncode, encodeQuery, fullSchema) import Data.Morpheus.Server.ClientRegister (GQLState, publishUpdates) import Data.Morpheus.Types.IO (GQLRequest(..), GQLResponse(..)) import Data.Morpheus.Types.Internal.AST.Operator (Operator(..), Operator'(..)) import Data.Morpheus.Types.Internal.WebSocket (OutputAction(..)) import Data.Morpheus.Types.Resolver (GQLRootResolver(..), unpackEffect, unpackEffect2) import Data.Morpheus.Validation.Validation (validateRequest) resolveByteString :: Monad m => RootResCon m a b c => GQLRootResolver m a b c -> ByteString -> m ByteString resolveByteString rootResolver request = case eitherDecode request of Left aesonError' -> return $ badRequestError aesonError' Right req -> encode <$> resolve rootResolver req resolveStreamByteString :: Monad m => RootResCon m a b c => GQLRootResolver m a b c -> ByteString -> m (OutputAction m ByteString) resolveStreamByteString rootResolver request = case eitherDecode request of Left aesonError' -> return $ NoEffect $ badRequestError aesonError' Right req -> fmap encode <$> resolveStream rootResolver req resolve :: Monad m => RootResCon m a b c => GQLRootResolver m a b c -> GQLRequest -> m GQLResponse resolve GQLRootResolver {queryResolver, mutationResolver, subscriptionResolver} request = case fullSchema queryResolver mutationResolver subscriptionResolver of Left error' -> return $ Errors $ renderErrors error' Right validSchema -> do value <- runExceptT $ _resolve validSchema case value of Left x -> return $ Errors $ renderErrors x Right x -> return $ Data x where _resolve gqlSchema = do rootGQL <- ExceptT $ pure (parseGQL request >>= validateRequest gqlSchema) case rootGQL of Query operator' -> encodeQuery gqlSchema queryResolver $ operatorSelection operator' Mutation operator' -> snd <$> unpackEffect2 (effectEncode mutationResolver (operatorSelection operator')) Subscription operator' -> snd <$> unpackEffect2 (effectEncode subscriptionResolver (operatorSelection operator')) resolveStream :: Monad m => RootResCon m a b c => GQLRootResolver m a b c -> GQLRequest -> m (OutputAction m GQLResponse) resolveStream GQLRootResolver {queryResolver, mutationResolver, subscriptionResolver} request = case fullSchema queryResolver mutationResolver subscriptionResolver of Left error' -> return $ NoEffect $ Errors $ renderErrors error' Right validSchema -> do value <- runExceptT $ _resolve validSchema case value of Left x -> return $ NoEffect $ Errors $ renderErrors x Right value' -> return $ fmap Data value' where _resolve gqlSchema = (ExceptT $ pure (parseGQL request >>= validateRequest gqlSchema)) >>= resolveOperator where resolveOperator (Query operator') = do value <- encodeQuery gqlSchema queryResolver $ operatorSelection operator' return (NoEffect value) resolveOperator (Mutation operator') = do (channels, response) <- unpackEffect2 $ effectEncode mutationResolver $ operatorSelection operator' return PublishMutation {mutationChannels = channels, mutationResponse = response, currentSubscriptionStateResolver} where currentSubscriptionStateResolver selection' = do value <- unpackEffect (effectEncode subscriptionResolver selection') case value of Left x -> pure $ Errors $ renderErrors x Right (_, x') -> return $ Data x' resolveOperator (Subscription operator') = do (subscriptionChannels, _) <- unpackEffect2 $ effectEncode subscriptionResolver $ operatorSelection operator' return InitSubscription {subscriptionChannels, subscriptionQuery = operatorSelection operator'} packStream :: GQLState -> (ByteString -> IO (OutputAction IO ByteString)) -> ByteString -> IO ByteString packStream state streamAPI request = do value <- streamAPI request case value of PublishMutation {mutationChannels, mutationResponse, currentSubscriptionStateResolver} -> do publishUpdates mutationChannels currentSubscriptionStateResolver state return mutationResponse InitSubscription {} -> pure "subscriptions are only allowed in websocket" NoEffect res' -> return res'