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