-- |
-- Description: Create/delete SQL functions to/from Hasura metadata.
module Hasura.RQL.DDL.Schema.Function
  ( FunctionPermissionArgument (..),
    SetFunctionCustomization (..),
    TrackFunction (..),
    TrackFunctionV2 (..),
    UnTrackFunction (..),
    doesFunctionPermissionExist,
    dropFunctionInMetadata,
    dropFunctionPermissionInMetadata,
    handleMultipleFunctions,
    runCreateFunctionPermission,
    runDropFunctionPermission,
    runSetFunctionCustomization,
    runTrackFunc,
    runTrackFunctionV2,
    runUntrackFunc,
    trackFunctionP1,
    trackFunctionP2,
  )
where

import Control.Lens ((.~), (^.))
import Data.Aeson
import Data.HashMap.Strict qualified as Map
import Data.HashMap.Strict.InsOrd qualified as OMap
import Data.Text.Extended
import Hasura.Base.Error
import Hasura.EncJSON
import Hasura.Prelude
import Hasura.RQL.Types.Backend
import Hasura.RQL.Types.Common
import Hasura.RQL.Types.Function
import Hasura.RQL.Types.Metadata
import Hasura.RQL.Types.Metadata.Backend
import Hasura.RQL.Types.Metadata.Instances ()
import Hasura.RQL.Types.Metadata.Object
import Hasura.RQL.Types.SchemaCache
import Hasura.RQL.Types.SchemaCache.Build
import Hasura.SQL.AnyBackend qualified as AB
import Hasura.SQL.Backend
import Hasura.SQL.Tag
import Hasura.Session

newtype TrackFunction b = TrackFunction {TrackFunction b -> FunctionName b
tfName :: FunctionName b}

deriving instance (Backend b) => FromJSON (TrackFunction b)

deriving instance (Backend b) => ToJSON (TrackFunction b)

-- | Track function, Phase 1:
-- Validate function tracking operation. Fails if function is already being
-- tracked, or if a table with the same name is being tracked.
trackFunctionP1 ::
  forall b m.
  (CacheRM m, QErrM m, Backend b) =>
  SourceName ->
  FunctionName b ->
  m ()
trackFunctionP1 :: SourceName -> FunctionName b -> m ()
trackFunctionP1 SourceName
sourceName FunctionName b
qf = do
  SchemaCache
rawSchemaCache <- m SchemaCache
forall (m :: * -> *). CacheRM m => m SchemaCache
askSchemaCache
  Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Maybe (SourceInfo b) -> Bool
forall a. Maybe a -> Bool
isJust (Maybe (SourceInfo b) -> Bool) -> Maybe (SourceInfo b) -> Bool
forall a b. (a -> b) -> a -> b
$ forall (b :: BackendType) (i :: BackendType -> *).
HasTag b =>
AnyBackend i -> Maybe (i b)
forall (i :: BackendType -> *).
HasTag b =>
AnyBackend i -> Maybe (i b)
AB.unpackAnyBackend @b (AnyBackend SourceInfo -> Maybe (SourceInfo b))
-> Maybe (AnyBackend SourceInfo) -> Maybe (SourceInfo b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< SourceName
-> HashMap SourceName (AnyBackend SourceInfo)
-> Maybe (AnyBackend SourceInfo)
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
Map.lookup SourceName
sourceName (SchemaCache -> HashMap SourceName (AnyBackend SourceInfo)
scSources SchemaCache
rawSchemaCache)) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
    Code -> Text -> m ()
forall (m :: * -> *) a. QErrM m => Code -> Text -> m a
throw400 Code
NotExists (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ SourceName
sourceName SourceName -> Text -> Text
forall t. ToTxt t => t -> Text -> Text
<<> Text
" is not a known " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> BackendTag b -> BackendType
forall (b :: BackendType). BackendTag b -> BackendType
reify (HasTag b => BackendTag b
forall (b :: BackendType). HasTag b => BackendTag b
backendTag @b) BackendType -> Text -> Text
forall t. ToTxt t => t -> Text -> Text
<<> Text
" source"
  Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Maybe (FunctionInfo b) -> Bool
forall a. Maybe a -> Bool
isJust (Maybe (FunctionInfo b) -> Bool) -> Maybe (FunctionInfo b) -> Bool
forall a b. (a -> b) -> a -> b
$ SourceName
-> FunctionName b
-> HashMap SourceName (AnyBackend SourceInfo)
-> Maybe (FunctionInfo b)
forall (b :: BackendType).
Backend b =>
SourceName
-> FunctionName b
-> HashMap SourceName (AnyBackend SourceInfo)
-> Maybe (FunctionInfo b)
unsafeFunctionInfo @b SourceName
sourceName FunctionName b
qf (HashMap SourceName (AnyBackend SourceInfo)
 -> Maybe (FunctionInfo b))
-> HashMap SourceName (AnyBackend SourceInfo)
-> Maybe (FunctionInfo b)
forall a b. (a -> b) -> a -> b
$ SchemaCache -> HashMap SourceName (AnyBackend SourceInfo)
scSources SchemaCache
rawSchemaCache) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
    Code -> Text -> m ()
forall (m :: * -> *) a. QErrM m => Code -> Text -> m a
throw400 Code
AlreadyTracked (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ Text
"function already tracked : " Text -> FunctionName b -> Text
forall t. ToTxt t => Text -> t -> Text
<>> FunctionName b
qf
  let qt :: TableName b
qt = FunctionName b -> TableName b
forall (b :: BackendType).
Backend b =>
FunctionName b -> TableName b
functionToTable @b FunctionName b
qf
  Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Maybe (TableInfo b) -> Bool
forall a. Maybe a -> Bool
isJust (Maybe (TableInfo b) -> Bool) -> Maybe (TableInfo b) -> Bool
forall a b. (a -> b) -> a -> b
$ SourceName
-> TableName b
-> HashMap SourceName (AnyBackend SourceInfo)
-> Maybe (TableInfo b)
forall (b :: BackendType).
Backend b =>
SourceName
-> TableName b
-> HashMap SourceName (AnyBackend SourceInfo)
-> Maybe (TableInfo b)
unsafeTableInfo @b SourceName
sourceName TableName b
qt (HashMap SourceName (AnyBackend SourceInfo) -> Maybe (TableInfo b))
-> HashMap SourceName (AnyBackend SourceInfo)
-> Maybe (TableInfo b)
forall a b. (a -> b) -> a -> b
$ SchemaCache -> HashMap SourceName (AnyBackend SourceInfo)
scSources SchemaCache
rawSchemaCache) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
    Code -> Text -> m ()
