aboutsummaryrefslogtreecommitdiff
path: root/src/Language/GraphQL.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Language/GraphQL.hs')
-rw-r--r--src/Language/GraphQL.hs69
1 files changed, 56 insertions, 13 deletions
diff --git a/src/Language/GraphQL.hs b/src/Language/GraphQL.hs
index 961253f..9fce1d3 100644
--- a/src/Language/GraphQL.hs
+++ b/src/Language/GraphQL.hs
@@ -1,36 +1,79 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+
-- | This module provides the functions to parse and execute @GraphQL@ queries.
module Language.GraphQL
( graphql
, graphqlSubs
) where
+import Control.Monad.Catch (MonadCatch)
import qualified Data.Aeson as Aeson
-import Data.HashMap.Strict (HashMap)
+import qualified Data.Aeson.Types as Aeson
+import qualified Data.HashMap.Strict as HashMap
+import qualified Data.Sequence as Seq
import Data.Text (Text)
-import Language.GraphQL.AST.Document
-import Language.GraphQL.AST.Parser
+import Language.GraphQL.AST
import Language.GraphQL.Error
import Language.GraphQL.Execute
-import Language.GraphQL.Execute.Coerce
+import qualified Language.GraphQL.Validate as Validate
import Language.GraphQL.Type.Schema
import Text.Megaparsec (parse)
-- | If the text parses correctly as a @GraphQL@ query the query is
-- executed using the given 'Schema'.
-graphql :: Monad m
+graphql :: MonadCatch m
=> Schema m -- ^ Resolvers.
-> Text -- ^ Text representing a @GraphQL@ request document.
- -> m Aeson.Value -- ^ Response.
-graphql = flip graphqlSubs (mempty :: Aeson.Object)
+ -> m (Either (ResponseEventStream m Aeson.Value) Aeson.Object) -- ^ Response.
+graphql schema = graphqlSubs schema mempty mempty
-- | If the text parses correctly as a @GraphQL@ query the substitution is
-- applied to the query and the query is then executed using to the given
-- 'Schema'.
-graphqlSubs :: (Monad m, VariableValue a)
+graphqlSubs :: MonadCatch m
=> Schema m -- ^ Resolvers.
- -> HashMap Name a -- ^ Variable substitution function.
+ -> Maybe Text -- ^ Operation name.
+ -> Aeson.Object -- ^ Variable substitution function.
-> Text -- ^ Text representing a @GraphQL@ request document.
- -> m Aeson.Value -- ^ Response.
-graphqlSubs schema f
- = either parseError (execute schema f)
- . parse document ""
+ -> m (Either (ResponseEventStream m Aeson.Value) Aeson.Object) -- ^ Response.
+graphqlSubs schema operationName variableValues document' =
+ case parse document "" document' of
+ Left errorBundle -> pure . formatResponse <$> parseError errorBundle
+ Right parsed ->
+ case validate parsed of
+ Seq.Empty -> fmap formatResponse
+ <$> execute schema operationName variableValues parsed
+ errors -> pure $ pure
+ $ HashMap.singleton "errors"
+ $ Aeson.toJSON
+ $ fromValidationError <$> errors
+ where
+ validate = Validate.document schema Validate.specifiedRules
+ formatResponse (Response data'' Seq.Empty) = HashMap.singleton "data" data''
+ formatResponse (Response data'' errors') = HashMap.fromList
+ [ ("data", data'')
+ , ("errors", Aeson.toJSON $ fromError <$> errors')
+ ]
+ fromError Error{ locations = [], ..} =
+ Aeson.object [("message", Aeson.toJSON message)]
+ fromError Error{..} = Aeson.object
+ [ ("message", Aeson.toJSON message)
+ , ("locations", Aeson.listValue fromLocation locations)
+ ]
+ fromValidationError Validate.Error{..}
+ | [] <- path = Aeson.object
+ [ ("message", Aeson.toJSON message)
+ , ("locations", Aeson.listValue fromLocation locations)
+ ]
+ | otherwise = Aeson.object
+ [ ("message", Aeson.toJSON message)
+ , ("locations", Aeson.listValue fromLocation locations)
+ , ("path", Aeson.listValue fromPath path)
+ ]
+ fromPath (Validate.Segment segment) = Aeson.String segment
+ fromPath (Validate.Index index) = Aeson.toJSON index
+ fromLocation Location{..} = Aeson.object
+ [ ("line", Aeson.toJSON line)
+ , ("column", Aeson.toJSON column)
+ ]