2019-05-16 09:13:25 +03:00
|
|
|
module Hasura.RQL.DDL.QueryCollection
|
|
|
|
( runCreateCollection,
|
2022-08-19 16:36:02 +03:00
|
|
|
runRenameCollection,
|
2019-05-16 09:13:25 +03:00
|
|
|
runDropCollection,
|
|
|
|
runAddQueryToCollection,
|
|
|
|
runDropQueryFromCollection,
|
|
|
|
runAddCollectionToAllowlist,
|
|
|
|
runDropCollectionFromAllowlist,
|
2022-02-08 19:53:30 +03:00
|
|
|
runUpdateScopeOfCollectionInAllowlist,
|
2019-05-16 09:13:25 +03:00
|
|
|
)
|
|
|
|
where
|
2021-09-24 01:56:37 +03:00
|
|
|
|
2022-02-08 19:53:30 +03:00
|
|
|
import Control.Lens ((.~))
|
2023-04-26 20:28:48 +03:00
|
|
|
import Data.Aeson qualified as J
|
2023-04-27 10:41:55 +03:00
|
|
|
import Data.HashMap.Strict.InsOrd qualified as InsOrdHashMap
|
2021-08-03 12:22:29 +03:00
|
|
|
import Data.List.Extended (duplicates)
|
2020-10-21 19:35:06 +03:00
|
|
|
import Data.Text.Extended
|
2021-08-03 12:22:29 +03:00
|
|
|
import Data.Text.NonEmpty
|
2021-05-11 18:18:31 +03:00
|
|
|
import Hasura.Base.Error
|
2020-08-27 19:36:39 +03:00
|
|
|
import Hasura.EncJSON
|
2019-05-16 09:13:25 +03:00
|
|
|
import Hasura.Prelude
|
2022-04-27 16:57:28 +03:00
|
|
|
import Hasura.RQL.Types.Allowlist
|
|
|
|
import Hasura.RQL.Types.Common
|
|
|
|
import Hasura.RQL.Types.Metadata
|
|
|
|
import Hasura.RQL.Types.QueryCollection
|
|
|
|
import Hasura.RQL.Types.SchemaCache.Build
|
2021-09-24 01:56:37 +03:00
|
|
|
|
2021-08-03 12:22:29 +03:00
|
|
|
addCollectionP2 ::
|
|
|
|
(QErrM m) =>
|
|
|
|
CollectionDef ->
|
|
|
|
m ()
|
|
|
|
addCollectionP2 (CollectionDef queryList) =
|
|
|
|
withPathK "queries" $
|
|
|
|
unless (null duplicateNames) $
|
|
|
|
throw400 NotSupported $
|
|
|
|
"found duplicate query names "
|
|
|
|
<> dquoteList (unNonEmptyText . unQueryName <$> toList duplicateNames)
|
|
|
|
where
|
|
|
|
duplicateNames = duplicates $ map _lqName queryList
|
|
|
|
|
2019-05-16 09:13:25 +03:00
|
|
|
runCreateCollection ::
|
2020-12-08 17:22:31 +03:00
|
|
|
(QErrM m, CacheRWM m, MetadataM m) =>
|
2019-05-16 09:13:25 +03:00
|
|
|
CreateCollection ->
|
|
|
|
m EncJSON
|
2021-08-03 12:22:29 +03:00
|
|
|
runCreateCollection cc = do
|
|
|
|
collDetM <- getCollectionDefM collName
|
2019-05-16 09:13:25 +03:00
|
|
|
withPathK "name" $
|
2022-10-04 00:49:32 +03:00
|
|
|
for_ collDetM $
|
2019-05-16 09:13:25 +03:00
|
|
|
const $
|
|
|
|
throw400 AlreadyExists $
|
2021-08-03 12:22:29 +03:00
|
|
|
"query collection with name " <> collName <<> " already exists"
|
|
|
|
withPathK "definition" $ addCollectionP2 def
|
2020-12-08 17:22:31 +03:00
|
|
|
withNewInconsistentObjsCheck $
|
|
|
|
buildSchemaCache $
|
|
|
|
MetadataModifier $
|
2023-04-27 10:41:55 +03:00
|
|
|
metaQueryCollections %~ InsOrdHashMap.insert collName cc
|
2019-05-16 09:13:25 +03:00
|
|
|
return successMsg
|
2021-08-03 12:22:29 +03:00
|
|
|
where
|
|
|
|
CreateCollection collName def _ = cc
|
2019-05-16 09:13:25 +03:00
|
|
|
|
2022-08-19 16:36:02 +03:00
|
|
|
runRenameCollection ::
|
|
|
|
(QErrM m, CacheRWM m, MetadataM m) =>
|
|
|
|
RenameCollection ->
|
|
|
|
m EncJSON
|
|
|
|
runRenameCollection (RenameCollection oldName newName) = do
|
|
|
|
_ <- getCollectionDef oldName
|
|
|
|
newCollDefM <- getCollectionDefM newName
|
|
|
|
withPathK "new_name" $
|
2022-10-04 00:49:32 +03:00
|
|
|
for_ newCollDefM $
|
2022-08-19 16:36:02 +03:00
|
|
|
const $
|
|
|
|
throw400 AlreadyExists $
|
|
|
|
"query collection with name " <> newName <<> " already exists"
|
|
|
|
withNewInconsistentObjsCheck $
|
|
|
|
buildSchemaCache $
|
|
|
|
MetadataModifier $
|
|
|
|
metaQueryCollections %~ changeCollectionName oldName newName
|
|
|
|
return successMsg
|
|
|
|
where
|
|
|
|
changeCollectionName :: CollectionName -> CollectionName -> QueryCollections -> QueryCollections
|
2023-04-27 10:41:55 +03:00
|
|
|
changeCollectionName oldKey newKey oMap = case InsOrdHashMap.lookup oldKey oMap of
|
2022-08-19 16:36:02 +03:00
|
|
|
Nothing -> oMap
|
|
|
|
Just oldVal ->
|
|
|
|
let newVal = oldVal & ccName .~ newKey
|
2023-04-27 10:41:55 +03:00
|
|
|
in InsOrdHashMap.insert newKey newVal (InsOrdHashMap.delete oldKey oMap)
|
2022-08-19 16:36:02 +03:00
|
|
|
|
2019-05-16 09:13:25 +03:00
|
|
|
runAddQueryToCollection ::
|
2020-12-08 17:22:31 +03:00
|
|
|
(CacheRWM m, MonadError QErr m, MetadataM m) =>
|
2019-05-16 09:13:25 +03:00
|
|
|
AddQueryToCollection ->
|
|
|
|
m EncJSON
|
|
|
|
runAddQueryToCollection (AddQueryToCollection collName queryName query) = do
|
2020-12-08 17:22:31 +03:00
|
|
|
(CreateCollection _ (CollectionDef qList) comment) <- getCollectionDef collName
|
2021-08-03 12:22:29 +03:00
|
|
|
let queryExists = flip any qList $ \q -> _lqName q == queryName
|
2019-05-16 09:13:25 +03:00
|
|
|
|
2021-08-03 12:22:29 +03:00
|
|
|
when queryExists $
|
|
|
|
throw400 AlreadyExists $
|
|
|
|
"query with name "
|
|
|
|
<> queryName <<> " already exists in collection " <>> collName
|
2019-05-16 09:13:25 +03:00
|
|
|
let collDef = CollectionDef $ qList <> pure listQ
|
2020-12-08 17:22:31 +03:00
|
|
|
withNewInconsistentObjsCheck $
|
|
|
|
buildSchemaCache $
|
|
|
|
MetadataModifier $
|
|
|
|
metaQueryCollections
|
2023-04-27 10:41:55 +03:00
|
|
|
%~ InsOrdHashMap.insert collName (CreateCollection collName collDef comment)
|
2019-05-16 09:13:25 +03:00
|
|
|
return successMsg
|
|
|
|
where
|
|
|
|
listQ = ListedQuery queryName query
|
|
|
|
|
|
|
|
runDropCollection ::
|
2020-12-08 17:22:31 +03:00
|
|
|
(MonadError QErr m, MetadataM m, CacheRWM m) =>
|
2019-05-16 09:13:25 +03:00
|
|
|
DropCollection ->
|
|
|
|
m EncJSON
|
|
|
|
runDropCollection (DropCollection collName cascade) = do
|
2022-02-08 19:53:30 +03:00
|
|
|
cascadeModifier <- withPathK "collection" $ do
|
|
|
|
assertCollectionDefined collName
|
|
|
|
allowlist <- fetchAllAllowlistCollections
|
|
|
|
if (collName `elem` allowlist)
|
2020-12-08 17:22:31 +03:00
|
|
|
then
|
2022-02-08 19:53:30 +03:00
|
|
|
if not cascade
|
|
|
|
then
|
|
|
|
throw400 DependencyError $
|
|
|
|
"query collection with name "
|
|
|
|
<> collName <<> " is present in the allowlist; cannot proceed to drop. "
|
|
|
|
<> "please use cascade to confirm you wish to drop it from the allowlist as well"
|
|
|
|
else dropCollectionFromAllowlist collName
|
|
|
|
else pure mempty
|
2021-09-24 01:56:37 +03:00
|
|
|
|
2020-12-08 17:22:31 +03:00
|
|
|
withNewInconsistentObjsCheck $
|
|
|
|
buildSchemaCache $
|
2023-04-27 10:41:55 +03:00
|
|
|
cascadeModifier <> MetadataModifier (metaQueryCollections %~ InsOrdHashMap.delete collName)
|
2020-12-08 17:22:31 +03:00
|
|
|
|
|
|
|
pure successMsg
|
2019-05-16 09:13:25 +03:00
|
|
|
|
|
|
|
runDropQueryFromCollection ::
|
2020-12-08 17:22:31 +03:00
|
|
|
(CacheRWM m, MonadError QErr m, MetadataM m) =>
|
2019-05-16 09:13:25 +03:00
|
|
|
DropQueryFromCollection ->
|
|
|
|
m EncJSON
|
|
|
|
runDropQueryFromCollection (DropQueryFromCollection collName queryName) = do
|
2020-12-08 17:22:31 +03:00
|
|
|
CreateCollection _ (CollectionDef qList) _ <- getCollectionDef collName
|
2019-05-16 09:13:25 +03:00
|
|
|
let queryExists = flip any qList $ \q -> _lqName q == queryName
|
2020-10-28 19:40:33 +03:00
|
|
|
unless queryExists $
|
|
|
|
throw400 NotFound $
|
|
|
|
"query with name "
|
2019-05-16 09:13:25 +03:00
|
|
|
<> queryName <<> " not found in collection " <>> collName
|
2021-09-24 01:56:37 +03:00
|
|
|
|
2020-12-08 17:22:31 +03:00
|
|
|
withNewInconsistentObjsCheck $
|
|
|
|
buildSchemaCache $
|
|
|
|
MetadataModifier $
|
|
|
|
metaQueryCollections . ix collName . ccDefinition . cdQueries
|
|
|
|
%~ filter ((/=) queryName . _lqName)
|
|
|
|
pure successMsg
|
2019-05-16 09:13:25 +03:00
|
|
|
|
|
|
|
runAddCollectionToAllowlist ::
|
2020-12-08 17:22:31 +03:00
|
|
|
(MonadError QErr m, MetadataM m, CacheRWM m) =>
|
2022-02-08 19:53:30 +03:00
|
|
|
AllowlistEntry ->
|
2019-05-16 09:13:25 +03:00
|
|
|
m EncJSON
|
2022-02-08 19:53:30 +03:00
|
|
|
runAddCollectionToAllowlist entry = do
|
|
|
|
withPathK "collection" $ assertCollectionDefined (aeCollection entry)
|
|
|
|
allowlist <- withPathK "allowlist" fetchAllowlist
|
|
|
|
case metadataAllowlistInsert entry allowlist of
|
|
|
|
Left msg ->
|
2023-04-26 20:28:48 +03:00
|
|
|
pure . encJFromJValue . J.object $
|
|
|
|
["message" J..= msg]
|
2022-02-08 19:53:30 +03:00
|
|
|
Right allowlist' -> do
|
|
|
|
withNewInconsistentObjsCheck . buildSchemaCache $ MetadataModifier (metaAllowlist .~ allowlist')
|
|
|
|
pure successMsg
|
|
|
|
|
|
|
|
-- Create a metadata modifier that drops a collection from the allowlist.
|
|
|
|
-- This is factored out for use in 'runDropCollection'.
|
|
|
|
dropCollectionFromAllowlist ::
|
|
|
|
(MonadError QErr m, MetadataM m) =>
|
|
|
|
CollectionName ->
|
|
|
|
m MetadataModifier
|
|
|
|
dropCollectionFromAllowlist collName = do
|
|
|
|
withPathK "collection" $ assertCollectionDefined collName
|
|
|
|
allowList <- withPathK "allowlist" fetchAllowlist
|
2023-04-27 10:41:55 +03:00
|
|
|
case InsOrdHashMap.lookup collName allowList of
|
2022-02-08 19:53:30 +03:00
|
|
|
Nothing -> throw400 NotFound $ "collection " <> collName <<> " doesn't exist in the allowlist"
|
2023-04-27 10:41:55 +03:00
|
|
|
Just _ -> pure $ MetadataModifier $ metaAllowlist .~ InsOrdHashMap.delete collName allowList
|
2019-05-16 09:13:25 +03:00
|
|
|
|
|
|
|
runDropCollectionFromAllowlist ::
|
2022-02-08 19:53:30 +03:00
|
|
|
(MonadError QErr m, MetadataM m, CacheRWM m) =>
|
|
|
|
DropCollectionFromAllowlist ->
|
2019-05-16 09:13:25 +03:00
|
|
|
m EncJSON
|
2022-02-08 19:53:30 +03:00
|
|
|
runDropCollectionFromAllowlist (DropCollectionFromAllowlist collName) = do
|
|
|
|
withNewInconsistentObjsCheck . buildSchemaCache =<< dropCollectionFromAllowlist collName
|
|
|
|
return successMsg
|
|
|
|
|
|
|
|
runUpdateScopeOfCollectionInAllowlist ::
|
|
|
|
(MonadError QErr m, MetadataM m, CacheRWM m) =>
|
|
|
|
UpdateScopeOfCollectionInAllowlist ->
|
|
|
|
m EncJSON
|
|
|
|
runUpdateScopeOfCollectionInAllowlist (UpdateScopeOfCollectionInAllowlist entry) = do
|
|
|
|
withPathK "collection" $ assertCollectionDefined (aeCollection entry)
|
|
|
|
al <- withPathK "allowlist" fetchAllowlist
|
|
|
|
modifier <- case metadataAllowlistUpdateScope entry al of
|
|
|
|
Left err -> throw400 NotFound err
|
|
|
|
Right al' ->
|
|
|
|
pure . MetadataModifier $
|
|
|
|
metaAllowlist .~ al'
|
|
|
|
withNewInconsistentObjsCheck $ buildSchemaCache modifier
|
2019-05-16 09:13:25 +03:00
|
|
|
return successMsg
|
|
|
|
|
2022-02-08 19:53:30 +03:00
|
|
|
-- helpers
|
|
|
|
|
|
|
|
assertCollectionDefined :: (QErrM m, MetadataM m) => CollectionName -> m ()
|
|
|
|
assertCollectionDefined = void . getCollectionDef
|
|
|
|
|
2019-05-16 09:13:25 +03:00
|
|
|
getCollectionDef ::
|
2020-12-08 17:22:31 +03:00
|
|
|
(QErrM m, MetadataM m) =>
|
|
|
|
CollectionName ->
|
|
|
|
m CreateCollection
|
2019-05-16 09:13:25 +03:00
|
|
|
getCollectionDef collName = do
|
|
|
|
detM <- getCollectionDefM collName
|
|
|
|
onNothing detM $
|
|
|
|
throw400 NotExists $
|
2021-08-18 13:51:35 +03:00
|
|
|
"query collection with name " <> collName <<> " does not exist"
|
2021-09-24 01:56:37 +03:00
|
|
|
|
2019-05-16 09:13:25 +03:00
|
|
|
getCollectionDefM ::
|
2020-12-08 17:22:31 +03:00
|
|
|
(QErrM m, MetadataM m) =>
|
|
|
|
CollectionName ->
|
|
|
|
m (Maybe CreateCollection)
|
|
|
|
getCollectionDefM collName =
|
2023-04-27 10:41:55 +03:00
|
|
|
InsOrdHashMap.lookup collName <$> fetchAllCollections
|
2020-12-08 17:22:31 +03:00
|
|
|
|
|
|
|
fetchAllCollections :: MetadataM m => m QueryCollections
|
|
|
|
fetchAllCollections =
|
|
|
|
_metaQueryCollections <$> getMetadata
|
|
|
|
|
2022-02-08 19:53:30 +03:00
|
|
|
fetchAllowlist :: MetadataM m => m MetadataAllowlist
|
|
|
|
fetchAllowlist = _metaAllowlist <$> getMetadata
|
|
|
|
|
|
|
|
fetchAllAllowlistCollections :: MetadataM m => m [CollectionName]
|
|
|
|
fetchAllAllowlistCollections = metadataAllowlistAllCollections <$> fetchAllowlist
|