forall (m :: * -> *) a. QErrM m => Code -> Text -> m a
throw400 Code
NotSupported (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ Text
"table with name " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> FunctionName b
qf FunctionName b -> Text -> Text
forall t. ToTxt t => t -> Text -> Text
<<> Text
" already exists"

trackFunctionP2 ::
  forall b m.
  (MonadError QErr m, CacheRWM m, MetadataM m, BackendMetadata b) =>
  SourceName ->
  FunctionName b ->
  FunctionConfig ->
  Maybe Text ->
  m EncJSON
trackFunctionP2 :: SourceName
-> FunctionName b -> FunctionConfig -> Maybe Text -> m EncJSON
trackFunctionP2 SourceName
sourceName FunctionName b
qf FunctionConfig
config Maybe Text
comment = do
  MetadataObjId -> MetadataModifier -> m ()
forall (m :: * -> *).
(QErrM m, CacheRWM m, MetadataM m) =>
MetadataObjId -> MetadataModifier -> m ()
buildSchemaCacheFor
    (SourceName -> AnyBackend SourceMetadataObjId -> MetadataObjId
MOSourceObjId SourceName
sourceName (AnyBackend SourceMetadataObjId -> MetadataObjId)
-> AnyBackend SourceMetadataObjId -> MetadataObjId
forall a b. (a -> b) -> a -> b
$ SourceMetadataObjId b -> AnyBackend SourceMetadataObjId
forall (b :: BackendType) (i :: BackendType -> *).
HasTag b =>
i b -> AnyBackend i
AB.mkAnyBackend (SourceMetadataObjId b -> AnyBackend SourceMetadataObjId)
-> SourceMetadataObjId b -> AnyBackend SourceMetadataObjId
forall a b. (a -> b) -> a -> b
$ FunctionName b -> SourceMetadataObjId b
forall (b :: BackendType). FunctionName b -> SourceMetadataObjId b
SMOFunction @b FunctionName b
qf)
    (MetadataModifier -> m ()) -> MetadataModifier -> m ()
forall a b. (a -> b) -> a -> b
$ (Metadata -> Metadata) -> MetadataModifier
MetadataModifier ((Metadata -> Metadata) -> MetadataModifier)
-> (Metadata -> Metadata) -> MetadataModifier
forall a b. (a -> b) -> a -> b
$
      (Sources -> Identity Sources) -> Metadata -> Identity Metadata
Lens' Metadata Sources
metaSources ((Sources -> Identity Sources) -> Metadata -> Identity Metadata)
-> ((InsOrdHashMap (FunctionName b) (FunctionMetadata b)
     -> Identity (InsOrdHashMap (FunctionName b) (FunctionMetadata b)))
    -> Sources -> Identity Sources)
-> (InsOrdHashMap (FunctionName b) (FunctionMetadata b)
    -> Identity (InsOrdHashMap (FunctionName b) (FunctionMetadata b)))
-> Metadata
-> Identity Metadata
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Index Sources -> Traversal' Sources (IxValue Sources)
forall m. Ixed m => Index m -> Traversal' m (IxValue m)
ix Index Sources
SourceName
sourceName ((BackendSourceMetadata -> Identity BackendSourceMetadata)
 -> Sources -> Identity Sources)
-> ((InsOrdHashMap (FunctionName b) (FunctionMetadata b)
     -> Identity (InsOrdHashMap (FunctionName b) (FunctionMetadata b)))
    -> BackendSourceMetadata -> Identity BackendSourceMetadata)
-> (InsOrdHashMap (FunctionName b) (FunctionMetadata b)
    -> Identity (InsOrdHashMap (FunctionName b) (FunctionMetadata b)))
-> Sources
-> Identity Sources
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (SourceMetadata b -> Identity (SourceMetadata b))
-> BackendSourceMetadata -> Identity BackendSourceMetadata
forall (b :: BackendType).
Backend b =>
Prism' BackendSourceMetadata (SourceMetadata b)
toSourceMetadata ((SourceMetadata b -> Identity (SourceMetadata b))
 -> BackendSourceMetadata -> Identity BackendSourceMetadata)
-> ((InsOrdHashMap (FunctionName b) (FunctionMetadata b)
     -> Identity (InsOrdHashMap (FunctionName b) (FunctionMetadata b)))
    -> SourceMetadata b -> Identity (SourceMetadata b))
-> (InsOrdHashMap (FunctionName b) (FunctionMetadata b)
    -> Identity (InsOrdHashMap (FunctionName b) (FunctionMetadata b)))
-> BackendSourceMetadata
-> Identity BackendSourceMetadata
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall (b :: BackendType). Lens' (SourceMetadata b) (Functions b)
Lens'
  (SourceMetadata b)
  (InsOrdHashMap (FunctionName b) (FunctionMetadata b))
smFunctions @b)
        ((InsOrdHashMap (FunctionName b) (FunctionMetadata b)
  -> Identity (InsOrdHashMap (FunctionName b) (FunctionMetadata b)))
 -> Metadata -> Identity Metadata)
-> (InsOrdHashMap (FunctionName b) (FunctionMetadata b)
    -> InsOrdHashMap (FunctionName b) (FunctionMetadata b))
-> Metadata
-> Metadata
forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ FunctionName b
-> FunctionMetadata b
-> InsOrdHashMap (FunctionName b) (FunctionMetadata b)
-> InsOrdHashMap (FunctionName b) (FunctionMetadata b)
forall k v.
(Eq k, Hashable k) =>
k -> v -> InsOrdHashMap k v -> InsOrdHashMap k v
OMap.insert FunctionName b
qf (FunctionName b
-> FunctionConfig
-> [FunctionPermissionInfo]
-> Maybe Text
-> FunctionMetadata b
forall (b :: BackendType).
FunctionName b
-> FunctionConfig
-> [FunctionPermissionInfo]
-> Maybe Text
-> FunctionMetadata b
FunctionMetadata FunctionName b
qf FunctionConfig
config [FunctionPermissionInfo]
forall a. Monoid a => a
mempty Maybe Text
comment)
  EncJSON -> m EncJSON
forall (f :: * -> *) a. Applicative f => a -> f a
pure EncJSON
successMsg

handleMultipleFunctions ::
  forall b m a.
  (QErrM m, Backend b) =>
  FunctionName b ->
  [a] ->
  m a
handleMultipleFunctions :: FunctionName b -> [a] -> m a
handleMultipleFunctions FunctionName b
qf = \case
  [a
fi] -> a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return a
fi
  [] -> Code -> Text -> m a
forall (m :: * -> *) a. QErrM m => Code -> Text -> m a
throw400 Code
NotExists (Text -> m a) -> Text -> m a
forall a b. (a -> b) -> a -> b
$ Text
"no such function exists: " Text -> FunctionName b -> Text
forall t. ToTxt t => Text -> t -> Text
<>> FunctionName b
qf
  [a]
_ -> Code -> Text -> m a
forall (m :: * -> *) a. QErrM m => Code -> Text -> m a
throw400 Code
NotSupported (Text -> m a) -> Text -> m a
forall a b. (a -> b) -> a -> b
$ Text
"function " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> FunctionName b
qf FunctionName b -> Text -> Text
forall t. ToTxt t => t -> Text -> Text
<<> Text
" is overloaded. Overloaded functions are not supported"

runTrackFunc ::
  forall b m.
  (MonadError QErr m, CacheRWM m, MetadataM m, BackendMetadata b) =>
  TrackFunction b ->
  m EncJSON
runTrackFunc :: TrackFunction b -> m EncJSON
runTrackFunc (TrackFunction FunctionName b
qf) = do
  -- v1 track_function lacks a means to take extra arguments
  SourceName -> FunctionName b -> m ()
