2019-03-25 21:25:25 +03:00
|
|
|
module Hasura.GraphQL.Execute
|
|
|
|
( GQExecPlan(..)
|
2020-06-08 15:13:01 +03:00
|
|
|
, EQ.GraphQLQueryType(..)
|
2019-04-17 12:48:41 +03:00
|
|
|
|
|
|
|
, ExecPlanPartial
|
|
|
|
, getExecPlanPartial
|
|
|
|
|
|
|
|
, ExecOp(..)
|
2020-05-27 18:02:58 +03:00
|
|
|
, GQExecPlanResolved
|
2019-04-17 12:48:41 +03:00
|
|
|
, getResolvedExecPlan
|
2019-03-25 21:25:25 +03:00
|
|
|
, execRemoteGQ
|
2019-09-30 22:50:57 +03:00
|
|
|
, getSubsOp
|
2019-04-17 12:48:41 +03:00
|
|
|
|
|
|
|
, EP.PlanCache
|
2020-05-13 12:17:32 +03:00
|
|
|
, EP.PlanCacheOptions(..)
|
2019-04-17 12:48:41 +03:00
|
|
|
, EP.initPlanCache
|
|
|
|
, EP.clearPlanCache
|
|
|
|
, EP.dumpPlanCache
|
2020-07-14 22:00:58 +03:00
|
|
|
, EQ.PreparedSql(..)
|
2019-07-11 08:37:06 +03:00
|
|
|
, ExecutionCtx(..)
|
2020-06-16 18:23:06 +03:00
|
|
|
|
|
|
|
, MonadGQLExecutionCheck(..)
|
|
|
|
, checkQueryInAllowlist
|
2019-03-25 21:25:25 +03:00
|
|
|
) where
|
|
|
|
|
|
|
|
import Control.Lens
|
2019-04-17 12:48:41 +03:00
|
|
|
import Data.Has
|
2019-03-25 21:25:25 +03:00
|
|
|
|
2019-05-16 09:13:25 +03:00
|
|
|
import qualified Data.Aeson as J
|
2020-07-14 22:00:58 +03:00
|
|
|
import qualified Data.Environment as Env
|
2019-03-25 21:25:25 +03:00
|
|
|
import qualified Data.HashMap.Strict as Map
|
|
|
|
import qualified Data.HashSet as Set
|
|
|
|
import qualified Language.GraphQL.Draft.Syntax as G
|
|
|
|
import qualified Network.HTTP.Client as HTTP
|
2020-06-16 18:23:06 +03:00
|
|
|
import qualified Network.HTTP.Types as HTTP
|
|
|
|
import qualified Network.Wai.Extended as Wai
|
2019-03-25 21:25:25 +03:00
|
|
|
|
|
|
|
import Hasura.EncJSON
|
2019-04-17 12:48:41 +03:00
|
|
|
import Hasura.GraphQL.Context
|
2019-07-11 08:37:06 +03:00
|
|
|
import Hasura.GraphQL.Logging
|
2020-05-27 18:02:58 +03:00
|
|
|
import Hasura.GraphQL.RemoteServer (execRemoteGQ')
|
2020-04-24 10:55:51 +03:00
|
|
|
import Hasura.GraphQL.Resolve.Action
|
2019-04-17 12:48:41 +03:00
|
|
|
import Hasura.GraphQL.Resolve.Context
|
2019-03-25 21:25:25 +03:00
|
|
|
import Hasura.GraphQL.Schema
|
|
|
|
import Hasura.GraphQL.Transport.HTTP.Protocol
|
2019-04-17 12:48:41 +03:00
|
|
|
import Hasura.GraphQL.Validate.Types
|
2019-03-25 21:25:25 +03:00
|
|
|
import Hasura.HTTP
|
|
|
|
import Hasura.Prelude
|
|
|
|
import Hasura.RQL.Types
|
2020-05-27 18:02:58 +03:00
|
|
|
import Hasura.Server.Utils (RequestId)
|
2020-01-23 00:55:55 +03:00
|
|
|
import Hasura.Server.Version (HasVersion)
|
2020-04-24 12:10:53 +03:00
|
|
|
import Hasura.Session
|
2019-03-25 21:25:25 +03:00
|
|
|
|
2020-06-08 15:13:01 +03:00
|
|
|
import qualified Hasura.GraphQL.Context as GC
|
2019-04-17 12:48:41 +03:00
|
|
|
import qualified Hasura.GraphQL.Execute.LiveQuery as EL
|
|
|
|
import qualified Hasura.GraphQL.Execute.Plan as EP
|
|
|
|
import qualified Hasura.GraphQL.Execute.Query as EQ
|
|
|
|
import qualified Hasura.GraphQL.Resolve as GR
|
2019-03-25 21:25:25 +03:00
|
|
|
import qualified Hasura.GraphQL.Validate as VQ
|
2020-06-08 15:13:01 +03:00
|
|
|
import qualified Hasura.GraphQL.Validate.SelectionSet as VQ
|
2019-03-25 21:25:25 +03:00
|
|
|
import qualified Hasura.GraphQL.Validate.Types as VT
|
2019-07-11 08:37:06 +03:00
|
|
|
import qualified Hasura.Logging as L
|
2020-01-16 04:56:57 +03:00
|
|
|
import qualified Hasura.Server.Telemetry.Counters as Telem
|
2020-07-15 13:40:48 +03:00
|
|
|
import qualified Hasura.Tracing as Tracing
|
2019-03-25 21:25:25 +03:00
|
|
|
|
2019-04-17 12:48:41 +03:00
|
|
|
-- The current execution plan of a graphql operation, it is
|
|
|
|
-- currently, either local pg execution or a remote execution
|
|
|
|
--
|
|
|
|
-- The 'a' is parameterised so this AST can represent
|
|
|
|
-- intermediate passes
|
|
|
|
data GQExecPlan a
|
|
|
|
= GExPHasura !a
|
2019-03-25 21:25:25 +03:00
|
|
|
| GExPRemote !RemoteSchemaInfo !G.TypedOperationDefinition
|
2019-04-17 12:48:41 +03:00
|
|
|
deriving (Functor, Foldable, Traversable)
|
|
|
|
|
2019-07-11 08:37:06 +03:00
|
|
|
-- | Execution context
|
|
|
|
data ExecutionCtx
|
|
|
|
= ExecutionCtx
|
2019-11-26 15:14:21 +03:00
|
|
|
{ _ecxLogger :: !(L.Logger L.Hasura)
|
2019-07-11 08:37:06 +03:00
|
|
|
, _ecxSqlGenCtx :: !SQLGenCtx
|
|
|
|
, _ecxPgExecCtx :: !PGExecCtx
|
|
|
|
, _ecxPlanCache :: !EP.PlanCache
|
|
|
|
, _ecxSchemaCache :: !SchemaCache
|
|
|
|
, _ecxSchemaCacheVer :: !SchemaCacheVer
|
|
|
|
, _ecxHttpManager :: !HTTP.Manager
|
|
|
|
, _ecxEnableAllowList :: !Bool
|
|
|
|
}
|
|
|
|
|
2020-06-16 18:23:06 +03:00
|
|
|
-- | Typeclass representing safety checks (if any) that need to be performed
|
|
|
|
-- before a GraphQL query should be allowed to be executed. In OSS, the safety
|
|
|
|
-- check is to check in the query is in the allow list.
|
|
|
|
|
|
|
|
-- | TODO: Limitation: This parses the query, which is not ideal if we already
|
|
|
|
-- have the query cached. The parsing happens unnecessary. But getting this to
|
|
|
|
-- either return a plan or parse was tricky and complicated.
|
|
|
|
class Monad m => MonadGQLExecutionCheck m where
|
|
|
|
checkGQLExecution
|
|
|
|
:: UserInfo
|
|
|
|
-> ([HTTP.Header], Wai.IpAddress)
|
|
|
|
-> Bool
|
|
|
|
-- ^ allow list enabled?
|
|
|
|
-> SchemaCache
|
|
|
|
-- ^ needs allow list
|
|
|
|
-> GQLReqUnparsed
|
|
|
|
-- ^ the unparsed GraphQL query string (and related values)
|
|
|
|
-> m (Either QErr GQLReqParsed)
|
|
|
|
|
|
|
|
instance MonadGQLExecutionCheck m => MonadGQLExecutionCheck (ExceptT e m) where
|
|
|
|
checkGQLExecution ui det enableAL sc req =
|
|
|
|
lift $ checkGQLExecution ui det enableAL sc req
|
|
|
|
|
|
|
|
instance MonadGQLExecutionCheck m => MonadGQLExecutionCheck (ReaderT r m) where
|
|
|
|
checkGQLExecution ui det enableAL sc req =
|
|
|
|
lift $ checkGQLExecution ui det enableAL sc req
|
|
|
|
|
2020-07-15 13:40:48 +03:00
|
|
|
instance MonadGQLExecutionCheck m => MonadGQLExecutionCheck (Tracing.TraceT m) where
|
|
|
|
checkGQLExecution ui det enableAL sc req =
|
|
|
|
lift $ checkGQLExecution ui det enableAL sc req
|
|
|
|
|
2019-04-17 12:48:41 +03:00
|
|
|
-- Enforces the current limitation
|
|
|
|
assertSameLocationNodes
|
|
|
|
:: (MonadError QErr m) => [VT.TypeLoc] -> m VT.TypeLoc
|
|
|
|
assertSameLocationNodes typeLocs =
|
|
|
|
case Set.toList (Set.fromList typeLocs) of
|
|
|
|
-- this shouldn't happen
|
2019-07-08 08:51:41 +03:00
|
|
|
[] -> return VT.TLHasuraType
|
2019-04-17 12:48:41 +03:00
|
|
|
[loc] -> return loc
|
|
|
|
_ -> throw400 NotSupported msg
|
|
|
|
where
|
|
|
|
msg = "cannot mix top level fields from two different graphql servers"
|
2019-03-25 21:25:25 +03:00
|
|
|
|
2019-04-17 12:48:41 +03:00
|
|
|
-- TODO: we should fix this function asap
|
|
|
|
-- as this will fail when there is a fragment at the top level
|
|
|
|
getTopLevelNodes :: G.TypedOperationDefinition -> [G.Name]
|
|
|
|
getTopLevelNodes opDef =
|
|
|
|
mapMaybe f $ G._todSelectionSet opDef
|
|
|
|
where
|
|
|
|
f = \case
|
|
|
|
G.SelectionField fld -> Just $ G._fName fld
|
|
|
|
G.SelectionFragmentSpread _ -> Nothing
|
|
|
|
G.SelectionInlineFragment _ -> Nothing
|
|
|
|
|
|
|
|
gatherTypeLocs :: GCtx -> [G.Name] -> [VT.TypeLoc]
|
|
|
|
gatherTypeLocs gCtx nodes =
|
|
|
|
catMaybes $ flip map nodes $ \node ->
|
|
|
|
VT._fiLoc <$> Map.lookup node schemaNodes
|
|
|
|
where
|
|
|
|
schemaNodes =
|
|
|
|
let qr = VT._otiFields $ _gQueryRoot gCtx
|
|
|
|
mr = VT._otiFields <$> _gMutRoot gCtx
|
|
|
|
in maybe qr (Map.union qr) mr
|
|
|
|
|
|
|
|
-- This is for when the graphql query is validated
|
2020-06-08 15:13:01 +03:00
|
|
|
type ExecPlanPartial = GQExecPlan (GCtx, VQ.RootSelectionSet)
|
2019-04-17 12:48:41 +03:00
|
|
|
|
|
|
|
getExecPlanPartial
|
2019-10-16 17:33:34 +03:00
|
|
|
:: (MonadReusability m, MonadError QErr m)
|
2019-03-25 21:25:25 +03:00
|
|
|
=> UserInfo
|
|
|
|
-> SchemaCache
|
2020-06-08 15:13:01 +03:00
|
|
|
-> EQ.GraphQLQueryType
|
2019-04-17 12:48:41 +03:00
|
|
|
-> GQLReqParsed
|
|
|
|
-> m ExecPlanPartial
|
2020-06-16 18:23:06 +03:00
|
|
|
getExecPlanPartial userInfo sc queryType req = do
|
2020-06-08 15:13:01 +03:00
|
|
|
let gCtx = case queryType of
|
|
|
|
EQ.QueryHasura -> getGCtx (_uiBackendOnlyFieldAccess userInfo) sc roleName
|
2020-06-16 17:25:49 +03:00
|
|
|
EQ.QueryRelay -> fromMaybe GC.emptyGCtx $ Map.lookup roleName $ scRelayGCtxMap sc
|
2020-06-08 15:13:01 +03:00
|
|
|
|
2019-03-25 21:25:25 +03:00
|
|
|
queryParts <- flip runReaderT gCtx $ VQ.getQueryParts req
|
|
|
|
|
|
|
|
let opDef = VQ.qpOpDef queryParts
|
|
|
|
topLevelNodes = getTopLevelNodes opDef
|
|
|
|
-- gather TypeLoc of topLevelNodes
|
|
|
|
typeLocs = gatherTypeLocs gCtx topLevelNodes
|
|
|
|
|
|
|
|
-- see if they are all the same
|
|
|
|
typeLoc <- assertSameLocationNodes typeLocs
|
|
|
|
|
|
|
|
case typeLoc of
|
2019-07-08 08:51:41 +03:00
|
|
|
VT.TLHasuraType -> do
|
2019-09-14 09:01:06 +03:00
|
|
|
rootSelSet <- runReaderT (VQ.validateGQ queryParts) gCtx
|
2020-05-27 18:02:58 +03:00
|
|
|
pure $ GExPHasura (gCtx, rootSelSet)
|
2019-07-08 08:51:41 +03:00
|
|
|
VT.TLRemoteType _ rsi ->
|
2020-05-27 18:02:58 +03:00
|
|
|
pure $ GExPRemote rsi opDef
|
2020-02-13 20:38:23 +03:00
|
|
|
VT.TLCustom ->
|
|
|
|
throw500 "unexpected custom type for top level field"
|
2019-03-25 21:25:25 +03:00
|
|
|
where
|
2020-04-24 12:10:53 +03:00
|
|
|
roleName = _uiRole userInfo
|
2019-03-25 21:25:25 +03:00
|
|
|
|
2020-06-16 18:23:06 +03:00
|
|
|
checkQueryInAllowlist
|
|
|
|
:: (MonadError QErr m) => Bool -> UserInfo -> GQLReqParsed -> SchemaCache -> m ()
|
|
|
|
checkQueryInAllowlist enableAL userInfo req sc =
|
|
|
|
-- only for non-admin roles
|
|
|
|
-- check if query is in allowlist
|
|
|
|
when (enableAL && (_uiRole userInfo /= adminRoleName)) $ do
|
|
|
|
let notInAllowlist =
|
|
|
|
not $ VQ.isQueryInAllowlist (_grQuery req) (scAllowlist sc)
|
|
|
|
when notInAllowlist $ modifyQErr modErr $ throwVE "query is not allowed"
|
|
|
|
|
|
|
|
where
|
2019-05-16 09:13:25 +03:00
|
|
|
modErr e =
|
|
|
|
let msg = "query is not in any of the allowlists"
|
|
|
|
in e{qeInternal = Just $ J.object [ "message" J..= J.String msg]}
|
|
|
|
|
2019-07-11 08:37:06 +03:00
|
|
|
|
2020-06-16 18:23:06 +03:00
|
|
|
-- An execution operation, in case of queries and mutations it is just a
|
|
|
|
-- transaction to be executed
|
2020-07-14 22:00:58 +03:00
|
|
|
data ExecOp m
|
|
|
|
= ExOpQuery !(m EncJSON) !(Maybe EQ.GeneratedSqlMap) ![GR.QueryRootFldUnresolved]
|
|
|
|
| ExOpMutation !HTTP.ResponseHeaders !(m EncJSON)
|
2019-08-28 15:19:21 +03:00
|
|
|
| ExOpSubs !EL.LiveQueryPlan
|
2019-04-17 12:48:41 +03:00
|
|
|
|
|
|
|
-- The graphql query is resolved into an execution operation
|
2020-07-14 22:00:58 +03:00
|
|
|
type GQExecPlanResolved m = GQExecPlan (ExecOp m)
|
2019-04-17 12:48:41 +03:00
|
|
|
|
|
|
|
getResolvedExecPlan
|
2020-07-14 22:00:58 +03:00
|
|
|
:: forall m tx
|
|
|
|
. ( HasVersion
|
|
|
|
, MonadError QErr m
|
|
|
|
, MonadIO m
|
2020-07-15 13:40:48 +03:00
|
|
|
, Tracing.MonadTrace m
|
2020-07-14 22:00:58 +03:00
|
|
|
, MonadIO tx
|
|
|
|
, MonadTx tx
|
2020-07-15 13:40:48 +03:00
|
|
|
, Tracing.MonadTrace tx
|
2020-07-14 22:00:58 +03:00
|
|
|
)
|
|
|
|
=> Env.Environment
|
2020-07-29 16:30:29 +03:00
|
|
|
-> L.Logger L.Hasura
|
2020-07-14 22:00:58 +03:00
|
|
|
-> PGExecCtx
|
2019-04-17 12:48:41 +03:00
|
|
|
-> EP.PlanCache
|
|
|
|
-> UserInfo
|
|
|
|
-> SQLGenCtx
|
|
|
|
-> SchemaCache
|
|
|
|
-> SchemaCacheVer
|
2020-06-08 15:13:01 +03:00
|
|
|
-> EQ.GraphQLQueryType
|
2020-02-13 20:38:23 +03:00
|
|
|
-> HTTP.Manager
|
2020-06-16 18:23:06 +03:00
|
|
|
-> [HTTP.Header]
|
|
|
|
-> (GQLReqUnparsed, GQLReqParsed)
|
2020-07-14 22:00:58 +03:00
|
|
|
-> m (Telem.CacheHit, GQExecPlanResolved tx)
|
2020-07-29 16:30:29 +03:00
|
|
|
getResolvedExecPlan env logger pgExecCtx planCache userInfo sqlGenCtx
|
2020-06-16 18:23:06 +03:00
|
|
|
sc scVer queryType httpManager reqHeaders (reqUnparsed, reqParsed) = do
|
|
|
|
|
|
|
|
planM <- liftIO $ EP.getPlan scVer (_uiRole userInfo) operationNameM queryStr
|
|
|
|
queryType planCache
|
2020-04-24 12:10:53 +03:00
|
|
|
let usrVars = _uiSession userInfo
|
2019-04-17 12:48:41 +03:00
|
|
|
case planM of
|
|
|
|
-- plans are only for queries and subscriptions
|
2020-01-16 04:56:57 +03:00
|
|
|
Just plan -> (Telem.Hit,) . GExPHasura <$> case plan of
|
2020-07-14 22:00:58 +03:00
|
|
|
EP.RPQuery queryPlan asts -> do
|
|
|
|
(tx, genSql) <- EQ.queryOpFromPlan env httpManager reqHeaders userInfo queryVars queryPlan
|
|
|
|
pure $ ExOpQuery tx (Just genSql) asts
|
2019-04-17 12:48:41 +03:00
|
|
|
EP.RPSubs subsPlan ->
|
2019-08-28 15:19:21 +03:00
|
|
|
ExOpSubs <$> EL.reuseLiveQueryPlan pgExecCtx usrVars queryVars subsPlan
|
2020-01-16 04:56:57 +03:00
|
|
|
Nothing -> (Telem.Miss,) <$> noExistingPlan
|
2019-04-17 12:48:41 +03:00
|
|
|
where
|
2020-06-16 18:23:06 +03:00
|
|
|
GQLReq operationNameM queryStr queryVars = reqUnparsed
|
2019-04-17 12:48:41 +03:00
|
|
|
addPlanToCache plan =
|
2020-04-24 12:10:53 +03:00
|
|
|
liftIO $ EP.addPlan scVer (_uiRole userInfo)
|
2020-06-16 18:23:06 +03:00
|
|
|
operationNameM queryStr plan queryType planCache
|
2020-05-27 18:02:58 +03:00
|
|
|
|
2020-07-14 22:00:58 +03:00
|
|
|
noExistingPlan :: m (GQExecPlanResolved tx)
|
2019-04-17 12:48:41 +03:00
|
|
|
noExistingPlan = do
|
2020-06-16 18:23:06 +03:00
|
|
|
-- req <- toParsed reqUnparsed
|
2019-10-16 17:33:34 +03:00
|
|
|
(partialExecPlan, queryReusability) <- runReusabilityT $
|
2020-06-16 18:23:06 +03:00
|
|
|
getExecPlanPartial userInfo sc queryType reqParsed
|
2019-09-14 09:01:06 +03:00
|
|
|
forM partialExecPlan $ \(gCtx, rootSelSet) ->
|
2019-04-17 12:48:41 +03:00
|
|
|
case rootSelSet of
|
2020-03-20 09:46:45 +03:00
|
|
|
VQ.RMutation selSet -> do
|
2020-07-29 16:30:29 +03:00
|
|
|
(tx, respHeaders) <- getMutOp env logger gCtx sqlGenCtx userInfo httpManager reqHeaders selSet
|
2020-03-20 09:46:45 +03:00
|
|
|
pure $ ExOpMutation respHeaders tx
|
2019-04-17 12:48:41 +03:00
|
|
|
VQ.RQuery selSet -> do
|
2020-07-29 16:30:29 +03:00
|
|
|
(queryTx, plan, genSql, asts) <- getQueryOp env logger gCtx sqlGenCtx httpManager reqHeaders userInfo
|
2020-06-08 15:13:01 +03:00
|
|
|
queryReusability (allowQueryActionExecuter httpManager reqHeaders) selSet
|
2020-07-14 22:00:58 +03:00
|
|
|
traverse_ (addPlanToCache . flip EP.RPQuery asts) plan
|
|
|
|
return $ ExOpQuery queryTx (Just genSql) asts
|
2020-06-08 15:13:01 +03:00
|
|
|
VQ.RSubscription fields -> do
|
2020-07-29 16:30:29 +03:00
|
|
|
(lqOp, plan) <- getSubsOp env logger pgExecCtx gCtx sqlGenCtx userInfo queryReusability
|
2020-06-08 15:13:01 +03:00
|
|
|
(restrictActionExecuter "query actions cannot be run as a subscription") fields
|
2019-08-28 15:19:21 +03:00
|
|
|
traverse_ (addPlanToCache . EP.RPSubs) plan
|
2019-04-17 12:48:41 +03:00
|
|
|
return $ ExOpSubs lqOp
|
|
|
|
|
|
|
|
-- Monad for resolving a hasura query/mutation
|
|
|
|
type E m =
|
|
|
|
ReaderT ( UserInfo
|
2019-07-23 14:12:59 +03:00
|
|
|
, QueryCtxMap
|
|
|
|
, MutationCtxMap
|
2019-04-17 12:48:41 +03:00
|
|
|
, TypeMap
|
|
|
|
, FieldMap
|
|
|
|
, OrdByCtx
|
|
|
|
, InsCtxMap
|
|
|
|
, SQLGenCtx
|
2020-07-29 16:30:29 +03:00
|
|
|
, L.Logger L.Hasura
|
2019-04-17 12:48:41 +03:00
|
|
|
) (ExceptT QErr m)
|
|
|
|
|
|
|
|
runE
|
|
|
|
:: (MonadError QErr m)
|
2020-07-29 16:30:29 +03:00
|
|
|
=> L.Logger L.Hasura
|
|
|
|
-> GCtx
|
2019-04-17 12:48:41 +03:00
|
|
|
-> SQLGenCtx
|
|
|
|
-> UserInfo
|
|
|
|
-> E m a
|
|
|
|
-> m a
|
2020-07-29 16:30:29 +03:00
|
|
|
runE logger ctx sqlGenCtx userInfo action = do
|
2019-04-17 12:48:41 +03:00
|
|
|
res <- runExceptT $ runReaderT action
|
2020-07-29 16:30:29 +03:00
|
|
|
(userInfo, queryCtxMap, mutationCtxMap, typeMap, fldMap, ordByCtx, insCtxMap, sqlGenCtx, logger)
|
2019-04-17 12:48:41 +03:00
|
|
|
either throwError return res
|
|
|
|
where
|
2019-07-23 14:12:59 +03:00
|
|
|
queryCtxMap = _gQueryCtxMap ctx
|
|
|
|
mutationCtxMap = _gMutationCtxMap ctx
|
2019-04-17 12:48:41 +03:00
|
|
|
typeMap = _gTypes ctx
|
|
|
|
fldMap = _gFields ctx
|
|
|
|
ordByCtx = _gOrdByCtx ctx
|
|
|
|
insCtxMap = _gInsCtxMap ctx
|
|
|
|
|
|
|
|
getQueryOp
|
2020-04-16 10:25:19 +03:00
|
|
|
:: ( HasVersion
|
|
|
|
, MonadError QErr m
|
2020-07-14 22:00:58 +03:00
|
|
|
, MonadIO m
|
2020-07-15 13:40:48 +03:00
|
|
|
, Tracing.MonadTrace m
|
2020-07-14 22:00:58 +03:00
|
|
|
, MonadIO tx
|
|
|
|
, MonadTx tx
|
2020-07-15 13:40:48 +03:00
|
|
|
, Tracing.MonadTrace tx
|
2020-07-14 22:00:58 +03:00
|
|
|
)
|
|
|
|
=> Env.Environment
|
2020-07-29 16:30:29 +03:00
|
|
|
-> L.Logger L.Hasura
|
2020-07-14 22:00:58 +03:00
|
|
|
-> GCtx
|
2019-04-17 12:48:41 +03:00
|
|
|
-> SQLGenCtx
|
2020-05-27 18:02:58 +03:00
|
|
|
-> HTTP.Manager
|
2020-06-16 18:23:06 +03:00
|
|
|
-> [HTTP.Header]
|
2019-04-17 12:48:41 +03:00
|
|
|
-> UserInfo
|
2019-10-16 17:33:34 +03:00
|
|
|
-> QueryReusability
|
2020-04-16 10:25:19 +03:00
|
|
|
-> QueryActionExecuter
|
2020-06-08 15:13:01 +03:00
|
|
|
-> VQ.ObjectSelectionSet
|
2020-07-14 22:00:58 +03:00
|
|
|
-> m (tx EncJSON, Maybe EQ.ReusableQueryPlan, EQ.GeneratedSqlMap, [GR.QueryRootFldUnresolved])
|
2020-07-29 16:30:29 +03:00
|
|
|
getQueryOp env logger gCtx sqlGenCtx manager reqHdrs userInfo queryReusability actionExecuter selSet =
|
|
|
|
runE logger gCtx sqlGenCtx userInfo $ EQ.convertQuerySelSet env manager reqHdrs queryReusability selSet actionExecuter
|
2019-04-17 12:48:41 +03:00
|
|
|
|
|
|
|
resolveMutSelSet
|
2020-02-13 20:38:23 +03:00
|
|
|
:: ( HasVersion
|
|
|
|
, MonadError QErr m
|
2019-04-17 12:48:41 +03:00
|
|
|
, MonadReader r m
|
|
|
|
, Has UserInfo r
|
2019-07-23 14:12:59 +03:00
|
|
|
, Has MutationCtxMap r
|
2019-04-17 12:48:41 +03:00
|
|
|
, Has FieldMap r
|
|
|
|
, Has OrdByCtx r
|
|
|
|
, Has SQLGenCtx r
|
|
|
|
, Has InsCtxMap r
|
2020-02-13 20:38:23 +03:00
|
|
|
, Has HTTP.Manager r
|
2020-06-16 18:23:06 +03:00
|
|
|
, Has [HTTP.Header] r
|
2020-07-29 16:30:29 +03:00
|
|
|
, Has (L.Logger L.Hasura) r
|
2020-02-13 20:38:23 +03:00
|
|
|
, MonadIO m
|
2020-07-15 13:40:48 +03:00
|
|
|
, Tracing.MonadTrace m
|
2020-07-14 22:00:58 +03:00
|
|
|
, MonadIO tx
|
|
|
|
, MonadTx tx
|
2020-07-15 13:40:48 +03:00
|
|
|
, Tracing.MonadTrace tx
|
2019-04-17 12:48:41 +03:00
|
|
|
)
|
2020-07-14 22:00:58 +03:00
|
|
|
=> Env.Environment
|
|
|
|
-> VQ.ObjectSelectionSet
|
|
|
|
-> m (tx EncJSON, HTTP.ResponseHeaders)
|
|
|
|
resolveMutSelSet env fields = do
|
2020-06-08 15:13:01 +03:00
|
|
|
aliasedTxs <- traverseObjectSelectionSet fields $ \fld ->
|
|
|
|
case VQ._fName fld of
|
2020-04-24 12:10:53 +03:00
|
|
|
"__typename" -> return (return $ encJFromJValue mutationRootNamedType, [])
|
2020-07-14 22:00:58 +03:00
|
|
|
_ -> evalReusabilityT $ GR.mutFldToTx env fld
|
2019-04-17 12:48:41 +03:00
|
|
|
|
|
|
|
-- combines all transactions into a single transaction
|
2020-07-14 22:00:58 +03:00
|
|
|
return (toSingleTx aliasedTxs, concatMap (snd . snd) aliasedTxs)
|
2019-04-17 12:48:41 +03:00
|
|
|
where
|
|
|
|
-- A list of aliased transactions for eg
|
|
|
|
-- [("f1", Tx r1), ("f2", Tx r2)]
|
|
|
|
-- are converted into a single transaction as follows
|
|
|
|
-- Tx {"f1": r1, "f2": r2}
|
2020-02-13 20:38:23 +03:00
|
|
|
-- toSingleTx :: [(Text, LazyRespTx)] -> LazyRespTx
|
2019-04-17 12:48:41 +03:00
|
|
|
toSingleTx aliasedTxs =
|
2020-06-16 18:23:06 +03:00
|
|
|
fmap encJFromAssocList $ forM aliasedTxs $ \(al, (tx, _)) -> (,) al <$> tx
|
2019-04-17 12:48:41 +03:00
|
|
|
|
|
|
|
getMutOp
|
2020-07-14 22:00:58 +03:00
|
|
|
:: ( HasVersion
|
|
|
|
, MonadError QErr m
|
|
|
|
, MonadIO m
|
2020-07-15 13:40:48 +03:00
|
|
|
, Tracing.MonadTrace m
|
2020-07-14 22:00:58 +03:00
|
|
|
, MonadIO tx
|
|
|
|
, MonadTx tx
|
2020-07-15 13:40:48 +03:00
|
|
|
, Tracing.MonadTrace tx
|
2020-07-14 22:00:58 +03:00
|
|
|
)
|
|
|
|
=> Env.Environment
|
2020-07-29 16:30:29 +03:00
|
|
|
-> L.Logger L.Hasura
|
2020-07-14 22:00:58 +03:00
|
|
|
-> GCtx
|
2019-04-17 12:48:41 +03:00
|
|
|
-> SQLGenCtx
|
|
|
|
-> UserInfo
|
2020-02-13 20:38:23 +03:00
|
|
|
-> HTTP.Manager
|
2020-06-16 18:23:06 +03:00
|
|
|
-> [HTTP.Header]
|
2020-06-08 15:13:01 +03:00
|
|
|
-> VQ.ObjectSelectionSet
|
2020-07-14 22:00:58 +03:00
|
|
|
-> m (tx EncJSON, HTTP.ResponseHeaders)
|
2020-07-29 16:30:29 +03:00
|
|
|
getMutOp env logger ctx sqlGenCtx userInfo manager reqHeaders selSet =
|
2020-07-14 22:00:58 +03:00
|
|
|
peelReaderT $ resolveMutSelSet env selSet
|
2020-02-13 20:38:23 +03:00
|
|
|
where
|
2020-03-20 09:46:45 +03:00
|
|
|
peelReaderT action =
|
|
|
|
runReaderT action
|
2020-02-13 20:38:23 +03:00
|
|
|
( userInfo, queryCtxMap, mutationCtxMap
|
|
|
|
, typeMap, fldMap, ordByCtx, insCtxMap, sqlGenCtx
|
2020-07-29 16:30:29 +03:00
|
|
|
, manager, reqHeaders, logger
|
2020-02-13 20:38:23 +03:00
|
|
|
)
|
|
|
|
where
|
|
|
|
queryCtxMap = _gQueryCtxMap ctx
|
|
|
|
mutationCtxMap = _gMutationCtxMap ctx
|
|
|
|
typeMap = _gTypes ctx
|
|
|
|
fldMap = _gFields ctx
|
|
|
|
ordByCtx = _gOrdByCtx ctx
|
|
|
|
insCtxMap = _gInsCtxMap ctx
|
2019-04-17 12:48:41 +03:00
|
|
|
|
|
|
|
getSubsOp
|
|
|
|
:: ( MonadError QErr m
|
|
|
|
, MonadIO m
|
2020-04-16 10:25:19 +03:00
|
|
|
, HasVersion
|
2020-07-15 13:40:48 +03:00
|
|
|
, Tracing.MonadTrace m
|
2019-04-17 12:48:41 +03:00
|
|
|
)
|
2020-07-14 22:00:58 +03:00
|
|
|
=> Env.Environment
|
2020-07-29 16:30:29 +03:00
|
|
|
-> L.Logger L.Hasura
|
2020-07-14 22:00:58 +03:00
|
|
|
-> PGExecCtx
|
2019-04-17 12:48:41 +03:00
|
|
|
-> GCtx
|
|
|
|
-> SQLGenCtx
|
|
|
|
-> UserInfo
|
2019-10-16 17:33:34 +03:00
|
|
|
-> QueryReusability
|
2020-04-16 10:25:19 +03:00
|
|
|
-> QueryActionExecuter
|
2020-06-08 15:13:01 +03:00
|
|
|
-> VQ.ObjectSelectionSet
|
2019-08-28 15:19:21 +03:00
|
|
|
-> m (EL.LiveQueryPlan, Maybe EL.ReusableLiveQueryPlan)
|
2020-07-29 16:30:29 +03:00
|
|
|
getSubsOp env logger pgExecCtx gCtx sqlGenCtx userInfo queryReusability actionExecuter =
|
|
|
|
runE logger gCtx sqlGenCtx userInfo .
|
2020-07-14 22:00:58 +03:00
|
|
|
EL.buildLiveQueryPlan env pgExecCtx queryReusability actionExecuter
|
2019-04-17 12:48:41 +03:00
|
|
|
|
2019-03-25 21:25:25 +03:00
|
|
|
execRemoteGQ
|
2020-01-23 00:55:55 +03:00
|
|
|
:: ( HasVersion
|
|
|
|
, MonadIO m
|
2019-07-11 08:37:06 +03:00
|
|
|
, MonadError QErr m
|
|
|
|
, MonadReader ExecutionCtx m
|
2020-06-19 09:42:32 +03:00
|
|
|
, MonadQueryLog m
|
2020-07-15 13:40:48 +03:00
|
|
|
, Tracing.MonadTrace m
|
2019-07-11 08:37:06 +03:00
|
|
|
)
|
2020-07-14 22:00:58 +03:00
|
|
|
=> Env.Environment
|
|
|
|
-> RequestId
|
2019-03-25 21:25:25 +03:00
|
|
|
-> UserInfo
|
2020-06-16 18:23:06 +03:00
|
|
|
-> [HTTP.Header]
|
2019-07-11 08:37:06 +03:00
|
|
|
-> GQLReqUnparsed
|
2019-03-25 21:25:25 +03:00
|
|
|
-> RemoteSchemaInfo
|
2020-05-27 18:02:58 +03:00
|
|
|
-> G.OperationType
|
2020-01-16 04:56:57 +03:00
|
|
|
-> m (DiffTime, HttpResponse EncJSON)
|
|
|
|
-- ^ Also returns time spent in http request, for telemetry.
|
2020-07-14 22:00:58 +03:00
|
|
|
execRemoteGQ env reqId userInfo reqHdrs q rsi opType = do
|
2019-07-11 08:37:06 +03:00
|
|
|
execCtx <- ask
|
|
|
|
let logger = _ecxLogger execCtx
|
|
|
|
manager = _ecxHttpManager execCtx
|
2020-06-19 09:42:32 +03:00
|
|
|
logQueryLog logger q Nothing reqId
|
2020-07-14 22:00:58 +03:00
|
|
|
(time, respHdrs, resp) <- execRemoteGQ' env manager userInfo reqHdrs q rsi opType
|
2020-05-27 18:02:58 +03:00
|
|
|
let !httpResp = HttpResponse (encJFromLBS resp) respHdrs
|
2020-01-16 04:56:57 +03:00
|
|
|
return (time, httpResp)
|