diff --git a/CHANGELOG.md b/CHANGELOG.md index ee0465174..6c053c04a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). + Allows using or across related tables conditions - #1100, Customizable OpenAPI title - @AnthonyFisi +### Fixed + + - #2651, Add the missing `get` path item for RPCs to the OpenAPI output - @laurenceisla + ## [10.1.2] - 2023-02-01 ### Fixed diff --git a/src/PostgREST/Response/OpenAPI.hs b/src/PostgREST/Response/OpenAPI.hs index 379599b90..917a8feff 100644 --- a/src/PostgREST/Response/OpenAPI.hs +++ b/src/PostgREST/Response/OpenAPI.hs @@ -73,9 +73,15 @@ toSwaggerType colType = case T.takeEnd 2 colType of "[]" -> Just SwaggerArray _ -> Just SwaggerString -makeSwaggerItemType :: Maybe (SwaggerType t) -> Text -> Maybe (Referenced Schema) -makeSwaggerItemType itemType colType = case itemType of - Just SwaggerArray -> Just $ Inline (mempty & type_ .~ toSwaggerType (T.dropEnd 2 colType)) +typeFromArray :: Text -> Text +typeFromArray = T.dropEnd 2 + +toSwaggerTypeFromArray :: Text -> Maybe (SwaggerType t) +toSwaggerTypeFromArray arrType = toSwaggerType $ typeFromArray arrType + +makePropertyItems :: Text -> Maybe (Referenced Schema) +makePropertyItems arrType = case toSwaggerType arrType of + Just SwaggerArray -> Just $ Inline (mempty & type_ .~ toSwaggerTypeFromArray arrType) _ -> Nothing parseDefault :: Text -> Text -> Text @@ -129,7 +135,6 @@ makeProperty tbl rels col = (colName col, Inline s) Just $ T.append (maybe "" (`T.append` "\n\n") $ colDescription col) (T.intercalate "\n" n) else colDescription col - pType = toSwaggerType (colType col) s = (mempty :: Schema) & default_ .~ (JSON.decode . toUtf8Lazy . parseDefault (colType col) =<< colDefault col) @@ -137,8 +142,8 @@ makeProperty tbl rels col = (colName col, Inline s) & enum_ .~ e & format ?~ colType col & maxLength .~ (fromIntegral <$> colMaxLen col) - & type_ .~ pType - & items .~ (SwaggerItemsObject <$> makeSwaggerItemType pType (colType col)) + & type_ .~ toSwaggerType (colType col) + & items .~ (SwaggerItemsObject <$> makePropertyItems (colType col)) makeProcSchema :: ProcDescription -> Schema makeProcSchema pd = @@ -153,7 +158,7 @@ makeProcProperty (ProcParam n t _ _) = (n, Inline s) where s = (mempty :: Schema) & type_ .~ toSwaggerType t - & items .~ (SwaggerItemsObject <$> makeSwaggerItemType (toSwaggerType t) t) + & items .~ (SwaggerItemsObject <$> makePropertyItems t) & format ?~ t makePreferParam :: [Text] -> Param @@ -175,8 +180,37 @@ makePreferParam ts = "resolution" -> ["resolution=ignore-duplicates", "resolution=merge-duplicates"] _ -> [] -makeProcParam :: ProcDescription -> [Referenced Param] -makeProcParam pd = +makeProcGetParam :: ProcParam -> Referenced Param +makeProcGetParam (ProcParam n t r v) = + Inline $ (mempty :: Param) + & name .~ n + & required ?~ r + & schema .~ ParamOther fullSchema + where + fullSchema = if v then schemaMulti else schemaNotMulti + baseSchema = (mempty :: ParamOtherSchema) + & in_ .~ ParamQuery + schemaNotMulti = baseSchema + & format ?~ t + & type_ ?~ toParamType (toSwaggerType t) + schemaMulti = baseSchema + & type_ ?~ fromMaybe SwaggerString (toSwaggerType t) + & items ?~ SwaggerItemsPrimitive (Just CollectionMulti) + ((mempty :: ParamSchema x) + & type_ .~ toSwaggerTypeFromArray t + & format ?~ typeFromArray t) + toParamType paramType = case paramType of + -- Array uses {} in query params + Just SwaggerArray -> SwaggerString + -- Type must be specified in query params + Nothing -> SwaggerString + _ -> fromJust paramType + +makeProcGetParams :: [ProcParam] -> [Referenced Param] +makeProcGetParams = fmap makeProcGetParam + +makeProcPostParams :: ProcDescription -> [Referenced Param] +makeProcPostParams pd = [ Inline $ (mempty :: Param) & name .~ "args" & required ?~ True @@ -313,14 +347,19 @@ makeProcPathItem pd = ("/rpc/" ++ toS (pdName pd), pe) -- We strip leading newlines from description so that users can include a blank line between summary and description (pSum, pDesc) = fmap fst &&& fmap (T.dropWhile (=='\n') . snd) $ T.breakOn "\n" <$> pdDescription pd - postOp = (mempty :: Operation) + procOp = (mempty :: Operation) & summary .~ pSum & description .~ mfilter (/="") pDesc - & parameters .~ makeProcParam pd & tags .~ Set.fromList ["(rpc) " <> pdName pd] & produces ?~ makeMimeList [MTApplicationJSON, MTSingularJSON] & at 200 ?~ "OK" - pe = (mempty :: PathItem) & post ?~ postOp + getOp = procOp + & parameters .~ makeProcGetParams (pdParams pd) + postOp = procOp + & parameters .~ makeProcPostParams pd + pe = (mempty :: PathItem) + & get ?~ getOp + & post ?~ postOp makeRootPathItem :: (FilePath, PathItem) makeRootPathItem = ("/", p) diff --git a/test/spec/Feature/OpenApi/OpenApiSpec.hs b/test/spec/Feature/OpenApi/OpenApiSpec.hs index 6e9e0f365..f809b630c 100644 --- a/test/spec/Feature/OpenApi/OpenApiSpec.hs +++ b/test/spec/Feature/OpenApi/OpenApiSpec.hs @@ -708,7 +708,153 @@ spec actualPgVersion = describe "OpenAPI" $ do describe "RPC" $ do - it "includes function summary/description and body schema for arguments" $ do + it "includes function summary/description and query parameters for arguments in the get path item" $ do + r <- simpleBody <$> get "/" + + let method s = key "paths" . key "/rpc/varied_arguments_openapi" . key s + args = r ^? method "get" . key "parameters" + summary = r ^? method "get" . key "summary" + description = r ^? method "get" . key "description" + + liftIO $ do + + summary `shouldBe` Just "An RPC function" + + description `shouldBe` Just "Just a test for RPC function arguments" + + args `shouldBe` Just + [aesonQQ| + [ + { + "format": "double precision", + "in": "query", + "name": "double", + "required": true, + "type": "number" + }, + { + "format": "character varying", + "in": "query", + "name": "varchar", + "required": true, + "type": "string" + }, + { + "format": "boolean", + "in": "query", + "name": "boolean", + "required": true, + "type": "boolean" + }, + { + "format": "date", + "in": "query", + "name": "date", + "required": true, + "type": "string" + }, + { + "format": "money", + "in": "query", + "name": "money", + "required": true, + "type": "string" + }, + { + "format": "enum_menagerie_type", + "in": "query", + "name": "enum", + "required": true, + "type": "string" + }, + { + "format": "text[]", + "in": "query", + "name": "text_arr", + "required": true, + "type": "string" + }, + { + "format": "integer[]", + "in": "query", + "name": "int_arr", + "required": true, + "type": "string" + }, + { + "format": "boolean[]", + "in": "query", + "name": "bool_arr", + "required": true, + "type": "string" + }, + { + "format": "character[]", + "in": "query", + "name": "char_arr", + "required": true, + "type": "string" + }, + { + "format": "character varying[]", + "in": "query", + "name": "varchar_arr", + "required": true, + "type": "string" + }, + { + "format": "bigint[]", + "in": "query", + "name": "bigint_arr", + "required": true, + "type": "string" + }, + { + "format": "numeric[]", + "in": "query", + "name": "numeric_arr", + "required": true, + "type": "string" + }, + { + "format": "json[]", + "in": "query", + "name": "json_arr", + "required": true, + "type": "string" + }, + { + "format": "jsonb[]", + "in": "query", + "name": "jsonb_arr", + "required": true, + "type": "string" + }, + { + "format": "integer", + "in": "query", + "name": "integer", + "required": false, + "type": "integer" + }, + { + "format": "json", + "in": "query", + "name": "json", + "required": false, + "type": "string" + }, + { + "format": "jsonb", + "in": "query", + "name": "jsonb", + "required": false, + "type": "string" + } + ] + |] + + it "includes function summary/description and body schema for arguments in the post path item" $ do r <- simpleBody <$> get "/" let method s = key "paths" . key "/rpc/varied_arguments_openapi" . key s @@ -873,6 +1019,26 @@ spec actualPgVersion = describe "OpenAPI" $ do liftIO $ params `shouldBe` Just [aesonQQ|["num", "str"]|] + it "uses a multi collection format when the function has a VARIADIC parameter" $ do + r <- simpleBody <$> get "/" + let param = r ^? key "paths" . key "/rpc/variadic_param" + . key "get" . key "parameters" . nth 0 + + liftIO $ param `shouldBe` Just + [aesonQQ| + { + "collectionFormat": "multi", + "in": "query", + "items": { + "format": "text", + "type": "string" + }, + "name": "v", + "required": false, + "type": "array" + } + |] + describe "Security" $ it "does not include security or security definitions by default" $ do r <- simpleBody <$> get "/"