22{-# LANGUAGE PatternSynonyms #-}
33{-# LANGUAGE RankNTypes #-}
44{-# LANGUAGE ScopedTypeVariables #-}
5+ {-# LANGUAGE TypeFamilies #-}
56-- | Interface for GraphQL API.
67--
78-- __Note__: This module is highly subject to change. We're still figuring
@@ -10,14 +11,17 @@ module GraphQL
1011 (
1112 -- * Running queries
1213 interpretQuery
14+ , interpretRequest
1315 , interpretAnonymousQuery
1416 , Response (.. )
1517 -- * Preparing queries then running them
1618 , makeSchema
1719 , compileQuery
1820 , executeQuery
21+ , executeRequest
1922 , QueryError
2023 , Schema
24+ , SchemaRoot (.. )
2125 , VariableValues
2226 , Value
2327 ) where
@@ -30,7 +34,7 @@ import qualified Data.List.NonEmpty as NonEmpty
3034import GraphQL.API (HasObjectDefinition (.. ), SchemaError (.. ))
3135import GraphQL.Internal.Execution
3236 ( VariableValues
33- , ExecutionError
37+ , ExecutionError ( .. )
3438 , substituteVariables
3539 )
3640import qualified GraphQL.Internal.Execution as Execution
@@ -43,6 +47,9 @@ import GraphQL.Internal.Validation
4347 , validate
4448 , getSelectionSet
4549 , VariableValue
50+ , Operation (.. )
51+ , DefinitionType (.. )
52+ , getDefinitionType
4653 )
4754import GraphQL.Internal.Output
4855 ( GraphQLError (.. )
@@ -83,6 +90,16 @@ instance GraphQLError QueryError where
8390 formatError (NonObjectResult v) =
8491 " Query returned a value that is not an object: " <> show v
8592
93+ toResult :: Result Value -> Response
94+ toResult (Result errors result) = case result of
95+ -- TODO: Prevent this at compile time. Particularly frustrating since
96+ -- we *know* that queries and mutations have object definitions
97+ ValueObject object ->
98+ case NonEmpty. nonEmpty errors of
99+ Nothing -> Success object
100+ Just errs -> PartialSuccess object (map toError errs)
101+ v -> ExecutionFailure (singleError (NonObjectResult v))
102+
86103-- | Execute a GraphQL query.
87104executeQuery
88105 :: forall api m . (HasResolver m api , Applicative m , HasObjectDefinition api )
@@ -94,17 +111,7 @@ executeQuery
94111executeQuery handler document name variables =
95112 case getOperation document name variables of
96113 Left e -> pure (ExecutionFailure (singleError e))
97- Right operation -> toResult <$> resolve @ m @ api handler (Just operation)
98- where
99- toResult (Result errors result) =
100- case result of
101- -- TODO: Prevent this at compile time. Particularly frustrating since
102- -- we *know* that api has an object definition.
103- ValueObject object ->
104- case NonEmpty. nonEmpty errors of
105- Nothing -> Success object
106- Just errs -> PartialSuccess object (map toError errs)
107- v -> ExecutionFailure (singleError (NonObjectResult v))
114+ Right (_, ss) -> toResult <$> resolve @ m @ api handler (Just ss)
108115
109116-- | Create a GraphQL schema.
110117makeSchema :: forall api . HasObjectDefinition api => Either QueryError Schema
@@ -135,6 +142,75 @@ interpretAnonymousQuery
135142 -> m Response -- ^ The result of running the query.
136143interpretAnonymousQuery handler query = interpretQuery @ api @ m handler query Nothing mempty
137144
145+ data SchemaRoot m query mutation = SchemaRoot
146+ { queries :: Handler m query
147+ , mutations :: Handler m mutation
148+ }
149+
150+ -- | Execute a query or mutation
151+ --
152+ -- Similar to executeQuery, execept requests are dispatched against the
153+ -- SchemaRoot depending on whether they are a query or mutation
154+ executeRequest
155+ :: forall schema queries mutations m .
156+ ( schema ~ SchemaRoot m queries mutations
157+ , HasResolver m queries
158+ , HasObjectDefinition queries
159+ , HasResolver m mutations
160+ , HasObjectDefinition mutations
161+ , Monad m
162+ )
163+ => SchemaRoot m queries mutations
164+ -> QueryDocument VariableValue
165+ -> Maybe Name
166+ -> VariableValues
167+ -> m Response
168+ executeRequest (SchemaRoot qh mh) document name variables =
169+ case getOperation document name variables of
170+ Left e -> pure (ExecutionFailure (singleError e))
171+ Right (operation, ss) -> do
172+ toResult <$> case operation of
173+ Query _ _ _ -> resolve @ m @ queries qh (Just ss)
174+ Mutation _ _ _ -> resolve @ m @ mutations mh (Just ss)
175+
176+ -- | Interpret a query or mutation against a SchemaRoot
177+ interpretRequest
178+ :: forall schema queries mutations m .
179+ ( schema ~ SchemaRoot m queries mutations
180+ , HasResolver m queries
181+ , HasObjectDefinition queries
182+ , HasResolver m mutations
183+ , HasObjectDefinition mutations
184+ , Monad m
185+ )
186+ => SchemaRoot m queries mutations
187+ -> Text
188+ -> Maybe Name
189+ -> VariableValues
190+ -> m Response
191+ interpretRequest (SchemaRoot qh mh) text name variables = case parseQuery text of
192+ Left err -> pure (PreExecutionFailure (toError (ParseError err) :| [] ))
193+ Right document ->
194+ case getDefinitionType document name of
195+ Just operation -> case operation of
196+ QueryDefinition -> run @ m @ queries qh document
197+ MutationDefinition -> run @ m @ mutations mh document
198+ _ ->
199+ let err = maybe NoAnonymousOperation NoSuchOperation name
200+ in pure (ExecutionFailure (toError err :| [] ))
201+ where
202+ run :: forall n api .
203+ ( HasObjectDefinition api
204+ , HasResolver n api
205+ , Applicative n
206+ )
207+ => Handler n api -> AST. QueryDocument -> n Response
208+ run h doc = case makeSchema @ api of
209+ Left e -> pure (PreExecutionFailure (toError e :| [] ))
210+ Right schema -> case validate schema doc of
211+ Left e -> pure (PreExecutionFailure (toError (ValidationError e) :| [] ))
212+ Right validated -> executeQuery @ api h validated name variables
213+
138214-- | Turn some text into a valid query document.
139215compileQuery :: Schema -> Text -> Either QueryError (QueryDocument VariableValue )
140216compileQuery schema query = do
@@ -146,8 +222,8 @@ parseQuery :: Text -> Either Text AST.QueryDocument
146222parseQuery query = first toS (parseOnly (Parser. queryDocument <* endOfInput) query)
147223
148224-- | Get an operation from a query document ready to be processed.
149- getOperation :: QueryDocument VariableValue -> Maybe Name -> VariableValues -> Either QueryError (SelectionSetByType Value )
225+ getOperation :: QueryDocument VariableValue -> Maybe Name -> VariableValues -> Either QueryError (Operation VariableValue , SelectionSetByType Value )
150226getOperation document name vars = first ExecutionError $ do
151227 op <- Execution. getOperation document name
152228 resolved <- substituteVariables op vars
153- pure (getSelectionSet resolved)
229+ pure (op, getSelectionSet resolved)
0 commit comments