2
2
{-# LANGUAGE PatternSynonyms #-}
3
3
{-# LANGUAGE RankNTypes #-}
4
4
{-# LANGUAGE ScopedTypeVariables #-}
5
+ {-# LANGUAGE TypeFamilies #-}
5
6
-- | Interface for GraphQL API.
6
7
--
7
8
-- __Note__: This module is highly subject to change. We're still figuring
@@ -10,14 +11,17 @@ module GraphQL
10
11
(
11
12
-- * Running queries
12
13
interpretQuery
14
+ , interpretRequest
13
15
, interpretAnonymousQuery
14
16
, Response (.. )
15
17
-- * Preparing queries then running them
16
18
, makeSchema
17
19
, compileQuery
18
20
, executeQuery
21
+ , executeRequest
19
22
, QueryError
20
23
, Schema
24
+ , SchemaRoot (.. )
21
25
, VariableValues
22
26
, Value
23
27
) where
@@ -30,7 +34,7 @@ import qualified Data.List.NonEmpty as NonEmpty
30
34
import GraphQL.API (HasObjectDefinition (.. ), SchemaError (.. ))
31
35
import GraphQL.Internal.Execution
32
36
( VariableValues
33
- , ExecutionError
37
+ , ExecutionError ( .. )
34
38
, substituteVariables
35
39
)
36
40
import qualified GraphQL.Internal.Execution as Execution
@@ -43,6 +47,9 @@ import GraphQL.Internal.Validation
43
47
, validate
44
48
, getSelectionSet
45
49
, VariableValue
50
+ , Operation (.. )
51
+ , DefinitionType (.. )
52
+ , getDefinitionType
46
53
)
47
54
import GraphQL.Internal.Output
48
55
( GraphQLError (.. )
@@ -83,6 +90,16 @@ instance GraphQLError QueryError where
83
90
formatError (NonObjectResult v) =
84
91
" Query returned a value that is not an object: " <> show v
85
92
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
+
86
103
-- | Execute a GraphQL query.
87
104
executeQuery
88
105
:: forall api m . (HasResolver m api , Applicative m , HasObjectDefinition api )
@@ -94,17 +111,7 @@ executeQuery
94
111
executeQuery handler document name variables =
95
112
case getOperation document name variables of
96
113
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)
108
115
109
116
-- | Create a GraphQL schema.
110
117
makeSchema :: forall api . HasObjectDefinition api => Either QueryError Schema
@@ -135,6 +142,75 @@ interpretAnonymousQuery
135
142
-> m Response -- ^ The result of running the query.
136
143
interpretAnonymousQuery handler query = interpretQuery @ api @ m handler query Nothing mempty
137
144
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
+
138
214
-- | Turn some text into a valid query document.
139
215
compileQuery :: Schema -> Text -> Either QueryError (QueryDocument VariableValue )
140
216
compileQuery schema query = do
@@ -146,8 +222,8 @@ parseQuery :: Text -> Either Text AST.QueryDocument
146
222
parseQuery query = first toS (parseOnly (Parser. queryDocument <* endOfInput) query)
147
223
148
224
-- | 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 )
150
226
getOperation document name vars = first ExecutionError $ do
151
227
op <- Execution. getOperation document name
152
228
resolved <- substituteVariables op vars
153
- pure (getSelectionSet resolved)
229
+ pure (op, getSelectionSet resolved)
0 commit comments