Update OpenAPI (SQL COMMENT to description, constraints, cleaning up) (#885)
This commit is contained in:
committed by
Joe Nelson
parent
7fcdbf9153
commit
6d5f72bf5f
@@ -9,11 +9,13 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
- #889, Allow more than two conditions in a single and/or - @steve-chavez
|
- #889, Allow more than two conditions in a single and/or - @steve-chavez
|
||||||
- #883, Binary output support for RPC - @steve-chavez
|
- #883, Binary output support for RPC - @steve-chavez
|
||||||
|
- #885, Postgres COMMENTs on SCHEMA/TABLE/COLUMN are used for OpenAPI - @ldesgoui
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- #877, Base64 secret read from a file ending with a newline - @eric-brechemier
|
- #877, Base64 secret read from a file ending with a newline - @eric-brechemier
|
||||||
- #896, Boolean env var interpolation in config file - @begriffs
|
- #896, Boolean env var interpolation in config file - @begriffs
|
||||||
|
- #885, OpenAPI repetition reduced by using more definitions- @ldesgoui
|
||||||
|
|
||||||
## [0.4.2.0] - 2017-06-11
|
## [0.4.2.0] - 2017-06-11
|
||||||
|
|
||||||
|
|||||||
@@ -267,8 +267,8 @@ app dbStructure conf apiRequest =
|
|||||||
uri Nothing = ("http", host, port, "/")
|
uri Nothing = ("http", host, port, "/")
|
||||||
uri (Just Proxy { proxyScheme = s, proxyHost = h, proxyPort = p, proxyPath = b }) = (s, h, p, b)
|
uri (Just Proxy { proxyScheme = s, proxyHost = h, proxyPort = p, proxyPath = b }) = (s, h, p, b)
|
||||||
uri' = uri proxy
|
uri' = uri proxy
|
||||||
encodeApi ti = encodeOpenAPI (M.elems allProcs) ti uri'
|
encodeApi ti sd = encodeOpenAPI (M.elems allProcs) (toTableInfo ti) uri' sd (dbPrimaryKeys dbStructure)
|
||||||
body <- encodeApi . toTableInfo <$> H.query schema accessibleTables
|
body <- encodeApi <$> H.query schema accessibleTables <*> H.query schema schemaDescription
|
||||||
return $ responseLBS status200 [toHeader CTOpenAPI] $ toS body
|
return $ responseLBS status200 [toHeader CTOpenAPI] $ toS body
|
||||||
|
|
||||||
_ -> return notFound
|
_ -> return notFound
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ addRelations schema allRelations parentNode (Node readNode@(query, (name, _, ali
|
|||||||
_ -> n' <$> updateForest (Just (n' forest))
|
_ -> n' <$> updateForest (Just (n' forest))
|
||||||
where
|
where
|
||||||
n' = Node (query, (name, Just r, alias))
|
n' = Node (query, (name, Just r, alias))
|
||||||
t = Table schema name True -- !!! TODO find another way to get the table from the query
|
t = Table schema name Nothing True -- !!! TODO find another way to get the table from the query
|
||||||
r = Relation t [] t [] Root Nothing Nothing Nothing
|
r = Relation t [] t [] Root Nothing Nothing Nothing
|
||||||
where
|
where
|
||||||
updateForest :: Maybe ReadRequest -> Either ApiRequestError [ReadRequest]
|
updateForest :: Maybe ReadRequest -> Either ApiRequestError [ReadRequest]
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
module PostgREST.DbStructure (
|
module PostgREST.DbStructure (
|
||||||
getDbStructure
|
getDbStructure
|
||||||
, accessibleTables
|
, accessibleTables
|
||||||
|
, schemaDescription
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import qualified Hasql.Decoders as HD
|
import qualified Hasql.Decoders as HD
|
||||||
@@ -52,7 +53,9 @@ decodeTables :: HD.Result [Table]
|
|||||||
decodeTables =
|
decodeTables =
|
||||||
HD.rowsList tblRow
|
HD.rowsList tblRow
|
||||||
where
|
where
|
||||||
tblRow = Table <$> HD.value HD.text <*> HD.value HD.text
|
tblRow = Table <$> HD.value HD.text
|
||||||
|
<*> HD.value HD.text
|
||||||
|
<*> HD.nullableValue HD.text
|
||||||
<*> HD.value HD.bool
|
<*> HD.value HD.bool
|
||||||
|
|
||||||
decodeColumns :: [Table] -> HD.Result [Column]
|
decodeColumns :: [Table] -> HD.Result [Column]
|
||||||
@@ -60,11 +63,11 @@ decodeColumns tables =
|
|||||||
mapMaybe (columnFromRow tables) <$> HD.rowsList colRow
|
mapMaybe (columnFromRow tables) <$> HD.rowsList colRow
|
||||||
where
|
where
|
||||||
colRow =
|
colRow =
|
||||||
(,,,,,,,,,,)
|
(,,,,,,,,,,,)
|
||||||
<$> HD.value HD.text <*> HD.value HD.text
|
<$> HD.value HD.text <*> HD.value HD.text
|
||||||
<*> HD.value HD.text <*> HD.value HD.int4
|
<*> HD.value HD.text <*> HD.nullableValue HD.text
|
||||||
<*> HD.value HD.bool <*> HD.value HD.text
|
<*> HD.value HD.int4 <*> HD.value HD.bool
|
||||||
<*> HD.value HD.bool
|
<*> HD.value HD.text <*> HD.value HD.bool
|
||||||
<*> HD.nullableValue HD.int4
|
<*> HD.nullableValue HD.int4
|
||||||
<*> HD.nullableValue HD.int4
|
<*> HD.nullableValue HD.int4
|
||||||
<*> HD.nullableValue HD.text
|
<*> HD.nullableValue HD.text
|
||||||
@@ -103,6 +106,7 @@ accessibleProcs =
|
|||||||
(M.fromList . map addName <$>
|
(M.fromList . map addName <$>
|
||||||
HD.rowsList (
|
HD.rowsList (
|
||||||
ProcDescription <$> HD.value HD.text
|
ProcDescription <$> HD.value HD.text
|
||||||
|
<*> HD.nullableValue HD.text
|
||||||
<*> (parseArgs <$> HD.value HD.text)
|
<*> (parseArgs <$> HD.value HD.text)
|
||||||
<*> (parseRetType <$>
|
<*> (parseRetType <$>
|
||||||
HD.value HD.text <*>
|
HD.value HD.text <*>
|
||||||
@@ -150,6 +154,7 @@ accessibleProcs =
|
|||||||
|
|
||||||
sql = [q|
|
sql = [q|
|
||||||
SELECT p.proname as "proc_name",
|
SELECT p.proname as "proc_name",
|
||||||
|
d.description as "proc_description",
|
||||||
pg_get_function_arguments(p.oid) as "args",
|
pg_get_function_arguments(p.oid) as "args",
|
||||||
tn.nspname as "rettype_schema",
|
tn.nspname as "rettype_schema",
|
||||||
coalesce(comp.relname, t.typname) as "rettype_name",
|
coalesce(comp.relname, t.typname) as "rettype_name",
|
||||||
@@ -161,8 +166,22 @@ accessibleProcs =
|
|||||||
JOIN pg_type t ON t.oid = p.prorettype
|
JOIN pg_type t ON t.oid = p.prorettype
|
||||||
JOIN pg_namespace tn ON tn.oid = t.typnamespace
|
JOIN pg_namespace tn ON tn.oid = t.typnamespace
|
||||||
LEFT JOIN pg_class comp ON comp.oid = t.typrelid
|
LEFT JOIN pg_class comp ON comp.oid = t.typrelid
|
||||||
|
LEFT JOIN pg_catalog.pg_description as d on d.objoid = p.oid
|
||||||
WHERE pn.nspname = $1|]
|
WHERE pn.nspname = $1|]
|
||||||
|
|
||||||
|
schemaDescription :: H.Query Schema (Maybe Text)
|
||||||
|
schemaDescription =
|
||||||
|
H.statement sql (HE.value HE.text) (HD.singleRow $ HD.nullableValue HD.text) True
|
||||||
|
where
|
||||||
|
sql = [q|
|
||||||
|
select
|
||||||
|
description
|
||||||
|
from
|
||||||
|
pg_catalog.pg_namespace n
|
||||||
|
left join pg_catalog.pg_description d on d.objoid = n.oid
|
||||||
|
where
|
||||||
|
n.nspname = $1 |]
|
||||||
|
|
||||||
accessibleTables :: H.Query Schema [Table]
|
accessibleTables :: H.Query Schema [Table]
|
||||||
accessibleTables =
|
accessibleTables =
|
||||||
H.statement sql (HE.value HE.text) decodeTables True
|
H.statement sql (HE.value HE.text) decodeTables True
|
||||||
@@ -171,6 +190,7 @@ accessibleTables =
|
|||||||
select
|
select
|
||||||
n.nspname as table_schema,
|
n.nspname as table_schema,
|
||||||
relname as table_name,
|
relname as table_name,
|
||||||
|
d.description as table_description,
|
||||||
c.relkind = 'r' or (c.relkind IN ('v', 'f')) and (pg_relation_is_updatable(c.oid::regclass, false) & 8) = 8
|
c.relkind = 'r' or (c.relkind IN ('v', 'f')) and (pg_relation_is_updatable(c.oid::regclass, false) & 8) = 8
|
||||||
or (exists (
|
or (exists (
|
||||||
select 1
|
select 1
|
||||||
@@ -180,6 +200,7 @@ accessibleTables =
|
|||||||
from
|
from
|
||||||
pg_class c
|
pg_class c
|
||||||
join pg_namespace n on n.oid = c.relnamespace
|
join pg_namespace n on n.oid = c.relnamespace
|
||||||
|
left join pg_catalog.pg_description as d on d.objoid = c.oid and d.objsubid = 0
|
||||||
where
|
where
|
||||||
c.relkind in ('v', 'r', 'm')
|
c.relkind in ('v', 'r', 'm')
|
||||||
and n.nspname = $1
|
and n.nspname = $1
|
||||||
@@ -271,6 +292,7 @@ allTables =
|
|||||||
SELECT
|
SELECT
|
||||||
n.nspname AS table_schema,
|
n.nspname AS table_schema,
|
||||||
c.relname AS table_name,
|
c.relname AS table_name,
|
||||||
|
NULL AS table_description,
|
||||||
c.relkind = 'r' OR (c.relkind IN ('v','f'))
|
c.relkind = 'r' OR (c.relkind IN ('v','f'))
|
||||||
AND (pg_relation_is_updatable(c.oid::regclass, FALSE) & 8) = 8
|
AND (pg_relation_is_updatable(c.oid::regclass, FALSE) & 8) = 8
|
||||||
OR (EXISTS
|
OR (EXISTS
|
||||||
@@ -294,6 +316,7 @@ allColumns tabs =
|
|||||||
info.table_schema AS schema,
|
info.table_schema AS schema,
|
||||||
info.table_name AS table_name,
|
info.table_name AS table_name,
|
||||||
info.column_name AS name,
|
info.column_name AS name,
|
||||||
|
info.description AS description,
|
||||||
info.ordinal_position AS position,
|
info.ordinal_position AS position,
|
||||||
info.is_nullable::boolean AS nullable,
|
info.is_nullable::boolean AS nullable,
|
||||||
info.data_type AS col_type,
|
info.data_type AS col_type,
|
||||||
@@ -311,6 +334,7 @@ allColumns tabs =
|
|||||||
nc.nspname::information_schema.sql_identifier AS table_schema,
|
nc.nspname::information_schema.sql_identifier AS table_schema,
|
||||||
c.relname::information_schema.sql_identifier AS table_name,
|
c.relname::information_schema.sql_identifier AS table_name,
|
||||||
a.attname::information_schema.sql_identifier AS column_name,
|
a.attname::information_schema.sql_identifier AS column_name,
|
||||||
|
d.description::information_schema.sql_identifier AS description,
|
||||||
a.attnum::information_schema.cardinal_number AS ordinal_position,
|
a.attnum::information_schema.cardinal_number AS ordinal_position,
|
||||||
pg_get_expr(ad.adbin, ad.adrelid)::information_schema.character_data AS column_default,
|
pg_get_expr(ad.adbin, ad.adrelid)::information_schema.character_data AS column_default,
|
||||||
CASE
|
CASE
|
||||||
@@ -383,6 +407,7 @@ allColumns tabs =
|
|||||||
ELSE 'NO'::text
|
ELSE 'NO'::text
|
||||||
END::information_schema.yes_or_no AS is_updatable
|
END::information_schema.yes_or_no AS is_updatable
|
||||||
FROM pg_attribute a
|
FROM pg_attribute a
|
||||||
|
LEFT JOIN pg_catalog.pg_description AS d ON d.objoid = a.attrelid and d.objsubid = a.attnum
|
||||||
LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum
|
LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum
|
||||||
JOIN (pg_class c
|
JOIN (pg_class c
|
||||||
JOIN pg_namespace nc ON c.relnamespace = nc.oid) ON a.attrelid = c.oid
|
JOIN pg_namespace nc ON c.relnamespace = nc.oid) ON a.attrelid = c.oid
|
||||||
@@ -399,6 +424,7 @@ allColumns tabs =
|
|||||||
table_schema,
|
table_schema,
|
||||||
table_name,
|
table_name,
|
||||||
column_name,
|
column_name,
|
||||||
|
description,
|
||||||
ordinal_position,
|
ordinal_position,
|
||||||
is_nullable,
|
is_nullable,
|
||||||
data_type,
|
data_type,
|
||||||
@@ -424,14 +450,14 @@ allColumns tabs =
|
|||||||
ORDER BY schema, position |]
|
ORDER BY schema, position |]
|
||||||
|
|
||||||
columnFromRow :: [Table] ->
|
columnFromRow :: [Table] ->
|
||||||
(Text, Text, Text,
|
(Text, Text, Text,
|
||||||
Int32, Bool, Text,
|
Maybe Text, Int32, Bool,
|
||||||
Bool, Maybe Int32, Maybe Int32,
|
Text, Bool, Maybe Int32,
|
||||||
Maybe Text, Maybe Text)
|
Maybe Int32, Maybe Text, Maybe Text)
|
||||||
-> Maybe Column
|
-> Maybe Column
|
||||||
columnFromRow tabs (s, t, n, pos, nul, typ, u, l, p, d, e) = buildColumn <$> table
|
columnFromRow tabs (s, t, n, desc, pos, nul, typ, u, l, p, d, e) = buildColumn <$> table
|
||||||
where
|
where
|
||||||
buildColumn tbl = Column tbl n pos nul typ u l p d (parseEnum e) Nothing
|
buildColumn tbl = Column tbl n desc pos nul typ u l p d (parseEnum e) Nothing
|
||||||
table = find (\tbl -> tableSchema tbl == s && tableName tbl == t) tabs
|
table = find (\tbl -> tableSchema tbl == s && tableName tbl == t) tabs
|
||||||
parseEnum :: Maybe Text -> [Text]
|
parseEnum :: Maybe Text -> [Text]
|
||||||
parseEnum str = fromMaybe [] $ split (==',') <$> str
|
parseEnum str = fromMaybe [] $ split (==',') <$> str
|
||||||
|
|||||||
+147
-150
@@ -8,23 +8,22 @@ module PostgREST.OpenAPI (
|
|||||||
|
|
||||||
import Control.Lens
|
import Control.Lens
|
||||||
import Data.Aeson (decode, encode)
|
import Data.Aeson (decode, encode)
|
||||||
import qualified Data.HashMap.Strict as M
|
|
||||||
import Data.HashMap.Strict.InsOrd (InsOrdHashMap, fromList)
|
import Data.HashMap.Strict.InsOrd (InsOrdHashMap, fromList)
|
||||||
import Data.Maybe (fromJust)
|
import Data.Maybe (fromJust)
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
import Data.String (IsString (..))
|
import Data.String (IsString (..))
|
||||||
import Data.Text (unpack, pack, concat, intercalate, init, tail, toLower)
|
import Data.Text (unpack, pack, init, tail, toLower, intercalate, append)
|
||||||
import Network.URI (parseURI, isAbsoluteURI,
|
import Network.URI (parseURI, isAbsoluteURI,
|
||||||
URI (..), URIAuth (..))
|
URI (..), URIAuth (..))
|
||||||
|
|
||||||
import Protolude hiding (concat, (&), Proxy, get, intercalate)
|
import Protolude hiding ((&), Proxy, get, intercalate)
|
||||||
|
|
||||||
import Data.Swagger
|
import Data.Swagger
|
||||||
|
|
||||||
import PostgREST.ApiRequest (ContentType(..))
|
import PostgREST.ApiRequest (ContentType(..))
|
||||||
import PostgREST.Config (prettyVersion)
|
import PostgREST.Config (prettyVersion)
|
||||||
import PostgREST.Types (Table(..), Column(..), PgArg(..),
|
import PostgREST.Types (Table(..), Column(..), PgArg(..), ForeignKey(..),
|
||||||
Proxy(..), ProcDescription(..), toMime, operators)
|
PrimaryKey(..), Proxy(..), ProcDescription(..), toMime)
|
||||||
|
|
||||||
makeMimeList :: [ContentType] -> MimeList
|
makeMimeList :: [ContentType] -> MimeList
|
||||||
makeMimeList cs = MimeList $ map (fromString . toS . toMime) cs
|
makeMimeList cs = MimeList $ map (fromString . toS . toMime) cs
|
||||||
@@ -36,30 +35,48 @@ toSwaggerType "boolean" = SwaggerBoolean
|
|||||||
toSwaggerType "numeric" = SwaggerNumber
|
toSwaggerType "numeric" = SwaggerNumber
|
||||||
toSwaggerType _ = SwaggerString
|
toSwaggerType _ = SwaggerString
|
||||||
|
|
||||||
makeTableDef :: (Table, [Column], [Text]) -> (Text, Schema)
|
makeTableDef :: [PrimaryKey] -> (Table, [Column], [Text]) -> (Text, Schema)
|
||||||
makeTableDef (t, cs, _) =
|
makeTableDef pks (t, cs, _) =
|
||||||
let tn = tableName t in
|
let tn = tableName t in
|
||||||
(tn, (mempty :: Schema)
|
(tn, (mempty :: Schema)
|
||||||
|
& description .~ tableDescription t
|
||||||
& type_ .~ SwaggerObject
|
& type_ .~ SwaggerObject
|
||||||
& properties .~ fromList (map makeProperty cs))
|
& properties .~ fromList (map (makeProperty pks) cs))
|
||||||
|
|
||||||
makeProperty :: Column -> (Text, Referenced Schema)
|
makeProperty :: [PrimaryKey] -> Column -> (Text, Referenced Schema)
|
||||||
makeProperty c = (colName c, Inline u)
|
makeProperty pks c = (colName c, Inline s)
|
||||||
where
|
where
|
||||||
r = mempty :: Schema
|
e = if null $ colEnum c then Nothing else decode $ encode $ colEnum c
|
||||||
s = if null $ colEnum c
|
fk ForeignKey{fkCol=Column{colTable=Table{tableName=a}, colName=b}} =
|
||||||
then r
|
intercalate "" ["This is a Foreign Key to `", a, ".", b, "`.<fk table='", a, "' column='", b, "'/>"]
|
||||||
else r & enum_ .~ decode (encode (colEnum c))
|
pk :: Bool
|
||||||
t = s & type_ .~ toSwaggerType (colType c)
|
pk = any (\p -> pkTable p == colTable c && pkName p == colName c) pks
|
||||||
u = t & format ?~ colType c
|
n = catMaybes
|
||||||
|
[ Just "Note:"
|
||||||
|
, if pk then Just "This is a Primary Key.<pk/>" else Nothing
|
||||||
|
, fk <$> colFK c
|
||||||
|
]
|
||||||
|
d =
|
||||||
|
if length n > 1 then
|
||||||
|
Just $ append (fromMaybe "" ((`append` "\n\n") <$> colDescription c)) (intercalate "\n" n)
|
||||||
|
else
|
||||||
|
colDescription c
|
||||||
|
s =
|
||||||
|
(mempty :: Schema)
|
||||||
|
& default_ .~ (decode . toS =<< colDefault c)
|
||||||
|
& description .~ d
|
||||||
|
& enum_ .~ e
|
||||||
|
& format ?~ colType c
|
||||||
|
& maxLength .~ (fromIntegral <$> colMaxLen c)
|
||||||
|
& type_ .~ toSwaggerType (colType c)
|
||||||
|
|
||||||
makeProcDef :: ProcDescription -> (Text, Schema)
|
makeProcSchema :: ProcDescription -> Schema
|
||||||
makeProcDef pd = ("(rpc) " <> pdName pd, s)
|
makeProcSchema pd =
|
||||||
where
|
(mempty :: Schema)
|
||||||
s = (mempty :: Schema)
|
& description .~ pdDescription pd
|
||||||
& type_ .~ SwaggerObject
|
& type_ .~ SwaggerObject
|
||||||
& properties .~ fromList (map makeProcProperty (pdArgs pd))
|
& properties .~ fromList (map makeProcProperty (pdArgs pd))
|
||||||
& required .~ map pgaName (filter pgaReq (pdArgs pd))
|
& required .~ map pgaName (filter pgaReq (pdArgs pd))
|
||||||
|
|
||||||
makeProcProperty :: PgArg -> (Text, Referenced Schema)
|
makeProcProperty :: PgArg -> (Text, Referenced Schema)
|
||||||
makeProcProperty (PgArg n t _) = (n, Inline s)
|
makeProcProperty (PgArg n t _) = (n, Inline s)
|
||||||
@@ -68,68 +85,6 @@ makeProcProperty (PgArg n t _) = (n, Inline s)
|
|||||||
& type_ .~ toSwaggerType t
|
& type_ .~ toSwaggerType t
|
||||||
& format ?~ t
|
& format ?~ t
|
||||||
|
|
||||||
makeOperatorPattern :: Text
|
|
||||||
makeOperatorPattern =
|
|
||||||
intercalate "|"
|
|
||||||
[ concat ["^", x, y, "[.]"] |
|
|
||||||
x <- ["not[.]", ""],
|
|
||||||
y <- M.keys operators ]
|
|
||||||
|
|
||||||
makeRowFilter :: Column -> Param
|
|
||||||
makeRowFilter c =
|
|
||||||
(mempty :: Param)
|
|
||||||
& name .~ colName c
|
|
||||||
& required ?~ False
|
|
||||||
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
|
|
||||||
& in_ .~ ParamQuery
|
|
||||||
& type_ .~ SwaggerString
|
|
||||||
& format ?~ colType c
|
|
||||||
& pattern ?~ makeOperatorPattern)
|
|
||||||
|
|
||||||
makeRowFilters :: [Column] -> [Param]
|
|
||||||
makeRowFilters = map makeRowFilter
|
|
||||||
|
|
||||||
makeOrderItems :: [Column] -> [Text]
|
|
||||||
makeOrderItems cs =
|
|
||||||
[ concat [x, y, z] |
|
|
||||||
x <- map colName cs,
|
|
||||||
y <- [".asc", ".desc", ""],
|
|
||||||
z <- [".nullsfirst", ".nulllast", ""]
|
|
||||||
]
|
|
||||||
|
|
||||||
makeRangeParams :: [Param]
|
|
||||||
makeRangeParams =
|
|
||||||
[ (mempty :: Param)
|
|
||||||
& name .~ "Range"
|
|
||||||
& description ?~ "Limiting and Pagination"
|
|
||||||
& required ?~ False
|
|
||||||
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
|
|
||||||
& in_ .~ ParamHeader
|
|
||||||
& type_ .~ SwaggerString)
|
|
||||||
, (mempty :: Param)
|
|
||||||
& name .~ "Range-Unit"
|
|
||||||
& description ?~ "Limiting and Pagination"
|
|
||||||
& required ?~ False
|
|
||||||
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
|
|
||||||
& in_ .~ ParamHeader
|
|
||||||
& type_ .~ SwaggerString
|
|
||||||
& default_ .~ decode "\"items\"")
|
|
||||||
, (mempty :: Param)
|
|
||||||
& name .~ "offset"
|
|
||||||
& description ?~ "Limiting and Pagination"
|
|
||||||
& required ?~ False
|
|
||||||
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
|
|
||||||
& in_ .~ ParamQuery
|
|
||||||
& type_ .~ SwaggerString)
|
|
||||||
, (mempty :: Param)
|
|
||||||
& name .~ "limit"
|
|
||||||
& description ?~ "Limiting and Pagination"
|
|
||||||
& required ?~ False
|
|
||||||
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
|
|
||||||
& in_ .~ ParamQuery
|
|
||||||
& type_ .~ SwaggerString)
|
|
||||||
]
|
|
||||||
|
|
||||||
makePreferParam :: [Text] -> Param
|
makePreferParam :: [Text] -> Param
|
||||||
makePreferParam ts =
|
makePreferParam ts =
|
||||||
(mempty :: Param)
|
(mempty :: Param)
|
||||||
@@ -141,92 +96,126 @@ makePreferParam ts =
|
|||||||
& type_ .~ SwaggerString
|
& type_ .~ SwaggerString
|
||||||
& enum_ .~ decode (encode ts))
|
& enum_ .~ decode (encode ts))
|
||||||
|
|
||||||
makeSelectParam :: Param
|
makeProcParam :: ProcDescription -> [Referenced Param]
|
||||||
makeSelectParam =
|
makeProcParam pd =
|
||||||
(mempty :: Param)
|
[ Inline $ (mempty :: Param)
|
||||||
& name .~ "select"
|
& name .~ "args"
|
||||||
& description ?~ "Filtering Columns"
|
& required ?~ True
|
||||||
& required ?~ False
|
& schema .~ (ParamBody $ Inline $ makeProcSchema pd)
|
||||||
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
|
, Ref $ Reference "preferParams"
|
||||||
& in_ .~ ParamQuery
|
|
||||||
& type_ .~ SwaggerString)
|
|
||||||
|
|
||||||
makeGetParams :: [Column] -> [Param]
|
|
||||||
makeGetParams [] =
|
|
||||||
makeRangeParams ++
|
|
||||||
[ makeSelectParam
|
|
||||||
, makePreferParam ["count=none"]
|
|
||||||
]
|
]
|
||||||
makeGetParams cs =
|
|
||||||
makeRangeParams ++
|
makeParamDefs :: [(Table, [Column], [Text])] -> [(Text, Param)]
|
||||||
[ makeSelectParam
|
makeParamDefs ti =
|
||||||
, (mempty :: Param)
|
[ ("preferParams", makePreferParam ["params=single-object"])
|
||||||
& name .~ "order"
|
, ("preferReturn", makePreferParam ["return=representation", "return=minimal", "return=none"])
|
||||||
& description ?~ "Ordering"
|
, ("preferCount", makePreferParam ["count=none"])
|
||||||
& required ?~ False
|
, ("select", (mempty :: Param)
|
||||||
|
& name .~ "select"
|
||||||
|
& description ?~ "Filtering Columns"
|
||||||
|
& required ?~ False
|
||||||
|
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
|
||||||
|
& in_ .~ ParamQuery
|
||||||
|
& type_ .~ SwaggerString))
|
||||||
|
, ("order", (mempty :: Param)
|
||||||
|
& name .~ "order"
|
||||||
|
& description ?~ "Ordering"
|
||||||
|
& required ?~ False
|
||||||
|
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
|
||||||
|
& in_ .~ ParamQuery
|
||||||
|
& type_ .~ SwaggerString))
|
||||||
|
, ("range", (mempty :: Param)
|
||||||
|
& name .~ "Range"
|
||||||
|
& description ?~ "Limiting and Pagination"
|
||||||
|
& required ?~ False
|
||||||
|
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
|
||||||
|
& in_ .~ ParamHeader
|
||||||
|
& type_ .~ SwaggerString))
|
||||||
|
, ("rangeUnit", (mempty :: Param)
|
||||||
|
& name .~ "Range-Unit"
|
||||||
|
& description ?~ "Limiting and Pagination"
|
||||||
|
& required ?~ False
|
||||||
|
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
|
||||||
|
& in_ .~ ParamHeader
|
||||||
|
& type_ .~ SwaggerString
|
||||||
|
& default_ .~ decode "\"items\""))
|
||||||
|
, ("offset", (mempty :: Param)
|
||||||
|
& name .~ "offset"
|
||||||
|
& description ?~ "Limiting and Pagination"
|
||||||
|
& required ?~ False
|
||||||
|
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
|
||||||
|
& in_ .~ ParamQuery
|
||||||
|
& type_ .~ SwaggerString))
|
||||||
|
, ("limit", (mempty :: Param)
|
||||||
|
& name .~ "limit"
|
||||||
|
& description ?~ "Limiting and Pagination"
|
||||||
|
& required ?~ False
|
||||||
|
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
|
||||||
|
& in_ .~ ParamQuery
|
||||||
|
& type_ .~ SwaggerString))
|
||||||
|
]
|
||||||
|
<> concat [ makeObjectBody (tableName t) : makeRowFilters (tableName t) cs
|
||||||
|
| (t, cs, _) <- ti
|
||||||
|
]
|
||||||
|
|
||||||
|
makeObjectBody :: Text -> (Text, Param)
|
||||||
|
makeObjectBody tn =
|
||||||
|
("body." <> tn, (mempty :: Param)
|
||||||
|
& name .~ tn
|
||||||
|
& description ?~ tn
|
||||||
|
& required ?~ False
|
||||||
|
& schema .~ ParamBody (Ref (Reference tn)))
|
||||||
|
|
||||||
|
makeRowFilter :: Text -> Column -> (Text, Param)
|
||||||
|
makeRowFilter tn c =
|
||||||
|
(intercalate "." ["rowFilter", tn, colName c], (mempty :: Param)
|
||||||
|
& name .~ colName c
|
||||||
|
& description .~ colDescription c
|
||||||
|
& required ?~ False
|
||||||
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
|
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
|
||||||
& in_ .~ ParamQuery
|
& in_ .~ ParamQuery
|
||||||
& type_ .~ SwaggerString
|
& type_ .~ SwaggerString
|
||||||
& enum_ .~ decode (encode $ makeOrderItems cs))
|
& format ?~ colType c))
|
||||||
, makePreferParam ["count=none"]
|
|
||||||
]
|
|
||||||
|
|
||||||
makePostParams :: Text -> [Param]
|
makeRowFilters :: Text -> [Column] -> [(Text, Param)]
|
||||||
makePostParams tn =
|
makeRowFilters tn = map (makeRowFilter tn)
|
||||||
[ makePreferParam ["return=representation",
|
|
||||||
"return=minimal", "return=none"]
|
|
||||||
, (mempty :: Param)
|
|
||||||
& name .~ "body"
|
|
||||||
& description ?~ tn
|
|
||||||
& required ?~ False
|
|
||||||
& schema .~ ParamBody (Ref (Reference tn))
|
|
||||||
]
|
|
||||||
|
|
||||||
makeProcParam :: Text -> [Param]
|
|
||||||
makeProcParam refName =
|
|
||||||
[ makePreferParam ["params=single-object"]
|
|
||||||
, (mempty :: Param)
|
|
||||||
& name .~ "args"
|
|
||||||
& required ?~ True
|
|
||||||
& schema .~ ParamBody (Ref (Reference refName))
|
|
||||||
]
|
|
||||||
|
|
||||||
makeDeleteParams :: [Param]
|
|
||||||
makeDeleteParams =
|
|
||||||
[ makePreferParam ["return=representation", "return=minimal", "return=none"] ]
|
|
||||||
|
|
||||||
makePathItem :: (Table, [Column], [Text]) -> (FilePath, PathItem)
|
makePathItem :: (Table, [Column], [Text]) -> (FilePath, PathItem)
|
||||||
makePathItem (t, cs, _) = ("/" ++ unpack tn, p $ tableInsertable t)
|
makePathItem (t, cs, _) = ("/" ++ unpack tn, p $ tableInsertable t)
|
||||||
where
|
where
|
||||||
tOp = (mempty :: Operation)
|
tOp = (mempty :: Operation)
|
||||||
& tags .~ Set.fromList [tn]
|
& tags .~ Set.fromList [tn]
|
||||||
& produces ?~ makeMimeList [CTApplicationJSON, CTSingularJSON, CTTextCSV]
|
& description .~ tableDescription t
|
||||||
& at 200 ?~ "OK"
|
|
||||||
getOp = tOp
|
getOp = tOp
|
||||||
& parameters .~ map Inline (makeGetParams cs ++ rs)
|
& parameters .~ map ref (rs <> ["select", "order", "range", "rangeUnit", "offset", "limit", "preferCount"])
|
||||||
& at 206 ?~ "Partial Content"
|
& at 206 ?~ "Partial Content"
|
||||||
|
& at 200 ?~ Inline ((mempty :: Response)
|
||||||
|
& description .~ "OK"
|
||||||
|
& schema ?~ (Ref $ Reference $ tableName t)
|
||||||
|
)
|
||||||
postOp = tOp
|
postOp = tOp
|
||||||
& consumes ?~ makeMimeList [CTApplicationJSON, CTSingularJSON, CTTextCSV]
|
& parameters .~ map ref ["body." <> tn, "preferReturn"]
|
||||||
& parameters .~ map Inline (makePostParams tn)
|
|
||||||
& at 201 ?~ "Created"
|
& at 201 ?~ "Created"
|
||||||
patchOp = tOp
|
patchOp = tOp
|
||||||
& consumes ?~ makeMimeList [CTApplicationJSON, CTSingularJSON, CTTextCSV]
|
& parameters .~ map ref (rs <> ["body." <> tn, "preferReturn"])
|
||||||
& parameters .~ map Inline (makePostParams tn ++ rs)
|
|
||||||
& at 204 ?~ "No Content"
|
& at 204 ?~ "No Content"
|
||||||
deletOp = tOp
|
deletOp = tOp
|
||||||
& parameters .~ map Inline (makeDeleteParams ++ rs)
|
& parameters .~ map ref (rs <> ["preferReturn"])
|
||||||
|
& at 204 ?~ "No Content"
|
||||||
pr = (mempty :: PathItem) & get ?~ getOp
|
pr = (mempty :: PathItem) & get ?~ getOp
|
||||||
pw = pr & post ?~ postOp & patch ?~ patchOp & delete ?~ deletOp
|
pw = pr & post ?~ postOp & patch ?~ patchOp & delete ?~ deletOp
|
||||||
p False = pr
|
p False = pr
|
||||||
p True = pw
|
p True = pw
|
||||||
rs = makeRowFilters cs
|
|
||||||
tn = tableName t
|
tn = tableName t
|
||||||
|
rs = [ intercalate "." ["rowFilter", tn, colName c ] | c <- cs ]
|
||||||
|
ref = Ref . Reference
|
||||||
|
|
||||||
makeProcPathItem :: ProcDescription -> (FilePath, PathItem)
|
makeProcPathItem :: ProcDescription -> (FilePath, PathItem)
|
||||||
makeProcPathItem pd = ("/rpc/" ++ toS (pdName pd), pe)
|
makeProcPathItem pd = ("/rpc/" ++ toS (pdName pd), pe)
|
||||||
where
|
where
|
||||||
postOp = (mempty :: Operation)
|
postOp = (mempty :: Operation)
|
||||||
& parameters .~ map Inline (makeProcParam $ "(rpc) " <> pdName pd)
|
& description .~ pdDescription pd
|
||||||
|
& parameters .~ makeProcParam pd
|
||||||
& tags .~ Set.fromList ["(rpc) " <> pdName pd]
|
& tags .~ Set.fromList ["(rpc) " <> pdName pd]
|
||||||
& produces ?~ makeMimeList [CTApplicationJSON, CTSingularJSON]
|
& produces ?~ makeMimeList [CTApplicationJSON, CTSingularJSON]
|
||||||
& at 200 ?~ "OK"
|
& at 200 ?~ "OK"
|
||||||
@@ -236,7 +225,8 @@ makeRootPathItem :: (FilePath, PathItem)
|
|||||||
makeRootPathItem = ("/", p)
|
makeRootPathItem = ("/", p)
|
||||||
where
|
where
|
||||||
getOp = (mempty :: Operation)
|
getOp = (mempty :: Operation)
|
||||||
& tags .~ Set.fromList ["/"]
|
& tags .~ Set.fromList ["Introspection"]
|
||||||
|
& summary ?~ "OpenAPI description (this document)"
|
||||||
& produces ?~ makeMimeList [CTOpenAPI, CTApplicationJSON]
|
& produces ?~ makeMimeList [CTOpenAPI, CTApplicationJSON]
|
||||||
& at 200 ?~ "OK"
|
& at 200 ?~ "OK"
|
||||||
pr = (mempty :: PathItem) & get ?~ getOp
|
pr = (mempty :: PathItem) & get ?~ getOp
|
||||||
@@ -254,23 +244,30 @@ escapeHostName "*6" = "0.0.0.0"
|
|||||||
escapeHostName "!6" = "0.0.0.0"
|
escapeHostName "!6" = "0.0.0.0"
|
||||||
escapeHostName h = h
|
escapeHostName h = h
|
||||||
|
|
||||||
postgrestSpec :: [ProcDescription] -> [(Table, [Column], [Text])] -> (Text, Text, Integer, Text) -> Swagger
|
postgrestSpec :: [ProcDescription] -> [(Table, [Column], [Text])] -> (Text, Text, Integer, Text) -> Maybe Text -> [PrimaryKey] -> Swagger
|
||||||
postgrestSpec pds ti (s, h, p, b) = (mempty :: Swagger)
|
postgrestSpec pds ti (s, h, p, b) sd pks = (mempty :: Swagger)
|
||||||
& basePath ?~ unpack b
|
& basePath ?~ unpack b
|
||||||
& schemes ?~ [s']
|
& schemes ?~ [s']
|
||||||
& info .~ ((mempty :: Info)
|
& info .~ ((mempty :: Info)
|
||||||
& version .~ prettyVersion
|
& version .~ prettyVersion
|
||||||
& title .~ "PostgREST API"
|
& title .~ "PostgREST API"
|
||||||
& description ?~ "This is a dynamic API generated by PostgREST")
|
& description ?~ d)
|
||||||
|
& externalDocs ?~ ((mempty :: ExternalDocs)
|
||||||
|
& description ?~ "PostgREST Documentation"
|
||||||
|
& url .~ URL "https://postgrest.com/en/latest/api.html")
|
||||||
& host .~ h'
|
& host .~ h'
|
||||||
& definitions .~ fromList (map makeTableDef ti <> map makeProcDef pds)
|
& definitions .~ fromList (map (makeTableDef pks) ti)
|
||||||
|
& parameters .~ fromList (makeParamDefs ti)
|
||||||
& paths .~ makePathItems pds ti
|
& paths .~ makePathItems pds ti
|
||||||
|
& produces .~ makeMimeList [CTApplicationJSON, CTSingularJSON, CTTextCSV]
|
||||||
|
& consumes .~ makeMimeList [CTApplicationJSON, CTSingularJSON, CTTextCSV]
|
||||||
where
|
where
|
||||||
s' = if s == "http" then Http else Https
|
s' = if s == "http" then Http else Https
|
||||||
h' = Just $ Host (unpack $ escapeHostName h) (Just (fromInteger p))
|
h' = Just $ Host (unpack $ escapeHostName h) (Just (fromInteger p))
|
||||||
|
d = fromMaybe "This is a dynamic API generated by PostgREST" sd
|
||||||
|
|
||||||
encodeOpenAPI :: [ProcDescription] -> [(Table, [Column], [Text])] -> (Text, Text, Integer, Text) -> LByteString
|
encodeOpenAPI :: [ProcDescription] -> [(Table, [Column], [Text])] -> (Text, Text, Integer, Text) -> Maybe Text -> [PrimaryKey] -> LByteString
|
||||||
encodeOpenAPI pds ti uri = encode $ postgrestSpec pds ti uri
|
encodeOpenAPI pds ti uri sd pks = encode $ postgrestSpec pds ti uri sd pks
|
||||||
|
|
||||||
{-|
|
{-|
|
||||||
Test whether a proxy uri is malformed or not.
|
Test whether a proxy uri is malformed or not.
|
||||||
|
|||||||
+21
-49
@@ -46,10 +46,11 @@ data ProcVolatility = Volatile | Stable | Immutable
|
|||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
data ProcDescription = ProcDescription {
|
data ProcDescription = ProcDescription {
|
||||||
pdName :: Text
|
pdName :: Text
|
||||||
, pdArgs :: [PgArg]
|
, pdDescription :: Maybe Text
|
||||||
, pdReturnType :: RetType
|
, pdArgs :: [PgArg]
|
||||||
, pdVolatility :: ProcVolatility
|
, pdReturnType :: RetType
|
||||||
|
, pdVolatility :: ProcVolatility
|
||||||
} deriving (Show, Eq)
|
} deriving (Show, Eq)
|
||||||
|
|
||||||
type Schema = Text
|
type Schema = Text
|
||||||
@@ -59,26 +60,28 @@ type SqlFragment = Text
|
|||||||
type RequestBody = BL.ByteString
|
type RequestBody = BL.ByteString
|
||||||
|
|
||||||
data Table = Table {
|
data Table = Table {
|
||||||
tableSchema :: Schema
|
tableSchema :: Schema
|
||||||
, tableName :: TableName
|
, tableName :: TableName
|
||||||
, tableInsertable :: Bool
|
, tableDescription :: Maybe Text
|
||||||
|
, tableInsertable :: Bool
|
||||||
} deriving (Show, Ord)
|
} deriving (Show, Ord)
|
||||||
|
|
||||||
newtype ForeignKey = ForeignKey { fkCol :: Column } deriving (Show, Eq, Ord)
|
newtype ForeignKey = ForeignKey { fkCol :: Column } deriving (Show, Eq, Ord)
|
||||||
|
|
||||||
data Column =
|
data Column =
|
||||||
Column {
|
Column {
|
||||||
colTable :: Table
|
colTable :: Table
|
||||||
, colName :: Text
|
, colName :: Text
|
||||||
, colPosition :: Int32
|
, colDescription :: Maybe Text
|
||||||
, colNullable :: Bool
|
, colPosition :: Int32
|
||||||
, colType :: Text
|
, colNullable :: Bool
|
||||||
, colUpdatable :: Bool
|
, colType :: Text
|
||||||
, colMaxLen :: Maybe Int32
|
, colUpdatable :: Bool
|
||||||
, colPrecision :: Maybe Int32
|
, colMaxLen :: Maybe Int32
|
||||||
, colDefault :: Maybe Text
|
, colPrecision :: Maybe Int32
|
||||||
, colEnum :: [Text]
|
, colDefault :: Maybe Text
|
||||||
, colFK :: Maybe ForeignKey
|
, colEnum :: [Text]
|
||||||
|
, colFK :: Maybe ForeignKey
|
||||||
} deriving (Show, Ord)
|
} deriving (Show, Ord)
|
||||||
|
|
||||||
type Synonym = (Column,Column)
|
type Synonym = (Column,Column)
|
||||||
@@ -193,37 +196,6 @@ type ReadRequest = Tree ReadNode
|
|||||||
type MutateRequest = MutateQuery
|
type MutateRequest = MutateQuery
|
||||||
data DbRequest = DbRead ReadRequest | DbMutate MutateRequest
|
data DbRequest = DbRead ReadRequest | DbMutate MutateRequest
|
||||||
|
|
||||||
instance ToJSON Column where
|
|
||||||
toJSON c = object [
|
|
||||||
"schema" .= tableSchema t
|
|
||||||
, "name" .= colName c
|
|
||||||
, "position" .= colPosition c
|
|
||||||
, "nullable" .= colNullable c
|
|
||||||
, "type" .= colType c
|
|
||||||
, "updatable" .= colUpdatable c
|
|
||||||
, "maxLen" .= colMaxLen c
|
|
||||||
, "precision" .= colPrecision c
|
|
||||||
, "references".= colFK c
|
|
||||||
, "default" .= colDefault c
|
|
||||||
, "enum" .= colEnum c ]
|
|
||||||
where
|
|
||||||
t = colTable c
|
|
||||||
|
|
||||||
instance ToJSON ForeignKey where
|
|
||||||
toJSON fk = object [
|
|
||||||
"schema" .= tableSchema t
|
|
||||||
, "table" .= tableName t
|
|
||||||
, "column" .= colName c ]
|
|
||||||
where
|
|
||||||
c = fkCol fk
|
|
||||||
t = colTable c
|
|
||||||
|
|
||||||
instance ToJSON Table where
|
|
||||||
toJSON v = object [
|
|
||||||
"schema" .= tableSchema v
|
|
||||||
, "name" .= tableName v
|
|
||||||
, "insertable" .= tableInsertable v ]
|
|
||||||
|
|
||||||
instance Eq Table where
|
instance Eq Table where
|
||||||
Table{tableSchema=s1,tableName=n1} == Table{tableSchema=s2,tableName=n2} = s1 == s2 && n1 == n2
|
Table{tableSchema=s1,tableName=n1} == Table{tableSchema=s2,tableName=n2} = s1 == s2 && n1 == n2
|
||||||
|
|
||||||
|
|||||||
@@ -27,18 +27,82 @@ spec = do
|
|||||||
(acceptHdrs "application/openapi+json") ""
|
(acceptHdrs "application/openapi+json") ""
|
||||||
`shouldRespondWith` 415
|
`shouldRespondWith` 415
|
||||||
|
|
||||||
describe "RPC" $
|
describe "table" $
|
||||||
|
|
||||||
it "includes a representative function with parameters" $ do
|
it "includes paths to tables" $ do
|
||||||
r <- simpleBody <$> get "/"
|
r <- simpleBody <$> get "/"
|
||||||
let ref = r ^? key "paths" . key "/rpc/varied_arguments"
|
|
||||||
. key "post" . key "parameters"
|
let method s = key "paths" . key "/child_entities" . key s
|
||||||
. nth 1 . key "schema"
|
getParameters = r ^? method "get" . key "parameters"
|
||||||
. key "$ref" . _String
|
postResponse = r ^? method "post" . key "responses" . key "201" . key "description"
|
||||||
args = r ^? key "definitions" . key "(rpc) varied_arguments"
|
patchResponse = r ^? method "patch" . key "responses" . key "204" . key "description"
|
||||||
|
deleteResponse = r ^? method "delete" . key "responses" . key "204" . key "description"
|
||||||
|
|
||||||
liftIO $ do
|
liftIO $ do
|
||||||
ref `shouldBe` Just "#/definitions/(rpc) varied_arguments"
|
|
||||||
|
getParameters `shouldBe` Just
|
||||||
|
[aesonQQ|
|
||||||
|
[
|
||||||
|
{ "$ref": "#/parameters/rowFilter.child_entities.id" },
|
||||||
|
{ "$ref": "#/parameters/rowFilter.child_entities.name" },
|
||||||
|
{ "$ref": "#/parameters/rowFilter.child_entities.parent_id" },
|
||||||
|
{ "$ref": "#/parameters/select" },
|
||||||
|
{ "$ref": "#/parameters/order" },
|
||||||
|
{ "$ref": "#/parameters/range" },
|
||||||
|
{ "$ref": "#/parameters/rangeUnit" },
|
||||||
|
{ "$ref": "#/parameters/offset" },
|
||||||
|
{ "$ref": "#/parameters/limit" },
|
||||||
|
{ "$ref": "#/parameters/preferCount" }
|
||||||
|
]
|
||||||
|
|]
|
||||||
|
|
||||||
|
postResponse `shouldBe` Just "Created"
|
||||||
|
|
||||||
|
patchResponse `shouldBe` Just "No Content"
|
||||||
|
|
||||||
|
deleteResponse `shouldBe` Just "No Content"
|
||||||
|
|
||||||
|
it "includes definitions to tables" $ do
|
||||||
|
r <- simpleBody <$> get "/"
|
||||||
|
|
||||||
|
let def = r ^? key "definitions" . key "child_entities"
|
||||||
|
|
||||||
|
liftIO $
|
||||||
|
|
||||||
|
def `shouldBe` Just
|
||||||
|
[aesonQQ|
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"description": "child_entities comment",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"description": "child_entities id comment\n\nNote:\nThis is a Primary Key.<pk/>",
|
||||||
|
"format": "integer",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"description": "child_entities name comment",
|
||||||
|
"format": "text",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"parent_id": {
|
||||||
|
"description": "Note:\nThis is a Foreign Key to `entities.id`.<fk table='entities' column='id'/>",
|
||||||
|
"format": "integer",
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|]
|
||||||
|
|
||||||
|
describe "RPC" $
|
||||||
|
|
||||||
|
it "includes body schema for arguments" $ do
|
||||||
|
r <- simpleBody <$> get "/"
|
||||||
|
let args = r ^? key "paths" . key "/rpc/varied_arguments"
|
||||||
|
. key "post" . key "parameters"
|
||||||
|
. nth 0 . key "schema"
|
||||||
|
|
||||||
|
liftIO $
|
||||||
args `shouldBe` Just
|
args `shouldBe` Just
|
||||||
[aesonQQ|
|
[aesonQQ|
|
||||||
{
|
{
|
||||||
|
|||||||
Vendored
+6
@@ -1215,6 +1215,12 @@ create table grandchild_entities (
|
|||||||
jsonb_col jsonb
|
jsonb_col jsonb
|
||||||
);
|
);
|
||||||
|
|
||||||
|
-- OpenAPI description tests
|
||||||
|
|
||||||
|
comment on table child_entities is 'child_entities comment';
|
||||||
|
comment on column child_entities.id is 'child_entities id comment';
|
||||||
|
comment on column child_entities.name is 'child_entities name comment';
|
||||||
|
|
||||||
-- Used for testing that having the same return column name as the proc name
|
-- Used for testing that having the same return column name as the proc name
|
||||||
-- doesn't conflict with the required output, details in #901
|
-- doesn't conflict with the required output, details in #901
|
||||||
create function test.test() returns table(test text, value int) as $$
|
create function test.test() returns table(test text, value int) as $$
|
||||||
|
|||||||
Reference in New Issue
Block a user