11module Hasura.GraphQL.Execute
2- ( GQExecPlan (.. )
3- , GQFieldExecPlan (.. )
2+ ( GQExecPlanPartial (.. )
3+ , GQFieldPartialPlan (.. )
4+ , GQFieldResolvedPlan (.. )
45
5- , ExecPlanPartial
66 , getExecPlanPartial
77
88 , ExecOp (.. )
9- , ExecPlanResolved
109 , getResolvedExecPlan
1110 , execRemoteGQ
1211 , getSubsOp
@@ -61,12 +60,19 @@ import qualified Hasura.Logging as L
6160--
6261-- The 'a' is parameterised so this AST can represent
6362-- intermediate passes
64- data GQFieldExecPlan a
65- = GExPHasura ! a
66- | GExPRemote ! RemoteSchemaInfo ! VQ. Field
67- deriving (Functor , Foldable , Traversable )
68-
69- type GQExecPlan a = [GQFieldExecPlan a ]
63+ data GQFieldPartialPlan
64+ = GQFieldPartialHasura ! (GCtx , VQ. Field )
65+ | GQFieldPartialRemote ! RemoteSchemaInfo ! VQ. Field
66+
67+ data GQFieldResolvedPlan
68+ = GQFieldResolvedHasura ! ExecOp
69+ | GQFieldResolvedRemote ! RemoteSchemaInfo ! G. OperationType ! VQ. Field
70+
71+ data GQExecPlanPartial
72+ = GQExecPlanPartial
73+ { execOpType :: G. OperationType
74+ , execFieldPlans :: Seq. Seq GQFieldPartialPlan
75+ }
7076
7177-- | Execution context
7278data ExecutionCtx
@@ -81,41 +87,65 @@ data ExecutionCtx
8187 , _ecxEnableAllowList :: ! Bool
8288 }
8389
84- -- This is for when the graphql query is validated
85- type ExecPlanPartial = [GQFieldExecPlan (GCtx , VQ. Field )]
86-
8790getExecPlanPartial
8891 :: (MonadError QErr m )
8992 => UserInfo
9093 -> SchemaCache
9194 -> Bool
9295 -> GQLReqParsed
93- -> m ExecPlanPartial
94- getExecPlanPartial userInfo sc enableAL req = do
95-
96+ -> m GQExecPlanPartial
97+ getExecPlanPartial userInfo sc enableAL req
9698 -- check if query is in allowlist
99+ = do
97100 when enableAL checkQueryInAllowlist
98-
99- (gCtx, _) <- flip runStateT sc $ getGCtx role gCtxRoleMap
101+ (gCtx, _) <- flip runStateT sc $ getGCtx role gCtxRoleMap
100102 queryParts <- flip runReaderT gCtx $ VQ. getQueryParts req
101-
103+ let remoteSchemas = scRemoteSchemas sc
102104 rootSelSet <- runReaderT (VQ. validateGQ queryParts) gCtx
103- generatePlan rootSelSet
105+ runReaderT ( generatePlan rootSelSet) (gCtx, remoteSchemas)
104106 where
105- generatePlan = undefined
107+ generatePlan ::
108+ (MonadError QErr m , MonadReader (GCtx , RemoteSchemaMap ) m )
109+ => VQ. RootSelSet
110+ -> m GQExecPlanPartial
111+ generatePlan =
112+ \ case
113+ VQ. RQuery selSet ->
114+ (GQExecPlanPartial G. OperationTypeQuery ) <$>
115+ (mapM generateFieldPlan selSet)
116+ VQ. RMutation selSet ->
117+ (GQExecPlanPartial G. OperationTypeMutation ) <$>
118+ (mapM generateFieldPlan selSet)
119+ VQ. RSubscription field ->
120+ (GQExecPlanPartial G. OperationTypeMutation ) <$>
121+ (mapM generateFieldPlan (Seq. singleton field))
122+ generateFieldPlan ::
123+ (MonadError QErr m , MonadReader (GCtx , RemoteSchemaMap ) m )
124+ => VQ. Field
125+ -> m GQFieldPartialPlan
126+ generateFieldPlan field =
127+ case VQ. _fSource field of
128+ TLHasuraType -> do
129+ (gCtx, _) <- ask
130+ pure $ GQFieldPartialHasura (gCtx, field)
131+ TLRemoteType rsName -> do
132+ (_, rsMap) <- ask
133+ rsCtx <-
134+ onNothing (Map. lookup rsName rsMap) $
135+ throw500 " remote schema not found"
136+ pure $ GQFieldPartialRemote (rscInfo rsCtx) field
106137 role = userRole userInfo
107138 gCtxRoleMap = scGCtxMap sc
108-
109- checkQueryInAllowlist =
139+ checkQueryInAllowlist
110140 -- only for non-admin roles
141+ =
111142 when (role /= adminRole) $ do
112143 let notInAllowlist =
113144 not $ VQ. isQueryInAllowlist (_grQuery req) (scAllowlist sc)
114145 when notInAllowlist $ modifyQErr modErr $ throwVE " query is not allowed"
115-
116146 modErr e =
117147 let msg = " query is not in any of the allowlists"
118- in e{qeInternal = Just $ J. object [ " message" J. .= J. String msg]}
148+ in e {qeInternal = Just $ J. object [" message" J. .= J. String msg]}
119149
120150
121151-- An execution operation, in case of
@@ -126,10 +156,6 @@ data ExecOp
126156 | ExOpMutation ! LazyRespTx
127157 | ExOpSubs ! EL. LiveQueryPlan
128158
129- -- The graphql query is resolved into an execution operation
130- type ExecPlanResolved
131- = GQExecPlan ExecOp
132-
133159getResolvedExecPlan
134160 :: (MonadError QErr m , MonadIO m )
135161 => PGExecCtx
@@ -140,41 +166,63 @@ getResolvedExecPlan
140166 -> SchemaCache
141167 -> SchemaCacheVer
142168 -> GQLReqUnparsed
143- -> m ExecPlanResolved
144- getResolvedExecPlan pgExecCtx planCache userInfo sqlGenCtx
145- enableAL sc scVer reqUnparsed = do
146- planM <- liftIO $ EP. getPlan scVer (userRole userInfo)
147- opNameM queryStr planCache
169+ -> m (Seq. Seq GQFieldResolvedPlan )
170+ getResolvedExecPlan pgExecCtx planCache userInfo sqlGenCtx enableAL sc scVer reqUnparsed = do
171+ planM <-
172+ liftIO $ EP. getPlan scVer (userRole userInfo) opNameM queryStr planCache
148173 let usrVars = userVars userInfo
149- case planM of
174+ case planM
150175 -- plans are only for queries and subscriptions
151- Just plan -> GExPHasura <$> case plan of
152- EP. RPQuery queryPlan -> do
153- (tx, genSql) <- EQ. queryOpFromPlan usrVars queryVars queryPlan
154- return $ ExOpQuery tx (Just genSql)
155- EP. RPSubs subsPlan ->
156- ExOpSubs <$> EL. reuseLiveQueryPlan pgExecCtx usrVars queryVars subsPlan
176+ of
177+ Just plan ->
178+ case plan of
179+ EP. RPQuery queryPlan -> do
180+ (tx, genSql) <- EQ. queryOpFromPlan usrVars queryVars queryPlan
181+ let queryOp = ExOpQuery tx (Just genSql)
182+ pure $ pure $ GQFieldResolvedHasura queryOp
183+ EP. RPSubs subsPlan -> do
184+ subOp <-
185+ ExOpSubs <$>
186+ EL. reuseLiveQueryPlan pgExecCtx usrVars queryVars subsPlan
187+ pure $ pure $ GQFieldResolvedHasura subOp
157188 Nothing -> noExistingPlan
158189 where
159190 GQLReq opNameM queryStr queryVars = reqUnparsed
160191 addPlanToCache plan =
161- liftIO $ EP. addPlan scVer (userRole userInfo)
162- opNameM queryStr plan planCache
192+ liftIO $
193+ EP. addPlan scVer (userRole userInfo) opNameM queryStr plan planCache
163194 noExistingPlan = do
164195 req <- toParsed reqUnparsed
165- partialExecPlan <- getExecPlanPartial userInfo sc enableAL req
166- forM partialExecPlan $ \ (gCtx, rootSelSet) ->
167- case rootSelSet of
168- VQ. RMutation selSet ->
169- ExOpMutation <$> getMutOp gCtx sqlGenCtx userInfo selSet
170- VQ. RQuery selSet -> do
171- (queryTx, plan, genSql) <- getQueryOp gCtx sqlGenCtx userInfo selSet
172- traverse_ (addPlanToCache . EP. RPQuery ) plan
173- return $ ExOpQuery queryTx (Just genSql)
174- VQ. RSubscription fld -> do
175- (lqOp, plan) <- getSubsOp pgExecCtx gCtx sqlGenCtx userInfo fld
176- traverse_ (addPlanToCache . EP. RPSubs ) plan
177- return $ ExOpSubs lqOp
196+ (GQExecPlanPartial opType fieldPlans) <-
197+ getExecPlanPartial userInfo sc enableAL req
198+ case opType of
199+ G. OperationTypeQuery ->
200+ forM fieldPlans $ \ case
201+ GQFieldPartialHasura (gCtx, field) -> do
202+ (queryTx, plan, genSql) <-
203+ getQueryOp gCtx sqlGenCtx userInfo (Seq. singleton field)
204+ traverse_ (addPlanToCache . EP. RPQuery ) plan
205+ (return . GQFieldResolvedHasura ) $ ExOpQuery queryTx (Just genSql)
206+ GQFieldPartialRemote rsInfo field ->
207+ return $ GQFieldResolvedRemote rsInfo G. OperationTypeQuery field
208+ G. OperationTypeMutation ->
209+ forM fieldPlans $ \ case
210+ GQFieldPartialHasura (gCtx, field) -> do
211+ mutationTx <-
212+ getMutOp gCtx sqlGenCtx userInfo (Seq. singleton field)
213+ (return . GQFieldResolvedHasura ) $ ExOpMutation mutationTx
214+ GQFieldPartialRemote rsInfo field ->
215+ return $
216+ GQFieldResolvedRemote rsInfo G. OperationTypeMutation field
217+ G. OperationTypeSubscription ->
218+ forM fieldPlans $ \ case
219+ GQFieldPartialHasura (gCtx, field) -> do
220+ (lqOp, plan) <- getSubsOp pgExecCtx gCtx sqlGenCtx userInfo field
221+ traverse_ (addPlanToCache . EP. RPSubs ) plan
222+ (return . GQFieldResolvedHasura ) $ ExOpSubs lqOp
223+ GQFieldPartialRemote rsInfo field ->
224+ return $
225+ GQFieldResolvedRemote rsInfo G. OperationTypeSubscription field
178226
179227-- Monad for resolving a hasura query/mutation
180228type E m =
@@ -303,18 +351,19 @@ execRemoteGQ
303351 => RequestId
304352 -> UserInfo
305353 -> [N. Header ]
306- -> GQLReqUnparsed
307354 -> RemoteSchemaInfo
308- -> G. TypedOperationDefinition
355+ -> G. OperationType
356+ -> VQ. SelSet
309357 -> m (HttpResponse EncJSON )
310- execRemoteGQ reqId userInfo reqHdrs q rsi opDef = do
358+ execRemoteGQ reqId userInfo reqHdrs rsi opType selSet = do
311359 execCtx <- ask
312360 let logger = _ecxLogger execCtx
313361 manager = _ecxHttpManager execCtx
314- opTy = G. _todType opDef
315- when (opTy == G. OperationTypeSubscription ) $
362+ when (opType == G. OperationTypeSubscription ) $
316363 throw400 NotSupported " subscription to remote server is not supported"
317364 hdrs <- getHeadersFromConf hdrConf
365+ gqlReq <- fieldsToRequest opType selSet
366+ let body = encJToLBS (encJFromJValue gqlReq)
318367 let confHdrs = map (\ (k, v) -> (CI. mk $ CS. cs k, CS. cs v)) hdrs
319368 clientHdrs = bool [] filteredHeaders fwdClientHdrs
320369 -- filter out duplicate headers
@@ -330,11 +379,11 @@ execRemoteGQ reqId userInfo reqHdrs q rsi opDef = do
330379 let req = initReq
331380 { HTTP. method = " POST"
332381 , HTTP. requestHeaders = finalHeaders
333- , HTTP. requestBody = HTTP. RequestBodyLBS ( J. encode q)
382+ , HTTP. requestBody = HTTP. RequestBodyLBS body
334383 , HTTP. responseTimeout = HTTP. responseTimeoutMicro (timeout * 1000000 )
335384 }
336385
337- liftIO $ logGraphqlQuery logger $ QueryLog q Nothing reqId
386+ -- liftIO $ logGraphqlQuery logger $ QueryLog q Nothing reqId
338387 res <- liftIO $ try $ HTTP. httpLbs req manager
339388 resp <- either httpThrow return res
340389 let cookieHdrs = getCookieHdr (resp ^.. Wreq. responseHeader " Set-Cookie" )
@@ -361,3 +410,11 @@ execRemoteGQ reqId userInfo reqHdrs q rsi opDef = do
361410
362411 mkRespHeaders hdrs =
363412 map (\ (k, v) -> Header (bsToTxt $ CI. original k, bsToTxt v)) hdrs
413+
414+
415+ fieldsToRequest
416+ :: (MonadIO m , MonadError QErr m )
417+ => G. OperationType
418+ -> Seq. Seq VQ. Field
419+ -> m GQLReqParsed
420+ fieldsToRequest = undefined
0 commit comments