mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 09:22:43 +03:00
Add constraint_name
key to *_suggest_relationships
Metadata API and increase sensitivity of tables
argument
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/7625 GitOrigin-RevId: d764f1664b63abbe4a4ff166e0bc7224bcb0dc57
This commit is contained in:
parent
d15af96f10
commit
615fd64c04
server
lib/api-tests/src/Test/API/Metadata
src-lib/Hasura/RQL/DDL/Relationship
@ -11,7 +11,7 @@ import Harness.Quoter.Yaml (yaml)
|
||||
import Harness.Test.Fixture qualified as Fixture
|
||||
import Harness.Test.Schema qualified as Schema
|
||||
import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment, getBackendTypeConfig)
|
||||
import Harness.Yaml (mapObject, shouldReturnYaml, shouldReturnYamlF, sortArray)
|
||||
import Harness.Yaml (mapObject, shouldReturnYamlF, sortArray)
|
||||
import Hasura.Prelude
|
||||
import Test.Hspec (SpecWith, describe, it)
|
||||
|
||||
@ -132,7 +132,8 @@ tests opts = do
|
||||
sourceName = Fixture.backendSourceName backendTypeMetadata
|
||||
schemaName = Schema.unSchemaName $ Schema.getSchemaName testEnv
|
||||
|
||||
shouldReturnYaml
|
||||
shouldReturnYamlF
|
||||
(pure . mapObject sortArray)
|
||||
opts
|
||||
( GraphqlEngine.postMetadataWithStatus
|
||||
200
|
||||
@ -153,7 +154,8 @@ tests opts = do
|
||||
relationships:
|
||||
- from:
|
||||
columns:
|
||||
- publication_id
|
||||
- author_id
|
||||
constraint_name: article_author_id_fkey
|
||||
table:
|
||||
name: article
|
||||
schema: hasura
|
||||
@ -161,9 +163,23 @@ tests opts = do
|
||||
columns:
|
||||
- id
|
||||
table:
|
||||
name: publication
|
||||
name: author
|
||||
schema: hasura
|
||||
type: object
|
||||
- from:
|
||||
columns:
|
||||
- id
|
||||
table:
|
||||
name: author
|
||||
schema: hasura
|
||||
to:
|
||||
columns:
|
||||
- author_id
|
||||
constraint_name: article_author_id_fkey
|
||||
table:
|
||||
name: article
|
||||
schema: hasura
|
||||
type: array
|
||||
- from:
|
||||
columns:
|
||||
- id
|
||||
@ -173,17 +189,33 @@ tests opts = do
|
||||
to:
|
||||
columns:
|
||||
- publication_id
|
||||
constraint_name: article_publication_id_fkey
|
||||
table:
|
||||
name: article
|
||||
schema: hasura
|
||||
type: object
|
||||
- from:
|
||||
columns:
|
||||
- publication_id
|
||||
constraint_name: article_publication_id_fkey
|
||||
table:
|
||||
name: article
|
||||
schema: hasura
|
||||
to:
|
||||
columns:
|
||||
- id
|
||||
table:
|
||||
name: publication
|
||||
schema: hasura
|
||||
type: object
|
||||
|]
|
||||
|
||||
it "Omits tracked relationships if that is requested" $ \testEnv -> do
|
||||
let backendTypeMetadata = Maybe.fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnv
|
||||
sourceName = Fixture.backendSourceName backendTypeMetadata
|
||||
|
||||
shouldReturnYaml
|
||||
shouldReturnYamlF
|
||||
(pure . mapObject sortArray)
|
||||
opts
|
||||
( GraphqlEngine.postMetadataWithStatus
|
||||
200
|
||||
@ -201,6 +233,7 @@ tests opts = do
|
||||
- from:
|
||||
columns:
|
||||
- author_id
|
||||
constraint_name: article_author_id_fkey
|
||||
table:
|
||||
name: article
|
||||
schema: hasura
|
||||
@ -220,6 +253,7 @@ tests opts = do
|
||||
to:
|
||||
columns:
|
||||
- author_id
|
||||
constraint_name: article_author_id_fkey
|
||||
table:
|
||||
name: article
|
||||
schema: hasura
|
||||
@ -230,7 +264,8 @@ tests opts = do
|
||||
let backendTypeMetadata = Maybe.fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnv
|
||||
sourceName = Fixture.backendSourceName backendTypeMetadata
|
||||
|
||||
shouldReturnYaml
|
||||
shouldReturnYamlF
|
||||
(pure . mapObject sortArray)
|
||||
opts
|
||||
( GraphqlEngine.postMetadataWithStatus
|
||||
200
|
||||
@ -267,6 +302,7 @@ tests opts = do
|
||||
- from:
|
||||
columns:
|
||||
- author_id
|
||||
constraint_name: article_author_id_fkey
|
||||
table:
|
||||
name: article
|
||||
schema: hasura
|
||||
@ -286,6 +322,7 @@ tests opts = do
|
||||
to:
|
||||
columns:
|
||||
- author_id
|
||||
constraint_name: article_author_id_fkey
|
||||
table:
|
||||
name: article
|
||||
schema: hasura
|
||||
@ -299,6 +336,7 @@ tests opts = do
|
||||
to:
|
||||
columns:
|
||||
- publication_id
|
||||
constraint_name: article_publication_id_fkey
|
||||
table:
|
||||
name: article
|
||||
schema: hasura
|
||||
@ -306,6 +344,7 @@ tests opts = do
|
||||
- from:
|
||||
columns:
|
||||
- publication_id
|
||||
constraint_name: article_publication_id_fkey
|
||||
table:
|
||||
name: article
|
||||
schema: hasura
|
||||
|
@ -24,7 +24,7 @@ where
|
||||
import Autodocodec
|
||||
import Autodocodec.OpenAPI ()
|
||||
import Control.Lens (preview)
|
||||
import Data.Aeson (FromJSON (), ToJSON ())
|
||||
import Data.Aeson qualified as Aeson
|
||||
import Data.HashMap.Strict qualified as Map
|
||||
import Data.HashMap.Strict.NonEmpty qualified as MapNE
|
||||
import Data.HashSet qualified as H
|
||||
@ -38,7 +38,7 @@ import Hasura.RQL.Types.Metadata.Backend
|
||||
import Hasura.RQL.Types.Relationships.Local (RelInfo (riMapping, riRTable))
|
||||
import Hasura.RQL.Types.SchemaCache
|
||||
import Hasura.RQL.Types.SchemaCache.Build
|
||||
import Hasura.RQL.Types.Table (ForeignKey, UniqueConstraint, _fkColumnMapping, _fkForeignTable, _ucColumns)
|
||||
import Hasura.RQL.Types.Table (ForeignKey, UniqueConstraint, _cName, _fkColumnMapping, _fkConstraint, _fkForeignTable, _ucColumns)
|
||||
|
||||
-- | Datatype used by Metadata API to represent Request for Suggested Relationships
|
||||
data SuggestRels b = SuggestRels
|
||||
@ -47,7 +47,7 @@ data SuggestRels b = SuggestRels
|
||||
_srsOmitTracked :: Bool
|
||||
}
|
||||
deriving (Generic)
|
||||
deriving (FromJSON, ToJSON, ToSchema) via Autodocodec (SuggestRels b)
|
||||
deriving (Aeson.FromJSON, Aeson.ToJSON, ToSchema) via Autodocodec (SuggestRels b)
|
||||
|
||||
instance Backend b => HasCodec (SuggestRels b) where
|
||||
codec =
|
||||
@ -67,7 +67,7 @@ newtype SuggestedRelationships b = Relationships
|
||||
{ sRelationships :: [Relationship b]
|
||||
}
|
||||
deriving (Generic)
|
||||
deriving (FromJSON, ToJSON, ToSchema) via Autodocodec (SuggestedRelationships b)
|
||||
deriving (Aeson.FromJSON, Aeson.ToJSON, ToSchema) via Autodocodec (SuggestedRelationships b)
|
||||
|
||||
instance Backend b => HasCodec (SuggestedRelationships b) where
|
||||
codec =
|
||||
@ -84,7 +84,7 @@ data Relationship b = Relationship
|
||||
rTo :: Mapping b
|
||||
}
|
||||
deriving (Generic)
|
||||
deriving (FromJSON, ToJSON, ToSchema) via Autodocodec (Relationship b)
|
||||
deriving (Aeson.FromJSON, Aeson.ToJSON, ToSchema) via Autodocodec (Relationship b)
|
||||
|
||||
instance Backend b => HasCodec (Relationship b) where
|
||||
codec =
|
||||
@ -101,10 +101,11 @@ instance Backend b => HasCodec (Relationship b) where
|
||||
|
||||
data Mapping b = Mapping
|
||||
{ mTable :: TableName b,
|
||||
mColumns :: [Column b]
|
||||
mColumns :: [Column b],
|
||||
mConstraintName :: Maybe Aeson.Value
|
||||
}
|
||||
deriving (Generic)
|
||||
deriving (FromJSON, ToJSON, ToSchema) via Autodocodec (Mapping b)
|
||||
deriving (Aeson.FromJSON, Aeson.ToJSON, ToSchema) via Autodocodec (Mapping b)
|
||||
|
||||
instance Backend b => HasCodec (Mapping b) where
|
||||
codec =
|
||||
@ -115,6 +116,8 @@ instance Backend b => HasCodec (Mapping b) where
|
||||
.= mTable
|
||||
<*> requiredField' "columns"
|
||||
.= mColumns
|
||||
<*> optionalFieldOrNull' "constraint_name"
|
||||
.= mConstraintName
|
||||
)
|
||||
|
||||
-- | Most of the heavy lifting for this module occurs in this function.
|
||||
@ -130,40 +133,45 @@ suggestRelsFK ::
|
||||
TableName b ->
|
||||
HashSet (UniqueConstraint b) ->
|
||||
H.HashSet (TableName b, HashMap (Column b) (Column b)) ->
|
||||
(TableName b -> Bool) ->
|
||||
ForeignKey b ->
|
||||
[Relationship b]
|
||||
suggestRelsFK omitTracked tables name uniqueConstraints tracked foreignKey =
|
||||
suggestRelsFK omitTracked tables name uniqueConstraints tracked predicate foreignKey =
|
||||
case (omitTracked, H.member (relatedTable, columnRelationships) tracked, Map.lookup relatedTable tables) of
|
||||
(True, True, _) -> []
|
||||
(_, _, Nothing) -> []
|
||||
(_, _, Just _) ->
|
||||
[ Relationship
|
||||
{ rType = ObjRel,
|
||||
rFrom = Mapping {mTable = name, mColumns = localColumns},
|
||||
rTo = Mapping {mTable = relatedTable, mColumns = relatedColumns}
|
||||
},
|
||||
Relationship
|
||||
{ rType = if H.fromList localColumns `H.member` uniqueConstraintColumns then ObjRel else ArrRel,
|
||||
rTo = Mapping {mTable = name, mColumns = localColumns},
|
||||
rFrom = Mapping {mTable = relatedTable, mColumns = relatedColumns}
|
||||
}
|
||||
]
|
||||
(_, _, Just _)
|
||||
| not (predicate name || predicate relatedTable) -> []
|
||||
| otherwise ->
|
||||
[ Relationship
|
||||
{ rType = ObjRel,
|
||||
rFrom = Mapping {mTable = name, mColumns = localColumns, mConstraintName = Just constraintName},
|
||||
rTo = Mapping {mTable = relatedTable, mColumns = relatedColumns, mConstraintName = Nothing}
|
||||
},
|
||||
Relationship
|
||||
{ rType = if H.fromList localColumns `H.member` uniqueConstraintColumns then ObjRel else ArrRel,
|
||||
rTo = Mapping {mTable = name, mColumns = localColumns, mConstraintName = Just constraintName},
|
||||
rFrom = Mapping {mTable = relatedTable, mColumns = relatedColumns, mConstraintName = Nothing}
|
||||
}
|
||||
]
|
||||
where
|
||||
columnRelationships = MapNE.toHashMap (_fkColumnMapping foreignKey)
|
||||
localColumns = Map.keys columnRelationships
|
||||
relatedColumns = Map.elems columnRelationships
|
||||
uniqueConstraintColumns = H.map _ucColumns uniqueConstraints
|
||||
relatedTable = _fkForeignTable foreignKey
|
||||
constraintName = Aeson.toJSON (_cName (_fkConstraint foreignKey))
|
||||
|
||||
suggestRelsTable ::
|
||||
forall b.
|
||||
Backend b =>
|
||||
Bool ->
|
||||
HashMap (TableName b) (TableCoreInfo b) ->
|
||||
(TableName b -> Bool) ->
|
||||
(TableName b, TableCoreInfo b) ->
|
||||
[Relationship b]
|
||||
suggestRelsTable omitTracked tables (name, table) =
|
||||
suggestRelsFK omitTracked tables name constraints tracked =<< toList foreignKeys
|
||||
suggestRelsTable omitTracked tables predicate (name, table) =
|
||||
suggestRelsFK omitTracked tables name constraints tracked predicate =<< toList foreignKeys
|
||||
where
|
||||
foreignKeys = _tciForeignKeys table
|
||||
constraints = _tciUniqueConstraints table
|
||||
@ -176,15 +184,17 @@ suggestRelsResponse ::
|
||||
Backend b =>
|
||||
Bool ->
|
||||
HashMap (TableName b) (TableCoreInfo b) ->
|
||||
(TableName b -> Bool) ->
|
||||
SuggestedRelationships b
|
||||
suggestRelsResponse omitTracked tables =
|
||||
suggestRelsResponse omitTracked tables predicate =
|
||||
Relationships $
|
||||
suggestRelsTable omitTracked tables =<< Map.toList tables
|
||||
suggestRelsTable omitTracked tables predicate =<< Map.toList tables
|
||||
|
||||
-- | Helper to filter tables considered for relationships
|
||||
pluck :: Eq a => Maybe [a] -> Map.HashMap a b -> Map.HashMap a b
|
||||
pluck Nothing = id
|
||||
pluck (Just ks) = Map.mapMaybeWithKey (\k v -> if k `elem` ks then Just v else Nothing)
|
||||
tablePredicate :: Hashable a => Maybe [a] -> a -> Bool
|
||||
tablePredicate Nothing _ = True
|
||||
tablePredicate (Just ns) n = n `H.member` hash
|
||||
where
|
||||
hash = H.fromList ns
|
||||
|
||||
-- | The method invoked when dispatching on metadata calls in POST /v1/metadata
|
||||
runSuggestRels ::
|
||||
@ -196,4 +206,4 @@ runSuggestRels (SuggestRels source tablesM omitExistingB) = do
|
||||
tableCacheM <- fmap (fmap (_tiCoreInfo)) <$> askTableCache @b source
|
||||
case tableCacheM of
|
||||
Nothing -> throw500 "Couldn't find any schema source information"
|
||||
Just tableCache -> pure $ encJFromJValue $ suggestRelsResponse @b omitExistingB (pluck tablesM tableCache)
|
||||
Just tableCache -> pure $ encJFromJValue $ suggestRelsResponse @b omitExistingB tableCache (tablePredicate tablesM)
|
||||
|
Loading…
Reference in New Issue
Block a user