From 78323ed1a1b564062acf3125112f9e46c173be12 Mon Sep 17 00:00:00 2001 From: Daniel Harvey Date: Tue, 25 Jul 2023 11:09:34 +0100 Subject: [PATCH] chore(server): permissions for inline logical models PR-URL: https://github.com/hasura/graphql-engine-mono/pull/9835 GitOrigin-RevId: b36a4d5a8e0d4156a2d26803c7d046570f7afefa --- server/graphql-engine.cabal | 1 + .../src-lib/Hasura/GraphQL/Schema/Common.hs | 7 +- server/src-lib/Hasura/LogicalModel/API.hs | 115 ++++++++++---- server/src-lib/Hasura/LogicalModel/Types.hs | 15 +- .../Hasura/LogicalModelResolver/Lenses.hs | 15 ++ server/src-lib/Hasura/NativeQuery/API.hs | 1 + server/src-lib/Hasura/NativeQuery/Metadata.hs | 30 +++- server/src-lib/Hasura/RQL/DDL/Metadata.hs | 14 +- server/src-lib/Hasura/RQL/DDL/Permission.hs | 18 +-- .../Hasura/RQL/DDL/Permission/Internal.hs | 8 +- server/src-lib/Hasura/RQL/DDL/Schema/Cache.hs | 84 +++++----- .../Hasura/RQL/DDL/Schema/Cache/Common.hs | 7 +- .../RQL/DDL/Schema/Cache/Dependencies.hs | 25 +-- .../Hasura/RQL/DDL/Schema/Cache/Permission.hs | 146 +++++++++++------- server/src-lib/Hasura/RQL/Types/Metadata.hs | 11 ++ .../Hasura/RQL/Types/Metadata/Object.hs | 20 ++- .../src-lib/Hasura/RQL/Types/SchemaCache.hs | 50 +++--- .../Hasura/RQL/Types/SchemaCacheTypes.hs | 7 +- 18 files changed, 376 insertions(+), 198 deletions(-) create mode 100644 server/src-lib/Hasura/LogicalModelResolver/Lenses.hs diff --git a/server/graphql-engine.cabal b/server/graphql-engine.cabal index 7cebcf4a6c1..58a8ed2aabe 100644 --- a/server/graphql-engine.cabal +++ b/server/graphql-engine.cabal @@ -726,6 +726,7 @@ library , Hasura.GC , Hasura.LogicalModelResolver.Codec + , Hasura.LogicalModelResolver.Lenses , Hasura.LogicalModelResolver.Metadata , Hasura.LogicalModelResolver.Schema , Hasura.LogicalModelResolver.Types diff --git a/server/src-lib/Hasura/GraphQL/Schema/Common.hs b/server/src-lib/Hasura/GraphQL/Schema/Common.hs index 5ad8cc30052..f99a8bbc587 100644 --- a/server/src-lib/Hasura/GraphQL/Schema/Common.hs +++ b/server/src-lib/Hasura/GraphQL/Schema/Common.hs @@ -82,7 +82,7 @@ import Hasura.GraphQL.Schema.Parser qualified as P import Hasura.GraphQL.Schema.Typename import Hasura.LogicalModel.Cache (LogicalModelInfo (_lmiPermissions)) import Hasura.LogicalModel.Types (LogicalModelName) -import Hasura.NativeQuery.Cache (NativeQueryCache, NativeQueryInfo) +import Hasura.NativeQuery.Cache (NativeQueryCache, NativeQueryInfo (..)) import Hasura.NativeQuery.Types (NativeQueryName) import Hasura.Prelude import Hasura.RQL.IR qualified as IR @@ -348,7 +348,10 @@ getTableRoles bsi = AB.dispatchAnyBackend @Backend bsi go getLogicalModelRoles :: BackendSourceInfo -> [RoleName] getLogicalModelRoles bsi = AB.dispatchAnyBackend @Backend bsi go where - go si = HashMap.keys . _lmiPermissions =<< HashMap.elems (_siLogicalModels si) + go si = + let namedLogicalModelRoles = HashMap.keys . _lmiPermissions =<< HashMap.elems (_siLogicalModels si) + inlineLogicalModelRoles = HashMap.keys . _lmiPermissions . _nqiReturns =<< HashMap.elems (_siNativeQueries si) + in namedLogicalModelRoles <> inlineLogicalModelRoles askScalarTypeParsingContext :: forall b r m. diff --git a/server/src-lib/Hasura/LogicalModel/API.hs b/server/src-lib/Hasura/LogicalModel/API.hs index 7435a104577..e7a56442415 100644 --- a/server/src-lib/Hasura/LogicalModel/API.hs +++ b/server/src-lib/Hasura/LogicalModel/API.hs @@ -27,7 +27,10 @@ import Hasura.Base.Error import Hasura.EncJSON import Hasura.LogicalModel.Lenses (lmmSelectPermissions) import Hasura.LogicalModel.Metadata (LogicalModelMetadata (..)) -import Hasura.LogicalModel.Types (LogicalModelField, LogicalModelName, logicalModelFieldMapCodec) +import Hasura.LogicalModel.Types (LogicalModelField, LogicalModelLocation (..), LogicalModelName, logicalModelFieldMapCodec) +import Hasura.LogicalModelResolver.Lenses +import Hasura.NativeQuery.API (assertNativeQueryExists) +import Hasura.NativeQuery.Lenses (nqmReturns) import Hasura.Prelude import Hasura.RQL.Types.Backend (Backend (..)) import Hasura.RQL.Types.BackendTag (backendPrefix, backendTag, reify) @@ -230,12 +233,22 @@ execUntrackLogicalModel q metadata = do source = utlmSource q fieldName = utlmName q +-- type for the `type` field in permissions +data LogicalModelSource + = LMLogicalModel + | LMNativeQuery + +instance FromJSON LogicalModelSource where + parseJSON (String "logical_model") = pure LMLogicalModel + parseJSON (String "native_query") = pure LMNativeQuery + parseJSON _ = fail "Expected string" + -- | A permission for logical models is tied to a specific name and -- source. This wrapper adds both of those things to the JSON object that -- describes the permission. data CreateLogicalModelPermission a (b :: BackendType) = CreateLogicalModelPermission { clmpSource :: SourceName, - clmpName :: LogicalModelName, + clmpLocation :: LogicalModelLocation, clmpInfo :: PermDef b a } deriving stock (Generic) @@ -246,7 +259,12 @@ instance where parseJSON = withObject "CreateLogicalModelPermission" \obj -> do clmpSource <- obj .:? "source" .!= defaultSource - clmpName <- obj .: "name" + lmType <- obj .:? "type" .!= LMLogicalModel + + clmpLocation <- case lmType of + LMLogicalModel -> LMLLogicalModel <$> obj .: "name" + LMNativeQuery -> LMLNativeQuery <$> obj .: "name" + clmpInfo <- parseJSON (Object obj) pure CreateLogicalModelPermission {..} @@ -258,19 +276,38 @@ runCreateSelectLogicalModelPermission :: m EncJSON runCreateSelectLogicalModelPermission CreateLogicalModelPermission {..} = do metadata <- getMetadata - assertLogicalModelExists @b clmpSource clmpName metadata - let metadataObj :: MetadataObjId - metadataObj = - MOSourceObjId clmpSource - $ AB.mkAnyBackend - $ SMOLogicalModel @b clmpName + case clmpLocation of + LMLNativeQuery nativeQueryName -> do + assertNativeQueryExists @b clmpSource nativeQueryName metadata - buildSchemaCacheFor metadataObj - $ MetadataModifier - $ logicalModelMetadataSetter @b clmpSource clmpName - . lmmSelectPermissions - %~ InsOrdHashMap.insert (_pdRole clmpInfo) clmpInfo + let metadataObj :: MetadataObjId + metadataObj = + MOSourceObjId clmpSource + $ AB.mkAnyBackend + $ SMONativeQuery @b nativeQueryName + + buildSchemaCacheFor metadataObj + $ MetadataModifier + $ nativeQueryMetadataSetter @b clmpSource nativeQueryName + . nqmReturns + . _LMIInlineLogicalModel + . ilmmSelectPermissions + %~ InsOrdHashMap.insert (_pdRole clmpInfo) clmpInfo + LMLLogicalModel logicalModelName -> do + assertLogicalModelExists @b clmpSource logicalModelName metadata + + let metadataObj :: MetadataObjId + metadataObj = + MOSourceObjId clmpSource + $ AB.mkAnyBackend + $ SMOLogicalModel @b logicalModelName + + buildSchemaCacheFor metadataObj + $ MetadataModifier + $ logicalModelMetadataSetter @b clmpSource logicalModelName + . lmmSelectPermissions + %~ InsOrdHashMap.insert (_pdRole clmpInfo) clmpInfo pure successMsg @@ -278,7 +315,7 @@ runCreateSelectLogicalModelPermission CreateLogicalModelPermission {..} = do -- the logical model, as well as the role whose permission we want to drop. data DropLogicalModelPermission (b :: BackendType) = DropLogicalModelPermission { dlmpSource :: SourceName, - dlmpName :: LogicalModelName, + dlmpLocation :: LogicalModelLocation, dlmpRole :: RoleName } deriving stock (Generic) @@ -286,7 +323,13 @@ data DropLogicalModelPermission (b :: BackendType) = DropLogicalModelPermission instance FromJSON (DropLogicalModelPermission b) where parseJSON = withObject "DropLogicalModelPermission" \obj -> do dlmpSource <- obj .:? "source" .!= defaultSource - dlmpName <- obj .: "name" + + lmType <- obj .:? "type" .!= LMLogicalModel + + dlmpLocation <- case lmType of + LMLogicalModel -> LMLLogicalModel <$> obj .: "name" + LMNativeQuery -> LMLNativeQuery <$> obj .: "name" + dlmpRole <- obj .: "role" pure DropLogicalModelPermission {..} @@ -298,19 +341,37 @@ runDropSelectLogicalModelPermission :: m EncJSON runDropSelectLogicalModelPermission DropLogicalModelPermission {..} = do metadata <- getMetadata - assertLogicalModelExists @b dlmpSource dlmpName metadata + case dlmpLocation of + LMLNativeQuery nativeQueryName -> do + assertNativeQueryExists @b dlmpSource nativeQueryName metadata - let metadataObj :: MetadataObjId - metadataObj = - MOSourceObjId dlmpSource - $ AB.mkAnyBackend - $ SMOLogicalModel @b dlmpName + let metadataObj :: MetadataObjId + metadataObj = + MOSourceObjId dlmpSource + $ AB.mkAnyBackend + $ SMONativeQuery @b nativeQueryName - buildSchemaCacheFor metadataObj - $ MetadataModifier - $ logicalModelMetadataSetter @b dlmpSource dlmpName - . lmmSelectPermissions - %~ InsOrdHashMap.delete dlmpRole + buildSchemaCacheFor metadataObj + $ MetadataModifier + $ nativeQueryMetadataSetter @b dlmpSource nativeQueryName + . nqmReturns + . _LMIInlineLogicalModel + . ilmmSelectPermissions + %~ InsOrdHashMap.delete dlmpRole + LMLLogicalModel logicalModelName -> do + assertLogicalModelExists @b dlmpSource logicalModelName metadata + + let metadataObj :: MetadataObjId + metadataObj = + MOSourceObjId dlmpSource + $ AB.mkAnyBackend + $ SMOLogicalModel @b logicalModelName + + buildSchemaCacheFor metadataObj + $ MetadataModifier + $ logicalModelMetadataSetter @b dlmpSource logicalModelName + . lmmSelectPermissions + %~ InsOrdHashMap.delete dlmpRole pure successMsg diff --git a/server/src-lib/Hasura/LogicalModel/Types.hs b/server/src-lib/Hasura/LogicalModel/Types.hs index c53a9111b6d..5a755e0f971 100644 --- a/server/src-lib/Hasura/LogicalModel/Types.hs +++ b/server/src-lib/Hasura/LogicalModel/Types.hs @@ -9,6 +9,7 @@ module Hasura.LogicalModel.Types LogicalModelTypeArray (..), LogicalModelTypeReference (..), logicalModelFieldMapCodec, + LogicalModelLocation (..), ) where @@ -19,8 +20,9 @@ import Autodocodec import Autodocodec qualified as AC import Data.Aeson (FromJSON (..), FromJSONKey, ToJSON (..), ToJSONKey, Value) import Data.HashMap.Strict.InsOrd qualified as InsOrdHashMap -import Data.Text.Extended (ToTxt) +import Data.Text.Extended (ToTxt (..)) import Hasura.Metadata.DTO.Placeholder (placeholderCodecViaJSON) +import Hasura.NativeQuery.Types (NativeQueryName) import Hasura.Prelude hiding (first) import Hasura.RQL.Types.Backend (Backend (..)) import Hasura.RQL.Types.BackendTag (backendPrefix) @@ -317,3 +319,14 @@ logicalModelFieldMapCodec = ( fmap snd . InsOrdHashMap.toList ) (AC.codec @[LogicalModelField b]) + +-- when we are talking about permissions, they might be attached directly to a +-- Native Query or similar +data LogicalModelLocation + = LMLLogicalModel LogicalModelName + | LMLNativeQuery NativeQueryName + deriving (Eq, Ord, Show, Generic, Hashable) + +instance ToTxt LogicalModelLocation where + toTxt (LMLLogicalModel lmn) = toTxt lmn + toTxt (LMLNativeQuery nqn) = toTxt nqn diff --git a/server/src-lib/Hasura/LogicalModelResolver/Lenses.hs b/server/src-lib/Hasura/LogicalModelResolver/Lenses.hs new file mode 100644 index 00000000000..16096f74001 --- /dev/null +++ b/server/src-lib/Hasura/LogicalModelResolver/Lenses.hs @@ -0,0 +1,15 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Hasura.LogicalModelResolver.Lenses + ( ilmmFields, + ilmmSelectPermissions, + _LMIInlineLogicalModel, + _LMILogicalModelName, + ) +where + +import Control.Lens (makeLenses, makePrisms) +import Hasura.LogicalModelResolver.Metadata (InlineLogicalModelMetadata, LogicalModelIdentifier) + +makeLenses ''InlineLogicalModelMetadata +makePrisms ''LogicalModelIdentifier diff --git a/server/src-lib/Hasura/NativeQuery/API.hs b/server/src-lib/Hasura/NativeQuery/API.hs index 6c9d4fc081c..daa8c8674cb 100644 --- a/server/src-lib/Hasura/NativeQuery/API.hs +++ b/server/src-lib/Hasura/NativeQuery/API.hs @@ -9,6 +9,7 @@ module Hasura.NativeQuery.API execTrackNativeQuery, execUntrackNativeQuery, dropNativeQueryInMetadata, + assertNativeQueryExists, module Hasura.NativeQuery.Types, ) where diff --git a/server/src-lib/Hasura/NativeQuery/Metadata.hs b/server/src-lib/Hasura/NativeQuery/Metadata.hs index a1d7016e92d..4f28992a763 100644 --- a/server/src-lib/Hasura/NativeQuery/Metadata.hs +++ b/server/src-lib/Hasura/NativeQuery/Metadata.hs @@ -10,12 +10,14 @@ module Hasura.NativeQuery.Metadata InterpolatedQuery (..), parseInterpolatedQuery, module Hasura.NativeQuery.Types, + WithNativeQuery (..), ) where import Autodocodec import Autodocodec qualified as AC -import Data.Aeson (FromJSON, ToJSON) +import Data.Aeson (FromJSON (parseJSON), ToJSON, (.!=), (.:), (.:?)) +import Data.Aeson qualified as J import Data.HashMap.Strict.InsOrd.Autodocodec (sortedElemsCodec) import Data.Text.Extended qualified as T import Hasura.LogicalModelResolver.Metadata (LogicalModelIdentifier) @@ -25,7 +27,7 @@ import Hasura.Prelude hiding (first) import Hasura.RQL.Types.Backend import Hasura.RQL.Types.BackendTag (backendPrefix) import Hasura.RQL.Types.BackendType -import Hasura.RQL.Types.Common (RelName) +import Hasura.RQL.Types.Common (RelName, SourceName, ToAesonPairs (toAesonPairs), defaultSource) import Hasura.RQL.Types.Relationships.Local (RelDef (..), RelManualNativeQueryConfig (..)) -- | copy pasta'd from Hasura.RQL.Types.Metadata.Common, forgive me Padre i did @@ -94,3 +96,27 @@ deriving via (Autodocodec (NativeQueryMetadata b)) instance (Backend b) => (ToJSON (NativeQueryMetadata b)) + +-- | A wrapper to tie something to a particular native query. Specifically, it +-- assumes the underlying '_wlmInfo' is represented as an object, and adds two +-- keys to that object: @source@ and @root_field_name@. +data WithNativeQuery a = WithNativeQuery + { _wnqSource :: SourceName, + _wnqName :: NativeQueryName, + _wnqInfo :: a + } + deriving stock (Eq, Show) + +-- | something to note here: if the `a` contains a `name` or `source` key then +-- this won't work anymore. +instance (FromJSON a) => FromJSON (WithNativeQuery a) where + parseJSON = J.withObject "NativeQuery" \obj -> do + _wnqSource <- obj .:? "source" .!= defaultSource + _wnqName <- obj .: "name" + _wnqInfo <- J.parseJSON (J.Object obj) + + pure WithNativeQuery {..} + +instance (ToAesonPairs a) => ToJSON (WithNativeQuery a) where + toJSON (WithNativeQuery source name info) = + J.object $ ("source", J.toJSON source) : ("name", J.toJSON name) : toAesonPairs info diff --git a/server/src-lib/Hasura/RQL/DDL/Metadata.hs b/server/src-lib/Hasura/RQL/DDL/Metadata.hs index cca880d5612..aeb79e42dde 100644 --- a/server/src-lib/Hasura/RQL/DDL/Metadata.hs +++ b/server/src-lib/Hasura/RQL/DDL/Metadata.hs @@ -44,8 +44,10 @@ import Hasura.Eventing.Backend (BackendEventTrigger (..)) import Hasura.Function.API import Hasura.Logging qualified as HL import Hasura.LogicalModel.API +import Hasura.LogicalModelResolver.Lenses (_LMIInlineLogicalModel) import Hasura.Metadata.Class import Hasura.NativeQuery.API +import Hasura.NativeQuery.Lenses (nqmReturns) import Hasura.Prelude hiding (first) import Hasura.RQL.DDL.Action import Hasura.RQL.DDL.ApiLimit @@ -723,16 +725,24 @@ purgeMetadataObj = \case $ nativeQueryMetadataSetter @b source nativeQueryName %~ case nativeQueryMetadataObjId of NQMORel rn _ -> dropNativeQueryRelationshipInMetadata rn - NQMOReferencedLogicalModel _ -> id SMOStoredProcedure sp -> dropStoredProcedureInMetadata @b source sp SMOLogicalModel lm -> dropLogicalModelInMetadata @b source lm - SMOLogicalModelObj logicalModelName logicalModelMetadataObjId -> + SMOLogicalModelObj (LMLLogicalModel logicalModelName) logicalModelMetadataObjId -> MetadataModifier $ logicalModelMetadataSetter @b source logicalModelName %~ case logicalModelMetadataObjId of LMMOPerm roleName permType -> dropLogicalModelPermissionInMetadata roleName permType LMMOReferencedLogicalModel _ -> id + SMOLogicalModelObj (LMLNativeQuery nativeQueryName) logicalModelMetadataObjId -> + MetadataModifier + $ nativeQueryMetadataSetter @b source nativeQueryName + . nqmReturns + . _LMIInlineLogicalModel + %~ case logicalModelMetadataObjId of + LMMOPerm roleName permType -> + dropInlineLogicalModelPermissionInMetadata roleName permType + LMMOReferencedLogicalModel _ -> id SMOTableObj qt tableObj -> MetadataModifier $ tableMetadataSetter @b source qt diff --git a/server/src-lib/Hasura/RQL/DDL/Permission.hs b/server/src-lib/Hasura/RQL/DDL/Permission.hs index 11a44be1b10..8459a84385f 100644 --- a/server/src-lib/Hasura/RQL/DDL/Permission.hs +++ b/server/src-lib/Hasura/RQL/DDL/Permission.hs @@ -44,7 +44,7 @@ import Data.Text.Extended import Hasura.Base.Error import Hasura.EncJSON import Hasura.LogicalModel.Common (logicalModelFieldsToFieldInfo) -import Hasura.LogicalModel.Types (LogicalModelField (..), LogicalModelName) +import Hasura.LogicalModel.Types (LogicalModelField (..), LogicalModelLocation) import Hasura.Prelude import Hasura.RQL.DDL.Permission.Internal import Hasura.RQL.IR.BoolExp @@ -264,12 +264,12 @@ buildLogicalModelPermInfo :: Has (ScalarTypeParsingContext b) r ) => SourceName -> - LogicalModelName -> + LogicalModelLocation -> InsOrdHashMap.InsOrdHashMap (Column b) (LogicalModelField b) -> PermDefPermission b perm -> m (WithDeps (PermInfo perm b)) -buildLogicalModelPermInfo sourceName logicalModelName fieldInfoMap = \case - SelPerm' p -> buildLogicalModelSelPermInfo sourceName logicalModelName fieldInfoMap p +buildLogicalModelPermInfo sourceName logicalModelLocation fieldInfoMap = \case + SelPerm' p -> buildLogicalModelSelPermInfo sourceName logicalModelLocation fieldInfoMap p InsPerm' _ -> error "Not implemented yet" UpdPerm' _ -> error "Not implemented yet" DelPerm' _ -> error "Not implemented yet" @@ -464,11 +464,11 @@ buildLogicalModelSelPermInfo :: Has (ScalarTypeParsingContext b) r ) => SourceName -> - LogicalModelName -> + LogicalModelLocation -> InsOrdHashMap.InsOrdHashMap (Column b) (LogicalModelField b) -> SelPerm b -> m (WithDeps (SelPermInfo b)) -buildLogicalModelSelPermInfo source logicalModelName logicalModelFieldMap sp = withPathK "permission" do +buildLogicalModelSelPermInfo source logicalModelLocation logicalModelFieldMap sp = withPathK "permission" do let columns :: [Column b] columns = interpColSpec (lmfName <$> InsOrdHashMap.elems logicalModelFieldMap) (spColumns sp) @@ -477,7 +477,7 @@ buildLogicalModelSelPermInfo source logicalModelName logicalModelFieldMap sp = w -- filter out the non-scalars. (spiFilter, boolExpDeps) <- withPathK "filter" - $ procLogicalModelBoolExp source logicalModelName (logicalModelFieldsToFieldInfo logicalModelFieldMap) (spFilter sp) + $ procLogicalModelBoolExp source logicalModelLocation (logicalModelFieldsToFieldInfo logicalModelFieldMap) (spFilter sp) let -- What parts of the metadata are interesting when computing the -- permissions? These dependencies bubble all the way up to @@ -486,9 +486,9 @@ buildLogicalModelSelPermInfo source logicalModelName logicalModelFieldMap sp = w deps :: Seq SchemaDependency deps = mconcat - [ Seq.singleton (mkLogicalModelParentDep @b source logicalModelName), + [ Seq.singleton (mkLogicalModelParentDep @b source logicalModelLocation), boolExpDeps, - fmap (mkLogicalModelColDep @b DRUntyped source logicalModelName) + fmap (mkLogicalModelColDep @b DRUntyped source logicalModelLocation) $ Seq.fromList columns ] diff --git a/server/src-lib/Hasura/RQL/DDL/Permission/Internal.hs b/server/src-lib/Hasura/RQL/DDL/Permission/Internal.hs index 1c6b59f9902..db3533b1e54 100644 --- a/server/src-lib/Hasura/RQL/DDL/Permission/Internal.hs +++ b/server/src-lib/Hasura/RQL/DDL/Permission/Internal.hs @@ -24,7 +24,7 @@ import Data.Sequence qualified as Seq import Data.Text qualified as T import Data.Text.Extended import Hasura.Base.Error -import Hasura.LogicalModel.Types (LogicalModelName) +import Hasura.LogicalModel.Types (LogicalModelLocation) import Hasura.Prelude import Hasura.RQL.IR.BoolExp import Hasura.RQL.Types.Backend @@ -124,11 +124,11 @@ procLogicalModelBoolExp :: Has (ScalarTypeParsingContext b) r ) => SourceName -> - LogicalModelName -> + LogicalModelLocation -> FieldInfoMap (FieldInfo b) -> BoolExp b -> m (AnnBoolExpPartialSQL b, Seq SchemaDependency) -procLogicalModelBoolExp source lmn fieldInfoMap be = do +procLogicalModelBoolExp source logicalModelLocation fieldInfoMap be = do let -- The parser for the "right hand side" of operations. We use @rhsParser@ -- as the name here for ease of grepping, though it's maybe a bit vague. -- More specifically, if we think of an operation that combines a field @@ -147,7 +147,7 @@ procLogicalModelBoolExp source lmn fieldInfoMap be = do -- this boolean expression? This dependency system is explained more -- thoroughly in the 'buildLogicalModelSelPermInfo' inline comments. deps :: [SchemaDependency] - deps = getLogicalModelBoolExpDeps source lmn abe + deps = getLogicalModelBoolExpDeps source logicalModelLocation abe return (abe, Seq.fromList deps) diff --git a/server/src-lib/Hasura/RQL/DDL/Schema/Cache.hs b/server/src-lib/Hasura/RQL/DDL/Schema/Cache.hs index 89e8291ebb5..57d675681ab 100644 --- a/server/src-lib/Hasura/RQL/DDL/Schema/Cache.hs +++ b/server/src-lib/Hasura/RQL/DDL/Schema/Cache.hs @@ -50,7 +50,7 @@ import Hasura.Incremental qualified as Inc import Hasura.Logging import Hasura.LogicalModel.Cache (LogicalModelCache, LogicalModelInfo (..)) import Hasura.LogicalModel.Metadata (LogicalModelMetadata (..)) -import Hasura.LogicalModel.Types (LogicalModelField (..), LogicalModelName (..), LogicalModelType (..), LogicalModelTypeArray (..), LogicalModelTypeReference (..)) +import Hasura.LogicalModel.Types (LogicalModelField (..), LogicalModelLocation (..), LogicalModelName (..), LogicalModelType (..), LogicalModelTypeArray (..), LogicalModelTypeReference (..)) import Hasura.LogicalModelResolver.Metadata (InlineLogicalModelMetadata (..), LogicalModelIdentifier (..)) import Hasura.Metadata.Class import Hasura.NativeQuery.Cache (NativeQueryCache, NativeQueryInfo (..)) @@ -906,6 +906,24 @@ buildSchemaCacheRule logger env mSchemaRegistryContext = proc (MetadataWithResou getLogicalModelTypeDependencies ltmaArray LogicalModelTypeReference (LogicalModelTypeReferenceC lmtrr _) -> S.singleton lmtrr + let logicalModelDepObjects metadataJSON rootLogicalModelLocation logicalModelName = + let metadataObject = + MetadataObject + ( MOSourceObjId sourceName + $ AB.mkAnyBackend + $ SMOLogicalModelObj @b rootLogicalModelLocation + $ LMMOReferencedLogicalModel logicalModelName + ) + metadataJSON + + sourceObject :: SchemaObjId + sourceObject = + SOSourceObj sourceName + $ AB.mkAnyBackend + $ SOILogicalModelObj @b rootLogicalModelLocation + $ LMOReferencedLogicalModel logicalModelName + in (metadataObject, sourceObject) + logicalModelCacheMaybes <- interpretWriter -< for @@ -913,32 +931,17 @@ buildSchemaCacheRule logger env mSchemaRegistryContext = proc (MetadataWithResou \lmm@LogicalModelMetadata {..} -> withRecordInconsistencyM (mkLogicalModelMetadataObject lmm) $ do logicalModelPermissions <- - flip runReaderT sourceConfig $ buildLogicalModelPermissions sourceName tableCoreInfos _lmmName _lmmFields _lmmSelectPermissions orderedRoles - - let recordDependency logicalModelName = do - let metadataObject = - MetadataObject - ( MOSourceObjId sourceName - $ AB.mkAnyBackend - $ SMOLogicalModelObj @b _lmmName - $ LMMOReferencedLogicalModel logicalModelName - ) - ( toJSON lmm - ) - - sourceObject :: SchemaObjId - sourceObject = - SOSourceObj sourceName - $ AB.mkAnyBackend - $ SOILogicalModelObj @b _lmmName - $ LMOReferencedLogicalModel logicalModelName + flip runReaderT sourceConfig + $ buildLogicalModelPermissions sourceName tableCoreInfos (LMLLogicalModel _lmmName) _lmmFields _lmmSelectPermissions orderedRoles + let recordDep (metadataObject, sourceObject) = recordDependenciesM metadataObject sourceObject $ Seq.singleton (SchemaDependency sourceObject DRReferencedLogicalModel) - -- record a dependency with each Logical Model our types - -- reference - mapM_ recordDependency (concatMap (S.toList . getLogicalModelTypeDependencies . lmfType) _lmmFields) + -- record a dependency with each Logical Model our types reference + mapM_ + (recordDep . logicalModelDepObjects (toJSON lmm) (LMLLogicalModel _lmmName)) + (concatMap (S.toList . getLogicalModelTypeDependencies . lmfType) _lmmFields) pure LogicalModelInfo @@ -995,38 +998,21 @@ buildSchemaCacheRule logger env mSchemaRegistryContext = proc (MetadataWithResou (HashMap.lookup logicalModelName logicalModelsCache) (throw400 InvalidConfiguration ("The logical model " <> toTxt logicalModelName <> " could not be found")) LMIInlineLogicalModel (InlineLogicalModelMetadata {_ilmmFields, _ilmmSelectPermissions}) -> do - let logicalModelName = LogicalModelName (getNativeQueryName _nqmRootFieldName) - - recordDependency innerLogicalModelName = do - let nqMetadataObject = - MetadataObject - ( MOSourceObjId sourceName - $ AB.mkAnyBackend - $ SMONativeQueryObj @b _nqmRootFieldName - $ NQMOReferencedLogicalModel innerLogicalModelName - ) - ( toJSON preValidationNativeQuery - ) - - nqSourceObject :: SchemaObjId - nqSourceObject = - SOSourceObj sourceName - $ AB.mkAnyBackend - $ SOINativeQueryObj @b _nqmRootFieldName - $ NQOReferencedLogicalModel innerLogicalModelName - - recordDependenciesM nqMetadataObject nqSourceObject - $ Seq.singleton (SchemaDependency nqSourceObject DRReferencedLogicalModel) - logicalModelPermissions <- - flip runReaderT sourceConfig $ buildLogicalModelPermissions sourceName tableCoreInfos logicalModelName _ilmmFields _ilmmSelectPermissions orderedRoles + flip runReaderT sourceConfig $ buildLogicalModelPermissions sourceName tableCoreInfos (LMLNativeQuery _nqmRootFieldName) _ilmmFields _ilmmSelectPermissions orderedRoles + + let recordDep (metadataObject', sourceObject) = + recordDependenciesM metadataObject' sourceObject + $ Seq.singleton (SchemaDependency sourceObject DRReferencedLogicalModel) -- record a dependency with each Logical Model our types reference - mapM_ recordDependency (concatMap (S.toList . getLogicalModelTypeDependencies . lmfType) _ilmmFields) + mapM_ + (recordDep . logicalModelDepObjects (toJSON preValidationNativeQuery) (LMLNativeQuery _nqmRootFieldName)) + (concatMap (S.toList . getLogicalModelTypeDependencies . lmfType) _ilmmFields) pure $ LogicalModelInfo - { _lmiName = logicalModelName, + { _lmiName = LogicalModelName (getNativeQueryName _nqmRootFieldName), _lmiFields = _ilmmFields, _lmiDescription = _nqmDescription, _lmiPermissions = logicalModelPermissions diff --git a/server/src-lib/Hasura/RQL/DDL/Schema/Cache/Common.hs b/server/src-lib/Hasura/RQL/DDL/Schema/Cache/Common.hs index 2e19acae425..cad9d4ff4fa 100644 --- a/server/src-lib/Hasura/RQL/DDL/Schema/Cache/Common.hs +++ b/server/src-lib/Hasura/RQL/DDL/Schema/Cache/Common.hs @@ -53,7 +53,7 @@ import Data.Sequence qualified as Seq import Data.Text.Extended import Hasura.Base.Error import Hasura.Incremental qualified as Inc -import Hasura.LogicalModel.Types (LogicalModelName) +import Hasura.LogicalModel.Types (LogicalModelLocation (..), LogicalModelName) import Hasura.Prelude import Hasura.RQL.DDL.Schema.Cache.Config import Hasura.RQL.DDL.SchemaRegistry (SchemaRegistryAction) @@ -389,5 +389,6 @@ buildInfoMapPreservingMetadataM extractKey mkMetadataObject buildInfo = addTableContext :: (Backend b) => TableName b -> Text -> Text addTableContext tableName e = "in table " <> tableName <<> ": " <> e -addLogicalModelContext :: LogicalModelName -> Text -> Text -addLogicalModelContext logicalModelName e = "in logical model " <> logicalModelName <<> ": " <> e +addLogicalModelContext :: LogicalModelLocation -> Text -> Text +addLogicalModelContext (LMLLogicalModel logicalModelName) e = "in logical model " <> logicalModelName <<> ": " <> e +addLogicalModelContext (LMLNativeQuery nativeQueryName) e = "in logical model for native query" <> nativeQueryName <<> ": " <> e diff --git a/server/src-lib/Hasura/RQL/DDL/Schema/Cache/Dependencies.hs b/server/src-lib/Hasura/RQL/DDL/Schema/Cache/Dependencies.hs index 07f9af6f96d..c9b36d92568 100644 --- a/server/src-lib/Hasura/RQL/DDL/Schema/Cache/Dependencies.hs +++ b/server/src-lib/Hasura/RQL/DDL/Schema/Cache/Dependencies.hs @@ -16,6 +16,7 @@ import Hasura.Base.Error import Hasura.Function.Lenses (fiPermissions) import Hasura.LogicalModel.Cache (LogicalModelInfo (..)) import Hasura.LogicalModel.Lenses (lmiPermissions) +import Hasura.LogicalModel.Types (LogicalModelLocation (..)) import Hasura.NativeQuery.Cache (NativeQueryInfo (_nqiReturns)) import Hasura.NativeQuery.Lenses (nqiRelationships) import Hasura.Prelude @@ -173,8 +174,13 @@ pruneDanglingDependents cache = `onNothing` Left ("function " <> functionName <<> " is not tracked") SOILogicalModel logicalModelName -> void $ resolveLogicalModel sourceInfo logicalModelName - SOILogicalModelObj logicalModelName logicalModelObjId -> do - logicalModel <- resolveLogicalModel sourceInfo logicalModelName + SOILogicalModelObj logicalModelLocation logicalModelObjId -> do + (logicalModelDesc, logicalModel) <- case logicalModelLocation of + LMLLogicalModel logicalModelName -> + (,) ("logical model " <>> logicalModelName) <$> resolveLogicalModel sourceInfo logicalModelName + LMLNativeQuery nativeQueryName -> + (,) ("logical model for native query " <>> nativeQueryName) + <$> resolveLogicalModelInNativeQuery sourceInfo nativeQueryName case logicalModelObjId of LMOReferencedLogicalModel inner -> void $ resolveLogicalModel sourceInfo inner @@ -186,13 +192,13 @@ pruneDanglingDependents cache = $ Left $ "no " <> permTypeToCode permType - <> " permission defined on logical model " - <> logicalModelName + <> " permission defined on " + <> logicalModelDesc <<> " for role " <>> roleName LMOCol column -> unless (InsOrdHashMap.member column (_lmiFields logicalModel)) do - Left ("Could not find column " <> column <<> " in logical model " <>> logicalModelName) + Left ("Could not find column " <> column <<> " in " <>> logicalModelDesc) SOINativeQuery nativeQueryName -> do void $ resolveNativeQuery sourceInfo nativeQueryName SOINativeQueryObj nativeQueryName nativeQueryObjId -> do @@ -202,8 +208,6 @@ pruneDanglingDependents cache = unless (InsOrdHashMap.member colName (_lmiFields (_nqiReturns nativeQueryInfo))) $ Left ("native query " <> nativeQueryName <<> " has no field named " <>> colName) - NQOReferencedLogicalModel inner -> - void $ resolveLogicalModel sourceInfo inner SOIStoredProcedure storedProcedureName -> do void $ resolveStoredProcedure sourceInfo storedProcedureName SOIStoredProcedureObj storedProcedureName storedProcedureObjId -> do @@ -278,6 +282,9 @@ pruneDanglingDependents cache = HashMap.lookup logicalModelName (_siLogicalModels sourceInfo) `onNothing` Left ("logical model " <> logicalModelName <<> " is not tracked") + resolveLogicalModelInNativeQuery sourceInfo nativeQueryName = + resolveNativeQuery sourceInfo nativeQueryName <&> \nq -> _nqiReturns nq + columnToFieldName :: forall b. (Backend b) => TableInfo b -> Column b -> FieldName columnToFieldName _ = fromCol @b @@ -349,18 +356,18 @@ deleteMetadataObject = \case SMONativeQueryObj nativeQueryName nativeQueryObjId -> siNativeQueries . ix nativeQueryName %~ case nativeQueryObjId of NQMORel name _ -> nqiRelationships %~ InsOrdHashMap.delete name - NQMOReferencedLogicalModel _ -> id SMOStoredProcedure name -> siStoredProcedures %~ HashMap.delete name SMOLogicalModel name -> -- TODO: if I'm inconsistent, delete everything that depends on me siLogicalModels %~ HashMap.delete name - SMOLogicalModelObj logicalModelName logicalModelObjectId -> + SMOLogicalModelObj (LMLLogicalModel logicalModelName) logicalModelObjectId -> siLogicalModels . ix logicalModelName %~ case logicalModelObjectId of LMMOPerm roleName PTSelect -> lmiPermissions . ix roleName . permSel .~ Nothing LMMOPerm roleName PTInsert -> lmiPermissions . ix roleName . permIns .~ Nothing LMMOPerm roleName PTUpdate -> lmiPermissions . ix roleName . permUpd .~ Nothing LMMOPerm roleName PTDelete -> lmiPermissions . ix roleName . permDel .~ Nothing LMMOReferencedLogicalModel _ -> id + SMOLogicalModelObj (LMLNativeQuery _nativeQueryName) _logicalModelObjectId -> error "copy above pls" SMOTableObj tableName tableObjectId -> siTables . ix tableName %~ case tableObjectId of MTORel name _ -> tiCoreInfo . tciFieldInfoMap %~ HashMap.delete (fromRel name) diff --git a/server/src-lib/Hasura/RQL/DDL/Schema/Cache/Permission.hs b/server/src-lib/Hasura/RQL/DDL/Schema/Cache/Permission.hs index c8c41533805..ad5982f3b5b 100644 --- a/server/src-lib/Hasura/RQL/DDL/Schema/Cache/Permission.hs +++ b/server/src-lib/Hasura/RQL/DDL/Schema/Cache/Permission.hs @@ -9,7 +9,7 @@ module Hasura.RQL.DDL.Schema.Cache.Permission ) where -import Data.Aeson +import Data.Aeson (ToJSON (..)) import Data.Environment qualified as Env import Data.Graph qualified as G import Data.Has @@ -19,7 +19,8 @@ import Data.Sequence qualified as Seq import Data.Text.Extended import Hasura.Base.Error import Hasura.LogicalModel.Metadata (WithLogicalModel (..)) -import Hasura.LogicalModel.Types (LogicalModelField (..), LogicalModelName) +import Hasura.LogicalModel.Types (LogicalModelField (..), LogicalModelLocation (..)) +import Hasura.NativeQuery.Metadata (WithNativeQuery (..)) import Hasura.Prelude import Hasura.RQL.DDL.Permission import Hasura.RQL.DDL.Schema.Cache.Common @@ -303,12 +304,12 @@ buildLogicalModelPermissions :: ) => SourceName -> TableCoreCache b -> - LogicalModelName -> + LogicalModelLocation -> InsOrdHashMap (Column b) (LogicalModelField b) -> InsOrdHashMap RoleName (SelPermDef b) -> OrderedRoles -> m (RolePermInfoMap b) -buildLogicalModelPermissions sourceName tableCache logicalModelName logicalModelFields selectPermissions orderedRoles = do +buildLogicalModelPermissions sourceName tableCache logicalModelLocation logicalModelFields selectPermissions orderedRoles = do let combineRolePermissions :: RolePermInfoMap b -> Role -> m (RolePermInfoMap b) combineRolePermissions acc (Role roleName (ParentRoles parentRoles)) = do -- This error will ideally never be thrown, but if it's thrown then @@ -348,59 +349,88 @@ buildLogicalModelPermissions sourceName tableCache logicalModelName logicalModel -- At the moment, we only support select permissions for logical models metadataRolePermissions <- - for (InsOrdHashMap.toHashMap selectPermissions) \selectPermission -> do - let role :: RoleName - role = _pdRole selectPermission - - -- An identifier for the object on we're going to need to depend to - -- generate this permission. - sourceObjId :: MetadataObjId - sourceObjId = - MOSourceObjId sourceName - $ AB.mkAnyBackend - $ SMOLogicalModelObj @b logicalModelName - $ LMMOPerm role PTSelect - - -- The object we're going to use to track the dependency and any - -- potential cache inconsistencies. - metadataObject :: MetadataObject - metadataObject = - MetadataObject sourceObjId - $ toJSON - WithLogicalModel - { _wlmSource = sourceName, - _wlmName = logicalModelName, - _wlmInfo = selectPermission - } - - -- An identifier for this permission within the metadata structure. - schemaObject :: SchemaObjId - schemaObject = - SOSourceObj sourceName - $ AB.mkAnyBackend - $ SOILogicalModelObj @b logicalModelName - $ LMOPerm role PTSelect - - modifyError :: ExceptT QErr m a -> ExceptT QErr m a - modifyError = modifyErr \err -> - addLogicalModelContext logicalModelName - $ "in permission for role " - <> role - <<> ": " - <> err - - select <- withRecordInconsistencyM metadataObject $ modifyError do - when (role == adminRoleName) - $ throw400 ConstraintViolation "cannot define permission for admin role" - - (permissionInformation, dependencies) <- - flip runTableCoreCacheRT tableCache - $ buildLogicalModelPermInfo sourceName logicalModelName logicalModelFields - $ _pdPermission selectPermission - - recordDependenciesM metadataObject schemaObject dependencies - pure permissionInformation - - pure (RolePermInfo Nothing select Nothing Nothing) + for + (InsOrdHashMap.toHashMap selectPermissions) + (buildLogicalModelSelectPermission sourceName tableCache logicalModelLocation logicalModelFields) foldlM combineRolePermissions metadataRolePermissions (_unOrderedRoles orderedRoles) + +buildLogicalModelSelectPermission :: + forall b m r. + ( MonadError QErr m, + MonadWriter (Seq CollectItem) m, + BackendMetadata b, + GetAggregationPredicatesDeps b, + MonadReader r m, + Has (ScalarTypeParsingContext b) r + ) => + SourceName -> + TableCoreCache b -> + LogicalModelLocation -> + InsOrdHashMap (Column b) (LogicalModelField b) -> + SelPermDef b -> + m (RolePermInfo b) +buildLogicalModelSelectPermission sourceName tableCache logicalModelLocation logicalModelFields selectPermission = do + let role :: RoleName + role = _pdRole selectPermission + + -- An identifier for the object on we're going to need to depend to + -- generate this permission. + sourceObjId :: MetadataObjId + sourceObjId = + MOSourceObjId sourceName + $ AB.mkAnyBackend + $ SMOLogicalModelObj @b logicalModelLocation + $ LMMOPerm role PTSelect + + -- The object we're going to use to track the dependency and any + -- potential cache inconsistencies. + -- we'll need to add one for each location a Logical Model can live in future (a Stored Procedure, etc) + metadataObject :: MetadataObject + metadataObject = case logicalModelLocation of + LMLLogicalModel logicalModelName -> + MetadataObject sourceObjId + $ toJSON + WithLogicalModel + { _wlmSource = sourceName, + _wlmName = logicalModelName, + _wlmInfo = selectPermission + } + LMLNativeQuery nativeQueryName -> + MetadataObject sourceObjId + $ toJSON + WithNativeQuery + { _wnqSource = sourceName, + _wnqName = nativeQueryName, + _wnqInfo = selectPermission + } + + -- An identifier for this permission within the metadata structure. + schemaObject :: SchemaObjId + schemaObject = + SOSourceObj sourceName + $ AB.mkAnyBackend + $ SOILogicalModelObj @b logicalModelLocation + $ LMOPerm role PTSelect + + modifyError :: ExceptT QErr m a -> ExceptT QErr m a + modifyError = modifyErr \err -> + addLogicalModelContext logicalModelLocation + $ "in permission for role " + <> role + <<> ": " + <> err + + select <- withRecordInconsistencyM metadataObject $ modifyError do + when (role == adminRoleName) + $ throw400 ConstraintViolation "cannot define permission for admin role" + + (permissionInformation, dependencies) <- + flip runTableCoreCacheRT tableCache + $ buildLogicalModelPermInfo sourceName logicalModelLocation logicalModelFields + $ _pdPermission selectPermission + + recordDependenciesM metadataObject schemaObject dependencies + pure permissionInformation + + pure (RolePermInfo Nothing select Nothing Nothing) diff --git a/server/src-lib/Hasura/RQL/Types/Metadata.hs b/server/src-lib/Hasura/RQL/Types/Metadata.hs index 5e85b60d213..061100c6d55 100644 --- a/server/src-lib/Hasura/RQL/Types/Metadata.hs +++ b/server/src-lib/Hasura/RQL/Types/Metadata.hs @@ -13,6 +13,7 @@ module Hasura.RQL.Types.Metadata dropFunctionInMetadata, dropPermissionInMetadata, dropLogicalModelPermissionInMetadata, + dropInlineLogicalModelPermissionInMetadata, dropRelationshipInMetadata, dropNativeQueryRelationshipInMetadata, dropRemoteRelationshipInMetadata, @@ -61,6 +62,8 @@ import Hasura.Function.Metadata (FunctionMetadata (..)) import Hasura.Incremental qualified as Inc import Hasura.LogicalModel.Lenses (lmmSelectPermissions) import Hasura.LogicalModel.Metadata (LogicalModelMetadata, LogicalModelName) +import Hasura.LogicalModelResolver.Lenses (ilmmSelectPermissions) +import Hasura.LogicalModelResolver.Metadata (InlineLogicalModelMetadata (..)) import Hasura.Metadata.DTO.MetadataV3 (MetadataV3 (..)) import Hasura.NativeQuery.Lenses (nqmArrayRelationships) import Hasura.NativeQuery.Metadata (NativeQueryMetadata, NativeQueryName) @@ -436,6 +439,14 @@ dropLogicalModelPermissionInMetadata rn = \case PTDelete -> error "Not implemented yet" PTUpdate -> error "Not implemented yet" +dropInlineLogicalModelPermissionInMetadata :: + RoleName -> PermType -> InlineLogicalModelMetadata b -> InlineLogicalModelMetadata b +dropInlineLogicalModelPermissionInMetadata rn = \case + PTSelect -> ilmmSelectPermissions %~ InsOrdHashMap.delete rn + PTInsert -> error "Not implemented yet" + PTDelete -> error "Not implemented yet" + PTUpdate -> error "Not implemented yet" + dropComputedFieldInMetadata :: ComputedFieldName -> TableMetadata b -> TableMetadata b dropComputedFieldInMetadata name = diff --git a/server/src-lib/Hasura/RQL/Types/Metadata/Object.hs b/server/src-lib/Hasura/RQL/Types/Metadata/Object.hs index 0a1920d6a89..7017b4f9259 100644 --- a/server/src-lib/Hasura/RQL/Types/Metadata/Object.hs +++ b/server/src-lib/Hasura/RQL/Types/Metadata/Object.hs @@ -84,7 +84,6 @@ instance Hashable LogicalModelMetadataObjId -- | the native query should probably also link to its logical model data NativeQueryMetadataObjId = NQMORel RelName RelType - | NQMOReferencedLogicalModel LogicalModelName deriving (Show, Eq, Ord, Generic) instance Hashable NativeQueryMetadataObjId @@ -98,7 +97,7 @@ data SourceMetadataObjId b | SMONativeQueryObj NativeQueryName NativeQueryMetadataObjId | SMOStoredProcedure (FunctionName b) | SMOLogicalModel LogicalModelName - | SMOLogicalModelObj LogicalModelName LogicalModelMetadataObjId + | SMOLogicalModelObj LogicalModelLocation LogicalModelMetadataObjId deriving (Generic) deriving instance (Backend b) => Show (SourceMetadataObjId b) @@ -162,7 +161,6 @@ moiTypeName = \case SMONativeQuery _ -> "native_query" SMONativeQueryObj _ nativeQueryObjId -> case nativeQueryObjId of NQMORel _ relType -> relTypeToTxt relType <> "_relation" - NQMOReferencedLogicalModel name -> "inner_logical_model_" <> toTxt name SMOStoredProcedure _ -> "stored_procedure" SMOLogicalModel _ -> "logical_model" SMOLogicalModelObj _ logicalModelObjectId -> case logicalModelObjectId of @@ -223,10 +221,9 @@ moiName objectId = SMONativeQueryObj nativeQueryName nativeQueryObjId -> case nativeQueryObjId of NQMORel name _ -> toTxt name <> " in " <> toTxt nativeQueryName - NQMOReferencedLogicalModel name -> toTxt name <> " in " <> toTxt nativeQueryName SMOStoredProcedure name -> toTxt name <> " in source " <> toTxt source SMOLogicalModel name -> toTxt name <> " in source " <> toTxt source - SMOLogicalModelObj logicalModelName logicalModelObjectId -> do + SMOLogicalModelObj (LMLLogicalModel logicalModelName) logicalModelObjectId -> do let objectName :: Text objectName = case logicalModelObjectId of LMMOPerm name _ -> toTxt name @@ -238,6 +235,19 @@ moiName objectId = $ AB.mkAnyBackend $ SMOLogicalModel @b logicalModelName + objectName <> " in " <> moiName sourceObjectId + SMOLogicalModelObj (LMLNativeQuery nativeQueryName) logicalModelObjectId -> do + let objectName :: Text + objectName = case logicalModelObjectId of + LMMOPerm name _ -> toTxt name + LMMOReferencedLogicalModel name -> toTxt name + + sourceObjectId :: MetadataObjId + sourceObjectId = + MOSourceObjId source + $ AB.mkAnyBackend + $ SMONativeQuery @b nativeQueryName + objectName <> " in " <> moiName sourceObjectId SMOTableObj tableName tableObjectId -> let tableObjectName = case tableObjectId of diff --git a/server/src-lib/Hasura/RQL/Types/SchemaCache.hs b/server/src-lib/Hasura/RQL/Types/SchemaCache.hs index 0efd71ffe7b..ebf995719be 100644 --- a/server/src-lib/Hasura/RQL/Types/SchemaCache.hs +++ b/server/src-lib/Hasura/RQL/Types/SchemaCache.hs @@ -125,7 +125,7 @@ import Hasura.Backends.Postgres.Connection qualified as Postgres import Hasura.Base.Error import Hasura.Function.Cache import Hasura.GraphQL.Context (GQLContext, RoleContext) -import Hasura.LogicalModel.Types (LogicalModelName) +import Hasura.LogicalModel.Types (LogicalModelLocation (..)) import Hasura.Prelude import Hasura.RQL.IR.BoolExp import Hasura.RQL.Types.Action @@ -200,14 +200,20 @@ mkLogicalModelParentDep :: forall b. (Backend b) => SourceName -> - LogicalModelName -> + LogicalModelLocation -> SchemaDependency -mkLogicalModelParentDep source logicalModelName = do +mkLogicalModelParentDep source logicalModelLocation = do let sourceObject :: SchemaObjId sourceObject = - SOSourceObj source - $ AB.mkAnyBackend @b - $ SOILogicalModel logicalModelName + case logicalModelLocation of + LMLLogicalModel logicalModelName -> + SOSourceObj source + $ AB.mkAnyBackend @b + $ SOILogicalModel logicalModelName + LMLNativeQuery nativeQueryName -> + SOSourceObj source + $ AB.mkAnyBackend @b + $ SOINativeQuery nativeQueryName SchemaDependency sourceObject DRTable @@ -232,15 +238,15 @@ mkLogicalModelColDep :: (Backend b) => DependencyReason -> SourceName -> - LogicalModelName -> + LogicalModelLocation -> Column b -> SchemaDependency -mkLogicalModelColDep reason source logicalModelName column = do +mkLogicalModelColDep reason source logicalModelLocation column = do let sourceObject :: SchemaObjId sourceObject = SOSourceObj source $ AB.mkAnyBackend - $ SOILogicalModelObj @b logicalModelName + $ SOILogicalModelObj @b logicalModelLocation $ LMOCol @b column SchemaDependency sourceObject reason @@ -753,14 +759,14 @@ getLogicalModelBoolExpDeps :: forall b. (GetAggregationPredicatesDeps b) => SourceName -> - LogicalModelName -> + LogicalModelLocation -> AnnBoolExpPartialSQL b -> [SchemaDependency] -getLogicalModelBoolExpDeps source logicalModelName = \case - BoolAnd exps -> concatMap (getLogicalModelBoolExpDeps source logicalModelName) exps - BoolOr exps -> concatMap (getLogicalModelBoolExpDeps source logicalModelName) exps - BoolNot e -> getLogicalModelBoolExpDeps source logicalModelName e - BoolField fld -> getLogicalModelColExpDeps source logicalModelName fld +getLogicalModelBoolExpDeps source logicalModelLocation = \case + BoolAnd exps -> concatMap (getLogicalModelBoolExpDeps source logicalModelLocation) exps + BoolOr exps -> concatMap (getLogicalModelBoolExpDeps source logicalModelLocation) exps + BoolNot e -> getLogicalModelBoolExpDeps source logicalModelLocation e + BoolField fld -> getLogicalModelColExpDeps source logicalModelLocation fld BoolExists (GExists refqt whereExp) -> do let table :: SchemaObjId table = SOSourceObj source $ AB.mkAnyBackend $ SOITable @b refqt @@ -776,10 +782,10 @@ getLogicalModelColExpDeps :: forall b. (GetAggregationPredicatesDeps b) => SourceName -> - LogicalModelName -> + LogicalModelLocation -> AnnBoolExpFld b (PartialSQLExp b) -> [SchemaDependency] -getLogicalModelColExpDeps source logicalModelName = \case +getLogicalModelColExpDeps source logicalModelLocation = \case AVRelationship {} -> [] AVComputedField _ -> [] AVAggregationPredicates _ -> [] @@ -793,9 +799,9 @@ getLogicalModelColExpDeps source logicalModelName = \case colDepReason = bool DRSessionVariable DROnType (any hasStaticExp opExps) colDep :: SchemaDependency - colDep = mkLogicalModelColDep @b colDepReason source logicalModelName columnName + colDep = mkLogicalModelColDep @b colDepReason source logicalModelLocation columnName - colDep : getLogicalModelOpExpDeps source logicalModelName opExps + colDep : getLogicalModelOpExpDeps source logicalModelLocation opExps -- | Discover the schema dependencies of an @AnnBoolExpPartialSQL@. getBoolExpDeps :: @@ -904,12 +910,12 @@ getLogicalModelOpExpDeps :: forall b. (Backend b) => SourceName -> - LogicalModelName -> + LogicalModelLocation -> [OpExpG b (PartialSQLExp b)] -> [SchemaDependency] -getLogicalModelOpExpDeps source logicalModelName operatorExpressions = do +getLogicalModelOpExpDeps source logicalModelLocation operatorExpressions = do RootOrCurrentColumn _ column <- mapMaybe opExpDepCol operatorExpressions - pure (mkLogicalModelColDep @b DROnType source logicalModelName column) + pure (mkLogicalModelColDep @b DROnType source logicalModelLocation column) -- | Asking for a table's fields info without explicit @'SourceName' argument. -- The source name is implicitly inferred from @'SourceM' via @'TableCoreInfoRM'. diff --git a/server/src-lib/Hasura/RQL/Types/SchemaCacheTypes.hs b/server/src-lib/Hasura/RQL/Types/SchemaCacheTypes.hs index 1073018f77e..05d40a7787f 100644 --- a/server/src-lib/Hasura/RQL/Types/SchemaCacheTypes.hs +++ b/server/src-lib/Hasura/RQL/Types/SchemaCacheTypes.hs @@ -29,7 +29,7 @@ import Data.Text qualified as T import Data.Text.Extended import Data.Text.NonEmpty import Hasura.Base.Error -import Hasura.LogicalModel.Types (LogicalModelName) +import Hasura.LogicalModel.Types (LogicalModelLocation, LogicalModelName) import Hasura.NativeQuery.Types (NativeQueryName) import Hasura.Prelude import Hasura.RQL.IR.BoolExp (PartialSQLExp) @@ -79,7 +79,6 @@ instance (Backend b) => Hashable (LogicalModelObjId b) -- that the two columns that join an array relationship actually exist. data NativeQueryObjId (b :: BackendType) = NQOCol (Column b) - | NQOReferencedLogicalModel LogicalModelName deriving (Generic) deriving instance (Backend b) => Eq (NativeQueryObjId b) @@ -107,7 +106,7 @@ data SourceObjId (b :: BackendType) | SOIStoredProcedure (FunctionName b) | SOIStoredProcedureObj (FunctionName b) (StoredProcedureObjId b) | SOILogicalModel LogicalModelName - | SOILogicalModelObj LogicalModelName (LogicalModelObjId b) + | SOILogicalModelObj LogicalModelLocation (LogicalModelObjId b) deriving (Eq, Generic) instance (Backend b) => Hashable (SourceObjId b) @@ -139,8 +138,6 @@ reportSchemaObj = \case SOINativeQuery nqn -> "native query " <> toTxt nqn SOINativeQueryObj nqn (NQOCol cn) -> "column " <> toTxt nqn <> "." <> toTxt cn - SOINativeQueryObj nqn (NQOReferencedLogicalModel inner) -> - "inner logical model " <> toTxt nqn <> "." <> toTxt inner SOIStoredProcedure spn -> "stored procedure " <> toTxt spn SOIStoredProcedureObj spn (SPOCol cn) -> "column " <> toTxt spn <> "." <> toTxt cn