2018-07-20 10:22:46 +03:00
|
|
|
module Hasura.GraphQL.Resolve
|
2019-04-17 12:48:41 +03:00
|
|
|
( mutFldToTx
|
|
|
|
, queryFldToPGAST
|
|
|
|
, RS.traverseQueryRootFldAST
|
|
|
|
, RS.toPGQuery
|
|
|
|
, UnresolvedVal(..)
|
|
|
|
, AnnPGVal(..)
|
|
|
|
, RS.QueryRootFldUnresolved
|
|
|
|
, resolveValPrep
|
|
|
|
, queryFldToSQL
|
|
|
|
, schemaR
|
|
|
|
, typeR
|
2018-07-20 10:22:46 +03:00
|
|
|
) where
|
|
|
|
|
2019-04-17 12:48:41 +03:00
|
|
|
import Data.Has
|
2018-07-20 10:22:46 +03:00
|
|
|
|
2019-03-18 19:22:21 +03:00
|
|
|
import qualified Data.HashMap.Strict as Map
|
|
|
|
import qualified Database.PG.Query as Q
|
|
|
|
import qualified Language.GraphQL.Draft.Syntax as G
|
2018-11-23 16:02:46 +03:00
|
|
|
|
2019-03-25 21:25:25 +03:00
|
|
|
import Hasura.GraphQL.Context
|
2018-07-20 10:22:46 +03:00
|
|
|
import Hasura.GraphQL.Resolve.Context
|
|
|
|
import Hasura.GraphQL.Resolve.Introspect
|
|
|
|
import Hasura.GraphQL.Validate.Field
|
2019-04-17 12:48:41 +03:00
|
|
|
import Hasura.Prelude
|
|
|
|
import Hasura.RQL.DML.Internal (sessVarFromCurrentSetting)
|
2018-07-20 10:22:46 +03:00
|
|
|
import Hasura.RQL.Types
|
|
|
|
import Hasura.SQL.Types
|
|
|
|
|
2019-03-18 19:22:21 +03:00
|
|
|
import qualified Hasura.GraphQL.Resolve.Insert as RI
|
|
|
|
import qualified Hasura.GraphQL.Resolve.Mutation as RM
|
|
|
|
import qualified Hasura.GraphQL.Resolve.Select as RS
|
2018-07-20 10:22:46 +03:00
|
|
|
|
2019-03-25 21:25:25 +03:00
|
|
|
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
|
|
|
|
:: ( MonadError QErr m, MonadReader r m, Has FieldMap r
|
|
|
|
, Has OrdByCtx r, Has SQLGenCtx r, Has UserInfo r
|
|
|
|
, Has OpCtxMap r
|
|
|
|
)
|
|
|
|
=> Field
|
|
|
|
-> m RS.QueryRootFldUnresolved
|
|
|
|
queryFldToPGAST fld = do
|
|
|
|
opCtx <- getOpCtx $ _fName fld
|
|
|
|
userInfo <- asks getter
|
|
|
|
case opCtx of
|
2019-03-25 21:25:25 +03:00
|
|
|
OCSelect ctx -> do
|
|
|
|
validateHdrs userInfo (_socHeaders ctx)
|
|
|
|
RS.convertSelect ctx fld
|
|
|
|
OCSelectPkey ctx -> do
|
|
|
|
validateHdrs userInfo (_spocHeaders ctx)
|
|
|
|
RS.convertSelectByPKey ctx fld
|
|
|
|
OCSelectAgg ctx -> do
|
|
|
|
validateHdrs userInfo (_socHeaders ctx)
|
|
|
|
RS.convertAggSelect ctx fld
|
|
|
|
OCFuncQuery ctx -> do
|
|
|
|
validateHdrs userInfo (_fqocHeaders ctx)
|
2019-04-17 12:48:41 +03:00
|
|
|
RS.convertFuncQuerySimple ctx fld
|
2019-03-25 21:25:25 +03:00
|
|
|
OCFuncAggQuery ctx -> do
|
|
|
|
validateHdrs userInfo (_fqocHeaders ctx)
|
2019-04-17 12:48:41 +03:00
|
|
|
RS.convertFuncQueryAgg ctx fld
|
|
|
|
OCInsert _ ->
|
|
|
|
throw500 "unexpected OCInsert for query field context"
|
|
|
|
OCUpdate _ ->
|
|
|
|
throw500 "unexpected OCUpdate for query field context"
|
|
|
|
OCDelete _ ->
|
|
|
|
throw500 "unexpected OCDelete for query field context"
|
|
|
|
|
|
|
|
queryFldToSQL
|
|
|
|
:: ( MonadError QErr m, MonadReader r m, Has FieldMap r
|
|
|
|
, Has OrdByCtx r, Has SQLGenCtx r, Has UserInfo r
|
|
|
|
, Has OpCtxMap r
|
|
|
|
)
|
|
|
|
=> PrepFn m
|
|
|
|
-> Field
|
|
|
|
-> 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
|
|
|
|
return $ RS.toPGQuery resolvedAST
|
|
|
|
|
|
|
|
mutFldToTx
|
|
|
|
:: ( MonadError QErr m
|
|
|
|
, MonadReader r m
|
|
|
|
, Has UserInfo r
|
|
|
|
, Has OpCtxMap r
|
|
|
|
, Has FieldMap r
|
|
|
|
, Has OrdByCtx r
|
|
|
|
, Has SQLGenCtx r
|
|
|
|
, Has InsCtxMap r
|
|
|
|
)
|
|
|
|
=> Field
|
|
|
|
-> m RespTx
|
|
|
|
mutFldToTx fld = do
|
|
|
|
userInfo <- asks getter
|
|
|
|
opCtx <- getOpCtx $ _fName fld
|
|
|
|
case opCtx of
|
2019-03-25 21:25:25 +03:00
|
|
|
OCInsert ctx -> do
|
2019-04-17 12:48:41 +03:00
|
|
|
let roleName = userRole userInfo
|
2019-03-25 21:25:25 +03:00
|
|
|
validateHdrs userInfo (_iocHeaders ctx)
|
|
|
|
RI.convertInsert roleName (_iocTable ctx) fld
|
|
|
|
OCUpdate ctx -> do
|
|
|
|
validateHdrs userInfo (_uocHeaders ctx)
|
|
|
|
RM.convertUpdate ctx fld
|
|
|
|
OCDelete ctx -> do
|
|
|
|
validateHdrs userInfo (_docHeaders ctx)
|
|
|
|
RM.convertDelete ctx fld
|
2019-04-17 12:48:41 +03:00
|
|
|
OCSelect _ ->
|
|
|
|
throw500 "unexpected query field context for a mutation field"
|
|
|
|
OCSelectPkey _ ->
|
|
|
|
throw500 "unexpected query field context for a mutation field"
|
|
|
|
OCSelectAgg _ ->
|
|
|
|
throw500 "unexpected query field context for a mutation field"
|
|
|
|
OCFuncQuery _ ->
|
|
|
|
throw500 "unexpected query field context for a mutation field"
|
|
|
|
OCFuncAggQuery _ ->
|
|
|
|
throw500 "unexpected query field context for a mutation field"
|
2018-07-20 10:22:46 +03:00
|
|
|
|
2019-04-17 12:48:41 +03:00
|
|
|
getOpCtx
|
|
|
|
:: ( MonadError QErr m
|
|
|
|
, MonadReader r m
|
|
|
|
, Has OpCtxMap r
|
|
|
|
)
|
|
|
|
=> G.Name -> m OpCtx
|
|
|
|
getOpCtx f = do
|
|
|
|
opCtxMap <- asks getter
|
|
|
|
onNothing (Map.lookup f opCtxMap) $ throw500 $
|
|
|
|
"lookup failed: opctx: " <> showName f
|