mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-18 04:51:35 +03:00
b84db36ebb
* basic doc for actions * custom_types, sync and async actions * switch to graphql-parser-hs on github * update docs * metadata import/export * webhook calls are now supported * relationships in sync actions * initialise.sql is now in sync with the migration file * fix metadata tests * allow specifying arguments of actions * fix blacklist check on check_build_worthiness job * track custom_types and actions related tables * handlers are now triggered on async actions * default to pgjson unless a field is involved in relationships, for generating definition list * use 'true' for action filter for non admin role * fix create_action_permission sql query * drop permissions when dropping an action * add a hdb_role view (and relationships) to fetch all roles in the system * rename 'webhook' key in action definition to 'handler' * allow templating actions wehook URLs with env vars * add 'update_action' /v1/query type * allow forwarding client headers by setting `forward_client_headers` in action definition * add 'headers' configuration in action definition * handle webhook error response based on status codes * support array relationships for custom types * implement single row mutation, see https://github.com/hasura/graphql-engine/issues/3731 * single row mutation: rename 'pk_columns' -> 'columns' and no-op refactor * use top level primary key inputs for delete_by_pk & account select permissions for single row mutations * use only REST semantics to resolve the webhook response * use 'pk_columns' instead of 'columns' for update_by_pk input * add python basic tests for single row mutations * add action context (name) in webhook payload * Async action response is accessible for non admin roles only if the request session vars equals to action's * clean nulls, empty arrays for actions, custom types in export metadata * async action mutation returns only the UUID of the action * unit tests for URL template parser * Basic sync actions python tests * fix output in async query & add async tests * add admin secret header in async actions python test * document async action architecture in Resolve/Action.hs file * support actions returning array of objects * tests for list type response actions * update docs with actions and custom types metadata API reference * update actions python tests as per #f8e1330 Co-authored-by: Tirumarai Selvan <tirumarai.selvan@gmail.com> Co-authored-by: Aravind Shankar <face11301@gmail.com> Co-authored-by: Rakesh Emmadi <12475069+rakeshkky@users.noreply.github.com>
113 lines
4.3 KiB
Haskell
113 lines
4.3 KiB
Haskell
{-# LANGUAGE RecordWildCards #-}
|
|
module Hasura.GraphQL.Transport.HTTP
|
|
( runGQ
|
|
, runGQBatched
|
|
) where
|
|
|
|
import qualified Network.HTTP.Types as N
|
|
|
|
import Hasura.EncJSON
|
|
import Hasura.GraphQL.Logging
|
|
import Hasura.GraphQL.Transport.HTTP.Protocol
|
|
import Hasura.Prelude
|
|
import Hasura.RQL.Types
|
|
import Hasura.Server.Context
|
|
import Hasura.Server.Utils (RequestId)
|
|
import Hasura.Server.Version (HasVersion)
|
|
|
|
import qualified Database.PG.Query as Q
|
|
import qualified Hasura.GraphQL.Execute as E
|
|
import qualified Hasura.Logging as L
|
|
import qualified Hasura.Server.Telemetry.Counters as Telem
|
|
import qualified Language.GraphQL.Draft.Syntax as G
|
|
|
|
runGQ
|
|
:: ( HasVersion
|
|
, MonadIO m
|
|
, MonadError QErr m
|
|
, MonadReader E.ExecutionCtx m
|
|
)
|
|
=> RequestId
|
|
-> UserInfo
|
|
-> [N.Header]
|
|
-> GQLReq GQLQueryText
|
|
-> m (HttpResponse EncJSON)
|
|
runGQ reqId userInfo reqHdrs req = do
|
|
-- The response and misc telemetry data:
|
|
let telemTransport = Telem.HTTP
|
|
(telemTimeTot_DT, (telemCacheHit, telemLocality, (telemTimeIO_DT, telemQueryType, !resp))) <- withElapsedTime $ do
|
|
E.ExecutionCtx _ sqlGenCtx pgExecCtx planCache sc scVer httpManager enableAL <- ask
|
|
(telemCacheHit, execPlan) <- E.getResolvedExecPlan pgExecCtx planCache
|
|
userInfo sqlGenCtx enableAL sc scVer httpManager reqHdrs req
|
|
case execPlan of
|
|
E.GExPHasura resolvedOp -> do
|
|
(telemTimeIO, telemQueryType, resp) <- runHasuraGQ reqId req userInfo resolvedOp
|
|
return (telemCacheHit, Telem.Local, (telemTimeIO, telemQueryType, HttpResponse resp Nothing))
|
|
E.GExPRemote rsi opDef -> do
|
|
let telemQueryType | G._todType opDef == G.OperationTypeMutation = Telem.Mutation
|
|
| otherwise = Telem.Query
|
|
(telemTimeIO, resp) <- E.execRemoteGQ reqId userInfo reqHdrs req rsi opDef
|
|
return (telemCacheHit, Telem.Remote, (telemTimeIO, telemQueryType, resp))
|
|
let telemTimeIO = fromUnits telemTimeIO_DT
|
|
telemTimeTot = fromUnits telemTimeTot_DT
|
|
Telem.recordTimingMetric Telem.RequestDimensions{..} Telem.RequestTimings{..}
|
|
return resp
|
|
|
|
runGQBatched
|
|
:: ( HasVersion
|
|
, MonadIO m
|
|
, MonadError QErr m
|
|
, MonadReader E.ExecutionCtx m
|
|
)
|
|
=> RequestId
|
|
-> UserInfo
|
|
-> [N.Header]
|
|
-> GQLBatchedReqs GQLQueryText
|
|
-> m (HttpResponse EncJSON)
|
|
runGQBatched reqId userInfo reqHdrs reqs =
|
|
case reqs of
|
|
GQLSingleRequest req ->
|
|
runGQ reqId userInfo reqHdrs req
|
|
GQLBatchedReqs batch -> do
|
|
-- It's unclear what we should do if we receive multiple
|
|
-- responses with distinct headers, so just do the simplest thing
|
|
-- in this case, and don't forward any.
|
|
let removeHeaders =
|
|
flip HttpResponse Nothing
|
|
. encJFromList
|
|
. map (either (encJFromJValue . encodeGQErr False) _hrBody)
|
|
try = flip catchError (pure . Left) . fmap Right
|
|
fmap removeHeaders $
|
|
traverse (try . runGQ reqId userInfo reqHdrs) batch
|
|
|
|
runHasuraGQ
|
|
:: ( MonadIO m
|
|
, MonadError QErr m
|
|
, MonadReader E.ExecutionCtx m
|
|
)
|
|
=> RequestId
|
|
-> GQLReqUnparsed
|
|
-> UserInfo
|
|
-> E.ExecOp
|
|
-> m (DiffTime, Telem.QueryType, EncJSON)
|
|
-- ^ Also return 'Mutation' when the operation was a mutation, and the time
|
|
-- spent in the PG query; for telemetry.
|
|
runHasuraGQ reqId query userInfo resolvedOp = do
|
|
E.ExecutionCtx logger _ pgExecCtx _ _ _ _ _ <- ask
|
|
(telemTimeIO, respE) <- withElapsedTime $ liftIO $ runExceptT $ case resolvedOp of
|
|
E.ExOpQuery tx genSql -> do
|
|
-- log the generated SQL and the graphql query
|
|
L.unLogger logger $ QueryLog query genSql reqId
|
|
runLazyTx' pgExecCtx tx
|
|
E.ExOpMutation tx -> do
|
|
-- log the graphql query
|
|
L.unLogger logger $ QueryLog query Nothing reqId
|
|
runLazyTx pgExecCtx Q.ReadWrite $ withUserInfo userInfo tx
|
|
E.ExOpSubs _ ->
|
|
throw400 UnexpectedPayload
|
|
"subscriptions are not supported over HTTP, use websockets instead"
|
|
resp <- liftEither respE
|
|
let !json = encodeGQResp $ GQSuccess $ encJToLBS resp
|
|
telemQueryType = case resolvedOp of E.ExOpMutation{} -> Telem.Mutation ; _ -> Telem.Query
|
|
return (telemTimeIO, telemQueryType, json)
|