graphql-engine/server/src-lib/Hasura/GraphQL/Resolve.hs

128 lines
3.7 KiB
Haskell
Raw Normal View History

module Hasura.GraphQL.Resolve
2019-04-17 12:48:41 +03:00
( mutFldToTx
, queryFldToPGAST
, RS.traverseQueryRootFldAST
, RS.toPGQuery
, UnresolvedVal(..)
2019-04-17 12:48:41 +03:00
, AnnPGVal(..)
, txtConverter
2019-04-17 12:48:41 +03:00
, RS.QueryRootFldUnresolved
, resolveValPrep
, queryFldToSQL
, RIntro.schemaR
, RIntro.typeR
) where
2019-04-17 12:48:41 +03:00
import Data.Has
import qualified Data.HashMap.Strict as Map
import qualified Database.PG.Query as Q
import qualified Language.GraphQL.Draft.Syntax as G
import Hasura.GraphQL.Resolve.Context
2019-04-17 12:48:41 +03:00
import Hasura.Prelude
import Hasura.RQL.DML.Internal (currentSession,
sessVarFromCurrentSetting)
import Hasura.RQL.Types
import Hasura.SQL.Types
import qualified Hasura.GraphQL.Resolve.Insert as RI
import qualified Hasura.GraphQL.Resolve.Introspect as RIntro
import qualified Hasura.GraphQL.Resolve.Mutation as RM
import qualified Hasura.GraphQL.Resolve.Select as RS
import qualified Hasura.GraphQL.Validate as V
validateHdrs
:: (Foldable t, QErrM m) => UserInfo -> t Text -> m ()
validateHdrs userInfo hdrs = do
let receivedVars = userVars userInfo
forM_ hdrs $ \hdr ->
unless (isJust $ getVarVal hdr receivedVars) $
throw400 NotFound $ hdr <<> " header is expected but not found"
2019-04-17 12:48:41 +03:00
queryFldToPGAST
:: ( MonadReusability m, MonadError QErr m, MonadReader r m, Has FieldMap r
2019-04-17 12:48:41 +03:00
, Has OrdByCtx r, Has SQLGenCtx r, Has UserInfo r
, Has QueryCtxMap r
2019-04-17 12:48:41 +03:00
)
=> V.Field
2019-04-17 12:48:41 +03:00
-> m RS.QueryRootFldUnresolved
queryFldToPGAST fld = do
opCtx <- getOpCtx $ V._fName fld
2019-04-17 12:48:41 +03:00
userInfo <- asks getter
case opCtx of
QCSelect ctx -> do
validateHdrs userInfo (_socHeaders ctx)
RS.convertSelect ctx fld
QCSelectPkey ctx -> do
validateHdrs userInfo (_spocHeaders ctx)
RS.convertSelectByPKey ctx fld
QCSelectAgg ctx -> do
validateHdrs userInfo (_socHeaders ctx)
RS.convertAggSelect ctx fld
QCFuncQuery ctx -> do
validateHdrs userInfo (_fqocHeaders ctx)
2019-04-17 12:48:41 +03:00
RS.convertFuncQuerySimple ctx fld
QCFuncAggQuery ctx -> do
validateHdrs userInfo (_fqocHeaders ctx)
2019-04-17 12:48:41 +03:00
RS.convertFuncQueryAgg ctx fld
queryFldToSQL
:: ( MonadReusability m, MonadError QErr m, MonadReader r m, Has FieldMap r
2019-04-17 12:48:41 +03:00
, Has OrdByCtx r, Has SQLGenCtx r, Has UserInfo r
, Has QueryCtxMap r
2019-04-17 12:48:41 +03:00
)
=> PrepFn m
-> V.Field
2019-04-17 12:48:41 +03:00
-> m Q.Query
queryFldToSQL fn fld = do
pgAST <- queryFldToPGAST fld
resolvedAST <- flip RS.traverseQueryRootFldAST pgAST $ \case
UVPG annPGVal -> fn annPGVal
UVSQL sqlExp -> return sqlExp
UVSessVar colTy sessVar -> sessVarFromCurrentSetting colTy sessVar
UVSession -> pure currentSession
2019-04-17 12:48:41 +03:00
return $ RS.toPGQuery resolvedAST
mutFldToTx
:: ( MonadReusability m
, MonadError QErr m
2019-04-17 12:48:41 +03:00
, MonadReader r m
, Has UserInfo r
, Has MutationCtxMap r
2019-04-17 12:48:41 +03:00
, Has FieldMap r
, Has OrdByCtx r
, Has SQLGenCtx r
, Has InsCtxMap r
)
=> V.Field
2019-04-17 12:48:41 +03:00
-> m RespTx
mutFldToTx fld = do
userInfo <- asks getter
opCtx <- getOpCtx $ V._fName fld
2019-04-17 12:48:41 +03:00
case opCtx of
MCInsert ctx -> do
2019-04-17 12:48:41 +03:00
let roleName = userRole userInfo
validateHdrs userInfo (_iocHeaders ctx)
RI.convertInsert roleName (_iocTable ctx) fld
MCUpdate ctx -> do
validateHdrs userInfo (_uocHeaders ctx)
RM.convertUpdate ctx fld
MCDelete ctx -> do
validateHdrs userInfo (_docHeaders ctx)
RM.convertDelete ctx fld
2019-04-17 12:48:41 +03:00
getOpCtx
:: ( MonadReusability m
, MonadError QErr m
2019-04-17 12:48:41 +03:00
, MonadReader r m
, Has (OpCtxMap a) r
2019-04-17 12:48:41 +03:00
)
=> G.Name -> m a
2019-04-17 12:48:41 +03:00
getOpCtx f = do
opCtxMap <- asks getter
onNothing (Map.lookup f opCtxMap) $ throw500 $
"lookup failed: opctx: " <> showName f