mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-17 12:31:52 +03:00
230 lines
7.0 KiB
Haskell
230 lines
7.0 KiB
Haskell
module Hasura.RQL.DDL.QueryTemplate
|
|
( createQueryTemplateP1
|
|
, createQueryTemplateP2
|
|
, delQTemplateFromCatalog
|
|
, TemplateParamConf(..)
|
|
, CreateQueryTemplate(..)
|
|
, runCreateQueryTemplate
|
|
, QueryTP1
|
|
|
|
, DropQueryTemplate(..)
|
|
, runDropQueryTemplate
|
|
|
|
, SetQueryTemplateComment(..)
|
|
, runSetQueryTemplateComment
|
|
) where
|
|
|
|
import Hasura.Prelude
|
|
import Hasura.RQL.GBoolExp (txtRHSBuilder)
|
|
import Hasura.RQL.Types
|
|
import Hasura.SQL.Types
|
|
import Hasura.SQL.Value
|
|
|
|
import qualified Database.PG.Query as Q
|
|
import qualified Hasura.RQL.DML.Count as R
|
|
import qualified Hasura.RQL.DML.Delete as R
|
|
import qualified Hasura.RQL.DML.Insert as R
|
|
import qualified Hasura.RQL.DML.Select as R
|
|
import qualified Hasura.RQL.DML.Update as R
|
|
import qualified Hasura.SQL.DML as PS
|
|
|
|
import Data.Aeson
|
|
import Data.Aeson.Casing
|
|
import Data.Aeson.TH
|
|
import Instances.TH.Lift ()
|
|
import Language.Haskell.TH.Syntax (Lift)
|
|
|
|
import qualified Data.HashMap.Strict as M
|
|
import qualified Data.Text as T
|
|
|
|
data TemplateParamConf
|
|
= TemplateParamConf
|
|
{ tpcParam :: !TemplateParam
|
|
, tpcDefault :: !(Maybe Value)
|
|
} deriving (Show, Eq, Lift)
|
|
|
|
$(deriveJSON (aesonDrop 3 snakeCase){omitNothingFields=True} ''TemplateParamConf)
|
|
|
|
data CreateQueryTemplate
|
|
= CreateQueryTemplate
|
|
{ cqtName :: !TQueryName
|
|
, cqtTemplate :: !QueryT
|
|
, cqtComment :: !(Maybe T.Text)
|
|
} deriving (Show, Eq, Lift)
|
|
|
|
$(deriveJSON (aesonDrop 3 snakeCase){omitNothingFields=True} ''CreateQueryTemplate)
|
|
|
|
validateParam
|
|
:: (QErrM m)
|
|
=> PGColType
|
|
-> Value
|
|
-> m PS.SQLExp
|
|
validateParam pct val =
|
|
case val of
|
|
Object _ -> do
|
|
tpc <- decodeValue val
|
|
withPathK "default" $
|
|
maybe (return ()) validateDefault $ tpcDefault tpc
|
|
return $ PS.SELit "NULL"
|
|
_ -> txtRHSBuilder pct val
|
|
where
|
|
validateDefault =
|
|
void . runAesonParser (convToBin pct)
|
|
|
|
mkSelQ :: (QErrM m) => SelectQueryT -> m SelectQuery
|
|
mkSelQ (DMLQuery tn (SelectG c w o lim offset)) = do
|
|
intLim <- withPathK "limit" $ maybe returnNothing parseAsInt lim
|
|
intOffset <- withPathK "offset" $ maybe returnNothing parseAsInt offset
|
|
return $ DMLQuery tn $ SelectG c w o intLim intOffset
|
|
where
|
|
returnNothing = return Nothing
|
|
parseAsInt v = case v of
|
|
Object _ -> do
|
|
tpc <- decodeValue v
|
|
withPathK "default" $
|
|
mapM decodeValue $ tpcDefault tpc
|
|
_ -> Just <$> decodeValue v
|
|
|
|
data QueryTP1
|
|
= QTP1Insert R.InsertQueryP1
|
|
| QTP1Select R.AnnSel
|
|
| QTP1Update R.UpdateQueryP1
|
|
| QTP1Delete R.DeleteQueryP1
|
|
| QTP1Count R.CountQueryP1
|
|
| QTP1Bulk [QueryTP1]
|
|
deriving (Show, Eq)
|
|
|
|
validateTQuery
|
|
:: (QErrM m, UserInfoM m, CacheRM m, HasSQLGenCtx m)
|
|
=> QueryT
|
|
-> m QueryTP1
|
|
validateTQuery qt = withPathK "args" $ case qt of
|
|
QTInsert q -> QTP1Insert <$> R.convInsertQuery decodeInsObjs validateParam q
|
|
QTSelect q -> QTP1Select <$> (mkSelQ q >>= R.convSelectQuery validateParam)
|
|
QTUpdate q -> QTP1Update <$> R.validateUpdateQueryWith validateParam q
|
|
QTDelete q -> QTP1Delete <$> R.validateDeleteQWith validateParam q
|
|
QTCount q -> QTP1Count <$> R.validateCountQWith validateParam q
|
|
QTBulk q -> QTP1Bulk <$> mapM validateTQuery q
|
|
where
|
|
decodeInsObjs val = do
|
|
tpc <- decodeValue val
|
|
mDefObjs <- mapM decodeValue $ tpcDefault tpc
|
|
return $ fromMaybe [] mDefObjs
|
|
|
|
collectDeps
|
|
:: QueryTP1 -> [SchemaDependency]
|
|
collectDeps qt = case qt of
|
|
QTP1Insert qp1 -> R.getInsertDeps qp1
|
|
QTP1Select qp1 -> R.getSelectDeps qp1
|
|
QTP1Update qp1 -> R.getUpdateDeps qp1
|
|
QTP1Delete qp1 -> R.getDeleteDeps qp1
|
|
QTP1Count qp1 -> R.getCountDeps qp1
|
|
QTP1Bulk qp1 -> concatMap collectDeps qp1
|
|
|
|
createQueryTemplateP1
|
|
:: (UserInfoM m, QErrM m, CacheRM m, HasSQLGenCtx m)
|
|
=> CreateQueryTemplate
|
|
-> m (WithDeps QueryTemplateInfo)
|
|
createQueryTemplateP1 (CreateQueryTemplate qtn qt _) = do
|
|
adminOnly
|
|
sc <- askSchemaCache
|
|
withPathK "name" $ when (isJust $ M.lookup qtn $ scQTemplates sc) $
|
|
throw400 AlreadyExists $ "the query template already exists : " <>> qtn
|
|
qtp1 <- withPathK "template" $ liftP1 $ validateTQuery qt
|
|
let deps = collectDeps qtp1
|
|
return (QueryTemplateInfo qtn qt, deps)
|
|
|
|
addQTemplateToCatalog
|
|
:: CreateQueryTemplate
|
|
-> Q.TxE QErr ()
|
|
addQTemplateToCatalog (CreateQueryTemplate qtName qtDef mComment) =
|
|
Q.unitQE defaultTxErrorHandler [Q.sql|
|
|
INSERT INTO
|
|
hdb_catalog.hdb_query_template
|
|
(template_name, template_defn, comment)
|
|
VALUES ($1, $2 :: jsonb, $3)
|
|
|] (qtName, Q.AltJ qtDef, mComment) False
|
|
|
|
createQueryTemplateP2
|
|
:: (QErrM m, CacheRWM m, MonadTx m)
|
|
=> CreateQueryTemplate
|
|
-> WithDeps QueryTemplateInfo
|
|
-> m RespBody
|
|
createQueryTemplateP2 cqt (qti, deps) = do
|
|
addQTemplateToCache qti deps
|
|
liftTx $ addQTemplateToCatalog cqt
|
|
return successMsg
|
|
|
|
runCreateQueryTemplate
|
|
:: (QErrM m, UserInfoM m, CacheRWM m, MonadTx m, HasSQLGenCtx m)
|
|
=> CreateQueryTemplate -> m RespBody
|
|
runCreateQueryTemplate q =
|
|
createQueryTemplateP1 q >>= createQueryTemplateP2 q
|
|
|
|
data DropQueryTemplate
|
|
= DropQueryTemplate
|
|
{ dqtName :: !TQueryName
|
|
} deriving (Show, Eq, Lift)
|
|
|
|
$(deriveJSON (aesonDrop 3 snakeCase){omitNothingFields=True} ''DropQueryTemplate)
|
|
|
|
delQTemplateFromCatalog
|
|
:: TQueryName
|
|
-> Q.TxE QErr ()
|
|
delQTemplateFromCatalog qtn =
|
|
Q.unitQE defaultTxErrorHandler [Q.sql|
|
|
DELETE FROM
|
|
hdb_catalog.hdb_query_template
|
|
WHERE template_name = $1
|
|
|] (Identity qtn) False
|
|
|
|
runDropQueryTemplate
|
|
:: (QErrM m, UserInfoM m, CacheRWM m, MonadTx m)
|
|
=> DropQueryTemplate -> m RespBody
|
|
runDropQueryTemplate q = do
|
|
withPathK "name" $ void $ askQTemplateInfo qtn
|
|
delQTemplateFromCache qtn
|
|
liftTx $ delQTemplateFromCatalog qtn
|
|
return successMsg
|
|
where
|
|
qtn = dqtName q
|
|
|
|
data SetQueryTemplateComment
|
|
= SetQueryTemplateComment
|
|
{ sqtcName :: !TQueryName
|
|
, sqtcComment :: !(Maybe T.Text)
|
|
} deriving (Show, Eq, Lift)
|
|
|
|
$(deriveJSON (aesonDrop 4 snakeCase) ''SetQueryTemplateComment)
|
|
|
|
setQueryTemplateCommentP1
|
|
:: (UserInfoM m, QErrM m, CacheRM m)
|
|
=> SetQueryTemplateComment -> m ()
|
|
setQueryTemplateCommentP1 (SetQueryTemplateComment qtn _) = do
|
|
adminOnly
|
|
void $ askQTemplateInfo qtn
|
|
|
|
setQueryTemplateCommentP2
|
|
:: (QErrM m, MonadTx m) => SetQueryTemplateComment -> m RespBody
|
|
setQueryTemplateCommentP2 apc = do
|
|
liftTx $ setQueryTemplateCommentTx apc
|
|
return successMsg
|
|
|
|
setQueryTemplateCommentTx
|
|
:: SetQueryTemplateComment
|
|
-> Q.TxE QErr ()
|
|
setQueryTemplateCommentTx (SetQueryTemplateComment qtn comment) =
|
|
Q.unitQE defaultTxErrorHandler
|
|
[Q.sql|
|
|
UPDATE hdb_catalog.hdb_query_template
|
|
SET comment = $1
|
|
WHERE template_name = $2
|
|
|] (comment, qtn) False
|
|
|
|
runSetQueryTemplateComment
|
|
:: (QErrM m, UserInfoM m, CacheRWM m, MonadTx m)
|
|
=> SetQueryTemplateComment -> m RespBody
|
|
runSetQueryTemplateComment q = do
|
|
setQueryTemplateCommentP1 q
|
|
setQueryTemplateCommentP2 q
|