Add metadata commands for custom return type permissions

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/8587
Co-authored-by: Daniel Harvey <4729125+danieljharvey@users.noreply.github.com>
GitOrigin-RevId: 660f2eda9cf1c7c612d66745064b3998c77804e0
This commit is contained in:
Tom Harding 2023-03-31 17:27:49 +01:00 committed by hasura-bot
parent 79682e0598
commit 7e06b30e4d
13 changed files with 408 additions and 49 deletions

View File

@ -39,10 +39,12 @@ spec = do
] ]
Fixture.hgeWithEnv [(featureFlagForLogicalModels, "True")] do Fixture.hgeWithEnv [(featureFlagForLogicalModels, "True")] do
-- need to run isolated
traverse_ traverse_
(Fixture.runClean fixtures) (Fixture.runClean fixtures)
[testImplementation] [ testImplementation,
testPermissions,
testPermissionFailures
]
-- ** Setup and teardown -- ** Setup and teardown
@ -182,3 +184,243 @@ testImplementation = do
error: Custom type "nice" still being used by logical model "logical_model". error: Custom type "nice" still being used by logical model "logical_model".
path: $.args path: $.args
|] |]
----------------------
-- Test permissions --
----------------------
testPermissions :: SpecWith TestEnvironment
testPermissions = do
let customReturnType :: Schema.CustomType
customReturnType =
(Schema.customType "divided_stuff")
{ Schema.customTypeColumns =
[ (Schema.logicalModelColumn "divided" Schema.TInt)
{ Schema.logicalModelColumnDescription = Just "a divided thing"
}
]
}
describe "Permissions" do
it "Adds a custom return type with a select permission and returns a 200" $ \testEnvironment -> do
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
sourceName = BackendType.backendSourceName backendTypeMetadata
backendType = BackendType.backendTypeString backendTypeMetadata
Schema.trackCustomType sourceName customReturnType testEnvironment
shouldReturnYaml
testEnvironment
( GraphqlEngine.postMetadata
testEnvironment
[interpolateYaml|
type: bulk
args:
- type: #{backendType}_create_custom_return_type_select_permission
args:
source: #{sourceName}
name: divided_stuff
role: "test"
permission:
columns:
- divided
filter: {}
|]
)
[yaml|
- message: success
|]
shouldReturnYaml
testEnvironment
( GraphqlEngine.postMetadata
testEnvironment
[interpolateYaml|
type: #{backendType}_get_custom_return_type
args:
source: #{sourceName}
|]
)
[interpolateYaml|
- name: divided_stuff
description: ''
fields:
- description: a divided thing
name: divided
nullable: false
type: integer
select_permissions:
- role: "test"
permission:
columns:
- divided
filter: {}
|]
it "Adds a logical model, removes it, and returns 200" $ \testEnvironment -> do
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
sourceName = BackendType.backendSourceName backendTypeMetadata
backendType = BackendType.backendTypeString backendTypeMetadata
Schema.trackCustomType sourceName customReturnType testEnvironment
shouldReturnYaml
testEnvironment
( GraphqlEngine.postMetadata
testEnvironment
[interpolateYaml|
type: bulk
args:
- type: #{backendType}_create_custom_return_type_select_permission
args:
source: #{sourceName}
name: divided_stuff
role: "test"
permission:
columns:
- divided
filter: {}
- type: #{backendType}_drop_custom_return_type_select_permission
args:
source: #{sourceName}
name: divided_stuff
role: "test"
|]
)
[yaml|
- message: success
- message: success
|]
shouldReturnYaml
testEnvironment
( GraphqlEngine.postMetadata
testEnvironment
[interpolateYaml|
type: #{backendType}_get_custom_return_type
args:
source: #{sourceName}
|]
)
[interpolateYaml|
- name: divided_stuff
description: ''
fields:
- description: a divided thing
name: divided
nullable: false
type: integer
|]
testPermissionFailures :: SpecWith TestEnvironment
testPermissionFailures = do
describe "Permission failures" do
it "Fails to adds a select permission to a nonexisting source" $ \testEnvironment -> do
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
backendType = BackendType.backendTypeString backendTypeMetadata
shouldReturnYaml
testEnvironment
( GraphqlEngine.postMetadataWithStatus
400
testEnvironment
[interpolateYaml|
type: bulk
args:
- type: #{backendType}_create_custom_return_type_select_permission
args:
source: made_up_source
name: divided_stuff
role: "test"
permission:
columns:
- divided
filter: {}
|]
)
[yaml|
code: not-found
error: "Source \"made_up_source\" not found."
path: "$.args[0].args"
|]
it "Fails to adds a select permission to a nonexisting custom return type" $ \testEnvironment -> do
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
sourceName = BackendType.backendSourceName backendTypeMetadata
backendType = BackendType.backendTypeString backendTypeMetadata
shouldReturnYaml
testEnvironment
( GraphqlEngine.postMetadataWithStatus
400
testEnvironment
[interpolateYaml|
type: bulk
args:
- type: #{backendType}_create_custom_return_type_select_permission
args:
source: #{sourceName}
name: made_up_custom_return_type
role: "test"
permission:
columns:
- divided
filter: {}
|]
)
[interpolateYaml|
code: "not-found"
error: Custom return type "made_up_custom_return_type" not found in source "#{sourceName}".
path: "$.args[0].args"
|]
it "Fails to drop a select permission on a nonexisting source" $ \testEnvironment -> do
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
backendType = BackendType.backendTypeString backendTypeMetadata
shouldReturnYaml
testEnvironment
( GraphqlEngine.postMetadataWithStatus
400
testEnvironment
[interpolateYaml|
type: #{backendType}_drop_custom_return_type_select_permission
args:
source: made_up_source
name: made_up_custom_return_type
role: "test"
permission:
columns:
- divided
filter: {}
|]
)
[interpolateYaml|
code: not-found
error: "Source \"made_up_source\" not found."
path: "$.args"
|]
it "Fails to drop a select permission from a nonexisting custom return type" $ \testEnvironment -> do
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
sourceName = BackendType.backendSourceName backendTypeMetadata
backendType = BackendType.backendTypeString backendTypeMetadata
shouldReturnYaml
testEnvironment
( GraphqlEngine.postMetadataWithStatus
400
testEnvironment
[interpolateYaml|
type: #{backendType}_drop_custom_return_type_select_permission
args:
source: #{sourceName}
name: made_up_custom_return_type
role: "test"
|]
)
[interpolateYaml|
code: "not-found"
error: Custom return type "made_up_custom_return_type" not found in source "#{sourceName}".
path: "$.args"
|]

