2020-11-25 13:56:44 +03:00
|
|
|
|
{-# LANGUAGE UndecidableInstances #-}
|
2021-09-24 01:56:37 +03:00
|
|
|
|
|
|
|
|
|
-- | This module has type class and types which implements the Metadata Storage Abstraction
|
2020-11-25 13:56:44 +03:00
|
|
|
|
module Hasura.Metadata.Class
|
2021-09-24 01:56:37 +03:00
|
|
|
|
( SchemaSyncEventProcessResult (..),
|
|
|
|
|
MetadataStorageT (..),
|
|
|
|
|
runMetadataStorageT,
|
|
|
|
|
MonadMetadataStorage (..),
|
|
|
|
|
MonadMetadataStorageQueryAPI (..),
|
2020-11-25 13:56:44 +03:00
|
|
|
|
)
|
|
|
|
|
where
|
|
|
|
|
|
2021-09-24 01:56:37 +03:00
|
|
|
|
import Control.Monad.Morph (MFunctor, hoist)
|
|
|
|
|
import Control.Monad.Trans.Control (MonadBaseControl)
|
|
|
|
|
import Data.Aeson
|
Import `pg-client-hs` as `PG`
Result of executing the following commands:
```shell
# replace "as Q" imports with "as PG" (in retrospect this didn't need a regex)
git grep -lE 'as Q($|[^a-zA-Z])' -- '*.hs' | xargs sed -i -E 's/as Q($|[^a-zA-Z])/as PG\1/'
# replace " Q." with " PG."
git grep -lE ' Q\.' -- '*.hs' | xargs sed -i 's/ Q\./ PG./g'
# replace "(Q." with "(PG."
git grep -lE '\(Q\.' -- '*.hs' | xargs sed -i 's/(Q\./(PG./g'
# ditto, but for [, |, { and !
git grep -lE '\[Q\.' -- '*.hs' | xargs sed -i 's/\[Q\./\[PG./g'
git grep -l '|Q\.' -- '*.hs' | xargs sed -i 's/|Q\./|PG./g'
git grep -l '{Q\.' -- '*.hs' | xargs sed -i 's/{Q\./{PG./g'
git grep -l '!Q\.' -- '*.hs' | xargs sed -i 's/!Q\./!PG./g'
```
(Doing the `grep -l` before the `sed`, instead of `sed` on the entire codebase, reduces the number of `mtime` updates, and so reduces how many times a file gets recompiled while checking intermediate results.)
Finally, I manually removed a broken and unused `Arbitrary` instance in `Hasura.RQL.Network`. (It used an `import Test.QuickCheck.Arbitrary as Q` statement, which was erroneously caught by the first find-replace command.)
After this PR, `Q` is no longer used as an import qualifier. That was not the goal of this PR, but perhaps it's a useful fact for future efforts.
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/5933
GitOrigin-RevId: 8c84c59d57789111d40f5d3322c5a885dcfbf40e
2022-09-20 22:54:43 +03:00
|
|
|
|
import Database.PG.Query qualified as PG
|
2021-09-24 01:56:37 +03:00
|
|
|
|
import Hasura.Base.Error
|
|
|
|
|
import Hasura.Eventing.ScheduledTrigger.Types
|
|
|
|
|
import Hasura.Prelude
|
2022-04-27 16:57:28 +03:00
|
|
|
|
import Hasura.RQL.Types.Action
|
|
|
|
|
import Hasura.RQL.Types.EventTrigger
|
|
|
|
|
import Hasura.RQL.Types.Eventing
|
|
|
|
|
import Hasura.RQL.Types.Metadata
|
|
|
|
|
import Hasura.RQL.Types.ScheduledTrigger
|
|
|
|
|
import Hasura.RQL.Types.SchemaCache
|
|
|
|
|
import Hasura.RQL.Types.SchemaCache.Build
|
|
|
|
|
import Hasura.RQL.Types.Source
|
2021-09-24 01:56:37 +03:00
|
|
|
|
import Hasura.Server.Types
|
|
|
|
|
import Hasura.Session
|
|
|
|
|
import Hasura.Tracing qualified as Tracing
|
|
|
|
|
import Network.HTTP.Client.Manager (HasHttpManagerM (..))
|
|
|
|
|
import Network.HTTP.Types qualified as HTTP
|
|
|
|
|
|
|
|
|
|
data SchemaSyncEventProcessResult = SchemaSyncEventProcessResult
|
|
|
|
|
{ _sseprShouldReload :: !Bool,
|
|
|
|
|
_sseprCacheInvalidations :: !CacheInvalidations
|
2020-12-14 07:30:19 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-11-25 13:56:44 +03:00
|
|
|
|
{- Note [Todo: Common interface for eventing sub-system]
|
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
Postgres tables' event triggers and scheduled event triggers are similar in the
|
|
|
|
|
core logic. But currently, their implementation is completely isolated and do not
|
|
|
|
|
share a common schema in Postgres. We're having a plan to simplify them via a
|
|
|
|
|
common 'event storage and retrieval' interface (maybe via a Postgres extension?).
|
|
|
|
|
This will potentially reduce number of interactions made to database and schema foot print.
|
|
|
|
|
|
|
|
|
|
TODO: Reference to open issue or rfc?
|
|
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
-- | Metadata storage abstraction via a type class.
|
|
|
|
|
--
|
|
|
|
|
-- This type class enables storing and managing Hasura metadata in an isolated
|
|
|
|
|
-- database which will not interfere with user's database where tables/functions
|
|
|
|
|
-- are defined. Hence, it'll enable support for databases of multiple backends
|
|
|
|
|
-- like MySQL, MSSQL etc.
|
|
|
|
|
--
|
|
|
|
|
-- This class has functions broadly related to:
|
|
|
|
|
--
|
2020-12-14 07:30:19 +03:00
|
|
|
|
-- 1. Metadata Management
|
2020-11-25 13:56:44 +03:00
|
|
|
|
-- ----------------------
|
|
|
|
|
-- Basic metadata management functions such as retrieving metadata from storage
|
|
|
|
|
-- database and replacing the given metadata.
|
2020-12-14 07:30:19 +03:00
|
|
|
|
-- TODO: Console specific operations
|
2020-11-25 13:56:44 +03:00
|
|
|
|
--
|
|
|
|
|
-- 2. Scheduled Triggers
|
|
|
|
|
-- ---------------------
|
|
|
|
|
-- Eventing sub-system for scheduled triggers is implemented via metadata storage.
|
|
|
|
|
-- For more details, refer description in 'Hasura.Eventing.ScheduledTrigger' module.
|
|
|
|
|
--
|
|
|
|
|
-- TODO: Functions need to be added to the type class
|
|
|
|
|
-- - Retrieving invocation logs from storage (console requirement)
|
|
|
|
|
-- - Deleting an scheduled event
|
|
|
|
|
-- - Creating an one-off scheduled event
|
|
|
|
|
--
|
2020-12-14 07:30:19 +03:00
|
|
|
|
-- 3. Async Actions
|
2020-11-25 13:56:44 +03:00
|
|
|
|
-- ----------------
|
|
|
|
|
-- Operations to implement async actions sub-system. This includes recording an
|
|
|
|
|
-- async action event and retreiving the details of action delivery to the webhook.
|
|
|
|
|
-- For more details see Note [Async action architecture] in 'Hasura.GraphQL.Execute.Action' module.
|
|
|
|
|
--
|
|
|
|
|
-- It is believed that all the above three are implemented in a single storage
|
|
|
|
|
-- system (ex: a Postgres database). We can split the functions into appropriate and
|
|
|
|
|
-- specific type classes in future iterations if required.
|
|
|
|
|
class (MonadError QErr m) => MonadMetadataStorage m where
|
2020-12-14 07:30:19 +03:00
|
|
|
|
-- Metadata
|
2021-04-06 06:25:02 +03:00
|
|
|
|
fetchMetadataResourceVersion :: m MetadataResourceVersion
|
2021-02-19 05:39:30 +03:00
|
|
|
|
fetchMetadata :: m (Metadata, MetadataResourceVersion)
|
2021-09-24 01:56:37 +03:00
|
|
|
|
fetchMetadataNotifications :: MetadataResourceVersion -> InstanceId -> m [(MetadataResourceVersion, CacheInvalidations)]
|
2021-04-06 06:25:02 +03:00
|
|
|
|
setMetadata :: MetadataResourceVersion -> Metadata -> m MetadataResourceVersion
|
|
|
|
|
notifySchemaCacheSync :: MetadataResourceVersion -> InstanceId -> CacheInvalidations -> m ()
|
2021-01-07 12:04:22 +03:00
|
|
|
|
getCatalogState :: m CatalogState
|
2021-09-24 01:56:37 +03:00
|
|
|
|
|
2021-02-18 19:46:14 +03:00
|
|
|
|
-- the `setCatalogState` function is used by the console and CLI to store its state
|
|
|
|
|
-- it is disabled when maintenance mode is on
|
2021-01-07 12:04:22 +03:00
|
|
|
|
setCatalogState :: CatalogStateType -> Value -> m ()
|
2020-12-14 07:30:19 +03:00
|
|
|
|
|
2021-01-07 12:04:22 +03:00
|
|
|
|
-- get the @db_uuid@ that we store in the database.
|
2022-06-15 11:02:29 +03:00
|
|
|
|
getMetadataDbUid :: m MetadataDbId
|
2022-06-07 14:23:16 +03:00
|
|
|
|
checkMetadataStorageHealth :: m ()
|
2020-12-28 15:56:00 +03:00
|
|
|
|
|
2020-11-25 13:56:44 +03:00
|
|
|
|
-- Scheduled triggers
|
|
|
|
|
-- TODO:-
|
|
|
|
|
-- Ideally we would've liked to avoid having functions that are specific to
|
|
|
|
|
-- scheduled/cron triggers and instead have functions that provide a generic
|
|
|
|
|
-- 'event storage and retrieval' interface but we'll have to change a lot of
|
|
|
|
|
-- existing code for scheduled and cron triggers. We can get to this after the
|
|
|
|
|
-- multi-source work is done. See Note [Todo: Common interface for eventing sub-system]
|
2021-05-26 19:19:26 +03:00
|
|
|
|
getDeprivedCronTriggerStats :: [TriggerName] -> m [CronTriggerStats]
|
2020-11-25 13:56:44 +03:00
|
|
|
|
getScheduledEventsForDelivery :: m ([CronEvent], [OneOffScheduledEvent])
|
2021-09-13 21:00:53 +03:00
|
|
|
|
insertCronEvents :: [CronEventSeed] -> m ()
|
|
|
|
|
insertOneOffScheduledEvent :: OneOffEvent -> m EventId
|
2020-11-25 13:56:44 +03:00
|
|
|
|
insertScheduledEventInvocation :: Invocation 'ScheduledType -> ScheduledEventType -> m ()
|
|
|
|
|
setScheduledEventOp :: ScheduledEventId -> ScheduledEventOp -> ScheduledEventType -> m ()
|
|
|
|
|
unlockScheduledEvents :: ScheduledEventType -> [ScheduledEventId] -> m Int
|
|
|
|
|
unlockAllLockedScheduledEvents :: m ()
|
2021-05-26 19:19:26 +03:00
|
|
|
|
clearFutureCronEvents :: ClearCronEvents -> m ()
|
2021-09-24 01:56:37 +03:00
|
|
|
|
|
2021-05-26 19:19:26 +03:00
|
|
|
|
-- Console API requirements
|
2022-09-15 22:10:53 +03:00
|
|
|
|
getOneOffScheduledEvents :: ScheduledEventPagination -> [ScheduledEventStatus] -> RowsCountOption -> m (WithOptionalTotalCount [OneOffScheduledEvent])
|
|
|
|
|
getCronEvents :: TriggerName -> ScheduledEventPagination -> [ScheduledEventStatus] -> RowsCountOption -> m (WithOptionalTotalCount [CronEvent])
|
|
|
|
|
getInvocations :: GetEventInvocations -> m (WithOptionalTotalCount [ScheduledEventInvocation])
|
2021-01-07 12:04:22 +03:00
|
|
|
|
deleteScheduledEvent :: ScheduledEventId -> ScheduledEventType -> m ()
|
2020-12-14 07:30:19 +03:00
|
|
|
|
|
|
|
|
|
-- Async actions
|
2021-09-24 01:56:37 +03:00
|
|
|
|
insertAction ::
|
|
|
|
|
ActionName ->
|
|
|
|
|
SessionVariables ->
|
|
|
|
|
[HTTP.Header] ->
|
|
|
|
|
Value ->
|
|
|
|
|
m ActionId
|
2020-12-14 07:30:19 +03:00
|
|
|
|
fetchUndeliveredActionEvents :: m [ActionLogItem]
|
|
|
|
|
setActionStatus :: ActionId -> AsyncActionStatus -> m ()
|
|
|
|
|
fetchActionResponse :: ActionId -> m ActionLogResponse
|
2020-12-28 15:56:00 +03:00
|
|
|
|
clearActionData :: ActionName -> m ()
|
2021-05-14 12:38:37 +03:00
|
|
|
|
setProcessingActionLogsToPending :: LockedActionIdArray -> m ()
|
2020-11-25 13:56:44 +03:00
|
|
|
|
|
|
|
|
|
instance (MonadMetadataStorage m) => MonadMetadataStorage (ReaderT r m) where
|
2021-09-24 01:56:37 +03:00
|
|
|
|
fetchMetadataResourceVersion = lift fetchMetadataResourceVersion
|
|
|
|
|
fetchMetadata = lift fetchMetadata
|
|
|
|
|
fetchMetadataNotifications a b = lift $ fetchMetadataNotifications a b
|
|
|
|
|
setMetadata r = lift . setMetadata r
|
|
|
|
|
notifySchemaCacheSync a b c = lift $ notifySchemaCacheSync a b c
|
|
|
|
|
getCatalogState = lift getCatalogState
|
|
|
|
|
setCatalogState a b = lift $ setCatalogState a b
|
|
|
|
|
|
2022-06-15 11:02:29 +03:00
|
|
|
|
getMetadataDbUid = lift getMetadataDbUid
|
2021-09-24 01:56:37 +03:00
|
|
|
|
checkMetadataStorageHealth = lift checkMetadataStorageHealth
|
|
|
|
|
|
|
|
|
|
getDeprivedCronTriggerStats = lift . getDeprivedCronTriggerStats
|
|
|
|
|
getScheduledEventsForDelivery = lift getScheduledEventsForDelivery
|
|
|
|
|
insertCronEvents = lift . insertCronEvents
|
|
|
|
|
insertOneOffScheduledEvent = lift . insertOneOffScheduledEvent
|
|
|
|
|
insertScheduledEventInvocation a b = lift $ insertScheduledEventInvocation a b
|
|
|
|
|
setScheduledEventOp a b c = lift $ setScheduledEventOp a b c
|
|
|
|
|
unlockScheduledEvents a b = lift $ unlockScheduledEvents a b
|
|
|
|
|
unlockAllLockedScheduledEvents = lift $ unlockAllLockedScheduledEvents
|
|
|
|
|
clearFutureCronEvents = lift . clearFutureCronEvents
|
2022-09-15 22:10:53 +03:00
|
|
|
|
getOneOffScheduledEvents a b c = lift $ getOneOffScheduledEvents a b c
|
|
|
|
|
getCronEvents a b c d = lift $ getCronEvents a b c d
|
|
|
|
|
getInvocations a = lift $ getInvocations a
|
2021-09-24 01:56:37 +03:00
|
|
|
|
deleteScheduledEvent a b = lift $ deleteScheduledEvent a b
|
|
|
|
|
|
|
|
|
|
insertAction a b c d = lift $ insertAction a b c d
|
|
|
|
|
fetchUndeliveredActionEvents = lift fetchUndeliveredActionEvents
|
|
|
|
|
setActionStatus a b = lift $ setActionStatus a b
|
|
|
|
|
fetchActionResponse = lift . fetchActionResponse
|
|
|
|
|
clearActionData = lift . clearActionData
|
|
|
|
|
setProcessingActionLogsToPending = lift . setProcessingActionLogsToPending
|
2020-12-14 07:30:19 +03:00
|
|
|
|
|
|
|
|
|
instance (MonadMetadataStorage m) => MonadMetadataStorage (StateT s m) where
|
2021-09-24 01:56:37 +03:00
|
|
|
|
fetchMetadataResourceVersion = lift fetchMetadataResourceVersion
|
|
|
|
|
fetchMetadata = lift fetchMetadata
|
|
|
|
|
fetchMetadataNotifications a b = lift $ fetchMetadataNotifications a b
|
|
|
|
|
setMetadata r = lift . setMetadata r
|
|
|
|
|
notifySchemaCacheSync a b c = lift $ notifySchemaCacheSync a b c
|
|
|
|
|
getCatalogState = lift getCatalogState
|
|
|
|
|
setCatalogState a b = lift $ setCatalogState a b
|
|
|
|
|
|
2022-06-15 11:02:29 +03:00
|
|
|
|
getMetadataDbUid = lift getMetadataDbUid
|
2021-09-24 01:56:37 +03:00
|
|
|
|
checkMetadataStorageHealth = lift checkMetadataStorageHealth
|
|
|
|
|
|
|
|
|
|
getDeprivedCronTriggerStats = lift . getDeprivedCronTriggerStats
|
|
|
|
|
getScheduledEventsForDelivery = lift getScheduledEventsForDelivery
|
|
|
|
|
insertCronEvents = lift . insertCronEvents
|
|
|
|
|
insertOneOffScheduledEvent = lift . insertOneOffScheduledEvent
|
|
|
|
|
insertScheduledEventInvocation a b = lift $ insertScheduledEventInvocation a b
|
|
|
|
|
setScheduledEventOp a b c = lift $ setScheduledEventOp a b c
|
|
|
|
|
unlockScheduledEvents a b = lift $ unlockScheduledEvents a b
|
|
|
|
|
unlockAllLockedScheduledEvents = lift $ unlockAllLockedScheduledEvents
|
|
|
|
|
clearFutureCronEvents = lift . clearFutureCronEvents
|
2022-09-15 22:10:53 +03:00
|
|
|
|
getOneOffScheduledEvents a b c = lift $ getOneOffScheduledEvents a b c
|
|
|
|
|
getCronEvents a b c d = lift $ getCronEvents a b c d
|
|
|
|
|
getInvocations a = lift $ getInvocations a
|
2021-09-24 01:56:37 +03:00
|
|
|
|
deleteScheduledEvent a b = lift $ deleteScheduledEvent a b
|
|
|
|
|
|
|
|
|
|
insertAction a b c d = lift $ insertAction a b c d
|
|
|
|
|
fetchUndeliveredActionEvents = lift fetchUndeliveredActionEvents
|
|
|
|
|
setActionStatus a b = lift $ setActionStatus a b
|
|
|
|
|
fetchActionResponse = lift . fetchActionResponse
|
|
|
|
|
clearActionData = lift . clearActionData
|
|
|
|
|
setProcessingActionLogsToPending = lift . setProcessingActionLogsToPending
|
2020-11-25 13:56:44 +03:00
|
|
|
|
|
|
|
|
|
instance (MonadMetadataStorage m) => MonadMetadataStorage (Tracing.TraceT m) where
|
2021-09-24 01:56:37 +03:00
|
|
|
|
fetchMetadataResourceVersion = lift fetchMetadataResourceVersion
|
|
|
|
|
fetchMetadata = lift fetchMetadata
|
|
|
|
|
fetchMetadataNotifications a b = lift $ fetchMetadataNotifications a b
|
|
|
|
|
setMetadata r = lift . setMetadata r
|
|
|
|
|
notifySchemaCacheSync a b c = lift $ notifySchemaCacheSync a b c
|
|
|
|
|
getCatalogState = lift getCatalogState
|
|
|
|
|
setCatalogState a b = lift $ setCatalogState a b
|
|
|
|
|
|
2022-06-15 11:02:29 +03:00
|
|
|
|
getMetadataDbUid = lift getMetadataDbUid
|
2021-09-24 01:56:37 +03:00
|
|
|
|
checkMetadataStorageHealth = lift checkMetadataStorageHealth
|
|
|
|
|
|
|
|
|
|
getDeprivedCronTriggerStats = lift . getDeprivedCronTriggerStats
|
|
|
|
|
getScheduledEventsForDelivery = lift getScheduledEventsForDelivery
|
|
|
|
|
insertCronEvents = lift . insertCronEvents
|
|
|
|
|
insertOneOffScheduledEvent = lift . insertOneOffScheduledEvent
|
|
|
|
|
insertScheduledEventInvocation a b = lift $ insertScheduledEventInvocation a b
|
|
|
|
|
setScheduledEventOp a b c = lift $ setScheduledEventOp a b c
|
|
|
|
|
unlockScheduledEvents a b = lift $ unlockScheduledEvents a b
|
|
|
|
|
unlockAllLockedScheduledEvents = lift $ unlockAllLockedScheduledEvents
|
|
|
|
|
clearFutureCronEvents = lift . clearFutureCronEvents
|
2022-09-15 22:10:53 +03:00
|
|
|
|
getOneOffScheduledEvents a b c = lift $ getOneOffScheduledEvents a b c
|
|
|
|
|
getCronEvents a b c d = lift $ getCronEvents a b c d
|
|
|
|
|
getInvocations a = lift $ getInvocations a
|
2021-09-24 01:56:37 +03:00
|
|
|
|
deleteScheduledEvent a b = lift $ deleteScheduledEvent a b
|
|
|
|
|
|
|
|
|
|
insertAction a b c d = lift $ insertAction a b c d
|
|
|
|
|
fetchUndeliveredActionEvents = lift fetchUndeliveredActionEvents
|
|
|
|
|
setActionStatus a b = lift $ setActionStatus a b
|
|
|
|
|
fetchActionResponse = lift . fetchActionResponse
|
|
|
|
|
clearActionData = lift . clearActionData
|
|
|
|
|
setProcessingActionLogsToPending = lift . setProcessingActionLogsToPending
|
2020-12-14 07:30:19 +03:00
|
|
|
|
|
2020-12-28 15:56:00 +03:00
|
|
|
|
instance (MonadMetadataStorage m) => MonadMetadataStorage (ExceptT QErr m) where
|
2021-09-24 01:56:37 +03:00
|
|
|
|
fetchMetadataResourceVersion = lift fetchMetadataResourceVersion
|
|
|
|
|
fetchMetadata = lift fetchMetadata
|
|
|
|
|
fetchMetadataNotifications a b = lift $ fetchMetadataNotifications a b
|
|
|
|
|
setMetadata r = lift . setMetadata r
|
|
|
|
|
notifySchemaCacheSync a b c = lift $ notifySchemaCacheSync a b c
|
|
|
|
|
getCatalogState = lift getCatalogState
|
|
|
|
|
setCatalogState a b = lift $ setCatalogState a b
|
|
|
|
|
|
2022-06-15 11:02:29 +03:00
|
|
|
|
getMetadataDbUid = lift getMetadataDbUid
|
2021-09-24 01:56:37 +03:00
|
|
|
|
checkMetadataStorageHealth = lift checkMetadataStorageHealth
|
|
|
|
|
|
|
|
|
|
getDeprivedCronTriggerStats = lift . getDeprivedCronTriggerStats
|
|
|
|
|
getScheduledEventsForDelivery = lift getScheduledEventsForDelivery
|
|
|
|
|
insertCronEvents = lift . insertCronEvents
|
|
|
|
|
insertOneOffScheduledEvent = lift . insertOneOffScheduledEvent
|
|
|
|
|
insertScheduledEventInvocation a b = lift $ insertScheduledEventInvocation a b
|
|
|
|
|
setScheduledEventOp a b c = lift $ setScheduledEventOp a b c
|
|
|
|
|
unlockScheduledEvents a b = lift $ unlockScheduledEvents a b
|
|
|
|
|
unlockAllLockedScheduledEvents = lift $ unlockAllLockedScheduledEvents
|
|
|
|
|
clearFutureCronEvents = lift . clearFutureCronEvents
|
2022-09-15 22:10:53 +03:00
|
|
|
|
getOneOffScheduledEvents a b c = lift $ getOneOffScheduledEvents a b c
|
|
|
|
|
getCronEvents a b c d = lift $ getCronEvents a b c d
|
|
|
|
|
getInvocations a = lift $ getInvocations a
|
2021-09-24 01:56:37 +03:00
|
|
|
|
deleteScheduledEvent a b = lift $ deleteScheduledEvent a b
|
|
|
|
|
|
|
|
|
|
insertAction a b c d = lift $ insertAction a b c d
|
|
|
|
|
fetchUndeliveredActionEvents = lift fetchUndeliveredActionEvents
|
|
|
|
|
setActionStatus a b = lift $ setActionStatus a b
|
|
|
|
|
fetchActionResponse = lift . fetchActionResponse
|
|
|
|
|
clearActionData = lift . clearActionData
|
|
|
|
|
setProcessingActionLogsToPending = lift . setProcessingActionLogsToPending
|
2020-12-14 07:30:19 +03:00
|
|
|
|
|
|
|
|
|
instance (MonadMetadataStorage m) => MonadMetadataStorage (MetadataT m) where
|
2021-09-24 01:56:37 +03:00
|
|
|
|
fetchMetadataResourceVersion = lift fetchMetadataResourceVersion
|
|
|
|
|
fetchMetadata = lift fetchMetadata
|
|
|
|
|
fetchMetadataNotifications a b = lift $ fetchMetadataNotifications a b
|
|
|
|
|
setMetadata r = lift . setMetadata r
|
|
|
|
|
notifySchemaCacheSync a b c = lift $ notifySchemaCacheSync a b c
|
|
|
|
|
getCatalogState = lift getCatalogState
|
|
|
|
|
setCatalogState a b = lift $ setCatalogState a b
|
|
|
|
|
|
2022-06-15 11:02:29 +03:00
|
|
|
|
getMetadataDbUid = lift getMetadataDbUid
|
2021-09-24 01:56:37 +03:00
|
|
|
|
checkMetadataStorageHealth = lift checkMetadataStorageHealth
|
|
|
|
|
|
|
|
|
|
getDeprivedCronTriggerStats = lift . getDeprivedCronTriggerStats
|
|
|
|
|
getScheduledEventsForDelivery = lift getScheduledEventsForDelivery
|
|
|
|
|
insertCronEvents = lift . insertCronEvents
|
|
|
|
|
insertOneOffScheduledEvent = lift . insertOneOffScheduledEvent
|
|
|
|
|
insertScheduledEventInvocation a b = lift $ insertScheduledEventInvocation a b
|
|
|
|
|
setScheduledEventOp a b c = lift $ setScheduledEventOp a b c
|
|
|
|
|
unlockScheduledEvents a b = lift $ unlockScheduledEvents a b
|
|
|
|
|
unlockAllLockedScheduledEvents = lift $ unlockAllLockedScheduledEvents
|
|
|
|
|
clearFutureCronEvents = lift . clearFutureCronEvents
|
2022-09-15 22:10:53 +03:00
|
|
|
|
getOneOffScheduledEvents a b c = lift $ getOneOffScheduledEvents a b c
|
|
|
|
|
getCronEvents a b c d = lift $ getCronEvents a b c d
|
|
|
|
|
getInvocations a = lift $ getInvocations a
|
2021-09-24 01:56:37 +03:00
|
|
|
|
deleteScheduledEvent a b = lift $ deleteScheduledEvent a b
|
|
|
|
|
|
|
|
|
|
insertAction a b c d = lift $ insertAction a b c d
|
|
|
|
|
fetchUndeliveredActionEvents = lift fetchUndeliveredActionEvents
|
|
|
|
|
setActionStatus a b = lift $ setActionStatus a b
|
|
|
|
|
fetchActionResponse = lift . fetchActionResponse
|
|
|
|
|
clearActionData = lift . clearActionData
|
|
|
|
|
setProcessingActionLogsToPending = lift . setProcessingActionLogsToPending
|
2021-05-26 19:19:26 +03:00
|
|
|
|
|
Import `pg-client-hs` as `PG`
Result of executing the following commands:
```shell
# replace "as Q" imports with "as PG" (in retrospect this didn't need a regex)
git grep -lE 'as Q($|[^a-zA-Z])' -- '*.hs' | xargs sed -i -E 's/as Q($|[^a-zA-Z])/as PG\1/'
# replace " Q." with " PG."
git grep -lE ' Q\.' -- '*.hs' | xargs sed -i 's/ Q\./ PG./g'
# replace "(Q." with "(PG."
git grep -lE '\(Q\.' -- '*.hs' | xargs sed -i 's/(Q\./(PG./g'
# ditto, but for [, |, { and !
git grep -lE '\[Q\.' -- '*.hs' | xargs sed -i 's/\[Q\./\[PG./g'
git grep -l '|Q\.' -- '*.hs' | xargs sed -i 's/|Q\./|PG./g'
git grep -l '{Q\.' -- '*.hs' | xargs sed -i 's/{Q\./{PG./g'
git grep -l '!Q\.' -- '*.hs' | xargs sed -i 's/!Q\./!PG./g'
```
(Doing the `grep -l` before the `sed`, instead of `sed` on the entire codebase, reduces the number of `mtime` updates, and so reduces how many times a file gets recompiled while checking intermediate results.)
Finally, I manually removed a broken and unused `Arbitrary` instance in `Hasura.RQL.Network`. (It used an `import Test.QuickCheck.Arbitrary as Q` statement, which was erroneously caught by the first find-replace command.)
After this PR, `Q` is no longer used as an import qualifier. That was not the goal of this PR, but perhaps it's a useful fact for future efforts.
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/5933
GitOrigin-RevId: 8c84c59d57789111d40f5d3322c5a885dcfbf40e
2022-09-20 22:54:43 +03:00
|
|
|
|
instance (MonadMetadataStorage m) => MonadMetadataStorage (PG.TxET QErr m) where
|
2021-09-24 01:56:37 +03:00
|
|
|
|
fetchMetadataResourceVersion = lift fetchMetadataResourceVersion
|
|
|
|
|
fetchMetadata = lift fetchMetadata
|
|
|
|
|
fetchMetadataNotifications a b = lift $ fetchMetadataNotifications a b
|
|
|
|
|
setMetadata r = lift . setMetadata r
|
|
|
|
|
notifySchemaCacheSync a b c = lift $ notifySchemaCacheSync a b c
|
|
|
|
|
getCatalogState = lift getCatalogState
|
|
|
|
|
setCatalogState a b = lift $ setCatalogState a b
|
|
|
|
|
|
2022-06-15 11:02:29 +03:00
|
|
|
|
getMetadataDbUid = lift getMetadataDbUid
|
2021-09-24 01:56:37 +03:00
|
|
|
|
checkMetadataStorageHealth = lift checkMetadataStorageHealth
|
|
|
|
|
|
|
|
|
|
getDeprivedCronTriggerStats = lift . getDeprivedCronTriggerStats
|
|
|
|
|
getScheduledEventsForDelivery = lift getScheduledEventsForDelivery
|
|
|
|
|
insertCronEvents = lift . insertCronEvents
|
|
|
|
|
insertOneOffScheduledEvent = lift . insertOneOffScheduledEvent
|
|
|
|
|
insertScheduledEventInvocation a b = lift $ insertScheduledEventInvocation a b
|
|
|
|
|
setScheduledEventOp a b c = lift $ setScheduledEventOp a b c
|
|
|
|
|
unlockScheduledEvents a b = lift $ unlockScheduledEvents a b
|
|
|
|
|
unlockAllLockedScheduledEvents = lift $ unlockAllLockedScheduledEvents
|
|
|
|
|
clearFutureCronEvents = lift . clearFutureCronEvents
|
2022-09-15 22:10:53 +03:00
|
|
|
|
getOneOffScheduledEvents a b c = lift $ getOneOffScheduledEvents a b c
|
|
|
|
|
getCronEvents a b c d = lift $ getCronEvents a b c d
|
|
|
|
|
getInvocations a = lift $ getInvocations a
|
2021-09-24 01:56:37 +03:00
|
|
|
|
deleteScheduledEvent a b = lift $ deleteScheduledEvent a b
|
|
|
|
|
|
|
|
|
|
insertAction a b c d = lift $ insertAction a b c d
|
|
|
|
|
fetchUndeliveredActionEvents = lift fetchUndeliveredActionEvents
|
|
|
|
|
setActionStatus a b = lift $ setActionStatus a b
|
|
|
|
|
fetchActionResponse = lift . fetchActionResponse
|
|
|
|
|
clearActionData = lift . clearActionData
|
|
|
|
|
setProcessingActionLogsToPending = lift . setProcessingActionLogsToPending
|
2020-11-25 13:56:44 +03:00
|
|
|
|
|
|
|
|
|
{- Note [Generic MetadataStorageT transformer]
|
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
All methods of the MonadMetadataStorage class may fail, which we represent in
|
|
|
|
|
the usual way using a MonadError superclass:
|
|
|
|
|
|
|
|
|
|
class MonadError QErr m => MonadMetadataStorage m
|
|
|
|
|
|
|
|
|
|
However, unusually, the location where we pick a concrete MonadMetadataStorage
|
|
|
|
|
instance is not a context where we can handle errors, and as such the monad at
|
|
|
|
|
that point has no MonadError instance! Instead, clients of MonadMetadataStorage
|
|
|
|
|
are expected to handle errors /locally/, even though the code is parameterized
|
|
|
|
|
over an arbitrary metadata storage mechanism.
|
|
|
|
|
|
|
|
|
|
To encode this, we take a slightly unorthodox approach involving the auxiliary
|
|
|
|
|
MetadataStorageT transformer, which is really just a wrapper around ExceptT:
|
|
|
|
|
|
|
|
|
|
newtype MetadataStorageT m a
|
|
|
|
|
= MetadataStorageT { unMetadataStorageT :: ExceptT QErr m a }
|
|
|
|
|
|
|
|
|
|
We then define MonadMetadataStorage instances on a transformer stack comprising
|
|
|
|
|
both MetadataStorageT and a concrete base monad:
|
|
|
|
|
|
|
|
|
|
instance MonadMetadataStorage (MetadataStorageT PGMetadataStorageApp)
|
|
|
|
|
|
|
|
|
|
This looks unconventional, but it allows polymorphic code to be parameterized
|
|
|
|
|
over the metadata storage implementation while still handling errors locally.
|
|
|
|
|
Such functions include a constraint of the form
|
|
|
|
|
|
|
|
|
|
MonadMetadataStorage (MetadataStorageT m) => ...
|
|
|
|
|
|
|
|
|
|
and use runMetadataStorageT at the location where errors should be handled, e.g.:
|
|
|
|
|
|
|
|
|
|
result <- runMetadataStorageT do
|
|
|
|
|
{- ... some metadata operations ... -}
|
|
|
|
|
case result of
|
|
|
|
|
Left err -> ...
|
|
|
|
|
Right value -> ...
|
|
|
|
|
|
|
|
|
|
In other words, runMetadataStorageT serves as a marker that says “I’m going to
|
|
|
|
|
handle exceptions raised by metadata operations right here,” which allows them
|
|
|
|
|
to be handled more locally than the point at which the concrete
|
|
|
|
|
MonadMetadataStorage instance (and thus the particular metadata storage
|
|
|
|
|
implementation) is actually chosen. -}
|
|
|
|
|
|
|
|
|
|
-- | The 'MetadataStorageT' transformer adds ability to throw exceptions
|
|
|
|
|
-- for monads deriving @'MonadMetadataStorage' instance.
|
|
|
|
|
-- For more details see Note [Generic MetadataStorageT transformer]
|
2021-09-24 01:56:37 +03:00
|
|
|
|
newtype MetadataStorageT m a = MetadataStorageT {unMetadataStorageT :: ExceptT QErr m a}
|
|
|
|
|
deriving
|
|
|
|
|
( Functor,
|
|
|
|
|
Applicative,
|
|
|
|
|
Monad,
|
|
|
|
|
MonadError QErr,
|
|
|
|
|
MonadReader r,
|
|
|
|
|
MonadState s,
|
|
|
|
|
MonadTrans,
|
Rewrite OpenAPI
### Description
This PR rewrites OpenAPI to be more idiomatic. Some noteworthy changes:
- we accumulate all required information during the Analyze phase, to avoid having to do a single lookup in the schema cache during the OpenAPI generation phase (we now only need the schema cache as input to run the analysis)
- we no longer build intermediary endpoint information and aggregate it, we directly build the the `PathItem` for each endpoint; additionally, that means we no longer have to assume that different methods have the same metadata
- we no longer have to first declare types, then craft references: we do everything in one step
- we now properly deal with nullability by treating "typeName" and "typeName!" as different
- we add a bunch of additional fields in the generated "schema", such as title
- we do now support enum values in both input and output positions
- checking whether the request body is required is now performed on the fly rather than by introspecting the generated schema
- the methods in the file are sorted by topic
### Controversial point
However, this PR creates some additional complexity, that we might not want to keep. The main complexity is _knot-tying_: to avoid lookups when generating the OpenAPI, it builds an actual graph of input types, which means that we need something similar to (but simpler than) `MonadSchema`, to avoid infinite recursions when analyzing the input types of a query. To do this, this PR introduces `CircularT`, a lesser `SchemaT` that aims at avoiding ever having to reinvent this particular wheel ever again.
### Remaining work
- [x] fix existing tests (they are all failing due to some of the schema changes)
- [ ] add tests to cover the new features:
- [x] tests for `CircularT`
- [ ] tests for enums in output schemas
- [x] extract / document `CircularT` if we wish to keep it
- [x] add more comments to `OpenAPI`
- [x] have a second look at `buildVariableSchema`
- [x] fix all missing diagnostics in `Analyze`
- [x] add a Changelog entry?
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/4654
Co-authored-by: David Overton <7734777+dmoverton@users.noreply.github.com>
GitOrigin-RevId: f4a9191f22dfcc1dccefd6a52f5c586b6ad17172
2022-06-30 15:55:56 +03:00
|
|
|
|
MonadFix,
|
2021-09-24 01:56:37 +03:00
|
|
|
|
MonadIO,
|
|
|
|
|
MFunctor,
|
|
|
|
|
Tracing.HasReporter,
|
|
|
|
|
Tracing.MonadTrace,
|
|
|
|
|
MonadResolveSource,
|
|
|
|
|
HasHttpManagerM,
|
2022-05-24 10:21:39 +03:00
|
|
|
|
HasServerConfigCtx,
|
|
|
|
|
MonadBase b,
|
|
|
|
|
MonadBaseControl b
|
2021-09-24 01:56:37 +03:00
|
|
|
|
)
|
2020-11-25 13:56:44 +03:00
|
|
|
|
|
2021-09-24 01:56:37 +03:00
|
|
|
|
runMetadataStorageT ::
|
|
|
|
|
MetadataStorageT m a -> m (Either QErr a)
|
2020-11-25 13:56:44 +03:00
|
|
|
|
runMetadataStorageT =
|
|
|
|
|
runExceptT . unMetadataStorageT
|
2020-12-14 07:30:19 +03:00
|
|
|
|
|
2021-09-24 01:56:37 +03:00
|
|
|
|
instance
|
|
|
|
|
{-# OVERLAPPABLE #-}
|
|
|
|
|
(Monad m, Monad (t m), MonadTrans t, MonadMetadataStorage (MetadataStorageT m)) =>
|
|
|
|
|
MonadMetadataStorage (MetadataStorageT (t m))
|
|
|
|
|
where
|
|
|
|
|
fetchMetadataResourceVersion = hoist lift fetchMetadataResourceVersion
|
|
|
|
|
fetchMetadata = hoist lift fetchMetadata
|
|
|
|
|
fetchMetadataNotifications a b = hoist lift $ fetchMetadataNotifications a b
|
|
|
|
|
setMetadata r = hoist lift . setMetadata r
|
|
|
|
|
notifySchemaCacheSync a b c = hoist lift $ notifySchemaCacheSync a b c
|
|
|
|
|
getCatalogState = hoist lift getCatalogState
|
|
|
|
|
setCatalogState a b = hoist lift $ setCatalogState a b
|
2020-12-14 07:30:19 +03:00
|
|
|
|
|
2022-06-15 11:02:29 +03:00
|
|
|
|
getMetadataDbUid = hoist lift getMetadataDbUid
|
2020-12-28 15:56:00 +03:00
|
|
|
|
checkMetadataStorageHealth = hoist lift checkMetadataStorageHealth
|
|
|
|
|
|
2021-09-24 01:56:37 +03:00
|
|
|
|
getDeprivedCronTriggerStats = hoist lift . getDeprivedCronTriggerStats
|
|
|
|
|
getScheduledEventsForDelivery = hoist lift getScheduledEventsForDelivery
|
|
|
|
|
insertCronEvents = hoist lift . insertCronEvents
|
|
|
|
|
insertOneOffScheduledEvent = hoist lift . insertOneOffScheduledEvent
|
2021-05-26 19:19:26 +03:00
|
|
|
|
insertScheduledEventInvocation a b = hoist lift $ insertScheduledEventInvocation a b
|
2021-09-24 01:56:37 +03:00
|
|
|
|
setScheduledEventOp a b c = hoist lift $ setScheduledEventOp a b c
|
|
|
|
|
unlockScheduledEvents a b = hoist lift $ unlockScheduledEvents a b
|
|
|
|
|
unlockAllLockedScheduledEvents = hoist lift $ unlockAllLockedScheduledEvents
|
|
|
|
|
clearFutureCronEvents = hoist lift . clearFutureCronEvents
|
2022-09-15 22:10:53 +03:00
|
|
|
|
getOneOffScheduledEvents a b c = hoist lift $ getOneOffScheduledEvents a b c
|
|
|
|
|
getCronEvents a b c d = hoist lift $ getCronEvents a b c d
|
|
|
|
|
getInvocations a = hoist lift $ getInvocations a
|
2021-09-24 01:56:37 +03:00
|
|
|
|
deleteScheduledEvent a b = hoist lift $ deleteScheduledEvent a b
|
|
|
|
|
|
|
|
|
|
insertAction a b c d = hoist lift $ insertAction a b c d
|
|
|
|
|
fetchUndeliveredActionEvents = hoist lift fetchUndeliveredActionEvents
|
|
|
|
|
setActionStatus a b = hoist lift $ setActionStatus a b
|
|
|
|
|
fetchActionResponse = hoist lift . fetchActionResponse
|
|
|
|
|
clearActionData = hoist lift . clearActionData
|
|
|
|
|
setProcessingActionLogsToPending = hoist lift . setProcessingActionLogsToPending
|
2020-12-14 07:30:19 +03:00
|
|
|
|
|
2020-12-28 15:56:00 +03:00
|
|
|
|
-- | Operations from @'MonadMetadataStorage' used in '/v1/query' and '/v1/metadata' APIs
|
|
|
|
|
class (MonadMetadataStorage m) => MonadMetadataStorageQueryAPI m where
|
2021-09-13 21:00:53 +03:00
|
|
|
|
-- | Record a one-off event
|
|
|
|
|
createOneOffScheduledEvent :: OneOffEvent -> m EventId
|
|
|
|
|
createOneOffScheduledEvent = insertOneOffScheduledEvent
|
|
|
|
|
|
|
|
|
|
-- | Record a cron event
|
|
|
|
|
createCronEvents :: [CronEventSeed] -> m ()
|
|
|
|
|
createCronEvents = insertCronEvents
|
2020-12-14 07:30:19 +03:00
|
|
|
|
|
|
|
|
|
-- | Clear cron events
|
2021-05-26 19:19:26 +03:00
|
|
|
|
dropFutureCronEvents :: ClearCronEvents -> m ()
|
2020-12-14 07:30:19 +03:00
|
|
|
|
dropFutureCronEvents = clearFutureCronEvents
|
|
|
|
|
|
2020-12-28 15:56:00 +03:00
|
|
|
|
-- | Delete async action logs
|
|
|
|
|
deleteActionData :: ActionName -> m ()
|
|
|
|
|
deleteActionData = clearActionData
|
|
|
|
|
|
2021-01-07 12:04:22 +03:00
|
|
|
|
-- | Fetch cron/oneoff scheduled event invocations
|
2021-09-24 01:56:37 +03:00
|
|
|
|
fetchInvocations ::
|
2022-09-15 22:10:53 +03:00
|
|
|
|
GetEventInvocations ->
|
|
|
|
|
m (WithOptionalTotalCount [ScheduledEventInvocation])
|
2021-01-07 12:04:22 +03:00
|
|
|
|
fetchInvocations = getInvocations
|
|
|
|
|
|
|
|
|
|
-- | Fetch cron/oneoff scheduled events
|
|
|
|
|
fetchScheduledEvents :: GetScheduledEvents -> m Value
|
2021-09-24 01:56:37 +03:00
|
|
|
|
fetchScheduledEvents GetScheduledEvents {..} = do
|
2022-09-15 22:10:53 +03:00
|
|
|
|
let totalCountToJSON WithOptionalTotalCount {..} =
|
|
|
|
|
object $
|
|
|
|
|
("events" .= _wtcData) : (maybe mempty (\count -> ["count" .= count]) _wtcCount)
|
2021-01-07 12:04:22 +03:00
|
|
|
|
case _gseScheduledEvent of
|
2022-09-15 22:10:53 +03:00
|
|
|
|
SEOneOff -> totalCountToJSON <$> getOneOffScheduledEvents _gsePagination _gseStatus _gseGetRowsCount
|
|
|
|
|
SECron name -> totalCountToJSON <$> getCronEvents name _gsePagination _gseStatus _gseGetRowsCount
|
2021-01-07 12:04:22 +03:00
|
|
|
|
|
|
|
|
|
-- | Drop a cron/oneoff scheduled event
|
|
|
|
|
dropEvent :: ScheduledEventId -> ScheduledEventType -> m ()
|
|
|
|
|
dropEvent = deleteScheduledEvent
|
|
|
|
|
|
|
|
|
|
-- | Retrieve the state from metadata storage catalog
|
|
|
|
|
fetchCatalogState :: m CatalogState
|
|
|
|
|
fetchCatalogState = getCatalogState
|
|
|
|
|
|
|
|
|
|
-- | Update the state from metadata storage catalog
|
|
|
|
|
updateCatalogState :: CatalogStateType -> Value -> m ()
|
|
|
|
|
updateCatalogState = setCatalogState
|
|
|
|
|
|
2020-12-28 15:56:00 +03:00
|
|
|
|
instance (MonadMetadataStorageQueryAPI m) => MonadMetadataStorageQueryAPI (ReaderT r m)
|
2021-09-24 01:56:37 +03:00
|
|
|
|
|
2020-12-28 15:56:00 +03:00
|
|
|
|
instance (MonadMetadataStorageQueryAPI m) => MonadMetadataStorageQueryAPI (StateT s m)
|
2021-09-24 01:56:37 +03:00
|
|
|
|
|
2020-12-28 15:56:00 +03:00
|
|
|
|
instance (MonadMetadataStorageQueryAPI m) => MonadMetadataStorageQueryAPI (Tracing.TraceT m)
|
2021-09-24 01:56:37 +03:00
|
|
|
|
|
2020-12-28 15:56:00 +03:00
|
|
|
|
instance (MonadMetadataStorageQueryAPI m) => MonadMetadataStorageQueryAPI (MetadataT m)
|
2021-09-24 01:56:37 +03:00
|
|
|
|
|
Import `pg-client-hs` as `PG`
Result of executing the following commands:
```shell
# replace "as Q" imports with "as PG" (in retrospect this didn't need a regex)
git grep -lE 'as Q($|[^a-zA-Z])' -- '*.hs' | xargs sed -i -E 's/as Q($|[^a-zA-Z])/as PG\1/'
# replace " Q." with " PG."
git grep -lE ' Q\.' -- '*.hs' | xargs sed -i 's/ Q\./ PG./g'
# replace "(Q." with "(PG."
git grep -lE '\(Q\.' -- '*.hs' | xargs sed -i 's/(Q\./(PG./g'
# ditto, but for [, |, { and !
git grep -lE '\[Q\.' -- '*.hs' | xargs sed -i 's/\[Q\./\[PG./g'
git grep -l '|Q\.' -- '*.hs' | xargs sed -i 's/|Q\./|PG./g'
git grep -l '{Q\.' -- '*.hs' | xargs sed -i 's/{Q\./{PG./g'
git grep -l '!Q\.' -- '*.hs' | xargs sed -i 's/!Q\./!PG./g'
```
(Doing the `grep -l` before the `sed`, instead of `sed` on the entire codebase, reduces the number of `mtime` updates, and so reduces how many times a file gets recompiled while checking intermediate results.)
Finally, I manually removed a broken and unused `Arbitrary` instance in `Hasura.RQL.Network`. (It used an `import Test.QuickCheck.Arbitrary as Q` statement, which was erroneously caught by the first find-replace command.)
After this PR, `Q` is no longer used as an import qualifier. That was not the goal of this PR, but perhaps it's a useful fact for future efforts.
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/5933
GitOrigin-RevId: 8c84c59d57789111d40f5d3322c5a885dcfbf40e
2022-09-20 22:54:43 +03:00
|
|
|
|
instance (MonadMetadataStorageQueryAPI m) => MonadMetadataStorageQueryAPI (PG.TxET QErr m)
|