2018-06-27 16:11:32 +03:00
|
|
|
module Hasura.GraphQL.Schema
|
|
|
|
( mkGCtxMap
|
|
|
|
, GCtxMap
|
2019-07-08 08:51:41 +03:00
|
|
|
, buildGCtxMapPG
|
2018-06-27 16:11:32 +03:00
|
|
|
, getGCtx
|
|
|
|
, GCtx(..)
|
2019-07-23 14:12:59 +03:00
|
|
|
, QueryCtx(..)
|
|
|
|
, MutationCtx(..)
|
2018-10-05 18:13:51 +03:00
|
|
|
, InsCtx(..)
|
|
|
|
, InsCtxMap
|
|
|
|
, RelationInfoMap
|
2018-11-14 15:59:59 +03:00
|
|
|
, isAggFld
|
2019-01-25 06:31:54 +03:00
|
|
|
, qualObjectToName
|
2018-11-23 16:02:46 +03:00
|
|
|
, ppGCtx
|
2018-06-27 16:11:32 +03:00
|
|
|
|
2019-08-09 12:19:17 +03:00
|
|
|
, checkConflictingNode
|
|
|
|
, checkSchemaConflicts
|
|
|
|
) where
|
2018-11-23 16:02:46 +03:00
|
|
|
|
2019-08-28 22:27:15 +03:00
|
|
|
import Control.Lens.Extended hiding (op)
|
2018-06-27 16:11:32 +03:00
|
|
|
|
2019-08-09 12:19:17 +03:00
|
|
|
import qualified Data.HashMap.Strict as Map
|
|
|
|
import qualified Data.HashSet as Set
|
|
|
|
import qualified Data.Sequence as Seq
|
2019-01-25 06:31:54 +03:00
|
|
|
|
2019-08-09 12:19:17 +03:00
|
|
|
import qualified Data.Text as T
|
|
|
|
import qualified Language.GraphQL.Draft.Syntax as G
|
2018-06-27 16:11:32 +03:00
|
|
|
|
2018-11-23 16:02:46 +03:00
|
|
|
import Hasura.GraphQL.Context
|
2019-08-09 12:19:17 +03:00
|
|
|
import Hasura.GraphQL.Resolve.Types
|
2018-06-27 16:11:32 +03:00
|
|
|
import Hasura.GraphQL.Validate.Types
|
2018-07-12 17:03:02 +03:00
|
|
|
import Hasura.Prelude
|
2019-08-09 12:19:17 +03:00
|
|
|
import Hasura.RQL.DML.Internal (mkAdminRolePermInfo)
|
2018-06-27 16:11:32 +03:00
|
|
|
import Hasura.RQL.Types
|
2019-09-19 07:47:36 +03:00
|
|
|
import Hasura.Server.Utils (duplicates)
|
2018-06-27 16:11:32 +03:00
|
|
|
import Hasura.SQL.Types
|
|
|
|
|
2019-08-09 12:19:17 +03:00
|
|
|
import Hasura.GraphQL.Schema.BoolExp
|
|
|
|
import Hasura.GraphQL.Schema.Common
|
|
|
|
import Hasura.GraphQL.Schema.Function
|
2019-08-28 22:27:15 +03:00
|
|
|
import Hasura.GraphQL.Schema.Merge
|
2019-08-09 12:19:17 +03:00
|
|
|
import Hasura.GraphQL.Schema.Mutation.Common
|
|
|
|
import Hasura.GraphQL.Schema.Mutation.Delete
|
|
|
|
import Hasura.GraphQL.Schema.Mutation.Insert
|
|
|
|
import Hasura.GraphQL.Schema.Mutation.Update
|
|
|
|
import Hasura.GraphQL.Schema.OrderBy
|
|
|
|
import Hasura.GraphQL.Schema.Select
|
2018-11-23 16:02:46 +03:00
|
|
|
|
2019-08-11 18:34:38 +03:00
|
|
|
getInsPerm :: TableInfo PGColumnInfo -> RoleName -> Maybe InsPermInfo
|
2018-10-05 18:13:51 +03:00
|
|
|
getInsPerm tabInfo role
|
|
|
|
| role == adminRole = _permIns $ mkAdminRolePermInfo tabInfo
|
|
|
|
| otherwise = Map.lookup role rolePermInfoMap >>= _permIns
|
|
|
|
where
|
2019-07-22 15:47:13 +03:00
|
|
|
rolePermInfoMap = _tiRolePermInfoMap tabInfo
|
2018-10-05 18:13:51 +03:00
|
|
|
|
|
|
|
getTabInfo
|
|
|
|
:: MonadError QErr m
|
2019-08-11 18:34:38 +03:00
|
|
|
=> TableCache PGColumnInfo -> QualifiedTable -> m (TableInfo PGColumnInfo)
|
2018-10-05 18:13:51 +03:00
|
|
|
getTabInfo tc t =
|
|
|
|
onNothing (Map.lookup t tc) $
|
|
|
|
throw500 $ "table not found: " <>> t
|
|
|
|
|
2019-01-25 06:31:54 +03:00
|
|
|
isValidObjectName :: (ToTxt a) => QualifiedObject a -> Bool
|
2019-09-19 07:47:36 +03:00
|
|
|
isValidObjectName = G.isValidName . qualObjectToName
|
2018-07-27 12:50:12 +03:00
|
|
|
|
2019-09-19 07:47:36 +03:00
|
|
|
isValidCol :: PGColumnInfo -> Bool
|
|
|
|
isValidCol = G.isValidName . pgiName
|
2019-04-15 10:04:30 +03:00
|
|
|
|
|
|
|
isValidRel :: ToTxt a => RelName -> QualifiedObject a -> Bool
|
2019-09-19 07:47:36 +03:00
|
|
|
isValidRel rn rt = G.isValidName (mkRelName rn) && isValidObjectName rt
|
2018-07-27 12:50:12 +03:00
|
|
|
|
2019-01-03 06:58:12 +03:00
|
|
|
upsertable :: [ConstraintName] -> Bool -> Bool -> Bool
|
2019-07-22 15:47:13 +03:00
|
|
|
upsertable uniqueOrPrimaryCons isUpsertAllowed isAView =
|
|
|
|
not (null uniqueOrPrimaryCons) && isUpsertAllowed && not isAView
|
2018-10-12 15:06:12 +03:00
|
|
|
|
2019-09-19 07:47:36 +03:00
|
|
|
getValidCols
|
|
|
|
:: FieldInfoMap PGColumnInfo -> [PGColumnInfo]
|
|
|
|
getValidCols fim = filter isValidCol cols
|
|
|
|
where
|
|
|
|
cols = fst $ partitionFieldInfos $ Map.elems fim
|
2018-10-26 14:57:33 +03:00
|
|
|
|
2019-08-11 18:34:38 +03:00
|
|
|
getValidRels :: FieldInfoMap PGColumnInfo -> [RelInfo]
|
2019-09-19 07:47:36 +03:00
|
|
|
getValidRels = filter isValidRel' . snd . partitionFieldInfos . Map.elems
|
|
|
|
where
|
|
|
|
isValidRel' (RelInfo rn _ _ remTab _) = isValidRel rn remTab
|
2018-10-26 14:57:33 +03:00
|
|
|
|
2019-03-22 10:08:42 +03:00
|
|
|
mkValidConstraints :: [ConstraintName] -> [ConstraintName]
|
2019-01-03 06:58:12 +03:00
|
|
|
mkValidConstraints =
|
2019-09-19 07:47:36 +03:00
|
|
|
filter (G.isValidName . G.Name . getConstraintTxt)
|
2018-07-27 12:50:12 +03:00
|
|
|
|
2019-09-19 07:47:36 +03:00
|
|
|
isRelNullable
|
|
|
|
:: FieldInfoMap PGColumnInfo -> RelInfo -> Bool
|
2018-08-10 15:44:44 +03:00
|
|
|
isRelNullable fim ri = isNullable
|
|
|
|
where
|
|
|
|
lCols = map fst $ riMapping ri
|
2018-10-26 14:57:33 +03:00
|
|
|
allCols = getValidCols fim
|
2018-08-27 17:17:03 +03:00
|
|
|
lColInfos = getColInfos lCols allCols
|
2018-08-10 15:44:44 +03:00
|
|
|
isNullable = any pgiIsNullable lColInfos
|
|
|
|
|
2019-09-19 07:47:36 +03:00
|
|
|
mkPGColGNameMap :: [PGColumnInfo] -> PGColGNameMap
|
|
|
|
mkPGColGNameMap cols = Map.fromList $
|
|
|
|
flip map cols $ \ci -> (pgiName ci, ci)
|
|
|
|
|
2018-11-14 15:59:59 +03:00
|
|
|
numAggOps :: [G.Name]
|
|
|
|
numAggOps = [ "sum", "avg", "stddev", "stddev_samp", "stddev_pop"
|
|
|
|
, "variance", "var_samp", "var_pop"
|
|
|
|
]
|
|
|
|
|
|
|
|
compAggOps :: [G.Name]
|
|
|
|
compAggOps = ["max", "min"]
|
|
|
|
|
|
|
|
isAggFld :: G.Name -> Bool
|
|
|
|
isAggFld = flip elem (numAggOps <> compAggOps)
|
|
|
|
|
2018-06-27 16:11:32 +03:00
|
|
|
mkGCtxRole'
|
|
|
|
:: QualifiedTable
|
2019-09-17 04:51:11 +03:00
|
|
|
-- Postgres description
|
|
|
|
-> Maybe PGDescription
|
|
|
|
-- insert permission
|
2019-08-11 18:34:38 +03:00
|
|
|
-> Maybe ([PGColumnInfo], RelationInfoMap)
|
2019-09-17 04:51:11 +03:00
|
|
|
-- select permission
|
2018-10-26 12:02:44 +03:00
|
|
|
-> Maybe (Bool, [SelField])
|
2019-09-19 07:47:36 +03:00
|
|
|
-- update cols
|
2019-08-11 18:34:38 +03:00
|
|
|
-> Maybe [PGColumnInfo]
|
2019-09-19 07:47:36 +03:00
|
|
|
-- delete cols
|
2018-06-27 16:11:32 +03:00
|
|
|
-> Maybe ()
|
2019-07-22 15:47:13 +03:00
|
|
|
-- ^ delete cols
|
2019-08-11 18:34:38 +03:00
|
|
|
-> [PGColumnInfo]
|
2019-07-22 15:47:13 +03:00
|
|
|
-- ^ primary key columns
|
2019-03-22 10:08:42 +03:00
|
|
|
-> [ConstraintName]
|
2019-07-22 15:47:13 +03:00
|
|
|
-- ^ constraints
|
2018-10-12 15:06:12 +03:00
|
|
|
-> Maybe ViewInfo
|
2019-01-25 06:31:54 +03:00
|
|
|
-> [FunctionInfo]
|
2019-07-22 15:47:13 +03:00
|
|
|
-- ^ all functions
|
|
|
|
-> Maybe EnumValues
|
|
|
|
-- ^ present iff this table is an enum table (see "Hasura.RQL.Schema.Enum")
|
2018-06-27 16:11:32 +03:00
|
|
|
-> TyAgg
|
2019-09-17 04:51:11 +03:00
|
|
|
mkGCtxRole' tn descM insPermM selPermM updColsM
|
2019-07-22 15:47:13 +03:00
|
|
|
delPermM pkeyCols constraints viM funcs enumValuesM =
|
2018-06-27 16:11:32 +03:00
|
|
|
|
2019-03-07 13:24:07 +03:00
|
|
|
TyAgg (mkTyInfoMap allTypes) fieldMap scalars ordByCtx
|
2018-06-27 16:11:32 +03:00
|
|
|
where
|
|
|
|
|
2018-10-26 14:57:33 +03:00
|
|
|
ordByCtx = fromMaybe Map.empty ordByCtxM
|
2019-04-15 10:04:30 +03:00
|
|
|
upsertPerm = isJust updColsM
|
2019-03-22 10:08:42 +03:00
|
|
|
isUpsertable = upsertable constraints upsertPerm $ isJust viM
|
2018-12-15 19:10:29 +03:00
|
|
|
updatableCols = maybe [] (map pgiName) updColsM
|
2019-03-22 10:08:42 +03:00
|
|
|
onConflictTypes = mkOnConflictTypes tn constraints updatableCols isUpsertable
|
2018-07-20 13:51:20 +03:00
|
|
|
jsonOpTys = fromMaybe [] updJSONOpInpObjTysM
|
2018-10-12 15:06:12 +03:00
|
|
|
relInsInpObjTys = maybe [] (map TIInpObj) $
|
|
|
|
mutHelper viIsInsertable relInsInpObjsM
|
2018-06-27 16:11:32 +03:00
|
|
|
|
2019-01-25 06:31:54 +03:00
|
|
|
funcInpArgTys = bool [] (map TIInpObj funcArgInpObjs) $ isJust selFldsM
|
|
|
|
|
2018-10-12 15:06:12 +03:00
|
|
|
allTypes = relInsInpObjTys <> onConflictTypes <> jsonOpTys
|
2018-10-26 12:02:44 +03:00
|
|
|
<> queryTypes <> aggQueryTypes <> mutationTypes
|
2019-01-25 06:31:54 +03:00
|
|
|
<> funcInpArgTys
|
2018-10-12 15:06:12 +03:00
|
|
|
|
|
|
|
queryTypes = catMaybes
|
|
|
|
[ TIInpObj <$> boolExpInpObjM
|
2018-10-26 14:57:33 +03:00
|
|
|
, TIInpObj <$> ordByInpObjM
|
2018-06-27 16:11:32 +03:00
|
|
|
, TIObj <$> selObjM
|
|
|
|
]
|
2018-12-12 15:58:39 +03:00
|
|
|
aggQueryTypes = map TIObj aggObjs <> map TIInpObj aggOrdByInps
|
2018-06-27 16:11:32 +03:00
|
|
|
|
2018-10-12 15:06:12 +03:00
|
|
|
mutationTypes = catMaybes
|
|
|
|
[ TIInpObj <$> mutHelper viIsInsertable insInpObjM
|
|
|
|
, TIInpObj <$> mutHelper viIsUpdatable updSetInpObjM
|
|
|
|
, TIInpObj <$> mutHelper viIsUpdatable updIncInpObjM
|
|
|
|
, TIObj <$> mutRespObjM
|
2018-11-14 15:59:59 +03:00
|
|
|
, TIEnum <$> selColInpTyM
|
2019-07-22 15:47:13 +03:00
|
|
|
, TIEnum <$> tableEnumTypeM
|
2018-10-12 15:06:12 +03:00
|
|
|
]
|
2018-12-13 10:26:15 +03:00
|
|
|
|
|
|
|
mutHelper :: (ViewInfo -> Bool) -> Maybe a -> Maybe a
|
2018-10-12 15:06:12 +03:00
|
|
|
mutHelper f objM = bool Nothing objM $ isMutable f viM
|
|
|
|
|
2018-06-27 16:11:32 +03:00
|
|
|
fieldMap = Map.unions $ catMaybes
|
2019-02-22 13:27:38 +03:00
|
|
|
[ insInpObjFldsM, updSetInpObjFldsM
|
|
|
|
, boolExpInpObjFldsM , selObjFldsM
|
2018-06-27 16:11:32 +03:00
|
|
|
]
|
2019-07-22 15:47:13 +03:00
|
|
|
scalars = selByPkScalarSet <> funcArgScalarSet
|
2018-06-27 16:11:32 +03:00
|
|
|
|
|
|
|
-- helper
|
2018-10-26 12:02:44 +03:00
|
|
|
mkColFldMap ty cols = Map.fromList $ flip map cols $
|
2019-09-19 07:47:36 +03:00
|
|
|
\ci -> ((ty, pgiName ci), Left ci)
|
2018-06-27 16:11:32 +03:00
|
|
|
|
|
|
|
-- insert input type
|
2019-04-15 10:04:30 +03:00
|
|
|
insInpObjM = uncurry (mkInsInp tn) <$> insPermM
|
2018-10-05 18:13:51 +03:00
|
|
|
-- column fields used in insert input object
|
2019-04-15 10:04:30 +03:00
|
|
|
insInpObjFldsM = (mkColFldMap (mkInsInpTy tn) . fst) <$> insPermM
|
2018-10-05 18:13:51 +03:00
|
|
|
-- relationship input objects
|
2019-04-15 10:04:30 +03:00
|
|
|
relInsInpObjsM = const (mkRelInsInps tn isUpsertable) <$> insPermM
|
2018-06-27 16:11:32 +03:00
|
|
|
-- update set input type
|
2018-07-20 13:51:20 +03:00
|
|
|
updSetInpObjM = mkUpdSetInp tn <$> updColsM
|
|
|
|
-- update increment input type
|
|
|
|
updIncInpObjM = mkUpdIncInp tn updColsM
|
|
|
|
-- update json operator input type
|
|
|
|
updJSONOpInpObjsM = mkUpdJSONOpInp tn <$> updColsM
|
|
|
|
updJSONOpInpObjTysM = map TIInpObj <$> updJSONOpInpObjsM
|
2018-06-27 16:11:32 +03:00
|
|
|
-- fields used in set input object
|
|
|
|
updSetInpObjFldsM = mkColFldMap (mkUpdSetTy tn) <$> updColsM
|
|
|
|
|
2018-10-26 12:02:44 +03:00
|
|
|
selFldsM = snd <$> selPermM
|
2019-09-19 07:47:36 +03:00
|
|
|
selColNamesM = (map pgiName . lefts) <$> selFldsM
|
|
|
|
selColInpTyM = mkSelColumnTy tn <$> selColNamesM
|
2018-06-27 16:11:32 +03:00
|
|
|
-- boolexp input type
|
|
|
|
boolExpInpObjM = case selFldsM of
|
2018-10-26 14:57:33 +03:00
|
|
|
Just selFlds -> Just $ mkBoolExpInp tn selFlds
|
2018-06-27 16:11:32 +03:00
|
|
|
-- no select permission
|
|
|
|
Nothing ->
|
|
|
|
-- but update/delete is defined
|
|
|
|
if isJust updColsM || isJust delPermM
|
|
|
|
then Just $ mkBoolExpInp tn []
|
|
|
|
else Nothing
|
|
|
|
|
2019-01-25 06:31:54 +03:00
|
|
|
-- funcargs input type
|
2019-02-22 13:27:38 +03:00
|
|
|
funcArgInpObjs = mapMaybe mkFuncArgsInp funcs
|
|
|
|
-- funcArgCtx = Map.unions funcArgCtxs
|
2019-07-22 15:47:13 +03:00
|
|
|
funcArgScalarSet = funcs ^.. folded.to fiInputArgs.folded.to faType
|
2019-01-25 06:31:54 +03:00
|
|
|
|
2018-06-27 16:11:32 +03:00
|
|
|
-- helper
|
2018-10-26 12:02:44 +03:00
|
|
|
mkFldMap ty = Map.fromList . concatMap (mkFld ty)
|
|
|
|
mkFld ty = \case
|
2019-09-19 07:47:36 +03:00
|
|
|
Left ci -> [((ty, pgiName ci), Left ci)]
|
|
|
|
Right (RelationshipFieldInfo relInfo allowAgg cols permFilter permLimit _) ->
|
|
|
|
let relationshipName = riName relInfo
|
|
|
|
relFld = ( (ty, mkRelName relationshipName)
|
|
|
|
, Right $ RelationshipField relInfo False cols permFilter permLimit
|
2018-10-26 12:02:44 +03:00
|
|
|
)
|
2019-09-19 07:47:36 +03:00
|
|
|
aggRelFld = ( (ty, mkAggRelName relationshipName)
|
|
|
|
, Right $ RelationshipField relInfo True cols permFilter permLimit
|
2018-10-26 12:02:44 +03:00
|
|
|
)
|
2019-09-19 07:47:36 +03:00
|
|
|
in case riType relInfo of
|
2018-10-26 12:02:44 +03:00
|
|
|
ObjRel -> [relFld]
|
|
|
|
ArrRel -> bool [relFld] [relFld, aggRelFld] allowAgg
|
|
|
|
|
2018-06-27 16:11:32 +03:00
|
|
|
-- the fields used in bool exp
|
|
|
|
boolExpInpObjFldsM = mkFldMap (mkBoolExpTy tn) <$> selFldsM
|
|
|
|
|
2018-08-30 12:49:21 +03:00
|
|
|
-- mut resp obj
|
|
|
|
mutRespObjM =
|
2018-10-12 15:06:12 +03:00
|
|
|
if isMut
|
2019-03-22 10:08:42 +03:00
|
|
|
then Just $ mkMutRespObj tn $ isJust selFldsM
|
2018-06-27 16:11:32 +03:00
|
|
|
else Nothing
|
|
|
|
|
2019-04-15 10:04:30 +03:00
|
|
|
isMut = (isJust insPermM || isJust updColsM || isJust delPermM)
|
2018-10-12 15:06:12 +03:00
|
|
|
&& any (`isMutable` viM) [viIsInsertable, viIsUpdatable, viIsDeletable]
|
|
|
|
|
2018-06-27 16:11:32 +03:00
|
|
|
-- table obj
|
2019-09-17 04:51:11 +03:00
|
|
|
selObjM = mkTableObj tn descM <$> selFldsM
|
2019-03-07 13:24:07 +03:00
|
|
|
|
2018-12-12 15:58:39 +03:00
|
|
|
-- aggregate objs and order by inputs
|
|
|
|
(aggObjs, aggOrdByInps) = case selPermM of
|
2018-10-26 12:02:44 +03:00
|
|
|
Just (True, selFlds) ->
|
2019-09-19 07:47:36 +03:00
|
|
|
let cols = lefts selFlds
|
|
|
|
numCols = onlyNumCols cols
|
|
|
|
compCols = onlyComparableCols cols
|
2018-12-12 15:58:39 +03:00
|
|
|
objs = [ mkTableAggObj tn
|
2019-08-09 12:19:17 +03:00
|
|
|
, mkTableAggFldsObj tn (numCols, numAggOps) (compCols, compAggOps)
|
2018-12-12 15:58:39 +03:00
|
|
|
] <> mkColAggFldsObjs selFlds
|
2019-08-09 12:19:17 +03:00
|
|
|
ordByInps = mkTabAggOrdByInpObj tn (numCols, numAggOps) (compCols, compAggOps)
|
|
|
|
: mkTabAggOpOrdByInpObjs tn (numCols, numAggOps) (compCols, compAggOps)
|
2018-12-12 15:58:39 +03:00
|
|
|
in (objs, ordByInps)
|
|
|
|
_ -> ([], [])
|
|
|
|
|
2018-10-26 12:02:44 +03:00
|
|
|
getNumCols = onlyNumCols . lefts
|
|
|
|
getCompCols = onlyComparableCols . lefts
|
2018-11-14 15:59:59 +03:00
|
|
|
onlyFloat = const $ mkScalarTy PGFloat
|
|
|
|
|
2019-07-22 15:47:13 +03:00
|
|
|
mkTypeMaker "sum" = mkColumnType
|
2018-11-14 15:59:59 +03:00
|
|
|
mkTypeMaker _ = onlyFloat
|
|
|
|
|
2018-10-26 12:02:44 +03:00
|
|
|
mkColAggFldsObjs flds =
|
|
|
|
let numCols = getNumCols flds
|
|
|
|
compCols = getCompCols flds
|
2018-11-14 15:59:59 +03:00
|
|
|
mkNumObjFld n = mkTableColAggFldsObj tn n (mkTypeMaker n) numCols
|
2019-07-22 15:47:13 +03:00
|
|
|
mkCompObjFld n = mkTableColAggFldsObj tn n mkColumnType compCols
|
2018-11-14 15:59:59 +03:00
|
|
|
numFldsObjs = bool (map mkNumObjFld numAggOps) [] $ null numCols
|
|
|
|
compFldsObjs = bool (map mkCompObjFld compAggOps) [] $ null compCols
|
2018-10-26 12:02:44 +03:00
|
|
|
in numFldsObjs <> compFldsObjs
|
2018-06-27 16:11:32 +03:00
|
|
|
-- the fields used in table object
|
|
|
|
selObjFldsM = mkFldMap (mkTableTy tn) <$> selFldsM
|
2019-02-22 13:27:38 +03:00
|
|
|
-- the scalar set for table_by_pk arguments
|
2019-07-22 15:47:13 +03:00
|
|
|
selByPkScalarSet = pkeyCols ^.. folded.to pgiType._PGColumnScalar
|
2018-06-27 16:11:32 +03:00
|
|
|
|
2018-10-26 14:57:33 +03:00
|
|
|
ordByInpCtxM = mkOrdByInpObj tn <$> selFldsM
|
|
|
|
(ordByInpObjM, ordByCtxM) = case ordByInpCtxM of
|
|
|
|
Just (a, b) -> (Just a, Just b)
|
|
|
|
Nothing -> (Nothing, Nothing)
|
2018-06-27 16:11:32 +03:00
|
|
|
|
2019-07-22 15:47:13 +03:00
|
|
|
tableEnumTypeM = enumValuesM <&> \enumValues ->
|
|
|
|
mkHsraEnumTyInfo Nothing (mkTableEnumType tn) $
|
|
|
|
EnumValuesReference (EnumReference tn enumValues)
|
|
|
|
|
2018-06-27 16:11:32 +03:00
|
|
|
getRootFldsRole'
|
|
|
|
:: QualifiedTable
|
2019-09-19 07:47:36 +03:00
|
|
|
-> [PGColumnInfo]
|
2019-03-22 10:08:42 +03:00
|
|
|
-> [ConstraintName]
|
2019-08-11 18:34:38 +03:00
|
|
|
-> FieldInfoMap PGColumnInfo
|
2019-01-25 06:31:54 +03:00
|
|
|
-> [FunctionInfo]
|
2018-10-05 18:13:51 +03:00
|
|
|
-> Maybe ([T.Text], Bool) -- insert perm
|
2019-04-17 12:48:41 +03:00
|
|
|
-> Maybe (AnnBoolExpPartialSQL, Maybe Int, [T.Text], Bool) -- select filter
|
2019-09-19 07:47:36 +03:00
|
|
|
-> Maybe ([PGColumnInfo], PreSetColsPartial, AnnBoolExpPartialSQL, [T.Text]) -- update filter
|
2019-04-17 12:48:41 +03:00
|
|
|
-> Maybe (AnnBoolExpPartialSQL, [T.Text]) -- delete filter
|
2018-10-12 15:06:12 +03:00
|
|
|
-> Maybe ViewInfo
|
2019-09-19 07:47:36 +03:00
|
|
|
-> TableConfig -- custom config
|
2019-07-23 14:12:59 +03:00
|
|
|
-> RootFields
|
2019-09-19 07:47:36 +03:00
|
|
|
getRootFldsRole' tn primCols constraints fields funcs insM
|
|
|
|
selM updM delM viM tableConfig =
|
2019-07-23 14:12:59 +03:00
|
|
|
RootFields
|
|
|
|
{ rootQueryFields = makeFieldMap
|
|
|
|
$ funcQueries
|
|
|
|
<> funcAggQueries
|
|
|
|
<> catMaybes
|
|
|
|
[ getSelDet <$> selM
|
|
|
|
, getSelAggDet selM
|
2019-09-19 07:47:36 +03:00
|
|
|
, getPKeySelDet selM primCols
|
2019-07-23 14:12:59 +03:00
|
|
|
]
|
|
|
|
, rootMutationFields = makeFieldMap $ catMaybes
|
|
|
|
[ mutHelper viIsInsertable getInsDet insM
|
|
|
|
, mutHelper viIsUpdatable getUpdDet updM
|
|
|
|
, mutHelper viIsDeletable getDelDet delM
|
|
|
|
]
|
|
|
|
}
|
2018-06-27 16:11:32 +03:00
|
|
|
where
|
2019-09-19 07:47:36 +03:00
|
|
|
customRootFields = _tcCustomRootFields tableConfig
|
|
|
|
colGNameMap = mkPGColGNameMap $ getValidCols fields
|
|
|
|
|
2019-07-23 14:12:59 +03:00
|
|
|
makeFieldMap = mapFromL (_fiName . snd)
|
2019-03-22 10:08:42 +03:00
|
|
|
allCols = getCols fields
|
2019-01-25 06:31:54 +03:00
|
|
|
funcQueries = maybe [] getFuncQueryFlds selM
|
|
|
|
funcAggQueries = maybe [] getFuncAggQueryFlds selM
|
|
|
|
|
2018-12-13 10:26:15 +03:00
|
|
|
mutHelper :: (ViewInfo -> Bool) -> (a -> b) -> Maybe a -> Maybe b
|
2018-10-12 15:06:12 +03:00
|
|
|
mutHelper f getDet mutM =
|
|
|
|
bool Nothing (getDet <$> mutM) $ isMutable f viM
|
2018-12-13 10:26:15 +03:00
|
|
|
|
2019-09-19 07:47:36 +03:00
|
|
|
getCustomNameWith f = f customRootFields
|
|
|
|
|
|
|
|
insCustName = getCustomNameWith _tcrfInsert
|
2018-10-05 18:13:51 +03:00
|
|
|
getInsDet (hdrs, upsertPerm) =
|
2019-03-22 10:08:42 +03:00
|
|
|
let isUpsertable = upsertable constraints upsertPerm $ isJust viM
|
2019-09-19 07:47:36 +03:00
|
|
|
in ( MCInsert $ InsOpCtx tn $ hdrs `union` maybe [] (\(_, _, _, x) -> x) updM
|
|
|
|
, mkInsMutFld insCustName tn isUpsertable
|
2018-10-05 18:13:51 +03:00
|
|
|
)
|
2018-12-13 10:26:15 +03:00
|
|
|
|
2019-09-19 07:47:36 +03:00
|
|
|
updCustName = getCustomNameWith _tcrfUpdate
|
2019-02-11 15:45:30 +03:00
|
|
|
getUpdDet (updCols, preSetCols, updFltr, hdrs) =
|
2019-09-19 07:47:36 +03:00
|
|
|
( MCUpdate $ UpdOpCtx tn hdrs colGNameMap updFltr preSetCols
|
|
|
|
, mkUpdMutFld updCustName tn updCols
|
2018-07-20 13:51:20 +03:00
|
|
|
)
|
2019-02-11 15:45:30 +03:00
|
|
|
|
2019-09-19 07:47:36 +03:00
|
|
|
delCustName = getCustomNameWith _tcrfDelete
|
2018-06-27 16:11:32 +03:00
|
|
|
getDelDet (delFltr, hdrs) =
|
2019-07-23 14:12:59 +03:00
|
|
|
( MCDelete $ DelOpCtx tn hdrs delFltr allCols
|
2019-09-19 07:47:36 +03:00
|
|
|
, mkDelMutFld delCustName tn
|
2019-02-22 13:27:38 +03:00
|
|
|
)
|
2019-09-19 07:47:36 +03:00
|
|
|
|
|
|
|
|
|
|
|
selCustName = getCustomNameWith _tcrfSelect
|
2018-10-26 12:02:44 +03:00
|
|
|
getSelDet (selFltr, pLimit, hdrs, _) =
|
2019-09-19 07:47:36 +03:00
|
|
|
selFldHelper QCSelect (mkSelFld selCustName) selFltr pLimit hdrs
|
2018-06-27 16:11:32 +03:00
|
|
|
|
2019-09-19 07:47:36 +03:00
|
|
|
selAggCustName = getCustomNameWith _tcrfSelectAggregate
|
2019-02-22 13:27:38 +03:00
|
|
|
getSelAggDet (Just (selFltr, pLimit, hdrs, True)) =
|
2019-09-19 07:47:36 +03:00
|
|
|
Just $ selFldHelper QCSelectAgg (mkAggSelFld selAggCustName)
|
|
|
|
selFltr pLimit hdrs
|
2019-02-22 13:27:38 +03:00
|
|
|
getSelAggDet _ = Nothing
|
|
|
|
|
|
|
|
selFldHelper f g pFltr pLimit hdrs =
|
2019-09-19 07:47:36 +03:00
|
|
|
( f $ SelOpCtx tn hdrs colGNameMap pFltr pLimit
|
2019-07-23 14:12:59 +03:00
|
|
|
, g tn
|
2019-02-22 13:27:38 +03:00
|
|
|
)
|
2018-10-26 12:02:44 +03:00
|
|
|
|
2019-09-19 07:47:36 +03:00
|
|
|
selByPkCustName = getCustomNameWith _tcrfSelectByPk
|
2018-08-27 17:17:03 +03:00
|
|
|
getPKeySelDet Nothing _ = Nothing
|
|
|
|
getPKeySelDet _ [] = Nothing
|
2018-10-26 12:02:44 +03:00
|
|
|
getPKeySelDet (Just (selFltr, _, hdrs, _)) pCols = Just
|
2019-09-19 07:47:36 +03:00
|
|
|
( QCSelectPkey . SelPkOpCtx tn hdrs selFltr $ mkPGColGNameMap pCols
|
|
|
|
, mkSelFldPKey selByPkCustName tn pCols
|
2019-02-22 13:27:38 +03:00
|
|
|
)
|
2018-08-27 17:17:03 +03:00
|
|
|
|
2019-01-25 06:31:54 +03:00
|
|
|
getFuncQueryFlds (selFltr, pLimit, hdrs, _) =
|
2019-07-23 14:12:59 +03:00
|
|
|
funcFldHelper QCFuncQuery mkFuncQueryFld selFltr pLimit hdrs
|
2019-01-25 06:31:54 +03:00
|
|
|
|
|
|
|
getFuncAggQueryFlds (selFltr, pLimit, hdrs, True) =
|
2019-07-23 14:12:59 +03:00
|
|
|
funcFldHelper QCFuncAggQuery mkFuncAggQueryFld selFltr pLimit hdrs
|
2019-02-22 13:27:38 +03:00
|
|
|
getFuncAggQueryFlds _ = []
|
|
|
|
|
|
|
|
funcFldHelper f g pFltr pLimit hdrs =
|
2019-01-25 06:31:54 +03:00
|
|
|
flip map funcs $ \fi ->
|
2019-09-19 07:47:36 +03:00
|
|
|
( f . FuncQOpCtx tn hdrs colGNameMap pFltr pLimit (fiName fi) $ mkFuncArgItemSeq fi
|
2019-09-17 04:51:11 +03:00
|
|
|
, g fi $ fiDescription fi
|
2019-02-22 13:27:38 +03:00
|
|
|
)
|
|
|
|
|
2019-08-28 22:27:15 +03:00
|
|
|
mkFuncArgItemSeq fi = Seq.fromList $ procFuncArgs (fiInputArgs fi)
|
|
|
|
$ \fa t -> FuncArgItem (G.Name t) (faName fa) (faHasDefault fa)
|
2019-01-25 06:31:54 +03:00
|
|
|
|
|
|
|
|
2019-08-11 18:34:38 +03:00
|
|
|
getSelPermission :: TableInfo PGColumnInfo -> RoleName -> Maybe SelPermInfo
|
2018-10-26 14:57:33 +03:00
|
|
|
getSelPermission tabInfo role =
|
2019-07-22 15:47:13 +03:00
|
|
|
Map.lookup role (_tiRolePermInfoMap tabInfo) >>= _permSel
|
2018-10-26 14:57:33 +03:00
|
|
|
|
2018-10-26 12:02:44 +03:00
|
|
|
getSelPerm
|
2018-06-27 16:11:32 +03:00
|
|
|
:: (MonadError QErr m)
|
2019-08-11 18:34:38 +03:00
|
|
|
=> TableCache PGColumnInfo
|
2018-06-27 16:11:32 +03:00
|
|
|
-- all the fields of a table
|
2019-08-11 18:34:38 +03:00
|
|
|
-> FieldInfoMap PGColumnInfo
|
2018-06-27 16:11:32 +03:00
|
|
|
-- role and its permission
|
|
|
|
-> RoleName -> SelPermInfo
|
2018-10-26 12:02:44 +03:00
|
|
|
-> m (Bool, [SelField])
|
|
|
|
getSelPerm tableCache fields role selPermInfo = do
|
2019-09-19 07:47:36 +03:00
|
|
|
|
|
|
|
relFlds <- fmap catMaybes $ forM validRels $ \relInfo -> do
|
2018-10-05 18:13:51 +03:00
|
|
|
remTableInfo <- getTabInfo tableCache $ riRTable relInfo
|
2018-10-26 14:57:33 +03:00
|
|
|
let remTableSelPermM = getSelPermission remTableInfo role
|
2019-09-19 07:47:36 +03:00
|
|
|
remTableFlds = _tiFieldInfoMap remTableInfo
|
|
|
|
remTableColGNameMap =
|
|
|
|
mkPGColGNameMap $ getValidCols remTableFlds
|
2018-08-06 15:15:08 +03:00
|
|
|
return $ flip fmap remTableSelPermM $
|
2019-09-19 07:47:36 +03:00
|
|
|
\rmSelPermM -> Right RelationshipFieldInfo
|
|
|
|
{ _rfiInfo = relInfo
|
|
|
|
, _rfiAllowAgg = spiAllowAgg rmSelPermM
|
|
|
|
, _rfiColumns = remTableColGNameMap
|
|
|
|
, _rfiPermFilter = spiFilter rmSelPermM
|
|
|
|
, _rfiPermLimit = spiLimit rmSelPermM
|
|
|
|
, _rfiIsNullable = isRelNullable fields relInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
return (spiAllowAgg selPermInfo, cols <> relFlds)
|
2018-06-27 16:11:32 +03:00
|
|
|
where
|
2019-09-19 07:47:36 +03:00
|
|
|
validRels = getValidRels fields
|
|
|
|
validCols = getValidCols fields
|
|
|
|
cols = catMaybes $ flip map validCols $
|
|
|
|
\colInfo -> fmap Left $ bool Nothing (Just colInfo) $
|
|
|
|
Set.member (pgiColumn colInfo) allowedCols
|
|
|
|
|
2018-06-27 16:11:32 +03:00
|
|
|
allowedCols = spiCols selPermInfo
|
2018-10-05 18:13:51 +03:00
|
|
|
|
|
|
|
mkInsCtx
|
|
|
|
:: MonadError QErr m
|
|
|
|
=> RoleName
|
2019-08-11 18:34:38 +03:00
|
|
|
-> TableCache PGColumnInfo
|
|
|
|
-> FieldInfoMap PGColumnInfo
|
2019-03-07 13:24:07 +03:00
|
|
|
-> InsPermInfo
|
|
|
|
-> Maybe UpdPermInfo
|
|
|
|
-> m InsCtx
|
2019-03-22 10:08:42 +03:00
|
|
|
mkInsCtx role tableCache fields insPermInfo updPermM = do
|
2018-10-05 18:13:51 +03:00
|
|
|
relTupsM <- forM rels $ \relInfo -> do
|
|
|
|
let remoteTable = riRTable relInfo
|
|
|
|
relName = riName relInfo
|
|
|
|
remoteTableInfo <- getTabInfo tableCache remoteTable
|
2018-10-16 13:25:41 +03:00
|
|
|
let insPermM = getInsPerm remoteTableInfo role
|
2019-07-22 15:47:13 +03:00
|
|
|
viewInfoM = _tiViewInfo remoteTableInfo
|
2018-10-16 13:25:41 +03:00
|
|
|
return $ bool Nothing (Just (relName, relInfo)) $
|
2019-04-15 10:04:30 +03:00
|
|
|
isInsertable insPermM viewInfoM && isValidRel relName remoteTable
|
2018-10-05 18:13:51 +03:00
|
|
|
|
|
|
|
let relInfoMap = Map.fromList $ catMaybes relTupsM
|
2019-09-19 07:47:36 +03:00
|
|
|
return $ InsCtx iView gNamePGColMap setCols relInfoMap updPermForIns
|
2018-10-05 18:13:51 +03:00
|
|
|
where
|
2019-09-19 07:47:36 +03:00
|
|
|
gNamePGColMap = mkPGColGNameMap allCols
|
2019-04-15 10:04:30 +03:00
|
|
|
allCols = getCols fields
|
2018-10-26 14:57:33 +03:00
|
|
|
rels = getValidRels fields
|
2018-10-05 18:13:51 +03:00
|
|
|
iView = ipiView insPermInfo
|
2018-10-26 17:58:20 +03:00
|
|
|
setCols = ipiSet insPermInfo
|
2018-12-15 19:10:29 +03:00
|
|
|
updPermForIns = mkUpdPermForIns <$> updPermM
|
2019-02-23 13:36:42 +03:00
|
|
|
mkUpdPermForIns upi = UpdPermForIns (toList $ upiCols upi)
|
|
|
|
(upiFilter upi) (upiSet upi)
|
2018-10-05 18:13:51 +03:00
|
|
|
|
2018-10-16 13:25:41 +03:00
|
|
|
isInsertable Nothing _ = False
|
|
|
|
isInsertable (Just _) viewInfoM = isMutable viIsInsertable viewInfoM
|
|
|
|
|
|
|
|
mkAdminInsCtx
|
|
|
|
:: MonadError QErr m
|
2019-03-07 13:24:07 +03:00
|
|
|
=> QualifiedTable
|
2019-08-11 18:34:38 +03:00
|
|
|
-> TableCache PGColumnInfo
|
|
|
|
-> FieldInfoMap PGColumnInfo
|
2019-03-07 13:24:07 +03:00
|
|
|
-> m InsCtx
|
2019-03-22 10:08:42 +03:00
|
|
|
mkAdminInsCtx tn tc fields = do
|
2018-10-16 13:25:41 +03:00
|
|
|
relTupsM <- forM rels $ \relInfo -> do
|
|
|
|
let remoteTable = riRTable relInfo
|
|
|
|
relName = riName relInfo
|
|
|
|
remoteTableInfo <- getTabInfo tc remoteTable
|
2019-07-22 15:47:13 +03:00
|
|
|
let viewInfoM = _tiViewInfo remoteTableInfo
|
2018-10-16 13:25:41 +03:00
|
|
|
return $ bool Nothing (Just (relName, relInfo)) $
|
2019-04-15 10:04:30 +03:00
|
|
|
isMutable viIsInsertable viewInfoM && isValidRel relName remoteTable
|
2018-10-16 13:25:41 +03:00
|
|
|
|
2018-12-15 19:10:29 +03:00
|
|
|
let relInfoMap = Map.fromList $ catMaybes relTupsM
|
2019-04-15 10:04:30 +03:00
|
|
|
updPerm = UpdPermForIns updCols noFilter Map.empty
|
2018-12-15 19:10:29 +03:00
|
|
|
|
2019-09-19 07:47:36 +03:00
|
|
|
return $ InsCtx tn colGNameMap Map.empty relInfoMap (Just updPerm)
|
2018-10-05 18:13:51 +03:00
|
|
|
where
|
2019-04-15 10:04:30 +03:00
|
|
|
allCols = getCols fields
|
2019-09-19 07:47:36 +03:00
|
|
|
colGNameMap = mkPGColGNameMap allCols
|
|
|
|
updCols = map pgiColumn allCols
|
2018-10-26 14:57:33 +03:00
|
|
|
rels = getValidRels fields
|
2018-06-27 16:11:32 +03:00
|
|
|
|
2019-09-19 07:47:36 +03:00
|
|
|
mkAdminSelFlds
|
|
|
|
:: MonadError QErr m
|
|
|
|
=> FieldInfoMap PGColumnInfo
|
|
|
|
-> TableCache PGColumnInfo
|
|
|
|
-> m [SelField]
|
|
|
|
mkAdminSelFlds fields tableCache = do
|
|
|
|
relSelFlds <- forM validRels $ \relInfo -> do
|
|
|
|
let remoteTable = riRTable relInfo
|
|
|
|
remoteTableInfo <- getTabInfo tableCache remoteTable
|
|
|
|
let remoteTableFlds = _tiFieldInfoMap remoteTableInfo
|
|
|
|
remoteTableColGNameMap =
|
|
|
|
mkPGColGNameMap $ getValidCols remoteTableFlds
|
|
|
|
return $ Right RelationshipFieldInfo
|
|
|
|
{ _rfiInfo = relInfo
|
|
|
|
, _rfiAllowAgg = True
|
|
|
|
, _rfiColumns = remoteTableColGNameMap
|
|
|
|
, _rfiPermFilter = noFilter
|
|
|
|
, _rfiPermLimit = Nothing
|
|
|
|
, _rfiIsNullable = isRelNullable fields relInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
return $ colSelFlds <> relSelFlds
|
|
|
|
where
|
|
|
|
cols = getValidCols fields
|
|
|
|
colSelFlds = map Left cols
|
|
|
|
validRels = getValidRels fields
|
|
|
|
|
2018-06-27 16:11:32 +03:00
|
|
|
mkGCtxRole
|
|
|
|
:: (MonadError QErr m)
|
2019-08-11 18:34:38 +03:00
|
|
|
=> TableCache PGColumnInfo
|
2018-06-27 16:11:32 +03:00
|
|
|
-> QualifiedTable
|
2019-09-17 04:51:11 +03:00
|
|
|
-> Maybe PGDescription
|
2019-08-11 18:34:38 +03:00
|
|
|
-> FieldInfoMap PGColumnInfo
|
2019-09-19 07:47:36 +03:00
|
|
|
-> [PGColumnInfo]
|
2019-03-22 10:08:42 +03:00
|
|
|
-> [ConstraintName]
|
2019-01-25 06:31:54 +03:00
|
|
|
-> [FunctionInfo]
|
2018-10-12 15:06:12 +03:00
|
|
|
-> Maybe ViewInfo
|
2019-07-22 15:47:13 +03:00
|
|
|
-> Maybe EnumValues
|
2019-09-19 07:47:36 +03:00
|
|
|
-> TableConfig
|
2018-06-27 16:11:32 +03:00
|
|
|
-> RoleName
|
|
|
|
-> RolePermInfo
|
2019-07-23 14:12:59 +03:00
|
|
|
-> m (TyAgg, RootFields, InsCtxMap)
|
2019-09-19 07:47:36 +03:00
|
|
|
mkGCtxRole tableCache tn descM fields pColInfos constraints funcs viM
|
|
|
|
enumValuesM tabConfigM role permInfo = do
|
2018-10-26 12:02:44 +03:00
|
|
|
selPermM <- mapM (getSelPerm tableCache fields role) $ _permSel permInfo
|
2019-04-15 10:04:30 +03:00
|
|
|
tabInsInfoM <- forM (_permIns permInfo) $ \ipi -> do
|
|
|
|
ctx <- mkInsCtx role tableCache fields ipi $ _permUpd permInfo
|
2019-09-19 07:47:36 +03:00
|
|
|
let permCols = flip getColInfos allCols $ Set.toList $ ipiCols ipi
|
2019-04-15 10:04:30 +03:00
|
|
|
return (ctx, (permCols, icRelations ctx))
|
|
|
|
let insPermM = snd <$> tabInsInfoM
|
|
|
|
insCtxM = fst <$> tabInsInfoM
|
2019-09-19 07:47:36 +03:00
|
|
|
updColsM = filterColFlds . upiCols <$> _permUpd permInfo
|
2019-09-17 04:51:11 +03:00
|
|
|
tyAgg = mkGCtxRole' tn descM insPermM selPermM updColsM
|
2019-07-22 15:47:13 +03:00
|
|
|
(void $ _permDel permInfo) pColInfos constraints viM funcs enumValuesM
|
2019-09-19 07:47:36 +03:00
|
|
|
rootFlds = getRootFldsRole tn pColInfos constraints fields funcs
|
|
|
|
viM permInfo tabConfigM
|
2019-04-15 10:04:30 +03:00
|
|
|
insCtxMap = maybe Map.empty (Map.singleton tn) insCtxM
|
2018-10-05 18:13:51 +03:00
|
|
|
return (tyAgg, rootFlds, insCtxMap)
|
2018-06-27 16:11:32 +03:00
|
|
|
where
|
2019-03-07 13:24:07 +03:00
|
|
|
allCols = getCols fields
|
2019-09-19 07:47:36 +03:00
|
|
|
cols = getValidCols fields
|
|
|
|
filterColFlds allowedSet =
|
|
|
|
filter ((`Set.member` allowedSet) . pgiColumn) cols
|
2018-06-27 16:11:32 +03:00
|
|
|
|
|
|
|
getRootFldsRole
|
|
|
|
:: QualifiedTable
|
2019-09-19 07:47:36 +03:00
|
|
|
-> [PGColumnInfo]
|
2019-03-22 10:08:42 +03:00
|
|
|
-> [ConstraintName]
|
2019-08-11 18:34:38 +03:00
|
|
|
-> FieldInfoMap PGColumnInfo
|
2019-01-25 06:31:54 +03:00
|
|
|
-> [FunctionInfo]
|
2018-10-12 15:06:12 +03:00
|
|
|
-> Maybe ViewInfo
|
2018-06-27 16:11:32 +03:00
|
|
|
-> RolePermInfo
|
2019-09-19 07:47:36 +03:00
|
|
|
-> TableConfig
|
2019-07-23 14:12:59 +03:00
|
|
|
-> RootFields
|
2019-09-19 07:47:36 +03:00
|
|
|
getRootFldsRole tn pCols constraints fields funcs viM (RolePermInfo insM selM updM delM)=
|
2019-01-25 06:31:54 +03:00
|
|
|
getRootFldsRole' tn pCols constraints fields funcs
|
2018-06-27 16:11:32 +03:00
|
|
|
(mkIns <$> insM) (mkSel <$> selM)
|
2019-09-19 07:47:36 +03:00
|
|
|
(mkUpd <$> updM) (mkDel <$> delM) viM
|
2018-06-27 16:11:32 +03:00
|
|
|
where
|
2018-12-15 19:10:29 +03:00
|
|
|
mkIns i = (ipiRequiredHeaders i, isJust updM)
|
2018-10-26 12:02:44 +03:00
|
|
|
mkSel s = ( spiFilter s, spiLimit s
|
|
|
|
, spiRequiredHeaders s, spiAllowAgg s
|
|
|
|
)
|
2019-09-19 07:47:36 +03:00
|
|
|
mkUpd u = ( flip getColInfos allCols $ Set.toList $ upiCols u
|
2019-02-11 15:45:30 +03:00
|
|
|
, upiSet u
|
2018-07-20 13:51:20 +03:00
|
|
|
, upiFilter u
|
|
|
|
, upiRequiredHeaders u
|
|
|
|
)
|
2018-06-27 16:11:32 +03:00
|
|
|
mkDel d = (dpiFilter d, dpiRequiredHeaders d)
|
|
|
|
|
2019-09-19 07:47:36 +03:00
|
|
|
allCols = getCols fields
|
|
|
|
|
2018-06-27 16:11:32 +03:00
|
|
|
mkGCtxMapTable
|
|
|
|
:: (MonadError QErr m)
|
2019-08-11 18:34:38 +03:00
|
|
|
=> TableCache PGColumnInfo
|
2019-01-25 06:31:54 +03:00
|
|
|
-> FunctionCache
|
2019-08-11 18:34:38 +03:00
|
|
|
-> TableInfo PGColumnInfo
|
2019-07-23 14:12:59 +03:00
|
|
|
-> m (Map.HashMap RoleName (TyAgg, RootFields, InsCtxMap))
|
2019-03-07 13:24:07 +03:00
|
|
|
mkGCtxMapTable tableCache funcCache tabInfo = do
|
2019-09-19 07:47:36 +03:00
|
|
|
m <- flip Map.traverseWithKey rolePerms $
|
|
|
|
mkGCtxRole tableCache tn descM fields pkeyColInfos validConstraints
|
|
|
|
tabFuncs viewInfo enumValues customConfig
|
2019-03-22 10:08:42 +03:00
|
|
|
adminInsCtx <- mkAdminInsCtx tn tableCache fields
|
2019-09-19 07:47:36 +03:00
|
|
|
adminSelFlds <- mkAdminSelFlds fields tableCache
|
|
|
|
let adminCtx = mkGCtxRole' tn descM (Just (cols, icRelations adminInsCtx))
|
|
|
|
(Just (True, adminSelFlds)) (Just cols) (Just ())
|
2019-07-22 15:47:13 +03:00
|
|
|
pkeyColInfos validConstraints viewInfo tabFuncs enumValues
|
2018-10-05 18:13:51 +03:00
|
|
|
adminInsCtxMap = Map.singleton tn adminInsCtx
|
|
|
|
return $ Map.insert adminRole (adminCtx, adminRootFlds, adminInsCtxMap) m
|
2018-06-27 16:11:32 +03:00
|
|
|
where
|
2019-09-19 07:47:36 +03:00
|
|
|
TableInfo tn descM _ fields rolePerms constraints
|
|
|
|
pkeyCols viewInfo _ enumValues customConfig = tabInfo
|
2018-07-27 12:50:12 +03:00
|
|
|
validConstraints = mkValidConstraints constraints
|
2019-09-19 07:47:36 +03:00
|
|
|
cols = getValidCols fields
|
|
|
|
colInfos = getCols fields
|
2018-08-27 17:17:03 +03:00
|
|
|
pkeyColInfos = getColInfos pkeyCols colInfos
|
2019-02-22 13:27:38 +03:00
|
|
|
tabFuncs = filter (isValidObjectName . fiName) $
|
|
|
|
getFuncsOfTable tn funcCache
|
2018-06-27 16:11:32 +03:00
|
|
|
adminRootFlds =
|
2019-09-19 07:47:36 +03:00
|
|
|
getRootFldsRole' tn pkeyColInfos validConstraints fields tabFuncs
|
2018-10-26 12:02:44 +03:00
|
|
|
(Just ([], True)) (Just (noFilter, Nothing, [], True))
|
2019-09-19 07:47:36 +03:00
|
|
|
(Just (cols, mempty, noFilter, [])) (Just (noFilter, []))
|
|
|
|
viewInfo customConfig
|
2018-06-27 16:11:32 +03:00
|
|
|
|
2019-04-17 12:48:41 +03:00
|
|
|
noFilter :: AnnBoolExpPartialSQL
|
2018-11-16 15:40:23 +03:00
|
|
|
noFilter = annBoolExpTrue
|
2018-10-26 14:57:33 +03:00
|
|
|
|
2018-06-27 16:11:32 +03:00
|
|
|
mkGCtxMap
|
|
|
|
:: (MonadError QErr m)
|
2019-08-11 18:34:38 +03:00
|
|
|
=> TableCache PGColumnInfo -> FunctionCache -> m GCtxMap
|
2019-01-25 06:31:54 +03:00
|
|
|
mkGCtxMap tableCache functionCache = do
|
|
|
|
typesMapL <- mapM (mkGCtxMapTable tableCache functionCache) $
|
2018-07-27 12:50:12 +03:00
|
|
|
filter tableFltr $ Map.elems tableCache
|
2019-09-19 07:47:36 +03:00
|
|
|
-- since root field names are customisable, we need to check for
|
|
|
|
-- duplicate root field names across all tables
|
|
|
|
duplicateRootFlds <- (duplicates . concat) <$> forM typesMapL getRootFlds
|
|
|
|
unless (null duplicateRootFlds) $
|
|
|
|
throw400 Unexpected $ "following root fields are duplicated: "
|
|
|
|
<> showNames duplicateRootFlds
|
2018-06-27 16:11:32 +03:00
|
|
|
let typesMap = foldr (Map.unionWith mappend) Map.empty typesMapL
|
2018-10-05 18:13:51 +03:00
|
|
|
return $ flip Map.map typesMap $ \(ty, flds, insCtxMap) ->
|
|
|
|
mkGCtx ty flds insCtxMap
|
2018-07-27 12:50:12 +03:00
|
|
|
where
|
2019-07-22 15:47:13 +03:00
|
|
|
tableFltr ti = not (_tiSystemDefined ti)
|
|
|
|
&& isValidObjectName (_tiName ti)
|
2018-06-27 16:11:32 +03:00
|
|
|
|
2019-09-19 07:47:36 +03:00
|
|
|
getRootFlds roleMap = do
|
|
|
|
(_, RootFields query mutation, _) <- onNothing
|
|
|
|
(Map.lookup adminRole roleMap) $ throw500 "admin schema not found"
|
|
|
|
return $ Map.keys query <> Map.keys mutation
|
|
|
|
|
2019-07-08 08:51:41 +03:00
|
|
|
-- | build GraphQL schema from postgres tables and functions
|
|
|
|
buildGCtxMapPG
|
|
|
|
:: (QErrM m, CacheRWM m)
|
|
|
|
=> m ()
|
|
|
|
buildGCtxMapPG = do
|
|
|
|
sc <- askSchemaCache
|
2019-04-17 19:29:39 +03:00
|
|
|
gCtxMap <- mkGCtxMap (scTables sc) (scFunctions sc)
|
2019-07-08 08:51:41 +03:00
|
|
|
writeSchemaCache sc {scGCtxMap = gCtxMap}
|
2018-06-27 16:11:32 +03:00
|
|
|
|
2018-11-23 16:02:46 +03:00
|
|
|
getGCtx :: (CacheRM m) => RoleName -> GCtxMap -> m GCtx
|
|
|
|
getGCtx rn ctxMap = do
|
|
|
|
sc <- askSchemaCache
|
|
|
|
return $ fromMaybe (scDefaultRemoteGCtx sc) $ Map.lookup rn ctxMap
|
|
|
|
|
|
|
|
-- pretty print GCtx
|
2018-12-12 15:01:18 +03:00
|
|
|
ppGCtx :: GCtx -> String
|
|
|
|
ppGCtx gCtx =
|
|
|
|
"GCtx ["
|
|
|
|
<> "\n types = " <> show types
|
|
|
|
<> "\n query root = " <> show qRoot
|
|
|
|
<> "\n mutation root = " <> show mRoot
|
|
|
|
<> "\n subscription root = " <> show sRoot
|
|
|
|
<> "\n]"
|
|
|
|
|
|
|
|
where
|
|
|
|
types = map (G.unName . G.unNamedType) $ Map.keys $ _gTypes gCtx
|
|
|
|
qRoot = (,) (_otiName qRootO) $
|
|
|
|
map G.unName $ Map.keys $ _otiFields qRootO
|
|
|
|
mRoot = (,) (_otiName <$> mRootO) $
|
|
|
|
maybe [] (map G.unName . Map.keys . _otiFields) mRootO
|
|
|
|
sRoot = (,) (_otiName <$> sRootO) $
|
|
|
|
maybe [] (map G.unName . Map.keys . _otiFields) sRootO
|
|
|
|
qRootO = _gQueryRoot gCtx
|
|
|
|
mRootO = _gMutRoot gCtx
|
|
|
|
sRootO = _gSubRoot gCtx
|
2019-08-09 12:19:17 +03:00
|
|
|
|
2019-07-23 14:12:59 +03:00
|
|
|
-- | A /types aggregate/, which holds role-specific information about visible GraphQL types.
|
|
|
|
-- Importantly, it holds more than just the information needed by GraphQL: it also includes how the
|
|
|
|
-- GraphQL types relate to Postgres types, which is used to validate literals provided for
|
|
|
|
-- Postgres-specific scalars.
|
2019-08-09 12:19:17 +03:00
|
|
|
data TyAgg
|
|
|
|
= TyAgg
|
|
|
|
{ _taTypes :: !TypeMap
|
|
|
|
, _taFields :: !FieldMap
|
2019-08-06 18:27:35 +03:00
|
|
|
, _taScalars :: !(Set.HashSet PGScalarType)
|
2019-08-09 12:19:17 +03:00
|
|
|
, _taOrdBy :: !OrdByCtx
|
|
|
|
} deriving (Show, Eq)
|
|
|
|
|
|
|
|
instance Semigroup TyAgg where
|
|
|
|
(TyAgg t1 f1 s1 o1) <> (TyAgg t2 f2 s2 o2) =
|
|
|
|
TyAgg (Map.union t1 t2) (Map.union f1 f2)
|
|
|
|
(Set.union s1 s2) (Map.union o1 o2)
|
|
|
|
|
|
|
|
instance Monoid TyAgg where
|
|
|
|
mempty = TyAgg Map.empty Map.empty Set.empty Map.empty
|
|
|
|
mappend = (<>)
|
|
|
|
|
2019-07-23 14:12:59 +03:00
|
|
|
-- | A role-specific mapping from root field names to allowed operations.
|
|
|
|
data RootFields
|
|
|
|
= RootFields
|
2019-08-28 22:27:15 +03:00
|
|
|
{ rootQueryFields :: !(Map.HashMap G.Name (QueryCtx, ObjFldInfo))
|
2019-07-23 14:12:59 +03:00
|
|
|
, rootMutationFields :: !(Map.HashMap G.Name (MutationCtx, ObjFldInfo))
|
2019-08-09 12:19:17 +03:00
|
|
|
} deriving (Show, Eq)
|
|
|
|
|
2019-07-23 14:12:59 +03:00
|
|
|
instance Semigroup RootFields where
|
|
|
|
RootFields a1 b1 <> RootFields a2 b2
|
|
|
|
= RootFields (Map.union a1 a2) (Map.union b1 b2)
|
2019-08-09 12:19:17 +03:00
|
|
|
|
2019-07-23 14:12:59 +03:00
|
|
|
instance Monoid RootFields where
|
|
|
|
mempty = RootFields Map.empty Map.empty
|
2019-08-09 12:19:17 +03:00
|
|
|
mappend = (<>)
|
|
|
|
|
2019-07-23 14:12:59 +03:00
|
|
|
mkGCtx :: TyAgg -> RootFields -> InsCtxMap -> GCtx
|
|
|
|
mkGCtx tyAgg (RootFields queryFields mutationFields) insCtxMap =
|
2019-08-09 12:19:17 +03:00
|
|
|
let queryRoot = mkQueryRootTyInfo qFlds
|
|
|
|
scalarTys = map (TIScalar . mkHsraScalarTyInfo) (Set.toList allScalarTypes)
|
|
|
|
compTys = map (TIInpObj . mkCompExpInp) (Set.toList allComparableTypes)
|
|
|
|
ordByEnumTyM = bool (Just ordByEnumTy) Nothing $ null qFlds
|
|
|
|
allTys = Map.union tyInfos $ mkTyInfoMap $
|
|
|
|
catMaybes [ Just $ TIObj queryRoot
|
|
|
|
, TIObj <$> mutRootM
|
|
|
|
, TIObj <$> subRootM
|
|
|
|
, TIEnum <$> ordByEnumTyM
|
|
|
|
] <>
|
|
|
|
scalarTys <> compTys <> defaultTypes <> wiredInGeoInputTypes
|
2019-08-29 16:07:05 +03:00
|
|
|
<> wiredInRastInputTypes
|
2019-08-09 12:19:17 +03:00
|
|
|
-- for now subscription root is query root
|
2019-07-23 14:12:59 +03:00
|
|
|
in GCtx allTys fldInfos queryRoot mutRootM subRootM ordByEnums
|
|
|
|
(Map.map fst queryFields) (Map.map fst mutationFields) insCtxMap
|
2019-08-09 12:19:17 +03:00
|
|
|
where
|
|
|
|
TyAgg tyInfos fldInfos scalars ordByEnums = tyAgg
|
|
|
|
colTys = Set.fromList $ map pgiType $ lefts $ Map.elems fldInfos
|
|
|
|
mkMutRoot =
|
|
|
|
mkHsraObjTyInfo (Just "mutation root") (G.NamedType "mutation_root") Set.empty .
|
|
|
|
mapFromL _fiName
|
|
|
|
mutRootM = bool (Just $ mkMutRoot mFlds) Nothing $ null mFlds
|
|
|
|
mkSubRoot =
|
|
|
|
mkHsraObjTyInfo (Just "subscription root")
|
|
|
|
(G.NamedType "subscription_root") Set.empty . mapFromL _fiName
|
|
|
|
subRootM = bool (Just $ mkSubRoot qFlds) Nothing $ null qFlds
|
2019-07-23 14:12:59 +03:00
|
|
|
|
|
|
|
qFlds = rootFieldInfos queryFields
|
|
|
|
mFlds = rootFieldInfos mutationFields
|
|
|
|
rootFieldInfos = map snd . Map.elems
|
2019-08-09 12:19:17 +03:00
|
|
|
|
2019-07-22 15:47:13 +03:00
|
|
|
anyGeoTypes = any (isScalarColumnWhere isGeoType) colTys
|
2019-08-09 12:19:17 +03:00
|
|
|
allComparableTypes =
|
|
|
|
if anyGeoTypes
|
|
|
|
-- due to casting, we need to generate both geometry and geography
|
|
|
|
-- operations even if just one of the two appears in the schema
|
2019-07-22 15:47:13 +03:00
|
|
|
then Set.union (Set.fromList [PGColumnScalar PGGeometry, PGColumnScalar PGGeography]) colTys
|
2019-08-09 12:19:17 +03:00
|
|
|
else colTys
|
2019-08-29 16:07:05 +03:00
|
|
|
|
|
|
|
additionalScalars =
|
|
|
|
Set.fromList
|
|
|
|
-- raster comparison expression needs geometry input
|
|
|
|
(guard anyRasterTypes *> pure PGGeometry)
|
|
|
|
|
|
|
|
allScalarTypes = (allComparableTypes ^.. folded._PGColumnScalar)
|
|
|
|
<> additionalScalars <> scalars
|
2019-08-09 12:19:17 +03:00
|
|
|
|
|
|
|
wiredInGeoInputTypes = guard anyGeoTypes *> map TIInpObj geoInputTypes
|
2019-08-29 16:07:05 +03:00
|
|
|
|
|
|
|
anyRasterTypes = any (isScalarColumnWhere (== PGRaster)) colTys
|
|
|
|
wiredInRastInputTypes = guard anyRasterTypes *>
|
|
|
|
map TIInpObj rasterIntersectsInputTypes
|