diff options
Diffstat (limited to 'src/Language/GraphQL.hs')
| -rw-r--r-- | src/Language/GraphQL.hs | 69 |
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) + ] |
