From f49c6aa0f31574994aaace674763e2932f8a6e60 Mon Sep 17 00:00:00 2001 From: Joe Nelson Date: Thu, 8 Sep 2016 21:23:16 -0700 Subject: [PATCH 1/9] WIP: show procs in OpenAPI description Thanks @LogvinovLeon --- src/PostgREST/App.hs | 8 +++++--- src/PostgREST/DbStructure.hs | 14 +++++++++++--- src/PostgREST/OpenAPI.hs | 28 ++++++++++++++++++++-------- src/PostgREST/Types.hs | 8 +++++++- 4 files changed, 43 insertions(+), 15 deletions(-) diff --git a/src/PostgREST/App.hs b/src/PostgREST/App.hs index cca8392a5..30ae755f7 100644 --- a/src/PostgREST/App.hs +++ b/src/PostgREST/App.hs @@ -213,7 +213,8 @@ app dbStructure conf apiRequest = singular = iPreferSingular apiRequest jwtSecret = configJwtSecret conf returnType = lookup (qiName qi) $ dbProcs dbStructure - returnsJWT = fromMaybe False $ isInfixOf "jwt_claims" <$> returnType + returnsJWT = fromMaybe False $ + isInfixOf "jwt_claims" . pdReturnType <$> returnType serves [CTApplicationJSON] (iAccepts apiRequest) $ \_ -> case readSqlParts of Left e -> return $ responseLBS status400 [jsonH] $ toS e Right (q,cq) -> respondToRange $ do @@ -233,7 +234,7 @@ app dbStructure conf apiRequest = uri Nothing = ("http", host, port, "/") uri (Just Proxy { proxyScheme = s, proxyHost = h, proxyPort = p, proxyPath = b }) = (s, h, p, b) uri' = uri proxy - encodeApi ti = encodeOpenAPI ti uri' + encodeApi ti = encodeOpenAPI (map snd $ dbProcs dbStructure) ti uri' serves [CTOpenAPI] (iAccepts apiRequest) $ \_ -> do body <- encodeApi . toTableInfo <$> H.query schema accessibleTables return $ responseLBS status200 [openapiH] $ toS body @@ -269,7 +270,8 @@ app dbStructure conf apiRequest = schema = toS $ configSchema conf shouldCount = iPreferCount apiRequest topLevelRange = fromMaybe allRange $ M.lookup "limit" $ iRange apiRequest - readDbRequest = DbRead <$> buildReadRequest (configMaxRows conf) (dbRelations dbStructure) (dbProcs dbStructure) apiRequest + mapSnd f (a, b) = (a, f b) + readDbRequest = DbRead <$> buildReadRequest (configMaxRows conf) (dbRelations dbStructure) (map (mapSnd pdReturnType) $ dbProcs dbStructure) apiRequest mutateDbRequest = DbMutate <$> buildMutateRequest apiRequest selectQuery = requestToQuery schema False <$> readDbRequest countQuery = requestToCountQuery schema <$> readDbRequest diff --git a/src/PostgREST/DbStructure.hs b/src/PostgREST/DbStructure.hs index 419de824f..b57f5d398 100644 --- a/src/PostgREST/DbStructure.hs +++ b/src/PostgREST/DbStructure.hs @@ -95,12 +95,20 @@ decodeSynonyms cols = <*> HD.value HD.text <*> HD.value HD.text <*> HD.value HD.text <*> HD.value HD.text -accessibleProcs :: H.Query Schema [(Text, Text)] +accessibleProcs :: H.Query Schema [(Text, ProcDescription)] accessibleProcs = - H.statement sql (HE.value HE.text) (HD.rowsList ((,) <$> HD.value HD.text <*> HD.value HD.text)) True + H.statement sql (HE.value HE.text) + (map addName <$> HD.rowsList (ProcDescription <$> HD.value HD.text + <*> HD.value HD.text + <*> HD.value HD.text)) True where + addName :: ProcDescription -> (Text, ProcDescription) + addName pd = (pdName pd, pd) + sql = [q| - SELECT p.proname as "proc_name", pg_get_function_result(p.oid) as "return_type" + SELECT p.proname as "proc_name", + pg_get_function_arguments(p.oid) as "args", + pg_get_function_result(p.oid) as "return_type" FROM pg_namespace n JOIN pg_proc p ON pronamespace = n.oid diff --git a/src/PostgREST/OpenAPI.hs b/src/PostgREST/OpenAPI.hs index af2167f5e..daba8a247 100644 --- a/src/PostgREST/OpenAPI.hs +++ b/src/PostgREST/OpenAPI.hs @@ -24,7 +24,7 @@ import PostgREST.ApiRequest (ContentType(..), toHeader) import PostgREST.Config (prettyVersion) import PostgREST.QueryBuilder (operators) import PostgREST.Types (Table(..), Column(..), - Proxy(..)) + Proxy(..), ProcDescription(..)) makeMimeList :: [ContentType] -> MimeList makeMimeList cs = MimeList $ map (fromString . toS . toHeader) cs @@ -204,6 +204,17 @@ makePathItem (t, cs, _) = ("/" ++ unpack tn, p $ tableInsertable t) rs = makeRowFilters cs tn = tableName t +makeProcPathItem :: ProcDescription -> (FilePath, PathItem) +makeProcPathItem pd = ("/rpc/" ++ toS (pdName pd), pe) + where + postOp = (mempty :: Operation) + & parameters .~ [] + & tags .~ Set.fromList ["/rpc/" <> pdName pd] + & produces ?~ makeMimeList [CTApplicationJSON] + & at 200 ?~ "OK" + pe = (mempty :: PathItem) & post ?~ postOp + + makeRootPathItem :: (FilePath, PathItem) makeRootPathItem = ("/", p) where @@ -214,8 +225,9 @@ makeRootPathItem = ("/", p) pr = (mempty :: PathItem) & get ?~ getOp p = pr -makePathItems :: [(Table, [Column], [Text])] -> InsOrdHashMap FilePath PathItem -makePathItems ti = fromList $ makeRootPathItem : map makePathItem ti +makePathItems :: [ProcDescription] -> [(Table, [Column], [Text])] -> InsOrdHashMap FilePath PathItem +makePathItems pds ti = fromList $ makeRootPathItem : + (map makePathItem ti) ++ (map makeProcPathItem pds) escapeHostName :: Text -> Text escapeHostName "*" = "0.0.0.0" @@ -225,8 +237,8 @@ escapeHostName "*6" = "0.0.0.0" escapeHostName "!6" = "0.0.0.0" escapeHostName h = h -postgrestSpec:: [(Table, [Column], [Text])] -> (Text, Text, Integer, Text) -> Swagger -postgrestSpec ti (s, h, p, b) = (mempty :: Swagger) +postgrestSpec :: [ProcDescription] -> [(Table, [Column], [Text])] -> (Text, Text, Integer, Text) -> Swagger +postgrestSpec pds ti (s, h, p, b) = (mempty :: Swagger) & basePath ?~ unpack b & schemes ?~ [s'] & info .~ ((mempty :: Info) @@ -235,13 +247,13 @@ postgrestSpec ti (s, h, p, b) = (mempty :: Swagger) & description ?~ "This is a dynamic API generated by PostgREST") & host .~ h' & definitions .~ makeDefinitions ti - & paths .~ makePathItems ti + & paths .~ makePathItems pds ti where s' = if s == "http" then Http else Https h' = Just $ Host (unpack $ escapeHostName h) (Just (fromInteger p)) -encodeOpenAPI :: [(Table, [Column], [Text])] -> (Text, Text, Integer, Text) -> LByteString -encodeOpenAPI ti uri = encode $ postgrestSpec ti uri +encodeOpenAPI :: [ProcDescription] -> [(Table, [Column], [Text])] -> (Text, Text, Integer, Text) -> LByteString +encodeOpenAPI pds ti uri = encode $ postgrestSpec pds ti uri {-| Test whether a proxy uri is malformed or not. diff --git a/src/PostgREST/Types.hs b/src/PostgREST/Types.hs index 3788696a4..c5f30a099 100644 --- a/src/PostgREST/Types.hs +++ b/src/PostgREST/Types.hs @@ -13,7 +13,13 @@ data DbStructure = DbStructure { , dbColumns :: [Column] , dbRelations :: [Relation] , dbPrimaryKeys :: [PrimaryKey] -, dbProcs :: [(Text,Text)] +, dbProcs :: [(Text,ProcDescription)] +} deriving (Show, Eq) + +data ProcDescription = ProcDescription { + pdName :: Text +, pdArgs :: Text +, pdReturnType :: Text } deriving (Show, Eq) type Schema = Text From b9777dec35f8bc6f37699a8c48022453acd5418d Mon Sep 17 00:00:00 2001 From: Joe Nelson Date: Thu, 8 Sep 2016 23:00:52 -0700 Subject: [PATCH 2/9] Include proc parama and their types in openapi --- src/PostgREST/DbStructure.hs | 15 ++++++++++++--- src/PostgREST/OpenAPI.hs | 19 +++++++++++++++++-- src/PostgREST/Types.hs | 5 ++++- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/PostgREST/DbStructure.hs b/src/PostgREST/DbStructure.hs index b57f5d398..b677f7090 100644 --- a/src/PostgREST/DbStructure.hs +++ b/src/PostgREST/DbStructure.hs @@ -16,7 +16,7 @@ import Control.Applicative import Data.List (elemIndex) import Data.Maybe (fromJust) import Data.Monoid -import Data.Text (split) +import Data.Text (split, strip) import qualified Hasql.Session as H import PostgREST.Types import Text.InterpolatedString.Perl6 (q) @@ -99,12 +99,21 @@ accessibleProcs :: H.Query Schema [(Text, ProcDescription)] accessibleProcs = H.statement sql (HE.value HE.text) (map addName <$> HD.rowsList (ProcDescription <$> HD.value HD.text - <*> HD.value HD.text - <*> HD.value HD.text)) True + <*> (parseArgs <$> HD.value HD.text) + <*> HD.value HD.text)) True where addName :: ProcDescription -> (Text, ProcDescription) addName pd = (pdName pd, pd) + parseArgs :: Text -> [(PgArgName, PgArgType)] + parseArgs = mapMaybe list2pair + . map (split (==' ') . strip) + . split (==',') + + list2pair :: [a] -> Maybe (a,a) + list2pair (x:y:_) = Just (x,y) + list2pair _ = Nothing + sql = [q| SELECT p.proname as "proc_name", pg_get_function_arguments(p.oid) as "args", diff --git a/src/PostgREST/OpenAPI.hs b/src/PostgREST/OpenAPI.hs index daba8a247..50e1e330d 100644 --- a/src/PostgREST/OpenAPI.hs +++ b/src/PostgREST/OpenAPI.hs @@ -24,7 +24,8 @@ import PostgREST.ApiRequest (ContentType(..), toHeader) import PostgREST.Config (prettyVersion) import PostgREST.QueryBuilder (operators) import PostgREST.Types (Table(..), Column(..), - Proxy(..), ProcDescription(..)) + Proxy(..), ProcDescription(..), + PgArgName, PgArgType) makeMimeList :: [ContentType] -> MimeList makeMimeList cs = MimeList $ map (fromString . toS . toHeader) cs @@ -173,6 +174,20 @@ makePostParams tn = & schema .~ ParamBody (Ref (Reference tn)) ] +makeProcParams :: ProcDescription -> [Param] +makeProcParams pd = + map (makeProcParam $ pdName pd) (pdArgs pd) + +makeProcParam :: Text -> (PgArgName, PgArgType) -> Param +makeProcParam refName (n, t) = + (mempty :: Param) + & name .~ n + & required ?~ True + -- & schema .~ ParamBody ((Ref (Reference refName)) + & schema .~ ParamOther ((mempty :: ParamOtherSchema) + -- & in_ .~ ParamQuery + & type_ .~ toSwaggerType t) + makeDeleteParams :: [Param] makeDeleteParams = [ makePreferParam ["return=representation", "return=minimal", "return=none"] ] @@ -208,7 +223,7 @@ makeProcPathItem :: ProcDescription -> (FilePath, PathItem) makeProcPathItem pd = ("/rpc/" ++ toS (pdName pd), pe) where postOp = (mempty :: Operation) - & parameters .~ [] + & parameters .~ map Inline (makeProcParams pd) & tags .~ Set.fromList ["/rpc/" <> pdName pd] & produces ?~ makeMimeList [CTApplicationJSON] & at 200 ?~ "OK" diff --git a/src/PostgREST/Types.hs b/src/PostgREST/Types.hs index c5f30a099..7c5af1032 100644 --- a/src/PostgREST/Types.hs +++ b/src/PostgREST/Types.hs @@ -16,9 +16,12 @@ data DbStructure = DbStructure { , dbProcs :: [(Text,ProcDescription)] } deriving (Show, Eq) +type PgArgName = Text +type PgArgType = Text + data ProcDescription = ProcDescription { pdName :: Text -, pdArgs :: Text +, pdArgs :: [(PgArgName, PgArgType)] , pdReturnType :: Text } deriving (Show, Eq) From 466090c79bd6f1dca07032dc6e3ef0dd340fd571 Mon Sep 17 00:00:00 2001 From: Joe Nelson Date: Sun, 11 Sep 2016 12:33:19 -0700 Subject: [PATCH 3/9] Expose stored proc args properly as json body param --- src/PostgREST/OpenAPI.hs | 53 ++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/src/PostgREST/OpenAPI.hs b/src/PostgREST/OpenAPI.hs index 50e1e330d..3cb33fd35 100644 --- a/src/PostgREST/OpenAPI.hs +++ b/src/PostgREST/OpenAPI.hs @@ -37,6 +37,13 @@ toSwaggerType "boolean" = SwaggerBoolean toSwaggerType "numeric" = SwaggerNumber toSwaggerType _ = SwaggerString +makeTableDef :: (Table, [Column], [Text]) -> (Text, Schema) +makeTableDef (t, cs, _) = + let tn = tableName t in + (tn, (mempty :: Schema) + & type_ .~ SwaggerObject + & properties .~ (fromList $ map makeProperty cs)) + makeProperty :: Column -> (Text, Referenced Schema) makeProperty c = (colName c, Inline u) where @@ -47,18 +54,18 @@ makeProperty c = (colName c, Inline u) t = s & type_ .~ toSwaggerType (colType c) u = t & format ?~ colType c -makeProperties :: [Column] -> InsOrdHashMap Text (Referenced Schema) -makeProperties cs = fromList $ map makeProperty cs +makeProcDef :: ProcDescription -> (Text, Schema) +makeProcDef pd = + ("(rpc) " <> pdName pd, (mempty :: Schema) + & type_ .~ SwaggerObject + & properties .~ (fromList $ map makeProcProperty (pdArgs pd))) -makeDefinition :: (Table, [Column], [Text]) -> (Text, Schema) -makeDefinition (t, cs, _) = - let tn = tableName t in - (tn, (mempty :: Schema) - & type_ .~ SwaggerObject - & properties .~ makeProperties cs) - -makeDefinitions :: [(Table, [Column], [Text])] -> InsOrdHashMap Text Schema -makeDefinitions ti = fromList $ map makeDefinition ti +makeProcProperty :: (PgArgName, PgArgType) -> (Text, Referenced Schema) +makeProcProperty (n, t) = (n, Inline s) + where + s = (mempty :: Schema) + & type_ .~ toSwaggerType t + & format ?~ t makeOperatorPattern :: Text makeOperatorPattern = @@ -174,19 +181,12 @@ makePostParams tn = & schema .~ ParamBody (Ref (Reference tn)) ] -makeProcParams :: ProcDescription -> [Param] -makeProcParams pd = - map (makeProcParam $ pdName pd) (pdArgs pd) - -makeProcParam :: Text -> (PgArgName, PgArgType) -> Param -makeProcParam refName (n, t) = +makeProcParam :: Text -> Param +makeProcParam refName = (mempty :: Param) - & name .~ n - & required ?~ True - -- & schema .~ ParamBody ((Ref (Reference refName)) - & schema .~ ParamOther ((mempty :: ParamOtherSchema) - -- & in_ .~ ParamQuery - & type_ .~ toSwaggerType t) + & name .~ "args" + & required ?~ True + & schema .~ ParamBody (Ref (Reference refName)) makeDeleteParams :: [Param] makeDeleteParams = @@ -223,13 +223,12 @@ makeProcPathItem :: ProcDescription -> (FilePath, PathItem) makeProcPathItem pd = ("/rpc/" ++ toS (pdName pd), pe) where postOp = (mempty :: Operation) - & parameters .~ map Inline (makeProcParams pd) - & tags .~ Set.fromList ["/rpc/" <> pdName pd] + & parameters .~ [Inline (makeProcParam $ "(rpc) " <> pdName pd)] + & tags .~ Set.fromList ["(rpc) " <> pdName pd] & produces ?~ makeMimeList [CTApplicationJSON] & at 200 ?~ "OK" pe = (mempty :: PathItem) & post ?~ postOp - makeRootPathItem :: (FilePath, PathItem) makeRootPathItem = ("/", p) where @@ -261,7 +260,7 @@ postgrestSpec pds ti (s, h, p, b) = (mempty :: Swagger) & title .~ "PostgREST API" & description ?~ "This is a dynamic API generated by PostgREST") & host .~ h' - & definitions .~ makeDefinitions ti + & definitions .~ (fromList $ map makeTableDef ti <> map makeProcDef pds) & paths .~ makePathItems pds ti where s' = if s == "http" then Http else Https From 25c2cd1f2d0b4b9b010ff46b08470eb0e91737a4 Mon Sep 17 00:00:00 2001 From: Joe Nelson Date: Sun, 11 Sep 2016 15:15:38 -0700 Subject: [PATCH 4/9] Note which rpc params are optional --- src/PostgREST/DbStructure.hs | 11 ++++++----- src/PostgREST/OpenAPI.hs | 19 ++++++++++--------- src/PostgREST/Types.hs | 9 ++++++--- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/PostgREST/DbStructure.hs b/src/PostgREST/DbStructure.hs index b677f7090..92467e322 100644 --- a/src/PostgREST/DbStructure.hs +++ b/src/PostgREST/DbStructure.hs @@ -105,14 +105,15 @@ accessibleProcs = addName :: ProcDescription -> (Text, ProcDescription) addName pd = (pdName pd, pd) - parseArgs :: Text -> [(PgArgName, PgArgType)] - parseArgs = mapMaybe list2pair + parseArgs :: Text -> [PgArg] + parseArgs = mapMaybe toks2arg . map (split (==' ') . strip) . split (==',') - list2pair :: [a] -> Maybe (a,a) - list2pair (x:y:_) = Just (x,y) - list2pair _ = Nothing + toks2arg :: [Text] -> Maybe PgArg + toks2arg (x:y:"DEFAULT":_) = Just (PgArg x y False) + toks2arg (x:y:_) = Just (PgArg x y True) + toks2arg _ = Nothing sql = [q| SELECT p.proname as "proc_name", diff --git a/src/PostgREST/OpenAPI.hs b/src/PostgREST/OpenAPI.hs index 3cb33fd35..1846895e3 100644 --- a/src/PostgREST/OpenAPI.hs +++ b/src/PostgREST/OpenAPI.hs @@ -23,9 +23,8 @@ import Data.Swagger import PostgREST.ApiRequest (ContentType(..), toHeader) import PostgREST.Config (prettyVersion) import PostgREST.QueryBuilder (operators) -import PostgREST.Types (Table(..), Column(..), - Proxy(..), ProcDescription(..), - PgArgName, PgArgType) +import PostgREST.Types (Table(..), Column(..), PgArg(..), + Proxy(..), ProcDescription(..)) makeMimeList :: [ContentType] -> MimeList makeMimeList cs = MimeList $ map (fromString . toS . toHeader) cs @@ -55,13 +54,15 @@ makeProperty c = (colName c, Inline u) u = t & format ?~ colType c makeProcDef :: ProcDescription -> (Text, Schema) -makeProcDef pd = - ("(rpc) " <> pdName pd, (mempty :: Schema) - & type_ .~ SwaggerObject - & properties .~ (fromList $ map makeProcProperty (pdArgs pd))) +makeProcDef pd = ("(rpc) " <> pdName pd, s) + where + s = (mempty :: Schema) + & type_ .~ SwaggerObject + & properties .~ (fromList $ map makeProcProperty (pdArgs pd)) + & required .~ (map pgaName $ filter pgaReq (pdArgs pd)) -makeProcProperty :: (PgArgName, PgArgType) -> (Text, Referenced Schema) -makeProcProperty (n, t) = (n, Inline s) +makeProcProperty :: PgArg -> (Text, Referenced Schema) +makeProcProperty (PgArg n t _) = (n, Inline s) where s = (mempty :: Schema) & type_ .~ toSwaggerType t diff --git a/src/PostgREST/Types.hs b/src/PostgREST/Types.hs index 7c5af1032..9aee6d9e5 100644 --- a/src/PostgREST/Types.hs +++ b/src/PostgREST/Types.hs @@ -16,12 +16,15 @@ data DbStructure = DbStructure { , dbProcs :: [(Text,ProcDescription)] } deriving (Show, Eq) -type PgArgName = Text -type PgArgType = Text +data PgArg = PgArg { + pgaName :: Text +, pgaType :: Text +, pgaReq :: Bool +} deriving (Show, Eq) data ProcDescription = ProcDescription { pdName :: Text -, pdArgs :: [(PgArgName, PgArgType)] +, pdArgs :: [PgArg] , pdReturnType :: Text } deriving (Show, Eq) From 362ad7b7d0655a56636dea7cb2c67484e0543b88 Mon Sep 17 00:00:00 2001 From: Joe Nelson Date: Sun, 11 Sep 2016 16:22:32 -0700 Subject: [PATCH 5/9] Hlint adjustment --- src/PostgREST/DbStructure.hs | 4 +--- src/PostgREST/OpenAPI.hs | 10 +++++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/PostgREST/DbStructure.hs b/src/PostgREST/DbStructure.hs index 92467e322..5fdae392e 100644 --- a/src/PostgREST/DbStructure.hs +++ b/src/PostgREST/DbStructure.hs @@ -106,9 +106,7 @@ accessibleProcs = addName pd = (pdName pd, pd) parseArgs :: Text -> [PgArg] - parseArgs = mapMaybe toks2arg - . map (split (==' ') . strip) - . split (==',') + parseArgs = mapMaybe (toks2arg . split (==' ') . strip) . split (==',') toks2arg :: [Text] -> Maybe PgArg toks2arg (x:y:"DEFAULT":_) = Just (PgArg x y False) diff --git a/src/PostgREST/OpenAPI.hs b/src/PostgREST/OpenAPI.hs index 1846895e3..bf43f3689 100644 --- a/src/PostgREST/OpenAPI.hs +++ b/src/PostgREST/OpenAPI.hs @@ -41,7 +41,7 @@ makeTableDef (t, cs, _) = let tn = tableName t in (tn, (mempty :: Schema) & type_ .~ SwaggerObject - & properties .~ (fromList $ map makeProperty cs)) + & properties .~ fromList (map makeProperty cs)) makeProperty :: Column -> (Text, Referenced Schema) makeProperty c = (colName c, Inline u) @@ -58,8 +58,8 @@ makeProcDef pd = ("(rpc) " <> pdName pd, s) where s = (mempty :: Schema) & type_ .~ SwaggerObject - & properties .~ (fromList $ map makeProcProperty (pdArgs pd)) - & required .~ (map pgaName $ filter pgaReq (pdArgs pd)) + & properties .~ fromList (map makeProcProperty (pdArgs pd)) + & required .~ map pgaName (filter pgaReq (pdArgs pd)) makeProcProperty :: PgArg -> (Text, Referenced Schema) makeProcProperty (PgArg n t _) = (n, Inline s) @@ -242,7 +242,7 @@ makeRootPathItem = ("/", p) makePathItems :: [ProcDescription] -> [(Table, [Column], [Text])] -> InsOrdHashMap FilePath PathItem makePathItems pds ti = fromList $ makeRootPathItem : - (map makePathItem ti) ++ (map makeProcPathItem pds) + map makePathItem ti ++ map makeProcPathItem pds escapeHostName :: Text -> Text escapeHostName "*" = "0.0.0.0" @@ -261,7 +261,7 @@ postgrestSpec pds ti (s, h, p, b) = (mempty :: Swagger) & title .~ "PostgREST API" & description ?~ "This is a dynamic API generated by PostgREST") & host .~ h' - & definitions .~ (fromList $ map makeTableDef ti <> map makeProcDef pds) + & definitions .~ fromList (map makeTableDef ti <> map makeProcDef pds) & paths .~ makePathItems pds ti where s' = if s == "http" then Http else Https From ffd2859cba8f78542b08ab51568cd20949540508 Mon Sep 17 00:00:00 2001 From: Joe Nelson Date: Sun, 11 Sep 2016 16:23:41 -0700 Subject: [PATCH 6/9] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d9519712..a4aeed42e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Filtering, shaping and embedding with &select for the /rpc path - @ruslantalpa - Output names of used-defined types (instead of 'USER-DEFINED') - @martingms - Implement support for singular representation responses for POST/PATCH requests - @ehamberg +- Include RPC endpoints in OpenAPI output - @begriffs, @LogvinovLeon ### Fixed - Do not apply limit to parent items - @ruslantalpa From 0314f4bdea3716a20a734062520cb15f8f9cd1cd Mon Sep 17 00:00:00 2001 From: Joe Nelson Date: Wed, 21 Sep 2016 20:23:11 -0700 Subject: [PATCH 7/9] First test of OpenAPI RPC params --- postgrest.cabal | 1 + test/Feature/StructureSpec.hs | 39 ++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/postgrest.cabal b/postgrest.cabal index 697762dbe..6b2774b9c 100644 --- a/postgrest.cabal +++ b/postgrest.cabal @@ -151,6 +151,7 @@ Test-Suite spec , SpecHelper , TestTypes Build-Depends: aeson + , aeson-qq , async , base , protolude diff --git a/test/Feature/StructureSpec.hs b/test/Feature/StructureSpec.hs index 1fc60a0df..40fa2dc02 100644 --- a/test/Feature/StructureSpec.hs +++ b/test/Feature/StructureSpec.hs @@ -4,10 +4,14 @@ import Test.Hspec hiding (pendingWith) import Test.Hspec.Wai import Network.HTTP.Types +import Control.Lens ((^?)) +import Data.Aeson.Lens +import Data.Aeson.QQ + import SpecHelper import Network.Wai (Application) -import Network.Wai.Test (SResponse(simpleHeaders)) +import Network.Wai.Test (SResponse(..)) spec :: SpecWith Application spec = do @@ -21,6 +25,39 @@ spec = do (acceptHdrs "application/openapi+json") "" `shouldRespondWith` 415 + describe "RPC" $ + + it "includes a representative function with parameters" $ do + r <- simpleBody <$> get "/" + let ref = r ^? key "paths" . key "/rpc/login" + . key "post" . key "parameters" + . nth 0 . key "schema" + . key "$ref" . _String + login = r ^? key "definitions" . key "(rpc) login" + + liftIO $ do + ref `shouldBe` Just "#/definitions/(rpc) login" + login `shouldBe` Just + [aesonQQ| + { + "required": [ + "id", + "pass" + ], + "properties": { + "id": { + "format": "text", + "type": "string" + }, + "pass": { + "format": "text", + "type": "string" + } + }, + "type": "object" + } + |] + describe "Allow header" $ do it "includes read/write verbs for writeable table" $ do From 539df21627419ebe5bcac7a983713fa66804f503 Mon Sep 17 00:00:00 2001 From: Joe Nelson Date: Wed, 21 Sep 2016 23:54:08 -0700 Subject: [PATCH 8/9] WIP: more comprehensive test of rpc args Breaks toks2arg --- test/Feature/StructureSpec.hs | 6 +++--- test/fixtures/schema.sql | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/test/Feature/StructureSpec.hs b/test/Feature/StructureSpec.hs index 40fa2dc02..def7ce72e 100644 --- a/test/Feature/StructureSpec.hs +++ b/test/Feature/StructureSpec.hs @@ -29,14 +29,14 @@ spec = do it "includes a representative function with parameters" $ do r <- simpleBody <$> get "/" - let ref = r ^? key "paths" . key "/rpc/login" + let ref = r ^? key "paths" . key "/rpc/varied_arguments" . key "post" . key "parameters" . nth 0 . key "schema" . key "$ref" . _String - login = r ^? key "definitions" . key "(rpc) login" + login = r ^? key "definitions" . key "(rpc) varied_arguments" liftIO $ do - ref `shouldBe` Just "#/definitions/(rpc) login" + ref `shouldBe` Just "#/definitions/(rpc) varied_arguments" login `shouldBe` Just [aesonQQ| { diff --git a/test/fixtures/schema.sql b/test/fixtures/schema.sql index 7f81a58ac..0f9a7db36 100755 --- a/test/fixtures/schema.sql +++ b/test/fixtures/schema.sql @@ -215,6 +215,21 @@ SELECT rolname::text, id::text FROM postgrest.auth WHERE id = id AND pass = pass $$; +CREATE FUNCTION varied_arguments( + double double precision, + "varchar" character varying, + "boolean" boolean, + date date, + money money, + enum enum_menagerie_type, + "integer" integer default 42 +) RETURNS text + LANGUAGE sql +AS $_$ + SELECT 'Hi'; +$_$; + + -- -- Name: jwt_test(); Type: FUNCTION; Schema: test; Owner: - -- From a88a704beffd008d39b7d26ee759eaafc8ca694b Mon Sep 17 00:00:00 2001 From: Joe Nelson Date: Thu, 22 Sep 2016 23:30:47 -0700 Subject: [PATCH 9/9] Parse proc arg definitions that have multi-word types --- src/PostgREST/DbStructure.hs | 18 ++++++++++------ test/Feature/StructureSpec.hs | 40 ++++++++++++++++++++++++++++------- test/fixtures/schema.sql | 2 +- 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/PostgREST/DbStructure.hs b/src/PostgREST/DbStructure.hs index 5fdae392e..72ae007b5 100644 --- a/src/PostgREST/DbStructure.hs +++ b/src/PostgREST/DbStructure.hs @@ -16,7 +16,9 @@ import Control.Applicative import Data.List (elemIndex) import Data.Maybe (fromJust) import Data.Monoid -import Data.Text (split, strip) +import Data.Text (split, strip, + breakOn, dropAround) +import qualified Data.Text as T import qualified Hasql.Session as H import PostgREST.Types import Text.InterpolatedString.Perl6 (q) @@ -106,12 +108,16 @@ accessibleProcs = addName pd = (pdName pd, pd) parseArgs :: Text -> [PgArg] - parseArgs = mapMaybe (toks2arg . split (==' ') . strip) . split (==',') + parseArgs = mapMaybe (parseArg . strip) . split (==',') - toks2arg :: [Text] -> Maybe PgArg - toks2arg (x:y:"DEFAULT":_) = Just (PgArg x y False) - toks2arg (x:y:_) = Just (PgArg x y True) - toks2arg _ = Nothing + parseArg :: Text -> Maybe PgArg + parseArg a = + let (body, def) = breakOn " DEFAULT " a + (name, typ) = breakOn " " body in + if T.null typ + then Nothing + else Just $ + PgArg (dropAround (== '"') name) (strip typ) (T.null def) sql = [q| SELECT p.proname as "proc_name", diff --git a/test/Feature/StructureSpec.hs b/test/Feature/StructureSpec.hs index def7ce72e..9c0f2acb3 100644 --- a/test/Feature/StructureSpec.hs +++ b/test/Feature/StructureSpec.hs @@ -33,25 +33,49 @@ spec = do . key "post" . key "parameters" . nth 0 . key "schema" . key "$ref" . _String - login = r ^? key "definitions" . key "(rpc) varied_arguments" + args = r ^? key "definitions" . key "(rpc) varied_arguments" liftIO $ do ref `shouldBe` Just "#/definitions/(rpc) varied_arguments" - login `shouldBe` Just + args `shouldBe` Just [aesonQQ| { "required": [ - "id", - "pass" + "double", + "varchar", + "boolean", + "date", + "money", + "enum" ], "properties": { - "id": { - "format": "text", + "double": { + "format": "double precision", "type": "string" }, - "pass": { - "format": "text", + "varchar": { + "format": "character varying", "type": "string" + }, + "boolean": { + "format": "boolean", + "type": "boolean" + }, + "date": { + "format": "date", + "type": "string" + }, + "money": { + "format": "money", + "type": "string" + }, + "enum": { + "format": "test.enum_menagerie_type", + "type": "string" + }, + "integer": { + "format": "integer", + "type": "integer" } }, "type": "object" diff --git a/test/fixtures/schema.sql b/test/fixtures/schema.sql index 0f9a7db36..2c162b41d 100755 --- a/test/fixtures/schema.sql +++ b/test/fixtures/schema.sql @@ -226,7 +226,7 @@ CREATE FUNCTION varied_arguments( ) RETURNS text LANGUAGE sql AS $_$ - SELECT 'Hi'; + SELECT 'Hi'::text; $_$;