forall (b :: BackendType) (m :: * -> *).
(CacheRM m, QErrM m, Backend b) =>
SourceName -> FunctionName b -> m ()
trackFunctionP1 @b SourceName
defaultSource FunctionName b
qf
  SourceName
-> FunctionName b -> FunctionConfig -> Maybe Text -> m EncJSON
forall (b :: BackendType) (m :: * -> *).
(MonadError QErr m, CacheRWM m, MetadataM m, BackendMetadata b) =>
SourceName
-> FunctionName b -> FunctionConfig -> Maybe Text -> m EncJSON
trackFunctionP2 @b SourceName
defaultSource FunctionName b
qf FunctionConfig
emptyFunctionConfig Maybe Text
forall a. Maybe a
Nothing

-- | JSON API payload for v2 of 'track_function':
--
-- https://hasura.io/docs/latest/graphql/core/api-reference/schema-metadata-api/custom-functions.html#track-function-v2
data TrackFunctionV2 (b :: BackendType) = TrackFunctionV2
  { TrackFunctionV2 b -> SourceName
_tfv2Source :: SourceName,
    TrackFunctionV2 b -> FunctionName b
_tfv2Function :: FunctionName b,
    TrackFunctionV2 b -> FunctionConfig
_tfv2Configuration :: FunctionConfig,
    TrackFunctionV2 b -> Maybe Text
_tfv2Comment :: Maybe Text
  }

instance Backend b => FromJSON (TrackFunctionV2 b) where
  parseJSON :: Value -> Parser (TrackFunctionV2 b)
parseJSON = String
-> (Object -> Parser (TrackFunctionV2 b))
-> Value
-> Parser (TrackFunctionV2 b)
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"TrackFunctionV2" ((Object -> Parser (TrackFunctionV2 b))
 -> Value -> Parser (TrackFunctionV2 b))
-> (Object -> Parser (TrackFunctionV2 b))
-> Value
-> Parser (TrackFunctionV2 b)
forall a b. (a -> b) -> a -> b
$ \Object
o ->
    SourceName
-> FunctionName b
-> FunctionConfig
-> Maybe Text
-> TrackFunctionV2 b
forall (b :: BackendType).
SourceName
-> FunctionName b
-> FunctionConfig
-> Maybe Text
-> TrackFunctionV2 b
TrackFunctionV2
      (SourceName
 -> FunctionName b
 -> FunctionConfig
 -> Maybe Text
 -> TrackFunctionV2 b)
