feat: expose unique columns and many-to-many markers in OpenAPI
Add unique constraint and many-to-many relationship metadata to the generated OpenAPI spec so clients can render them. - Store unique constraints on Table as tableUniqueCols (mirroring tablePKCols) instead of denormalizing them onto each Column. - Compute unique constraints via a per-table tbl_unique_cols CTE in tablesSqlQuery. - Annotate unique columns and composite unique constraints in property descriptions, and emit m2m markers in table descriptions.
This commit is contained in:
@@ -30,8 +30,8 @@ import PostgREST.Network (escapeHostName)
|
||||
import PostgREST.Query.OpenApi (TableAccess (..), TablesAccess)
|
||||
import PostgREST.SchemaCache (SchemaCache (..))
|
||||
import PostgREST.SchemaCache.Identifiers (FieldName, QualifiedIdentifier (..))
|
||||
import PostgREST.SchemaCache.Relationship (Cardinality (..), Relationship (..),
|
||||
RelationshipsMap)
|
||||
import PostgREST.SchemaCache.Relationship (Cardinality (..), Junction (..),
|
||||
Relationship (..), RelationshipsMap)
|
||||
import PostgREST.SchemaCache.Routine (FuncVolatility (..), Routine (..),
|
||||
RoutineParam (..))
|
||||
import PostgREST.SchemaCache.Table (Column (..), Table (..), TablesMap,
|
||||
@@ -110,13 +110,35 @@ parseDefault colType colDefault =
|
||||
makeTableDef :: RelationshipsMap -> (Table, TableAccess) -> (Text, Schema)
|
||||
makeTableDef rels (t, access) =
|
||||
(tn, (mempty :: Schema)
|
||||
& description .~ tableDescription t
|
||||
& description .~ tblDescription
|
||||
& type_ ?~ SwaggerObject
|
||||
& properties .~ fromList (makeProperty t rels <$> cols)
|
||||
& required .~ fmap colName (filter (not . colNullable) cols))
|
||||
where
|
||||
tn = tableName t
|
||||
cols = accessibleCols t (taSelectCols access)
|
||||
tblDescription = case m2mMarkers t rels of
|
||||
[] -> tableDescription t
|
||||
ms -> Just $ maybe "" (`T.append` "\n\n") (tableDescription t) <> T.intercalate "\n" ms
|
||||
|
||||
-- | Emits markers for the many-to-many relationships of a table, so that clients
|
||||
-- can render these relations. The marker includes the target table(embedding key),
|
||||
-- the junction table and the junction columns referencing source and target.
|
||||
m2mMarkers :: Table -> RelationshipsMap -> [Text]
|
||||
m2mMarkers tbl rels = mapMaybe m2mMarker searchedRels
|
||||
where
|
||||
searchedRels = fromMaybe mempty $ HM.lookup (QualifiedIdentifier (tableSchema tbl) (tableName tbl), tableSchema tbl) rels
|
||||
m2mMarker Relationship{relForeignTable, relCardinality=M2M junction} =
|
||||
Just $ T.intercalate ""
|
||||
[ "<m2m table='", qiName relForeignTable
|
||||
, "' junction='", qiName (junTable junction)
|
||||
, "' source='", junctionSourceCol junction
|
||||
, "' target='", junctionTargetCol junction
|
||||
, "'/>"
|
||||
]
|
||||
m2mMarker _ = Nothing
|
||||
junctionSourceCol junction = maybe mempty snd (headMay $ junColsSource junction)
|
||||
junctionTargetCol junction = maybe mempty snd (headMay $ junColsTarget junction)
|
||||
|
||||
accessibleCols :: Table -> [FieldName] -> [Column]
|
||||
accessibleCols t cols = filter ((`elem` cols) . colName) (tableColumnsList t)
|
||||
@@ -144,11 +166,18 @@ makeProperty tbl rels col = (colName col, Inline s)
|
||||
(\(a, b) -> T.intercalate "" ["This is a Foreign Key to `", a, ".", b, "`.<fk table='", a, "' column='", b, "'/>"]) <$> fTblCol
|
||||
pk :: Bool
|
||||
pk = colName col `elem` tablePKCols tbl
|
||||
uniqueNotes :: [Text]
|
||||
uniqueNotes = mapMaybe uniqueNote (filter (colName col `elem`) (tableUniqueCols tbl))
|
||||
where
|
||||
uniqueNote cols
|
||||
| length cols == 1 = Just "This is a Unique column.<unique/>"
|
||||
| otherwise = Just $ "This is part of a composite unique constraint.<unique cols='" <> T.intercalate "," cols <> "'/>"
|
||||
n = catMaybes
|
||||
[ Just "Note:"
|
||||
, if pk then Just "This is a Primary Key.<pk/>" else Nothing
|
||||
, fk
|
||||
]
|
||||
<> uniqueNotes
|
||||
<> catMaybes [fk]
|
||||
d =
|
||||
if length n > 1 then
|
||||
Just $ T.append (maybe "" (`T.append` "\n\n") $ colDescription col) (T.intercalate "\n" n)
|
||||
|
||||
Reference in New Issue
Block a user