View File

@ -67,14 +67,13 @@ spec = do
] ]
Fixture.hgeWithEnv [(featureFlagForLogicalModels, "True")] do Fixture.hgeWithEnv [(featureFlagForLogicalModels, "True")] do
-- do not need to run isolated
traverse_ traverse_
(Fixture.runClean fixtures) (Fixture.runClean fixtures)
[testAdminAccess, testPermissionFailures] [ testAdminAccess,
-- need to run isolated testImplementation,
traverse_ testPermissions,
(Fixture.runClean fixtures) testPermissionFailures
[testImplementation, testPermissions] ]
metadataHandlingWhenDisabledSpec metadataHandlingWhenDisabledSpec

View File

@ -164,7 +164,7 @@ logicalModelToPreparedStatement customReturnType model = do
returnedColumnNames :: Text returnedColumnNames :: Text
returnedColumnNames = returnedColumnNames =
commaSeparated $ InsOrd.keys (_ctmFields customReturnType) commaSeparated $ InsOrd.keys (_crtmFields customReturnType)
wrapInCTE :: Text -> Text wrapInCTE :: Text -> Text
wrapInCTE query = wrapInCTE query =

View File

@ -9,6 +9,10 @@ module Hasura.CustomReturnType.API
runTrackCustomReturnType, runTrackCustomReturnType,
runUntrackCustomReturnType, runUntrackCustomReturnType,
dropCustomReturnTypeInMetadata, dropCustomReturnTypeInMetadata,
CreateCustomReturnTypePermission (..),
DropCustomReturnTypePermission (..),
runCreateSelectCustomReturnTypePermission,
runDropSelectCustomReturnTypePermission,
getCustomTypes, getCustomTypes,
module Hasura.CustomReturnType.Types, module Hasura.CustomReturnType.Types,
) )
@ -22,7 +26,7 @@ import Data.HashMap.Strict.InsOrd qualified as InsOrd
import Data.HashMap.Strict.InsOrd.Extended qualified as OMap import Data.HashMap.Strict.InsOrd.Extended qualified as OMap
import Data.Text.Extended (toTxt, (<<>)) import Data.Text.Extended (toTxt, (<<>))
import Hasura.Base.Error import Hasura.Base.Error
import Hasura.CustomReturnType.Metadata (CustomReturnTypeMetadata (..)) import Hasura.CustomReturnType.Metadata (CustomReturnTypeMetadata (..), crtmSelectPermissions)
import Hasura.CustomReturnType.Types (CustomReturnTypeName) import Hasura.CustomReturnType.Types (CustomReturnTypeName)
import Hasura.EncJSON import Hasura.EncJSON
import Hasura.LogicalModel.Metadata (LogicalModelMetadata (..)) import Hasura.LogicalModel.Metadata (LogicalModelMetadata (..))
@ -30,15 +34,17 @@ import Hasura.LogicalModel.Types (NullableScalarType, nullableScalarTypeMapCodec
import Hasura.Metadata.DTO.Utils (codecNamePrefix) import Hasura.Metadata.DTO.Utils (codecNamePrefix)
import Hasura.Prelude import Hasura.Prelude
import Hasura.RQL.Types.Backend (Backend (..)) import Hasura.RQL.Types.Backend (Backend (..))
import Hasura.RQL.Types.Common (SourceName, sourceNameToText, successMsg) import Hasura.RQL.Types.Common (SourceName, defaultSource, sourceNameToText, successMsg)
import Hasura.RQL.Types.Metadata import Hasura.RQL.Types.Metadata
import Hasura.RQL.Types.Metadata.Backend import Hasura.RQL.Types.Metadata.Backend
import Hasura.RQL.Types.Metadata.Object import Hasura.RQL.Types.Metadata.Object
import Hasura.RQL.Types.Permission (PermDef (_pdRole), SelPerm)
import Hasura.RQL.Types.SchemaCache.Build import Hasura.RQL.Types.SchemaCache.Build
import Hasura.SQL.AnyBackend qualified as AB import Hasura.SQL.AnyBackend qualified as AB
import Hasura.SQL.Backend import Hasura.SQL.Backend
import Hasura.Server.Init.FeatureFlag as FF import Hasura.Server.Init.FeatureFlag as FF
import Hasura.Server.Types (HasServerConfigCtx (..), ServerConfigCtx (..)) import Hasura.Server.Types (HasServerConfigCtx (..), ServerConfigCtx (..))
import Hasura.Session (RoleName)
-- | Default implementation of the 'track_custom_return_type' request payload. -- | Default implementation of the 'track_custom_return_type' request payload.
data TrackCustomReturnType (b :: BackendType) = TrackCustomReturnType data TrackCustomReturnType (b :: BackendType) = TrackCustomReturnType
@ -56,7 +62,7 @@ instance (Backend b) => HasCodec (TrackCustomReturnType b) where
$ TrackCustomReturnType $ TrackCustomReturnType
<$> AC.requiredField "source" sourceDoc <$> AC.requiredField "source" sourceDoc
AC..= tctSource AC..= tctSource
<*> AC.requiredField "name" rootFieldDoc <*> AC.requiredField "name" nameDoc
AC..= tctName AC..= tctName
<*> AC.optionalField "description" descriptionDoc <*> AC.optionalField "description" descriptionDoc
AC..= tctDescription AC..= tctDescription
@ -64,7 +70,7 @@ instance (Backend b) => HasCodec (TrackCustomReturnType b) where
AC..= tctFields AC..= tctFields
where where
sourceDoc = "The source in which this custom return type should be tracked" sourceDoc = "The source in which this custom return type should be tracked"
rootFieldDoc = "Root field name for the custom return type" nameDoc = "Root field name for the custom return type"
fieldsDoc = "Return type of the expression" fieldsDoc = "Return type of the expression"
descriptionDoc = "A description of the query which appears in the graphql schema" descriptionDoc = "A description of the query which appears in the graphql schema"
@ -85,10 +91,10 @@ customTypeTrackToMetadata ::
CustomReturnTypeMetadata b CustomReturnTypeMetadata b
customTypeTrackToMetadata TrackCustomReturnType {..} = customTypeTrackToMetadata TrackCustomReturnType {..} =
CustomReturnTypeMetadata CustomReturnTypeMetadata
{ _ctmName = tctName, { _crtmName = tctName,
_ctmFields = tctFields, _crtmFields = tctFields,
_ctmSelectPermissions = mempty, _crtmSelectPermissions = mempty,
_ctmDescription = tctDescription _crtmDescription = tctDescription
} }
-- | API payload for the 'get_custom_return_type' endpoint. -- | API payload for the 'get_custom_return_type' endpoint.
@ -160,7 +166,7 @@ runTrackCustomReturnType trackCustomReturnTypeRequest = do
let (metadata :: CustomReturnTypeMetadata b) = customTypeTrackToMetadata trackCustomReturnTypeRequest let (metadata :: CustomReturnTypeMetadata b) = customTypeTrackToMetadata trackCustomReturnTypeRequest
let fieldName = _ctmName metadata let fieldName = _crtmName metadata
metadataObj = metadataObj =
MOSourceObjId source $ MOSourceObjId source $
AB.mkAnyBackend $ AB.mkAnyBackend $
@ -244,12 +250,94 @@ runUntrackCustomReturnType q = do
source = utctSource q source = utctSource q
fieldName = utctName q fieldName = utctName q
-- | A permission for logical models is tied to a specific root field name and
-- source. This wrapper adds both of those things to the JSON object that
-- describes the permission.
data CreateCustomReturnTypePermission a (b :: BackendType) = CreateCustomReturnTypePermission
{ ccrtpSource :: SourceName,
ccrtpName :: CustomReturnTypeName,
ccrtpInfo :: PermDef b a
}
deriving stock (Generic)
instance
FromJSON (PermDef b a) =>
FromJSON (CreateCustomReturnTypePermission a b)
where
parseJSON = withObject "CreateCustomReturnTypePermission" \obj -> do
ccrtpSource <- obj .:? "source" .!= defaultSource
ccrtpName <- obj .: "name"
ccrtpInfo <- parseJSON (Object obj)
pure CreateCustomReturnTypePermission {..}
runCreateSelectCustomReturnTypePermission ::
forall b m.
(Backend b, CacheRWM m, MetadataM m, MonadError QErr m, MonadIO m, HasServerConfigCtx m) =>
CreateCustomReturnTypePermission SelPerm b ->
m EncJSON
runCreateSelectCustomReturnTypePermission CreateCustomReturnTypePermission {..} = do
throwIfFeatureDisabled
assertCustomReturnTypeExists @b ccrtpSource ccrtpName
let metadataObj :: MetadataObjId
metadataObj =
MOSourceObjId ccrtpSource $
AB.mkAnyBackend $
SMOCustomReturnType @b ccrtpName
buildSchemaCacheFor metadataObj $
MetadataModifier $
customReturnTypeMetadataSetter @b ccrtpSource ccrtpName . crtmSelectPermissions
%~ OMap.insert (_pdRole ccrtpInfo) ccrtpInfo
pure successMsg
-- | To drop a permission, we need to know the source and root field name of
-- the logical model, as well as the role whose permission we want to drop.
data DropCustomReturnTypePermission (b :: BackendType) = DropCustomReturnTypePermission
{ dcrtpSource :: SourceName,
dcrtpName :: CustomReturnTypeName,
dcrtpRole :: RoleName
}
deriving stock (Generic)
instance FromJSON (DropCustomReturnTypePermission b) where
parseJSON = withObject "DropCustomReturnTypePermission" \obj -> do
dcrtpSource <- obj .:? "source" .!= defaultSource
dcrtpName <- obj .: "name"
dcrtpRole <- obj .: "role"
pure DropCustomReturnTypePermission {..}
runDropSelectCustomReturnTypePermission ::
forall b m.
(Backend b, CacheRWM m, MetadataM m, MonadError QErr m, MonadIO m, HasServerConfigCtx m) =>
DropCustomReturnTypePermission b ->
m EncJSON
runDropSelectCustomReturnTypePermission DropCustomReturnTypePermission {..} = do
throwIfFeatureDisabled
assertCustomReturnTypeExists @b dcrtpSource dcrtpName
let metadataObj :: MetadataObjId
metadataObj =
MOSourceObjId dcrtpSource $
AB.mkAnyBackend $
SMOCustomReturnType @b dcrtpName
buildSchemaCacheFor metadataObj $
MetadataModifier $
customReturnTypeMetadataSetter @b dcrtpSource dcrtpName . crtmSelectPermissions
%~ OMap.delete dcrtpRole
pure successMsg
-- | TODO: should this cascade and also delete associated permissions? -- | TODO: should this cascade and also delete associated permissions?
dropCustomReturnTypeInMetadata :: forall b. BackendMetadata b => SourceName -> CustomReturnTypeName -> MetadataModifier dropCustomReturnTypeInMetadata :: forall b. BackendMetadata b => SourceName -> CustomReturnTypeName -> MetadataModifier
dropCustomReturnTypeInMetadata source rootFieldName = do dropCustomReturnTypeInMetadata source name = do
MetadataModifier $ MetadataModifier $
metaSources . ix source . toSourceMetadata @b . smCustomReturnTypes metaSources . ix source . toSourceMetadata @b . smCustomReturnTypes
%~ OMap.delete rootFieldName %~ OMap.delete name
-- | check feature flag is enabled before carrying out any actions -- | check feature flag is enabled before carrying out any actions
throwIfFeatureDisabled :: (HasServerConfigCtx m, MonadIO m, MonadError QErr m) => m () throwIfFeatureDisabled :: (HasServerConfigCtx m, MonadIO m, MonadError QErr m) => m ()
@ -264,7 +352,7 @@ throwIfFeatureDisabled = do
-- | Check whether a custom return type with the given root field name exists for -- | Check whether a custom return type with the given root field name exists for
-- the given source. -- the given source.
assertCustomReturnTypeExists :: forall b m. (Backend b, MetadataM m, MonadError QErr m) => SourceName -> CustomReturnTypeName -> m () assertCustomReturnTypeExists :: forall b m. (Backend b, MetadataM m, MonadError QErr m) => SourceName -> CustomReturnTypeName -> m ()
assertCustomReturnTypeExists sourceName rootFieldName = do assertCustomReturnTypeExists sourceName name = do
metadata <- getMetadata metadata <- getMetadata
let sourceMetadataTraversal :: Traversal' Metadata (SourceMetadata b) let sourceMetadataTraversal :: Traversal' Metadata (SourceMetadata b)
@ -275,7 +363,7 @@ assertCustomReturnTypeExists sourceName rootFieldName = do
`onNothing` throw400 NotFound ("Source " <> sourceName <<> " not found.") `onNothing` throw400 NotFound ("Source " <> sourceName <<> " not found.")
let desiredCustomReturnType :: Traversal' (SourceMetadata b) (CustomReturnTypeMetadata b) let desiredCustomReturnType :: Traversal' (SourceMetadata b) (CustomReturnTypeMetadata b)
desiredCustomReturnType = smCustomReturnTypes . ix rootFieldName desiredCustomReturnType = smCustomReturnTypes . ix name
unless (has desiredCustomReturnType sourceMetadata) do unless (has desiredCustomReturnType sourceMetadata) do
throw400 NotFound ("Custom return type " <> rootFieldName <<> " not found in source " <> sourceName <<> ".") throw400 NotFound ("Custom return type " <> name <<> " not found in source " <> sourceName <<> ".")

View File

@ -1,13 +1,19 @@
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE UndecidableInstances #-}
module Hasura.CustomReturnType.Metadata module Hasura.CustomReturnType.Metadata
( CustomReturnTypeMetadata (..), ( CustomReturnTypeMetadata (..),
CustomReturnTypeName (..), CustomReturnTypeName (..),
crtmName,
crtmFields,
crtmDescription,
crtmSelectPermissions,
) )
where where
import Autodocodec (Autodocodec (Autodocodec), HasCodec) import Autodocodec (Autodocodec (Autodocodec), HasCodec)
import Autodocodec qualified as AC import Autodocodec qualified as AC
import Control.Lens (makeLenses)
import Data.Aeson (FromJSON, ToJSON) import Data.Aeson (FromJSON, ToJSON)
import Data.HashMap.Strict.InsOrd qualified as InsOrd import Data.HashMap.Strict.InsOrd qualified as InsOrd
import Data.HashMap.Strict.InsOrd.Autodocodec (sortedElemsCodec) import Data.HashMap.Strict.InsOrd.Autodocodec (sortedElemsCodec)
@ -22,13 +28,15 @@ import Hasura.Session (RoleName)
-- | Description of a custom return type for use in metadata (before schema cache) -- | Description of a custom return type for use in metadata (before schema cache)
data CustomReturnTypeMetadata (b :: BackendType) = CustomReturnTypeMetadata data CustomReturnTypeMetadata (b :: BackendType) = CustomReturnTypeMetadata
{ _ctmName :: CustomReturnTypeName, { _crtmName :: CustomReturnTypeName,
_ctmFields :: InsOrd.InsOrdHashMap (Column b) (NullableScalarType b), _crtmFields :: InsOrd.InsOrdHashMap (Column b) (NullableScalarType b),
_ctmDescription :: Maybe Text, _crtmDescription :: Maybe Text,
_ctmSelectPermissions :: InsOrdHashMap RoleName (SelPermDef b) _crtmSelectPermissions :: InsOrdHashMap RoleName (SelPermDef b)
} }
deriving (Generic) deriving (Generic)
makeLenses ''CustomReturnTypeMetadata
instance (Backend b) => HasCodec (CustomReturnTypeMetadata b) where instance (Backend b) => HasCodec (CustomReturnTypeMetadata b) where
codec = codec =
AC.CommentCodec AC.CommentCodec
@ -36,13 +44,13 @@ instance (Backend b) => HasCodec (CustomReturnTypeMetadata b) where
$ AC.object (codecNamePrefix @b <> "CustomReturnTypeMetadata") $ AC.object (codecNamePrefix @b <> "CustomReturnTypeMetadata")
$ CustomReturnTypeMetadata $ CustomReturnTypeMetadata
<$> AC.requiredField "name" nameDoc <$> AC.requiredField "name" nameDoc
AC..= _ctmName AC..= _crtmName
<*> AC.requiredFieldWith "fields" nullableScalarTypeMapCodec fieldsDoc <*> AC.requiredFieldWith "fields" nullableScalarTypeMapCodec fieldsDoc
AC..= _ctmFields AC..= _crtmFields
<*> AC.optionalField "description" descriptionDoc <*> AC.optionalField "description" descriptionDoc
AC..= _ctmDescription AC..= _crtmDescription
<*> optSortedList "select_permissions" _pdRole <*> optSortedList "select_permissions" _pdRole
AC..= _ctmSelectPermissions AC..= _crtmSelectPermissions
where where
nameDoc = "A name for a custom return type" nameDoc = "A name for a custom return type"
fieldsDoc = "Return types for the custom return type" fieldsDoc = "Return types for the custom return type"

View File

@ -736,30 +736,30 @@ buildSchemaCacheRule logger env = proc (MetadataWithResourceVersion metadataNoDe
liftIO @m $ checkFeatureFlag FF.logicalModelInterface liftIO @m $ checkFeatureFlag FF.logicalModelInterface
let mkCustomReturnTypeMetadataObject :: CustomReturnTypeMetadata b -> MetadataObject let mkCustomReturnTypeMetadataObject :: CustomReturnTypeMetadata b -> MetadataObject
mkCustomReturnTypeMetadataObject ctm = mkCustomReturnTypeMetadataObject crtm =
( MetadataObject ( MetadataObject
( MOSourceObjId sourceName $ ( MOSourceObjId sourceName $
AB.mkAnyBackend $ AB.mkAnyBackend $
SMOCustomReturnType @b (_ctmName ctm) SMOCustomReturnType @b (_crtmName crtm)
) )
(toJSON ctm) (toJSON crtm)
) )
customReturnTypeCacheMaybes <- customReturnTypeCacheMaybes <-
interpretWriter interpretWriter
-< for -< for
(OMap.elems customReturnTypes) (OMap.elems customReturnTypes)
\ctm@CustomReturnTypeMetadata {..} -> \crtm@CustomReturnTypeMetadata {..} ->
withRecordInconsistencyM (mkCustomReturnTypeMetadataObject ctm) $ do withRecordInconsistencyM (mkCustomReturnTypeMetadataObject crtm) $ do
unless areLogicalModelsEnabled $ unless areLogicalModelsEnabled $
throw400 InvalidConfiguration "The Logical Models feature is disabled" throw400 InvalidConfiguration "The Logical Models feature is disabled"
pure pure
CustomReturnTypeInfo CustomReturnTypeInfo
{ _ctiName = _ctmName, { _ctiName = _crtmName,
_ctiFields = _ctmFields, _ctiFields = _crtmFields,
_ctiPermissions = mempty, _ctiPermissions = mempty,
_ctiDescription = _ctmDescription _ctiDescription = _crtmDescription
} }
let customReturnTypesCache :: CustomReturnTypeCache b let customReturnTypesCache :: CustomReturnTypeCache b

View File

@ -22,6 +22,7 @@ module Hasura.RQL.Types.Metadata
emptyMetadata, emptyMetadata,
emptyMetadataDefaults, emptyMetadataDefaults,
functionMetadataSetter, functionMetadataSetter,
customReturnTypeMetadataSetter,
logicalModelMetadataSetter, logicalModelMetadataSetter,
metaActions, metaActions,
metaAllowlist, metaAllowlist,
@ -54,6 +55,7 @@ import Data.Aeson.TH
import Data.Aeson.Types import Data.Aeson.Types
import Data.HashMap.Strict.InsOrd.Extended qualified as OM import Data.HashMap.Strict.InsOrd.Extended qualified as OM
import Data.Monoid (Dual (..), Endo (..)) import Data.Monoid (Dual (..), Endo (..))
import Hasura.CustomReturnType.Metadata (CustomReturnTypeMetadata, CustomReturnTypeName)
import Hasura.Incremental qualified as Inc import Hasura.Incremental qualified as Inc
import Hasura.LogicalModel.Metadata (LogicalModelMetadata, LogicalModelName, lmmSelectPermissions) import Hasura.LogicalModel.Metadata (LogicalModelMetadata, LogicalModelName, lmmSelectPermissions)
import Hasura.Metadata.DTO.MetadataV3 (MetadataV3 (..)) import Hasura.Metadata.DTO.MetadataV3 (MetadataV3 (..))
@ -276,6 +278,16 @@ functionMetadataSetter ::
functionMetadataSetter source function = functionMetadataSetter source function =
metaSources . ix source . toSourceMetadata . smFunctions . ix function metaSources . ix source . toSourceMetadata . smFunctions . ix function
-- | A lens setter for the metadata of a custom return type as identified by the
-- source name and root field name.
customReturnTypeMetadataSetter ::
(Backend b) =>
SourceName ->
CustomReturnTypeName ->
ASetter' Metadata (CustomReturnTypeMetadata b)
customReturnTypeMetadataSetter source name =
metaSources . ix source . toSourceMetadata . smCustomReturnTypes . ix name
-- | A lens setter for the metadata of a logical model as identified by the -- | A lens setter for the metadata of a logical model as identified by the
-- source name and root field name. -- source name and root field name.
logicalModelMetadataSetter :: logicalModelMetadataSetter ::

View File

@ -443,7 +443,7 @@ instance (Backend b) => FromJSONWithContext (BackendSourceKind b) (SourceMetadat
_smTables <- oMapFromL _tmTable <$> o .: "tables" _smTables <- oMapFromL _tmTable <$> o .: "tables"
_smFunctions <- oMapFromL _fmFunction <$> o .:? "functions" .!= [] _smFunctions <- oMapFromL _fmFunction <$> o .:? "functions" .!= []
_smLogicalModels <- oMapFromL _lmmRootFieldName <$> o .:? "logical_models" .!= [] _smLogicalModels <- oMapFromL _lmmRootFieldName <$> o .:? "logical_models" .!= []
_smCustomReturnTypes <- oMapFromL _ctmName <$> o .:? "custom_return_types" .!= [] _smCustomReturnTypes <- oMapFromL _crtmName <$> o .:? "custom_return_types" .!= []
_smConfiguration <- o .: "configuration" _smConfiguration <- o .: "configuration"
_smQueryTags <- o .:? "query_tags" _smQueryTags <- o .:? "query_tags"
_smCustomization <- o .:? "customization" .!= emptySourceCustomization _smCustomization <- o .:? "customization" .!= emptySourceCustomization
@ -504,7 +504,7 @@ instance Backend b => HasCodec (SourceMetadata b) where
.== _smFunctions .== _smFunctions
<*> optionalFieldOrNullWithOmittedDefaultWith' "logical_models" (sortedElemsCodec _lmmRootFieldName) mempty <*> optionalFieldOrNullWithOmittedDefaultWith' "logical_models" (sortedElemsCodec _lmmRootFieldName) mempty
.== _smLogicalModels .== _smLogicalModels
<*> optionalFieldOrNullWithOmittedDefaultWith' "custom_return_types" (sortedElemsCodec _ctmName) mempty <*> optionalFieldOrNullWithOmittedDefaultWith' "custom_return_types" (sortedElemsCodec _crtmName) mempty
.== _smCustomReturnTypes .== _smCustomReturnTypes
<*> requiredField' "configuration" <*> requiredField' "configuration"
.== _smConfiguration .== _smConfiguration

View File

@ -122,7 +122,7 @@ sourcesToOrdJSONList sources =
tablesPair = ("tables", AO.array $ map tableMetaToOrdJSON $ sortOn _tmTable $ OM.elems _smTables) tablesPair = ("tables", AO.array $ map tableMetaToOrdJSON $ sortOn _tmTable $ OM.elems _smTables)
functionsPair = listToMaybeOrdPairSort "functions" functionMetadataToOrdJSON _fmFunction _smFunctions functionsPair = listToMaybeOrdPairSort "functions" functionMetadataToOrdJSON _fmFunction _smFunctions
logicalModelsPair = listToMaybeOrdPairSort "logical_models" AO.toOrdered _lmmRootFieldName (OM.elems _smLogicalModels) logicalModelsPair = listToMaybeOrdPairSort "logical_models" AO.toOrdered _lmmRootFieldName (OM.elems _smLogicalModels)
customReturnTypesPair = listToMaybeOrdPairSort "custom_return_types" AO.toOrdered _ctmName (OM.elems _smCustomReturnTypes) customReturnTypesPair = listToMaybeOrdPairSort "custom_return_types" AO.toOrdered _crtmName (OM.elems _smCustomReturnTypes)
configurationPair = [("configuration", AO.toOrdered _smConfiguration)] configurationPair = [("configuration", AO.toOrdered _smConfiguration)]
queryTagsConfigPair = maybe [] (\queryTagsConfig -> [("query_tags", AO.toOrdered queryTagsConfig)]) _smQueryTags queryTagsConfigPair = maybe [] (\queryTagsConfig -> [("query_tags", AO.toOrdered queryTagsConfig)]) _smQueryTags

View File

@ -181,5 +181,7 @@ customReturnTypesCommands :: forall (b :: BackendType). Backend b => [CommandPar
customReturnTypesCommands = customReturnTypesCommands =
[ commandParser "get_custom_return_type" $ RMGetCustomReturnType . mkAnyBackend @b, [ commandParser "get_custom_return_type" $ RMGetCustomReturnType . mkAnyBackend @b,
commandParser "track_custom_return_type" $ RMTrackCustomReturnType . mkAnyBackend @b, commandParser "track_custom_return_type" $ RMTrackCustomReturnType . mkAnyBackend @b,
commandParser "untrack_custom_return_type" $ RMUntrackCustomReturnType . mkAnyBackend @b commandParser "untrack_custom_return_type" $ RMUntrackCustomReturnType . mkAnyBackend @b,
commandParser "create_custom_return_type_select_permission" $ RMCreateSelectCustomReturnTypePermission . mkAnyBackend @b,
commandParser "drop_custom_return_type_select_permission" $ RMDropSelectCustomReturnTypePermission . mkAnyBackend @b
] ]

View File

@ -143,6 +143,8 @@ data RQLMetadataV1
RMGetCustomReturnType !(AnyBackend CustomReturnType.GetCustomReturnType) RMGetCustomReturnType !(AnyBackend CustomReturnType.GetCustomReturnType)
| RMTrackCustomReturnType !(AnyBackend CustomReturnType.TrackCustomReturnType) | RMTrackCustomReturnType !(AnyBackend CustomReturnType.TrackCustomReturnType)
| RMUntrackCustomReturnType !(AnyBackend CustomReturnType.UntrackCustomReturnType) | RMUntrackCustomReturnType !(AnyBackend CustomReturnType.UntrackCustomReturnType)
| RMCreateSelectCustomReturnTypePermission !(AnyBackend (CustomReturnType.CreateCustomReturnTypePermission SelPerm))
| RMDropSelectCustomReturnTypePermission !(AnyBackend CustomReturnType.DropCustomReturnTypePermission)
| -- Tables event triggers | -- Tables event triggers
RMCreateEventTrigger !(AnyBackend (Unvalidated1 CreateEventTriggerQuery)) RMCreateEventTrigger !(AnyBackend (Unvalidated1 CreateEventTriggerQuery))
| RMDeleteEventTrigger !(AnyBackend DeleteEventTriggerQuery) | RMDeleteEventTrigger !(AnyBackend DeleteEventTriggerQuery)
@ -501,11 +503,13 @@ queryModifiesMetadata = \case
RMGetLogicalModel _ -> False RMGetLogicalModel _ -> False
RMTrackLogicalModel _ -> True RMTrackLogicalModel _ -> True
RMUntrackLogicalModel _ -> True RMUntrackLogicalModel _ -> True
RMCreateSelectLogicalModelPermission _ -> True
RMDropSelectLogicalModelPermission _ -> True
RMGetCustomReturnType _ -> False RMGetCustomReturnType _ -> False
RMTrackCustomReturnType _ -> True RMTrackCustomReturnType _ -> True
RMUntrackCustomReturnType _ -> True RMUntrackCustomReturnType _ -> True
RMCreateSelectLogicalModelPermission _ -> True RMCreateSelectCustomReturnTypePermission _ -> True
RMDropSelectLogicalModelPermission _ -> True RMDropSelectCustomReturnTypePermission _ -> True
RMBulk qs -> any queryModifiesMetadata qs RMBulk qs -> any queryModifiesMetadata qs
-- We used to assume that the fallthrough was True, -- We used to assume that the fallthrough was True,
-- but it is better to be explicit here to warn when new constructors are added. -- but it is better to be explicit here to warn when new constructors are added.
@ -698,6 +702,8 @@ runMetadataQueryV1M env currentResourceVersion = \case
RMGetCustomReturnType q -> dispatchMetadata CustomReturnType.runGetCustomReturnType q RMGetCustomReturnType q -> dispatchMetadata CustomReturnType.runGetCustomReturnType q
RMTrackCustomReturnType q -> dispatchMetadata CustomReturnType.runTrackCustomReturnType q RMTrackCustomReturnType q -> dispatchMetadata CustomReturnType.runTrackCustomReturnType q
RMUntrackCustomReturnType q -> dispatchMetadata CustomReturnType.runUntrackCustomReturnType q RMUntrackCustomReturnType q -> dispatchMetadata CustomReturnType.runUntrackCustomReturnType q
RMCreateSelectCustomReturnTypePermission q -> dispatchMetadata CustomReturnType.runCreateSelectCustomReturnTypePermission q
RMDropSelectCustomReturnTypePermission q -> dispatchMetadata CustomReturnType.runDropSelectCustomReturnTypePermission q
RMCreateEventTrigger q -> RMCreateEventTrigger q ->
dispatchMetadataAndEventTrigger dispatchMetadataAndEventTrigger
( validateTransforms ( validateTransforms

View File

@ -98,6 +98,8 @@ data RQLMetadataV1
RMGetCustomReturnType !(AnyBackend CustomReturnType.GetCustomReturnType) RMGetCustomReturnType !(AnyBackend CustomReturnType.GetCustomReturnType)
| RMTrackCustomReturnType !(AnyBackend CustomReturnType.TrackCustomReturnType) | RMTrackCustomReturnType !(AnyBackend CustomReturnType.TrackCustomReturnType)
| RMUntrackCustomReturnType !(AnyBackend CustomReturnType.UntrackCustomReturnType) | RMUntrackCustomReturnType !(AnyBackend CustomReturnType.UntrackCustomReturnType)
| RMCreateSelectCustomReturnTypePermission !(AnyBackend (CustomReturnType.CreateCustomReturnTypePermission SelPerm))
| RMDropSelectCustomReturnTypePermission !(AnyBackend CustomReturnType.DropCustomReturnTypePermission)
| -- Tables event triggers | -- Tables event triggers
RMCreateEventTrigger !(AnyBackend (Unvalidated1 CreateEventTriggerQuery)) RMCreateEventTrigger !(AnyBackend (Unvalidated1 CreateEventTriggerQuery))
| RMDeleteEventTrigger !(AnyBackend DeleteEventTriggerQuery) | RMDeleteEventTrigger !(AnyBackend DeleteEventTriggerQuery)

View File

@ -69,10 +69,10 @@ spec = do
describe "Validation" do describe "Validation" do
let crtm = let crtm =
CustomReturnTypeMetadata CustomReturnTypeMetadata
{ _ctmName = CustomReturnTypeName (G.unsafeMkName "custom_return_type_name"), { _crtmName = CustomReturnTypeName (G.unsafeMkName "custom_return_type_name"),
_ctmFields = mempty, _crtmFields = mempty,
_ctmDescription = Nothing, _crtmDescription = Nothing,
_ctmSelectPermissions = mempty _crtmSelectPermissions = mempty
} }
lmm = lmm =