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:
Lyndon Maydwell 2023-01-24 20:26:08 +10:00 committed by hasura-bot
parent d15af96f10
commit 615fd64c04
2 changed files with 84 additions and 35 deletions

View File

@ -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

View File

@ -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,22 +133,25 @@ 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 _) ->
(_, _, Just _)
| not (predicate name || predicate relatedTable) -> []
| otherwise ->
[ Relationship
{ rType = ObjRel,
rFrom = Mapping {mTable = name, mColumns = localColumns},
rTo = Mapping {mTable = relatedTable, mColumns = relatedColumns}
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},
rFrom = Mapping {mTable = relatedTable, mColumns = relatedColumns}
rTo = Mapping {mTable = name, mColumns = localColumns, mConstraintName = Just constraintName},
rFrom = Mapping {mTable = relatedTable, mColumns = relatedColumns, mConstraintName = Nothing}
}
]
where
@ -154,16 +160,18 @@ suggestRelsFK omitTracked tables name uniqueConstraints tracked foreignKey =
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)