-> Parser SourceName
-> Parser
     (FunctionName b
      -> FunctionConfig -> Maybe Text -> TrackFunctionV2 b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
o Object -> Key -> Parser (Maybe SourceName)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"source" Parser (Maybe SourceName) -> SourceName -> Parser SourceName
forall a. Parser (Maybe a) -> a -> Parser a
.!= SourceName
defaultSource
      Parser
  (FunctionName b
   -> FunctionConfig -> Maybe Text -> TrackFunctionV2 b)
-> Parser (FunctionName b)
-> Parser (FunctionConfig -> Maybe Text -> TrackFunctionV2 b)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o Object -> Key -> Parser (FunctionName b)
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"function"
      Parser (FunctionConfig -> Maybe Text -> TrackFunctionV2 b)
-> Parser FunctionConfig
-> Parser (Maybe Text -> TrackFunctionV2 b)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o Object -> Key -> Parser (Maybe FunctionConfig)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"configuration" Parser (Maybe FunctionConfig)
-> FunctionConfig -> Parser FunctionConfig
forall a. Parser (Maybe a) -> a -> Parser a
.!= FunctionConfig
emptyFunctionConfig
      Parser (Maybe Text -> TrackFunctionV2 b)
-> Parser (Maybe Text) -> Parser (TrackFunctionV2 b)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o Object -> Key -> Parser (Maybe Text)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"comment"

runTrackFunctionV2 ::
  forall b m.
  (BackendMetadata b, QErrM m, CacheRWM m, MetadataM m) =>
  TrackFunctionV2 b ->
  m EncJSON
runTrackFunctionV2 :: TrackFunctionV2 b -> m EncJSON
runTrackFunctionV2 (TrackFunctionV2 SourceName
source FunctionName b
qf FunctionConfig
config Maybe Text
comment) = do
  SourceName -> FunctionName b -> m ()
forall (b :: BackendType) (m :: * -> *).
(CacheRM m, QErrM m, Backend b) =>
SourceName -> FunctionName b -> m ()
trackFunctionP1 @b SourceName
source FunctionName b
qf
  SourceName
-> FunctionName b -> FunctionConfig -> Maybe Text -> m EncJSON
forall (b :: BackendType) (m :: * -> *).
(MonadError QErr m, CacheRWM m, MetadataM m, BackendMetadata b) =>
SourceName
-> FunctionName b -> FunctionConfig -> Maybe Text -> m EncJSON
trackFunctionP2 @b SourceName
source FunctionName b
qf FunctionConfig
config Maybe Text
comment

-- | JSON API payload for 'untrack_function':
--
-- https://hasura.io/docs/latest/graphql/core/api-reference/schema-metadata-api/custom-functions.html#untrack-function
data UnTrackFunction b = UnTrackFunction
  { UnTrackFunction b -> FunctionName b
_utfFunction :: FunctionName b,
    UnTrackFunction b -> SourceName
_utfSource :: SourceName
  }

instance (Backend b) => FromJSON (UnTrackFunction b) where
  -- Following was the previous implementation, which while seems to be correct,
  -- has an unexpected behaviour. In the case when @source@ key is present but
  -- @function@ key is absent, it would silently coerce it into a @default@
  -- source. The culprint being the _alternative_ operator, which silently fails
  -- the first parse. This note exists so that we don't try to simplify using
  -- the _alternative_ pattern here.
  -- Previous implementation :-
  -- Consider the following JSON -
  --  {
  --    "source": "custom_source",
  --    "schema": "public",
  --    "name": "my_function"
  --  }
  -- it silently fails parsing the source here because @function@ key is not
  -- present, and proceeds to parse using @withoutSource@ as default source. Now
  -- this is surprising for the user, because they mention @source@ key
  -- explicitly. A better behaviour is to explicitly look for @function@ key if
  -- a @source@ key is present.
  -- >>
  -- parseJSON v = withSource <|> withoutSource
  --   where
  --     withoutSource = UnTrackFunction <$> parseJSON v <*> pure defaultSource
  --     withSource = flip (withObject "UnTrackFunction") v \o -> do
  --                  UnTrackFunction <$> o .: "function"
  --                                  <*> o .:? "source" .!= defaultSource
  parseJSON :: Value -> Parser (UnTrackFunction b)
parseJSON Value
v = ((Object -> Parser (UnTrackFunction b))
 -> Value -> Parser (UnTrackFunction b))
-> Value
-> (Object -> Parser (UnTrackFunction b))
-> Parser (UnTrackFunction b)
forall a b c. (a -> b -> c) -> b -> a -> c
flip (String
-> (Object -> Parser (UnTrackFunction b))
-> Value
-> Parser (UnTrackFunction b)
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"UnTrackFunction") Value
v ((Object -> Parser (UnTrackFunction b))
 -> Parser (UnTrackFunction b))
-> (Object -> Parser (UnTrackFunction b))
-> Parser (UnTrackFunction b)
forall a b. (a -> b) -> a -> b
$ \Object
o -> do
    Maybe SourceName
source <- Object
o Object -> Key -> Parser (Maybe SourceName)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"source"
    case Maybe SourceName
source of
      Just SourceName
src -> (FunctionName b -> SourceName -> UnTrackFunction b)
-> SourceName -> FunctionName b -> UnTrackFunction b
forall a b c. (a -> b -> c) -> b -> a -> c
flip FunctionName b -> SourceName -> UnTrackFunction b
forall (b :: BackendType).
FunctionName b -> SourceName -> UnTrackFunction b
UnTrackFunction SourceName
src (FunctionName b -> UnTrackFunction b)
-> Parser (FunctionName b) -> Parser (UnTrackFunction b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
o Object -> Key -> Parser (FunctionName b)
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"function"
      Maybe SourceName
Nothing -> FunctionName b -> SourceName -> UnTrackFunction b
forall (b :: BackendType).
FunctionName b -> SourceName -> UnTrackFunction b
UnTrackFunction (FunctionName b -> SourceName -> UnTrackFunction b)
-> Parser (FunctionName b)
-> Parser (SourceName -> UnTrackFunction b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Value -> Parser (FunctionName b)
forall a. FromJSON a => Value -> Parser a
parseJSON Value
v Parser (SourceName -> UnTrackFunction b)
-> Parser SourceName -> Parser (UnTrackFunction b)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> SourceName -> Parser SourceName
forall (f :: * -> *) a. Applicative f => a -> f a
pure SourceName
defaultSource

runUntrackFunc ::
  forall b m.
  (CacheRWM m, MonadError QErr m, MetadataM m, BackendMetadata b) =>
  UnTrackFunction b ->
  m EncJSON
runUntrackFunc :: UnTrackFunction b -> m EncJSON
runUntrackFunc (UnTrackFunction FunctionName b
functionName SourceName
sourceName) = do
  m (FunctionInfo b) -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m (FunctionInfo b) -> m ()) -> m (FunctionInfo b) -> m ()
forall a b. (a -> b) -> a -> b
$ SourceName -> FunctionName b -> m (FunctionInfo b)
forall (b :: BackendType) (m :: * -> *).
(QErrM m, CacheRM m, Backend b) =>
SourceName -> FunctionName b -> m (FunctionInfo b)
askFunctionInfo @b SourceName
sourceName FunctionName b
functionName
  m () -> m ()
forall (m :: * -> *) a. (QErrM m, CacheRM m) => m a -> m a
withNewInconsistentObjsCheck (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
    MetadataModifier -> m ()
forall (m :: * -> *).
(MetadataM m, CacheRWM m) =>
MetadataModifier -> m ()
buildSchemaCache (MetadataModifier -> m ()) -> MetadataModifier -> m ()
forall a b. (a -> b) -> a -> b
$
      SourceName -> FunctionName b -> MetadataModifier
forall (b :: BackendType).
Backend b =>
SourceName -> FunctionName b -> MetadataModifier
dropFunctionInMetadata @b SourceName
sourceName FunctionName b
functionName
  EncJSON -> m EncJSON
forall (f :: * -> *) a. Applicative f => a -> f a
pure EncJSON
successMsg

{- Note [Function Permissions]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before we started supporting tracking volatile functions, permissions
for a function was inferred from the target table of the function.
The rationale behind this is that a stable/immutable function does not
modify the database and the data returned by the function is filtered using
the permissions that are specified precisely for that data.
Now consider mutable/volatile functions, we can't automatically infer whether or
not these functions should be exposed for the sole reason that they can modify
the database. This necessitates a permission system for functions.
So, we introduce a new API `pg_create_function_permission` which will
explicitly grant permission to a function to a role. For creating a
function permission, the role must have select permissions configured
for the target table.
Since, this is a breaking change, we enable it only when the graphql-engine
is started with
`--infer-function-permissions`/HASURA_GRAPHQL_INFER_FUNCTION_PERMISSIONS set
to false (by default, it's set to true).
-}

data FunctionPermissionArgument b = FunctionPermissionArgument
  { FunctionPermissionArgument b -> FunctionName b
_afpFunction :: FunctionName b,
    FunctionPermissionArgument b -> SourceName
_afpSource :: SourceName,
    FunctionPermissionArgument b -> RoleName
_afpRole :: RoleName
  }

instance (Backend b) => FromJSON (FunctionPermissionArgument b) where
  parseJSON :: Value -> Parser (FunctionPermissionArgument b)
parseJSON Value
v =
    ((Object -> Parser (FunctionPermissionArgument b))
 -> Value -> Parser (FunctionPermissionArgument b))
-> Value
-> (Object -> Parser (FunctionPermissionArgument b))
-> Parser (FunctionPermissionArgument b)
forall a b c. (a -> b -> c) -> b -> a -> c
flip (String
-> (Object -> Parser (FunctionPermissionArgument b))
-> Value
-> Parser (FunctionPermissionArgument b)
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"FunctionPermissionArgument") Value
v ((Object -> Parser (FunctionPermissionArgument b))
 -> Parser (FunctionPermissionArgument b))
-> (Object -> Parser (FunctionPermissionArgument b))
-> Parser (FunctionPermissionArgument b)
forall a b. (a -> b) -> a -> b
$ \Object
o ->
      FunctionName b
-> SourceName -> RoleName -> FunctionPermissionArgument b
forall (b :: BackendType).
FunctionName b
-> SourceName -> RoleName -> FunctionPermissionArgument b
FunctionPermissionArgument
        (FunctionName b
 -> SourceName -> RoleName -> FunctionPermissionArgument b)
-> Parser (FunctionName b)
-> Parser (SourceName -> RoleName -> FunctionPermissionArgument b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
o Object -> Key -> Parser (FunctionName b)
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"function"
        Parser (SourceName -> RoleName -> FunctionPermissionArgument b)
-> Parser SourceName
-> Parser (RoleName -> FunctionPermissionArgument b)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o Object -> Key -> Parser (Maybe SourceName)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"source" Parser (Maybe SourceName) -> SourceName -> Parser SourceName
forall a. Parser (Maybe a) -> a -> Parser a
.!= SourceName
defaultSource
        Parser (RoleName -> FunctionPermissionArgument b)
-> Parser RoleName -> Parser (FunctionPermissionArgument b)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o Object -> Key -> Parser RoleName
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"role"

runCreateFunctionPermission ::
  forall b m.
  ( CacheRWM m,
    MonadError QErr m,
    MetadataM m,
    BackendMetadata b
  ) =>
  FunctionPermissionArgument b ->
  m EncJSON
runCreateFunctionPermission :: FunctionPermissionArgument b -> m EncJSON
runCreateFunctionPermission (FunctionPermissionArgument FunctionName b
functionName SourceName
source RoleName
role) = do
  Metadata
metadata <- m Metadata
forall (m :: * -> *). MetadataM m => m Metadata
getMetadata
  HashMap SourceName (AnyBackend SourceInfo)
sourceCache <- SchemaCache -> HashMap SourceName (AnyBackend SourceInfo)
scSources (SchemaCache -> HashMap SourceName (AnyBackend SourceInfo))
-> m SchemaCache -> m (HashMap SourceName (AnyBackend SourceInfo))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m SchemaCache
forall (m :: * -> *). CacheRM m => m SchemaCache
askSchemaCache
  FunctionInfo b
functionInfo <- SourceName -> FunctionName b -> m (FunctionInfo b)
forall (b :: BackendType) (m :: * -> *).
(QErrM m, CacheRM m, Backend b) =>
SourceName -> FunctionName b -> m (FunctionInfo b)
askFunctionInfo @b SourceName
source FunctionName b
functionName
  Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Metadata -> SourceName -> FunctionName b -> RoleName -> Bool
forall (b :: BackendType).
BackendMetadata b =>
Metadata -> SourceName -> FunctionName b -> RoleName -> Bool
doesFunctionPermissionExist @b Metadata
metadata SourceName
source FunctionName b
functionName RoleName
role) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
    Code -> Text -> m ()
forall (m :: * -> *) a. QErrM m => Code -> Text -> m a
throw400 Code
AlreadyExists (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$
      Text
"permission of role "
        Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> RoleName
role RoleName -> Text -> Text
forall t. ToTxt t => t -> Text -> Text
<<> Text
" already exists for function "
        Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> FunctionName b
functionName FunctionName b -> Text -> Text
forall t. ToTxt t => t -> Text -> Text
<<> Text
" in source: " Text -> SourceName -> Text
forall t. ToTxt t => Text -> t -> Text
<>> SourceName
source
  TableInfo b
functionTableInfo <-
    SourceName
-> TableName b
-> HashMap SourceName (AnyBackend SourceInfo)
-> Maybe (TableInfo b)
forall (b :: BackendType).
Backend b =>
SourceName
-> TableName b
-> HashMap SourceName (AnyBackend SourceInfo)
-> Maybe (TableInfo b)
unsafeTableInfo @b SourceName
source (FunctionInfo b -> TableName b
forall (b :: BackendType). FunctionInfo b -> TableName b
_fiReturnType FunctionInfo b
functionInfo) HashMap SourceName (AnyBackend SourceInfo)
sourceCache
      Maybe (TableInfo b) -> m (TableInfo b) -> m (TableInfo b)
forall (m :: * -> *) a. Applicative m => Maybe a -> m a -> m a
`onNothing` Code -> Text -> m (TableInfo b)
forall (m :: * -> *) a. QErrM m => Code -> Text -> m a
throw400 Code
NotExists (Text
"function's return table " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> FunctionInfo b -> TableName b
forall (b :: BackendType). FunctionInfo b -> TableName b
_fiReturnType FunctionInfo b
functionInfo TableName b -> Text -> Text
forall t. ToTxt t => t -> Text -> Text
<<> Text
" not found in the cache")
  Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (RoleName
role RoleName -> HashMap RoleName (RolePermInfo b) -> Bool
forall k a. (Eq k, Hashable k) => k -> HashMap k a -> Bool
`Map.member` TableInfo b -> HashMap RoleName (RolePermInfo b)
forall (b :: BackendType). TableInfo b -> RolePermInfoMap b
_tiRolePermInfoMap TableInfo b
functionTableInfo) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
    Code -> Text -> m ()
forall (m :: * -> *) a. QErrM m => Code -> Text -> m a
throw400 Code
NotSupported (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$
      Text
"function permission can only be added when the function's return table "
        Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> FunctionInfo b -> TableName b
forall (b :: BackendType). FunctionInfo b -> TableName b
_fiReturnType FunctionInfo b
functionInfo TableName b -> Text -> Text
forall t. ToTxt t => t -> Text -> Text
<<> Text
" has select permission configured for role: " Text -> RoleName -> Text
forall t. ToTxt t => Text -> t -> Text
<>> RoleName
role
  MetadataObjId -> MetadataModifier -> m ()
forall (m :: * -> *).
(QErrM m, CacheRWM m, MetadataM m) =>
MetadataObjId -> MetadataModifier -> m ()
buildSchemaCacheFor
    ( SourceName -> AnyBackend SourceMetadataObjId -> MetadataObjId
MOSourceObjId SourceName
source (AnyBackend SourceMetadataObjId -> MetadataObjId)
-> AnyBackend SourceMetadataObjId -> MetadataObjId
forall a b. (a -> b) -> a -> b
$
        SourceMetadataObjId b -> AnyBackend SourceMetadataObjId
forall (b :: BackendType) (i :: BackendType -> *).
HasTag b =>
i b -> AnyBackend i
AB.mkAnyBackend (FunctionName b -> RoleName -> SourceMetadataObjId b
forall (b :: BackendType).
FunctionName b -> RoleName -> SourceMetadataObjId b
SMOFunctionPermission @b FunctionName b
functionName RoleName
role)
    )
    (MetadataModifier -> m ()) -> MetadataModifier -> m ()
forall a b. (a -> b) -> a -> b
$ (Metadata -> Metadata) -> MetadataModifier
MetadataModifier ((Metadata -> Metadata) -> MetadataModifier)
-> (Metadata -> Metadata) -> MetadataModifier
forall a b. (a -> b) -> a -> b
$
      (Sources -> Identity Sources) -> Metadata -> Identity Metadata
Lens' Metadata Sources
metaSources
        ((Sources -> Identity Sources) -> Metadata -> Identity Metadata)
-> (([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
    -> Sources -> Identity Sources)
-> ([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
-> Metadata
-> Identity Metadata
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Index Sources -> Traversal' Sources (IxValue Sources)
forall m. Ixed m => Index m -> Traversal' m (IxValue m)
ix
          Index Sources
SourceName
source
        ((BackendSourceMetadata -> Identity BackendSourceMetadata)
 -> Sources -> Identity Sources)
-> (([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
    -> BackendSourceMetadata -> Identity BackendSourceMetadata)
-> ([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
-> Sources
-> Identity Sources
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (SourceMetadata b -> Identity (SourceMetadata b))
-> BackendSourceMetadata -> Identity BackendSourceMetadata
forall (b :: BackendType).
Backend b =>
Prism' BackendSourceMetadata (SourceMetadata b)
toSourceMetadata
        ((SourceMetadata b -> Identity (SourceMetadata b))
 -> BackendSourceMetadata -> Identity BackendSourceMetadata)
-> (([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
    -> SourceMetadata b -> Identity (SourceMetadata b))
-> ([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
-> BackendSourceMetadata
-> Identity BackendSourceMetadata
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall (b :: BackendType). Lens' (SourceMetadata b) (Functions b)
Lens'
  (SourceMetadata b)
  (InsOrdHashMap (FunctionName b) (FunctionMetadata b))
smFunctions @b)
        ((InsOrdHashMap (FunctionName b) (FunctionMetadata b)
  -> Identity (InsOrdHashMap (FunctionName b) (FunctionMetadata b)))
 -> SourceMetadata b -> Identity (SourceMetadata b))
-> (([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
    -> InsOrdHashMap (FunctionName b) (FunctionMetadata b)
    -> Identity (InsOrdHashMap (FunctionName b) (FunctionMetadata b)))
-> ([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
-> SourceMetadata b
-> Identity (SourceMetadata b)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Index (InsOrdHashMap (FunctionName b) (FunctionMetadata b))
-> Traversal'
     (InsOrdHashMap (FunctionName b) (FunctionMetadata b))
     (IxValue (InsOrdHashMap (FunctionName b) (FunctionMetadata b)))
forall m. Ixed m => Index m -> Traversal' m (IxValue m)
ix Index (InsOrdHashMap (FunctionName b) (FunctionMetadata b))
FunctionName b
functionName
        ((FunctionMetadata b -> Identity (FunctionMetadata b))
 -> InsOrdHashMap (FunctionName b) (FunctionMetadata b)
 -> Identity (InsOrdHashMap (FunctionName b) (FunctionMetadata b)))
-> (([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
    -> FunctionMetadata b -> Identity (FunctionMetadata b))
-> ([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
-> InsOrdHashMap (FunctionName b) (FunctionMetadata b)
-> Identity (InsOrdHashMap (FunctionName b) (FunctionMetadata b))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
-> FunctionMetadata b -> Identity (FunctionMetadata b)
forall (b :: BackendType).
Lens' (FunctionMetadata b) [FunctionPermissionInfo]
fmPermissions
        (([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
 -> Metadata -> Identity Metadata)
-> ([FunctionPermissionInfo] -> [FunctionPermissionInfo])
-> Metadata
-> Metadata
forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ (:) (RoleName -> FunctionPermissionInfo
FunctionPermissionInfo RoleName
role)
  EncJSON -> m EncJSON
forall (f :: * -> *) a. Applicative f => a -> f a
pure EncJSON
successMsg

dropFunctionPermissionInMetadata ::
  forall b.
  (BackendMetadata b) =>
  SourceName ->
  FunctionName b ->
  RoleName ->
  MetadataModifier
dropFunctionPermissionInMetadata :: SourceName -> FunctionName b -> RoleName -> MetadataModifier
dropFunctionPermissionInMetadata SourceName
source FunctionName b
function RoleName
role =
  (Metadata -> Metadata) -> MetadataModifier
MetadataModifier ((Metadata -> Metadata) -> MetadataModifier)
-> (Metadata -> Metadata) -> MetadataModifier
forall a b. (a -> b) -> a -> b
$
    (Sources -> Identity Sources) -> Metadata -> Identity Metadata
Lens' Metadata Sources
metaSources ((Sources -> Identity Sources) -> Metadata -> Identity Metadata)
-> (([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
    -> Sources -> Identity Sources)
-> ([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
-> Metadata
-> Identity Metadata
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Index Sources -> Traversal' Sources (IxValue Sources)
forall m. Ixed m => Index m -> Traversal' m (IxValue m)
ix Index Sources
SourceName
source ((BackendSourceMetadata -> Identity BackendSourceMetadata)
 -> Sources -> Identity Sources)
-> (([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
    -> BackendSourceMetadata -> Identity BackendSourceMetadata)
-> ([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
-> Sources
-> Identity Sources
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (SourceMetadata b -> Identity (SourceMetadata b))
-> BackendSourceMetadata -> Identity BackendSourceMetadata
forall (b :: BackendType).
Backend b =>
Prism' BackendSourceMetadata (SourceMetadata b)
toSourceMetadata ((SourceMetadata b -> Identity (SourceMetadata b))
 -> BackendSourceMetadata -> Identity BackendSourceMetadata)
-> (([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
    -> SourceMetadata b -> Identity (SourceMetadata b))
-> ([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
-> BackendSourceMetadata
-> Identity BackendSourceMetadata
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall (b :: BackendType). Lens' (SourceMetadata b) (Functions b)
Lens'
  (SourceMetadata b)
  (InsOrdHashMap (FunctionName b) (FunctionMetadata b))
smFunctions @b) ((InsOrdHashMap (FunctionName b) (FunctionMetadata b)
  -> Identity (InsOrdHashMap (FunctionName b) (FunctionMetadata b)))
 -> SourceMetadata b -> Identity (SourceMetadata b))
-> (([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
    -> InsOrdHashMap (FunctionName b) (FunctionMetadata b)
    -> Identity (InsOrdHashMap (FunctionName b) (FunctionMetadata b)))
-> ([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
-> SourceMetadata b
-> Identity (SourceMetadata b)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Index (InsOrdHashMap (FunctionName b) (FunctionMetadata b))
-> Traversal'
     (InsOrdHashMap (FunctionName b) (FunctionMetadata b))
     (IxValue (InsOrdHashMap (FunctionName b) (FunctionMetadata b)))
forall m. Ixed m => Index m -> Traversal' m (IxValue m)
ix Index (InsOrdHashMap (FunctionName b) (FunctionMetadata b))
FunctionName b
function ((FunctionMetadata b -> Identity (FunctionMetadata b))
 -> InsOrdHashMap (FunctionName b) (FunctionMetadata b)
 -> Identity (InsOrdHashMap (FunctionName b) (FunctionMetadata b)))
-> (([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
    -> FunctionMetadata b -> Identity (FunctionMetadata b))
-> ([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
-> InsOrdHashMap (FunctionName b) (FunctionMetadata b)
-> Identity (InsOrdHashMap (FunctionName b) (FunctionMetadata b))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
-> FunctionMetadata b -> Identity (FunctionMetadata b)
forall (b :: BackendType).
Lens' (FunctionMetadata b) [FunctionPermissionInfo]
fmPermissions (([FunctionPermissionInfo] -> Identity [FunctionPermissionInfo])
 -> Metadata -> Identity Metadata)
-> ([FunctionPermissionInfo] -> [FunctionPermissionInfo])
-> Metadata
-> Metadata
forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ (FunctionPermissionInfo -> Bool)
-> [FunctionPermissionInfo] -> [FunctionPermissionInfo]
forall a. (a -> Bool) -> [a] -> [a]
filter (RoleName -> RoleName -> Bool
forall a. Eq a => a -> a -> Bool
(/=) RoleName
role (RoleName -> Bool)
-> (FunctionPermissionInfo -> RoleName)
-> FunctionPermissionInfo
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FunctionPermissionInfo -> RoleName
_fpmRole)

doesFunctionPermissionExist :: forall b. (BackendMetadata b) => Metadata -> SourceName -> FunctionName b -> RoleName -> Bool
doesFunctionPermissionExist :: Metadata -> SourceName -> FunctionName b -> RoleName -> Bool
doesFunctionPermissionExist Metadata
metadata SourceName
sourceName FunctionName b
functionName RoleName
roleName =
  (FunctionPermissionInfo -> Bool)
-> [FunctionPermissionInfo] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any ((RoleName -> RoleName -> Bool
forall a. Eq a => a -> a -> Bool
== RoleName
roleName) (RoleName -> Bool)
-> (FunctionPermissionInfo -> RoleName)
-> FunctionPermissionInfo
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FunctionPermissionInfo -> RoleName
_fpmRole) ([FunctionPermissionInfo] -> Bool)
-> [FunctionPermissionInfo] -> Bool
forall a b. (a -> b) -> a -> b
$ Metadata
metadata Metadata
-> Getting
     [FunctionPermissionInfo] Metadata [FunctionPermissionInfo]
-> [FunctionPermissionInfo]
forall s a. s -> Getting a s a -> a
^. ((Sources -> Const [FunctionPermissionInfo] Sources)
-> Metadata -> Const [FunctionPermissionInfo] Metadata
Lens' Metadata Sources
metaSources ((Sources -> Const [FunctionPermissionInfo] Sources)
 -> Metadata -> Const [FunctionPermissionInfo] Metadata)
-> (([FunctionPermissionInfo]
     -> Const [FunctionPermissionInfo] [FunctionPermissionInfo])
    -> Sources -> Const [FunctionPermissionInfo] Sources)
-> Getting
     [FunctionPermissionInfo] Metadata [FunctionPermissionInfo]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Index Sources -> Traversal' Sources (IxValue Sources)
forall m. Ixed m => Index m -> Traversal' m (IxValue m)
ix Index Sources
SourceName
sourceName ((BackendSourceMetadata
  -> Const [FunctionPermissionInfo] BackendSourceMetadata)
 -> Sources -> Const [FunctionPermissionInfo] Sources)
-> (([FunctionPermissionInfo]
     -> Const [FunctionPermissionInfo] [FunctionPermissionInfo])
    -> BackendSourceMetadata
    -> Const [FunctionPermissionInfo] BackendSourceMetadata)
-> ([FunctionPermissionInfo]
    -> Const [FunctionPermissionInfo] [FunctionPermissionInfo])
-> Sources
-> Const [FunctionPermissionInfo] Sources
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (SourceMetadata b
 -> Const [FunctionPermissionInfo] (SourceMetadata b))
-> BackendSourceMetadata
-> Const [FunctionPermissionInfo] BackendSourceMetadata
forall (b :: BackendType).
Backend b =>
Prism' BackendSourceMetadata (SourceMetadata b)
toSourceMetadata ((SourceMetadata b
  -> Const [FunctionPermissionInfo] (SourceMetadata b))
 -> BackendSourceMetadata
 -> Const [FunctionPermissionInfo] BackendSourceMetadata)
-> (([FunctionPermissionInfo]
     -> Const [FunctionPermissionInfo] [FunctionPermissionInfo])
    -> SourceMetadata b
    -> Const [FunctionPermissionInfo] (SourceMetadata b))
-> ([FunctionPermissionInfo]
    -> Const [FunctionPermissionInfo] [FunctionPermissionInfo])
-> BackendSourceMetadata
-> Const [FunctionPermissionInfo] BackendSourceMetadata
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall (b :: BackendType). Lens' (SourceMetadata b) (Functions b)
Lens'
  (SourceMetadata b)
  (InsOrdHashMap (FunctionName b) (FunctionMetadata b))
smFunctions @b) ((InsOrdHashMap (FunctionName b) (FunctionMetadata b)
  -> Const
       [FunctionPermissionInfo]
       (InsOrdHashMap (FunctionName b) (FunctionMetadata b)))
 -> SourceMetadata b
 -> Const [FunctionPermissionInfo] (SourceMetadata b))
-> (([FunctionPermissionInfo]
     -> Const [FunctionPermissionInfo] [FunctionPermissionInfo])
    -> InsOrdHashMap (FunctionName b) (FunctionMetadata b)
    -> Const
         [FunctionPermissionInfo]
         (InsOrdHashMap (FunctionName b) (FunctionMetadata b)))
-> ([FunctionPermissionInfo]
    -> Const [FunctionPermissionInfo] [FunctionPermissionInfo])
-> SourceMetadata b
-> Const [FunctionPermissionInfo] (SourceMetadata b)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Index (InsOrdHashMap (FunctionName b) (FunctionMetadata b))
-> Traversal'
     (InsOrdHashMap (FunctionName b) (FunctionMetadata b))
     (IxValue (InsOrdHashMap (FunctionName b) (FunctionMetadata b)))
forall m. Ixed m => Index m -> Traversal' m (IxValue m)
ix Index (InsOrdHashMap (FunctionName b) (FunctionMetadata b))
FunctionName b
functionName ((FunctionMetadata b
  -> Const [FunctionPermissionInfo] (FunctionMetadata b))
 -> InsOrdHashMap (FunctionName b) (FunctionMetadata b)
 -> Const
      [FunctionPermissionInfo]
      (InsOrdHashMap (FunctionName b) (FunctionMetadata b)))
-> (([FunctionPermissionInfo]
     -> Const [FunctionPermissionInfo] [FunctionPermissionInfo])
    -> FunctionMetadata b
    -> Const [FunctionPermissionInfo] (FunctionMetadata b))
-> ([FunctionPermissionInfo]
    -> Const [FunctionPermissionInfo] [FunctionPermissionInfo])
-> InsOrdHashMap (FunctionName b) (FunctionMetadata b)
-> Const
     [FunctionPermissionInfo]
     (InsOrdHashMap (FunctionName b) (FunctionMetadata b))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([FunctionPermissionInfo]
 -> Const [FunctionPermissionInfo] [FunctionPermissionInfo])
-> FunctionMetadata b
-> Const [FunctionPermissionInfo] (FunctionMetadata b)
forall (b :: BackendType).
Lens' (FunctionMetadata b) [FunctionPermissionInfo]
fmPermissions)

runDropFunctionPermission ::
  forall m b.
  ( CacheRWM m,
    MonadError QErr m,
    MetadataM m,
    BackendMetadata b
  ) =>
  FunctionPermissionArgument b ->
  m EncJSON
runDropFunctionPermission :: FunctionPermissionArgument b -> m EncJSON
runDropFunctionPermission (FunctionPermissionArgument FunctionName b
functionName SourceName
source RoleName
role) = do
  Metadata
metadata <- m Metadata
forall (m :: * -> *). MetadataM m => m Metadata
getMetadata
  Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Metadata -> SourceName -> FunctionName b -> RoleName -> Bool
forall (b :: BackendType).
BackendMetadata b =>
Metadata -> SourceName -> FunctionName b -> RoleName -> Bool
doesFunctionPermissionExist @b Metadata
metadata SourceName
source FunctionName b
functionName RoleName
role) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
    Code -> Text -> m ()
forall (m :: * -> *) a. QErrM m => Code -> Text -> m a
throw400 Code
NotExists (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$
      Text
"permission of role "
        Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> RoleName
role RoleName -> Text -> Text
forall t. ToTxt t => t -> Text -> Text
<<> Text
" does not exist for function "
        Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> FunctionName b
functionName FunctionName b -> Text -> Text
forall t. ToTxt t => t -> Text -> Text
<<> Text
" in source: " Text -> SourceName -> Text
forall t. ToTxt t => Text -> t -> Text
<>> SourceName
source
  MetadataObjId -> MetadataModifier -> m ()
forall (m :: * -> *).
(QErrM m, CacheRWM m, MetadataM m) =>
MetadataObjId -> MetadataModifier -> m ()
buildSchemaCacheFor
    ( SourceName -> AnyBackend SourceMetadataObjId -> MetadataObjId
MOSourceObjId SourceName
source (AnyBackend SourceMetadataObjId -> MetadataObjId)
-> AnyBackend SourceMetadataObjId -> MetadataObjId
forall a b. (a -> b) -> a -> b
$
        SourceMetadataObjId b -> AnyBackend SourceMetadataObjId
forall (b :: BackendType) (i :: BackendType -> *).
HasTag b =>
i b -> AnyBackend i
AB.mkAnyBackend (SourceMetadataObjId b -> AnyBackend SourceMetadataObjId)
-> SourceMetadataObjId b -> AnyBackend SourceMetadataObjId
forall a b. (a -> b) -> a -> b
$
          FunctionName b -> RoleName -> SourceMetadataObjId b
forall (b :: BackendType).
FunctionName b -> RoleName -> SourceMetadataObjId b
SMOFunctionPermission @b FunctionName b
functionName RoleName
role
    )
    (MetadataModifier -> m ()) -> MetadataModifier -> m ()
forall a b. (a -> b) -> a -> b
$ SourceName -> FunctionName b -> RoleName -> MetadataModifier
forall (b :: BackendType).
BackendMetadata b =>
SourceName -> FunctionName b -> RoleName -> MetadataModifier
dropFunctionPermissionInMetadata @b SourceName
source FunctionName b
functionName RoleName
role
  EncJSON -> m EncJSON
forall (f :: * -> *) a. Applicative f => a -> f a
pure EncJSON
successMsg

-- | Represents the payload of the API command 'pg_set_function_customization'.
--
--   See the Hasura API reference for a detailed description.
data SetFunctionCustomization b = SetFunctionCustomization
  { SetFunctionCustomization b -> SourceName
_sfcSource :: SourceName,
    SetFunctionCustomization b -> FunctionName b
_sfcFunction :: FunctionName b,
    SetFunctionCustomization b -> FunctionConfig
_sfcConfiguration :: FunctionConfig
  }

deriving instance Backend b => Show (SetFunctionCustomization b)

deriving instance Backend b => Eq (SetFunctionCustomization b)

instance (Backend b) => FromJSON (SetFunctionCustomization b) where
  parseJSON :: Value -> Parser (SetFunctionCustomization b)
parseJSON = String
-> (Object -> Parser (SetFunctionCustomization b))
-> Value
-> Parser (SetFunctionCustomization b)
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"set function customization" ((Object -> Parser (SetFunctionCustomization b))
 -> Value -> Parser (SetFunctionCustomization b))
-> (Object -> Parser (SetFunctionCustomization b))
-> Value
-> Parser (SetFunctionCustomization b)
forall a b. (a -> b) -> a -> b
$ \Object
o ->
    SourceName
-> FunctionName b -> FunctionConfig -> SetFunctionCustomization b
forall (b :: BackendType).
SourceName
-> FunctionName b -> FunctionConfig -> SetFunctionCustomization b
SetFunctionCustomization
      (SourceName
 -> FunctionName b -> FunctionConfig -> SetFunctionCustomization b)
-> Parser SourceName
-> Parser
     (FunctionName b -> FunctionConfig -> SetFunctionCustomization b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
o Object -> Key -> Parser (Maybe SourceName)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"source" Parser (Maybe SourceName) -> SourceName -> Parser SourceName
forall a. Parser (Maybe a) -> a -> Parser a
.!= SourceName
defaultSource
      Parser
  (FunctionName b -> FunctionConfig -> SetFunctionCustomization b)
-> Parser (FunctionName b)
-> Parser (FunctionConfig -> SetFunctionCustomization b)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o Object -> Key -> Parser (FunctionName b)
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"function"
      Parser (FunctionConfig -> SetFunctionCustomization b)
-> Parser FunctionConfig -> Parser (SetFunctionCustomization b)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o Object -> Key -> Parser FunctionConfig
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"configuration"

-- | Changes the custom names of a function. Used in the API command 'pg_set_function_customization'.
runSetFunctionCustomization ::
  forall b m.
  (QErrM m, CacheRWM m, MetadataM m, Backend b, BackendMetadata b) =>
  SetFunctionCustomization b ->
  m EncJSON
runSetFunctionCustomization :: SetFunctionCustomization b -> m EncJSON
runSetFunctionCustomization (SetFunctionCustomization SourceName
source FunctionName b
function FunctionConfig
config) = do
  m (FunctionInfo b) -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m (FunctionInfo b) -> m ()) -> m (FunctionInfo b) -> m ()
forall a b. (a -> b) -> a -> b
$ SourceName -> FunctionName b -> m (FunctionInfo b)
forall (b :: BackendType) (m :: * -> *).
(QErrM m, CacheRM m, Backend b) =>
SourceName -> FunctionName b -> m (FunctionInfo b)
askFunctionInfo @b SourceName
source FunctionName b
function
  MetadataObjId -> MetadataModifier -> m ()
forall (m :: * -> *).
(QErrM m, CacheRWM m, MetadataM m) =>
MetadataObjId -> MetadataModifier -> m ()
buildSchemaCacheFor
    (SourceName -> AnyBackend SourceMetadataObjId -> MetadataObjId
MOSourceObjId SourceName
source (AnyBackend SourceMetadataObjId -> MetadataObjId)
-> AnyBackend SourceMetadataObjId -> MetadataObjId
forall a b. (a -> b) -> a -> b
$ SourceMetadataObjId b -> AnyBackend SourceMetadataObjId
forall (b :: BackendType) (i :: BackendType -> *).
HasTag b =>
i b -> AnyBackend i
AB.mkAnyBackend (SourceMetadataObjId b -> AnyBackend SourceMetadataObjId)
-> SourceMetadataObjId b -> AnyBackend SourceMetadataObjId
forall a b. (a -> b) -> a -> b
$ FunctionName b -> SourceMetadataObjId b
forall (b :: BackendType). FunctionName b -> SourceMetadataObjId b
SMOFunction @b FunctionName b
function)
    (MetadataModifier -> m ()) -> MetadataModifier -> m ()
forall a b. (a -> b) -> a -> b
$ (Metadata -> Metadata) -> MetadataModifier
MetadataModifier ((Metadata -> Metadata) -> MetadataModifier)
-> (Metadata -> Metadata) -> MetadataModifier
forall a b. (a -> b) -> a -> b
$
      ((SourceName
-> FunctionName b -> ASetter' Metadata (FunctionMetadata b)
forall (b :: BackendType).
Backend b =>
SourceName
-> FunctionName b -> ASetter' Metadata (FunctionMetadata b)
functionMetadataSetter @b SourceName
source FunctionName b
function) ASetter' Metadata (FunctionMetadata b)
-> ((FunctionConfig -> Identity FunctionConfig)
    -> FunctionMetadata b -> Identity (FunctionMetadata b))
-> (FunctionConfig -> Identity FunctionConfig)
-> Metadata
-> Identity Metadata
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (FunctionConfig -> Identity FunctionConfig)
-> FunctionMetadata b -> Identity (FunctionMetadata b)
forall (b :: BackendType).
Lens' (FunctionMetadata b) FunctionConfig
fmConfiguration) ((FunctionConfig -> Identity FunctionConfig)
 -> Metadata -> Identity Metadata)
-> FunctionConfig -> Metadata -> Metadata
forall s t a b. ASetter s t a b -> b -> s -> t
.~ FunctionConfig
config
  EncJSON -> m EncJSON
forall (m :: * -> *) a. Monad m => a -> m a
return EncJSON
successMsg