diff --git a/README.md b/README.md index 6a71301..c01c958 100644 --- a/README.md +++ b/README.md @@ -7,19 +7,19 @@ SQL functions to build the OpenAPI output of a PostgREST instance. - The first step in the roadmap is to migrate the OpenAPI spec from the PostgREST core repository (version 2.0 to 3.1): - [x] Info object - [x] Server object (replaces host, basePath and schemes from OAS 2.0) - - [ ] Components object + - [x] Components object - [x] Schemas (definitions in OAS 2.0) - [x] Security scheme (security definitions in OAS 2.0) - - [ ] Parameters - - [ ] Paths object + - [x] Parameters + - [x] Paths object - [x] Tables and Views - [x] GET - [x] POST - [x] PATCH - [x] DELETE - - [ ] Functions - - [ ] GET - - [ ] POST + - [x] Functions + - [x] GET + - [x] POST - [ ] External Documentation Object - [ ] Handle relevant OpenAPI elements according to user permissions - The next step is to fix the issues tagged with `OpenAPI` in the core repo. diff --git a/sql/components.sql b/sql/components.sql index b7580f4..38e00f2 100644 --- a/sql/components.sql +++ b/sql/components.sql @@ -19,6 +19,7 @@ returns jsonb language sql stable as $$ select oas_build_component_schemas_from_tables_and_composite_types(schemas) || oas_build_component_schemas_from_functions_return_types(schemas) || + oas_build_component_schemas_from_functions_arguments(schemas) || oas_build_component_schemas_headers() $$; @@ -180,6 +181,64 @@ from ( ) x; $$; +create or replace function oas_build_component_schemas_from_functions_arguments(schemas text[]) +returns jsonb language sql stable as +$$ +with all_functions_with_arguments as ( + -- Build Component Schemas for IN/INOUT or VARIADIC function arguments + select * + from postgrest_get_all_functions(schemas) + where argument_input_qty > 0 + and (argument_is_in or argument_is_inout or argument_is_variadic) + and argument_name <> '' +), +aggregated_function_arguments as ( + select + function_schema, + function_full_name, + function_description, + array_agg(argument_name order by argument_position) filter (where argument_is_required) AS required_arguments, + jsonb_object_agg( + argument_name, + case when argument_item_type_name is null and argument_is_composite then + oas_build_reference_to_schemas(argument_composite_full_name) + else + oas_schema_object( + type := postgrest_pgtype_to_oastype(argument_type_name), + format := argument_type_name::text, + items := + case + when argument_item_type_name is null then + null + when argument_is_composite then + oas_build_reference_to_schemas(argument_composite_full_name) + else + oas_schema_object( + type := postgrest_pgtype_to_oastype(argument_item_type_name), + format := argument_item_type_name::text + ) + end + ) + end order by argument_position + ) as arguments + from all_functions_with_arguments + group by function_schema, function_full_name, function_description +) +select jsonb_object_agg(x.component_name, x.oas_schema) +from ( + select + 'rpc.args.' || function_full_name as component_name, + oas_schema_object( + description := function_description, + properties := coalesce(arguments, '{}'), + type := 'object', + required := required_arguments + ) as oas_schema + from + aggregated_function_arguments +) x; +$$; + create or replace function oas_build_component_schemas_headers() returns jsonb language sql stable as $$ @@ -304,6 +363,7 @@ returns jsonb language sql stable as $$ select oas_build_component_parameters_query_params_from_tables(schemas) || oas_build_component_parameters_query_params_from_function_args(schemas) || + oas_build_component_parameters_query_params_from_function_ret(schemas) || oas_build_component_parameters_query_params_common() || oas_build_component_parameters_headers_common(); $$; @@ -322,14 +382,42 @@ from ( ) ) as param_schema from ( - select table_full_name, column_name - from postgrest_get_all_tables_and_composite_types() - where table_schema = any(schemas) - and (is_table or is_view) + select table_full_name, column_name + from postgrest_get_all_tables_and_composite_types() + where ( + table_schema = any(schemas) + and (is_table or is_view) + ) + -- composite type columns can also be used as row filters if a function returns it + or exists ( + select 1 + from postgrest_get_all_functions(schemas) + where return_type_composite_relid = table_oid + ) ) _ ) x; $$; +-- Builds "rowFilter"s for functions returning TABLE or INOUT/OUT types +create or replace function oas_build_component_parameters_query_params_from_function_ret(schemas text[]) +returns jsonb language sql stable as +$$ +select jsonb_object_agg(x.param_name, x.param_schema) +from ( + select format('rowFilter.rpc.%1$s.%2$s', function_full_name, argument_name) as param_name, + oas_parameter_object( + name := argument_name, + "in" := 'query', + schema := oas_schema_object( + type := 'string' + ) + ) as param_schema + from postgrest_get_all_functions(schemas) + where argument_name <> '' + and (argument_is_inout or argument_is_out or argument_is_table) +) x; +$$; + create or replace function oas_build_component_parameters_query_params_from_function_args(schemas text[]) returns jsonb language sql stable as $$ @@ -882,7 +970,8 @@ $$; create or replace function oas_build_request_bodies(schemas text[]) returns jsonb language sql stable as $$ -select oas_build_request_bodies_from_tables(schemas); +select oas_build_request_bodies_from_tables(schemas) || + oas_build_request_bodies_from_functions(schemas); $$; create or replace function oas_build_request_bodies_from_tables(schemas text[]) @@ -928,6 +1017,83 @@ from ( ) as x; $$; +create or replace function oas_build_request_bodies_from_functions(schemas text[]) +returns jsonb language sql stable as +$$ +select jsonb_object_agg('rpc.' || x.function_full_name, x.oas_req_body) +from ( + select + function_full_name, + oas_request_body_object( + description := function_full_name, + required := argument_default_qty < argument_input_qty, + content := + case when argument_input_qty = 1 and (array_agg(argument_name) filter (where argument_is_in or argument_is_inout))[1] = '' then + -- Media types according to the single unnamed parameter type + case function_input_argument_types[0] + when 'bytea'::regtype then + jsonb_build_object( + 'application/octet-stream', + oas_media_type_object( + "schema" := oas_schema_object( + type := 'string', + format := 'binary' + ) + ) + ) + when 'text'::regtype then + jsonb_build_object( + 'text/plain', + oas_media_type_object( + "schema" := oas_schema_object( + type := 'string' + ) + ) + ) + when 'xml'::regtype then + jsonb_build_object( + 'text/xml', + oas_media_type_object( + "schema" := oas_schema_object( + type := 'string', + format := 'xml' + ) + ) + ) + else -- single json or jsonb parameters + jsonb_build_object( + 'application/json', + oas_media_type_object( + "schema" := '{}' -- json/jsonb types can be any type + ) + ) + end + else + jsonb_build_object( + 'application/json', + oas_media_type_object( + "schema" := oas_build_reference_to_schemas('rpc.args.' || function_full_name) + ), + 'application/x-www-form-urlencoded', + oas_media_type_object( + "schema" := oas_build_reference_to_schemas('rpc.args.' || function_full_name) + ), + 'text/csv', + oas_media_type_object( + "schema" := oas_schema_object( + type := 'string', + format := 'csv' + ) + ) + ) + end + ) as oas_req_body + from postgrest_get_all_functions(schemas) + where argument_input_qty > 0 + group by function_full_name, argument_input_qty, argument_default_qty, function_input_argument_types +) as x; +$$; + -- Security Schemes create or replace function oas_build_component_security_schemes () diff --git a/sql/paths.sql b/sql/paths.sql index 63fb27b..b93808a 100644 --- a/sql/paths.sql +++ b/sql/paths.sql @@ -189,13 +189,73 @@ from ( 'default', oas_build_reference_to_responses('defaultError', 'Error') ) + ), + post := oas_operation_object( + summary := (postgrest_unfold_comment(function_description))[1], + description := (postgrest_unfold_comment(function_description))[2], + tags := array['(rpc) ' || function_name], + requestBody := case when argument_input_qty > 0 then oas_build_reference_to_request_bodies('rpc.' || function_full_name) end, + parameters := + -- TODO: The row filters for functions returning TABLE, OUT, INOUT and composite types should also work for the GET path. + -- Right now they're not included in GET, because the argument names (in rpcParams) could clash with the name of the return type columns (in rowFilter). + coalesce( + jsonb_agg( + oas_build_reference_to_parameters(format('rowFilter.rpc.%1$s.%2$s', function_full_name, argument_name)) + ) filter ( where argument_name <> '' and (argument_is_inout or argument_is_out or argument_is_table)), + '[]' + ) || + return_composite_param_ref || + case when return_type_is_table or return_type_is_out or return_type_composite_relid <> 0 then + jsonb_build_array( + oas_build_reference_to_parameters('select'), + oas_build_reference_to_parameters('order'), + oas_build_reference_to_parameters('limit'), + oas_build_reference_to_parameters('offset'), + oas_build_reference_to_parameters('or'), + oas_build_reference_to_parameters('and'), + oas_build_reference_to_parameters('not.or'), + oas_build_reference_to_parameters('not.and'), + oas_build_reference_to_parameters('preferPostRpc') + ) + else + jsonb_build_array( + oas_build_reference_to_parameters('preferPostRpc') + ) + end, + responses := + case when return_type_is_set then + jsonb_build_object( + '200', + oas_build_reference_to_responses('rpc.' || function_full_name, 'OK'), + '206', + oas_build_reference_to_responses('rpc.' || function_full_name, 'Partial Content') + ) + else + jsonb_build_object( + '200', + oas_build_reference_to_responses('rpc.' || function_full_name, 'OK') + ) + end || + jsonb_build_object( + 'default', + oas_build_reference_to_responses('defaultError', 'Error') + ) ) ) as oas_path_item from ( - select function_name, function_full_name, function_description, return_type_name, return_type_is_set, return_type_is_table, return_type_is_out, return_type_composite_relid, argument_name, argument_is_in, argument_is_inout, argument_is_variadic - from postgrest_get_all_functions(schemas) + select function_name, function_full_name, function_description, return_type_name, return_type_is_set, return_type_is_table, return_type_is_out, return_type_composite_relid, argument_name, argument_is_in, argument_is_inout, argument_is_out, argument_is_table, argument_is_variadic, argument_input_qty, + comp.return_composite_param_ref + from postgrest_get_all_functions(schemas) f + left join lateral ( + select coalesce(jsonb_agg(oas_build_reference_to_parameters(format('rowFilter.%1$s.%2$s', table_full_name, column_name))),'[]') as return_composite_param_ref + from ( + select c.table_full_name, c.column_name + from postgrest_get_all_tables_and_composite_types() c + where f.return_type_composite_relid = c.table_oid + ) _ + ) comp on true ) _ - group by function_name, function_full_name, function_description, return_type_name, return_type_is_set, return_type_is_table, return_type_is_out, return_type_composite_relid + group by function_name, function_full_name, function_description, return_type_name, return_type_is_set, return_type_is_table, return_type_is_out, return_type_composite_relid, argument_input_qty, return_composite_param_ref ) x; $$; diff --git a/sql/postgrest.sql b/sql/postgrest.sql index c1d00da..fd9a656 100644 --- a/sql/postgrest.sql +++ b/sql/postgrest.sql @@ -166,6 +166,7 @@ $$; create or replace function postgrest_get_all_functions(schemas text[]) returns table ( argument_input_qty int, + argument_default_qty int, argument_name text, argument_reg_type oid, argument_type_name text, @@ -185,6 +186,7 @@ returns table ( function_name name, function_full_name text, function_description text, + function_input_argument_types oidvector, return_type_name text, return_type_item_name text, return_type_is_set bool, @@ -225,6 +227,7 @@ $$ all_functions AS ( SELECT p.pronargs AS argument_input_qty, + p.pronargdefaults AS argument_default_qty, COALESCE(pa.name, '') AS argument_name, pa.type AS argument_reg_type, format_type(ta.oid, NULL::integer) AS argument_type_name, @@ -247,6 +250,7 @@ $$ -- The "full name" of the function `.`. We omit `.` when it belongs to the `current_schema` COALESCE(NULLIF(pn.nspname, current_schema) || '.', '') || p.proname AS function_full_name, d.description AS function_description, + p.proargtypes AS function_input_argument_types, format_type(t.oid, NULL::integer) AS return_type_name, format_type(t_arr.oid, NULL::integer) AS return_type_item_name, p.proretset AS return_type_is_set, @@ -290,7 +294,7 @@ $$ WHERE x.argument_input_qty > 0 AND x.argument_name = '' AND (x.argument_is_in OR x.argument_is_inout OR x.argument_is_variadic) - AND NOT (x.argument_input_qty = 1 AND x.argument_reg_type IN ('bytea'::regtype, 'json'::regtype, 'jsonb'::regtype, 'text'::regtype, 'xml'::regtype)) + AND NOT (x.argument_input_qty = 1 AND x.function_input_argument_types[0] IN ('bytea'::regtype, 'json'::regtype, 'jsonb'::regtype, 'text'::regtype, 'xml'::regtype)) AND x.function_oid = a.function_oid ); $$; diff --git a/test/expected/parameters.out b/test/expected/parameters.out index 6fb01f6..0158a4b 100644 --- a/test/expected/parameters.out +++ b/test/expected/parameters.out @@ -367,6 +367,86 @@ where key like 'rowFilter.big_products.%'; | } (4 rows) +-- shows composite type columns as parameters on `RETURNS ` functions +select key, jsonb_pretty(value) +from jsonb_each(get_openapi_document('{test}')->'components'->'parameters') +where key like 'rowFilter.types.attribute_ret.%'; + key | jsonb_pretty +--------------------------------------+-------------------------- + rowFilter.types.attribute_ret.dim | { + + | "in": "query", + + | "name": "dim", + + | "schema": { + + | "type": "string"+ + | } + + | } + rowFilter.types.attribute_ret.other | { + + | "in": "query", + + | "name": "other", + + | "schema": { + + | "type": "string"+ + | } + + | } + rowFilter.types.attribute_ret.colors | { + + | "in": "query", + + | "name": "colors", + + | "schema": { + + | "type": "string"+ + | } + + | } +(3 rows) + +-- shows table arguments as parameters on `RETURNS TABLE` functions +select key, jsonb_pretty(value) +from jsonb_each(get_openapi_document('{test}')->'components'->'parameters') +where key like 'rowFilter.rpc.returns_table.%'; + key | jsonb_pretty +----------------------------------+-------------------------- + rowFilter.rpc.returns_table.num | { + + | "in": "query", + + | "name": "num", + + | "schema": { + + | "type": "string"+ + | } + + | } + rowFilter.rpc.returns_table.val | { + + | "in": "query", + + | "name": "val", + + | "schema": { + + | "type": "string"+ + | } + + | } + rowFilter.rpc.returns_table.comp | { + + | "in": "query", + + | "name": "comp", + + | "schema": { + + | "type": "string"+ + | } + + | } +(3 rows) + +-- shows inout/out arguments as parameters on functions with INOUT/OUT parameters +select key, jsonb_pretty(value) +from jsonb_each(get_openapi_document('{test}')->'components'->'parameters') +where key like 'rowFilter.rpc.returns_inout_out.%'; + key | jsonb_pretty +-----------------------------------+-------------------------- + rowFilter.rpc.returns_inout_out.y | { + + | "in": "query", + + | "name": "y", + + | "schema": { + + | "type": "string"+ + | } + + | } + rowFilter.rpc.returns_inout_out.z | { + + | "in": "query", + + | "name": "z", + + | "schema": { + + | "type": "string"+ + | } + + | } +(2 rows) + -- shows `IN` function arguments as RPC parameters select key, jsonb_pretty(value) from jsonb_each(get_openapi_document('{test}')->'components'->'parameters') diff --git a/test/expected/paths.out b/test/expected/paths.out index 3883c4f..06d3140 100644 --- a/test/expected/paths.out +++ b/test/expected/paths.out @@ -880,3 +880,296 @@ select get_openapi_document('{test}')->'paths' ? '/rpc/named_and_unnamed_params' f (1 row) +-- POST operation object +-- shows the function name as tag +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/get_products_by_size'->'post'->'tags'); + jsonb_pretty +---------------------------------- + [ + + "(rpc) get_products_by_size"+ + ] +(1 row) + +-- uses a reference for the 200 HTTP code response +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/get_products_by_size'->'post'->'responses'->'200'); + jsonb_pretty +---------------------------------------------------------------- + { + + "$ref": "#/components/responses/rpc.get_products_by_size",+ + "description": "OK" + + } +(1 row) + +-- uses a reference for the 206 HTTP code response on `RETURNS SET OF` functions +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/get_products_by_size'->'post'->'responses'->'206'); + jsonb_pretty +---------------------------------------------------------------- + { + + "$ref": "#/components/responses/rpc.get_products_by_size",+ + "description": "Partial Content" + + } +(1 row) + +-- uses a reference for error responses +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/get_products_by_size'->'post'->'responses'->'default'); + jsonb_pretty +---------------------------------------------------- + { + + "$ref": "#/components/responses/defaultError",+ + "description": "Error" + + } +(1 row) + +-- uses a reference for request body when the function has a single unnamed parameter (json, jsonb, text, xml, bytea) +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/single_unnamed_json_param'->'post'->'requestBody'->'$ref'); + jsonb_pretty +------------------------------------------------------------ + "#/components/requestBodies/rpc.single_unnamed_json_param" +(1 row) + +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/single_unnamed_jsonb_param'->'post'->'requestBody'->'$ref'); + jsonb_pretty +------------------------------------------------------------- + "#/components/requestBodies/rpc.single_unnamed_jsonb_param" +(1 row) + +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/single_unnamed_text_param'->'post'->'requestBody'->'$ref'); + jsonb_pretty +------------------------------------------------------------ + "#/components/requestBodies/rpc.single_unnamed_text_param" +(1 row) + +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/single_unnamed_xml_param'->'post'->'requestBody'->'$ref'); + jsonb_pretty +----------------------------------------------------------- + "#/components/requestBodies/rpc.single_unnamed_xml_param" +(1 row) + +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/single_unnamed_bytea_param'->'post'->'requestBody'->'$ref'); + jsonb_pretty +------------------------------------------------------------- + "#/components/requestBodies/rpc.single_unnamed_bytea_param" +(1 row) + +-- uses a reference for the request body when the function has at least one no-default parameter +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/has_one_default_parameter'->'post'->'requestBody'->'$ref'); + jsonb_pretty +------------------------------------------------------------ + "#/components/requestBodies/rpc.has_one_default_parameter" +(1 row) + +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/has_all_default_parameters'->'post'->'requestBody'->'$ref'); + jsonb_pretty +------------------------------------------------------------- + "#/components/requestBodies/rpc.has_all_default_parameters" +(1 row) + +-- does not use a reference for the request body when the function has no parameters +select get_openapi_document('{test}')->'paths'->'/rpc/has_no_parameters'->'post' ? 'requestBody' as value; + value +------- + f +(1 row) + +-- shows the first line of the comment on the function as summary +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/get_products_by_size'->'post'->'summary'); + jsonb_pretty +-------------------------------- + "Get Products By Size summary" +(1 row) + +-- shows the second line of the comment on the function as description +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/get_products_by_size'->'post'->'description'); + jsonb_pretty +----------------------------------------------------------------- + "Get Products By Size description\nthat spans\nmultiple lines." +(1 row) + +-- uses references for columns as query parameters on `RETURNS ` functions +select value +from jsonb_array_elements(get_openapi_document('{test}')->'paths'->'/rpc/get_products_by_size'->'post'->'parameters') +where value->>'$ref' like '#/components/parameters/rowFilter.products.%' +order by value->>'$ref'; + value +-------------------------------------------------------------------- + {"$ref": "#/components/parameters/rowFilter.products.attr"} + {"$ref": "#/components/parameters/rowFilter.products.code"} + {"$ref": "#/components/parameters/rowFilter.products.description"} + {"$ref": "#/components/parameters/rowFilter.products.id"} + {"$ref": "#/components/parameters/rowFilter.products.name"} + {"$ref": "#/components/parameters/rowFilter.products.size"} +(6 rows) + +-- uses references for composite type columns as query parameters on `RETURNS ` functions +select value +from jsonb_array_elements(get_openapi_document('{test}')->'paths'->'/rpc/get_attribute'->'post'->'parameters') +where value->>'$ref' like '#/components/parameters/rowFilter.types.attribute_ret.%' +order by value->>'$ref'; + value +-------------------------------------------------------------------------- + {"$ref": "#/components/parameters/rowFilter.types.attribute_ret.colors"} + {"$ref": "#/components/parameters/rowFilter.types.attribute_ret.dim"} + {"$ref": "#/components/parameters/rowFilter.types.attribute_ret.other"} +(3 rows) + +-- uses references for table arguments as query parameters on `RETURNS TABLE` functions +select value +from jsonb_array_elements(get_openapi_document('{test}')->'paths'->'/rpc/returns_table'->'post'->'parameters') +where value->>'$ref' like '#/components/parameters/rowFilter.rpc.returns_table.%' +order by value->>'$ref'; + value +---------------------------------------------------------------------- + {"$ref": "#/components/parameters/rowFilter.rpc.returns_table.comp"} + {"$ref": "#/components/parameters/rowFilter.rpc.returns_table.num"} + {"$ref": "#/components/parameters/rowFilter.rpc.returns_table.val"} +(3 rows) + +-- uses references for inout/out arguments as query parameters on functions with INOUT/OUT parameters +select value +from jsonb_array_elements(get_openapi_document('{test}')->'paths'->'/rpc/returns_inout_out'->'post'->'parameters') +where value->>'$ref' like '#/components/parameters/rowFilter.rpc.returns_inout_out.%' +order by value->>'$ref'; + value +----------------------------------------------------------------------- + {"$ref": "#/components/parameters/rowFilter.rpc.returns_inout_out.y"} + {"$ref": "#/components/parameters/rowFilter.rpc.returns_inout_out.z"} +(2 rows) + +-- uses references for common parameters on `RETURNS
` functions +select value +from jsonb_array_elements(get_openapi_document('{test}')->'paths'->'/rpc/get_products_by_size'->'post'->'parameters') +where value->>'$ref' not like '#/components/parameters/rowFilter.products.%'; + value +--------------------------------------------------- + {"$ref": "#/components/parameters/select"} + {"$ref": "#/components/parameters/order"} + {"$ref": "#/components/parameters/limit"} + {"$ref": "#/components/parameters/offset"} + {"$ref": "#/components/parameters/or"} + {"$ref": "#/components/parameters/and"} + {"$ref": "#/components/parameters/not.or"} + {"$ref": "#/components/parameters/not.and"} + {"$ref": "#/components/parameters/preferPostRpc"} +(9 rows) + +-- uses references for common parameters on `RETURNS ` functions +select value +from jsonb_array_elements(get_openapi_document('{test}')->'paths'->'/rpc/get_attribute'->'post'->'parameters') +where value->>'$ref' not like '#/components/parameters/rowFilter.types.attribute_ret.%'; + value +--------------------------------------------------- + {"$ref": "#/components/parameters/select"} + {"$ref": "#/components/parameters/order"} + {"$ref": "#/components/parameters/limit"} + {"$ref": "#/components/parameters/offset"} + {"$ref": "#/components/parameters/or"} + {"$ref": "#/components/parameters/and"} + {"$ref": "#/components/parameters/not.or"} + {"$ref": "#/components/parameters/not.and"} + {"$ref": "#/components/parameters/preferPostRpc"} +(9 rows) + +-- uses references for common parameters on `RETURNS TABLE` functions +select value +from jsonb_array_elements(get_openapi_document('{test}')->'paths'->'/rpc/returns_table'->'post'->'parameters') +where value->>'$ref' not like '#/components/parameters/rowFilter.rpc.returns_table.%'; + value +--------------------------------------------------- + {"$ref": "#/components/parameters/select"} + {"$ref": "#/components/parameters/order"} + {"$ref": "#/components/parameters/limit"} + {"$ref": "#/components/parameters/offset"} + {"$ref": "#/components/parameters/or"} + {"$ref": "#/components/parameters/and"} + {"$ref": "#/components/parameters/not.or"} + {"$ref": "#/components/parameters/not.and"} + {"$ref": "#/components/parameters/preferPostRpc"} +(9 rows) + +-- uses references for common parameters on functions with INOUT/OUT parameters +select value +from jsonb_array_elements(get_openapi_document('{test}')->'paths'->'/rpc/returns_inout_out'->'post'->'parameters') +where value->>'$ref' not like '#/components/parameters/rowFilter.rpc.returns_inout_out.%'; + value +--------------------------------------------------- + {"$ref": "#/components/parameters/select"} + {"$ref": "#/components/parameters/order"} + {"$ref": "#/components/parameters/limit"} + {"$ref": "#/components/parameters/offset"} + {"$ref": "#/components/parameters/or"} + {"$ref": "#/components/parameters/and"} + {"$ref": "#/components/parameters/not.or"} + {"$ref": "#/components/parameters/not.and"} + {"$ref": "#/components/parameters/preferPostRpc"} +(9 rows) + +-- does not use a reference for the 206 HTTP code response on functions that do not return `SET OF` +select get_openapi_document('{test}')->'paths'->'/rpc/get_attribute'->'post'->'responses' ? '206' as value; + value +------- + f +(1 row) + +-- does not use a reference for common parameters (except for prefer headers) on functions that do not return composite types +select value +from jsonb_array_elements(get_openapi_document('{test}')->'paths'->'/rpc/returns_simple_type'->'post'->'parameters') +where value->>'$ref' not like '#/components/parameters/rpcParam.returns_simple_type.%'; + value +--------------------------------------------------- + {"$ref": "#/components/parameters/preferPostRpc"} +(1 row) + +-- shows a function with a single unnamed parameter of accepted types +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/single_unnamed_json_param'->'post'->'tags'); + jsonb_pretty +--------------------------------------- + [ + + "(rpc) single_unnamed_json_param"+ + ] +(1 row) + +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/single_unnamed_jsonb_param'->'post'->'tags'); + jsonb_pretty +---------------------------------------- + [ + + "(rpc) single_unnamed_jsonb_param"+ + ] +(1 row) + +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/single_unnamed_text_param'->'post'->'tags'); + jsonb_pretty +--------------------------------------- + [ + + "(rpc) single_unnamed_text_param"+ + ] +(1 row) + +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/single_unnamed_xml_param'->'post'->'tags'); + jsonb_pretty +-------------------------------------- + [ + + "(rpc) single_unnamed_xml_param"+ + ] +(1 row) + +-- does not show a function with a single unnamed parameter of non-accepted types +select get_openapi_document('{test}')->'paths' ? '/rpc/single_unnamed_unrecognized_param' as value; + value +------- + f +(1 row) + +-- does not show a function with unnamed parameters +select get_openapi_document('{test}')->'paths' ? '/rpc/unnamed_params' as value; + value +------- + f +(1 row) + +-- does not show a function with named and unnamed parameters +select get_openapi_document('{test}')->'paths' ? '/rpc/named_and_unnamed_params' as value; + value +------- + f +(1 row) + diff --git a/test/expected/reqbodies.out b/test/expected/reqbodies.out index 06d68c7..7ed87d8 100644 --- a/test/expected/reqbodies.out +++ b/test/expected/reqbodies.out @@ -95,3 +95,123 @@ select get_openapi_document('{test}')->'components'->'requestBodies' ? 'non_auto f (1 row) +-- Functions +-- defines an application/json request body +select jsonb_pretty(get_openapi_document('{test}')->'components'->'requestBodies'->'rpc.get_products_by_size'->'content'->'application/json'); + jsonb_pretty +---------------------------------------------------------------------- + { + + "schema": { + + "$ref": "#/components/schemas/rpc.args.get_products_by_size"+ + } + + } +(1 row) + +-- defines an application/x-www-form-urlencoded request body +select jsonb_pretty(get_openapi_document('{test}')->'components'->'requestBodies'->'rpc.get_products_by_size'->'content'->'application/x-www-form-urlencoded'); + jsonb_pretty +---------------------------------------------------------------------- + { + + "schema": { + + "$ref": "#/components/schemas/rpc.args.get_products_by_size"+ + } + + } +(1 row) + +-- defines a text/csv request body +select jsonb_pretty(get_openapi_document('{test}')->'components'->'requestBodies'->'rpc.get_products_by_size'->'content'->'text/csv'); + jsonb_pretty +--------------------------- + { + + "schema": { + + "type": "string",+ + "format": "csv" + + } + + } +(1 row) + +-- defines only one application/json request body for a single unnamed json/jsonb parameter +select jsonb_pretty(get_openapi_document('{test}')->'components'->'requestBodies'->'rpc.single_unnamed_json_param'->'content'); + jsonb_pretty +--------------------------- + { + + "application/json": {+ + "schema": { + + } + + } + + } +(1 row) + +select jsonb_pretty(get_openapi_document('{test}')->'components'->'requestBodies'->'rpc.single_unnamed_jsonb_param'->'content'); + jsonb_pretty +--------------------------- + { + + "application/json": {+ + "schema": { + + } + + } + + } +(1 row) + +-- defines only one text/pain request body for a single unnamed text parameter +select jsonb_pretty(get_openapi_document('{test}')->'components'->'requestBodies'->'rpc.single_unnamed_text_param'->'content'); + jsonb_pretty +------------------------------ + { + + "text/plain": { + + "schema": { + + "type": "string"+ + } + + } + + } +(1 row) + +-- defines only one text/xml request body for a single unnamed xml parameter +select jsonb_pretty(get_openapi_document('{test}')->'components'->'requestBodies'->'rpc.single_unnamed_xml_param'->'content'); + jsonb_pretty +------------------------------- + { + + "text/xml": { + + "schema": { + + "type": "string",+ + "format": "xml" + + } + + } + + } +(1 row) + +-- defines only one application/octect-stream request body for a single unnamed bytea parameter +select jsonb_pretty(get_openapi_document('{test}')->'components'->'requestBodies'->'rpc.single_unnamed_bytea_param'->'content'); + jsonb_pretty +----------------------------------- + { + + "application/octet-stream": {+ + "schema": { + + "type": "string", + + "format": "binary" + + } + + } + + } +(1 row) + +-- the request body is not required when all the parameters have default values +select get_openapi_document('{test}')->'components'->'requestBodies'->'rpc.has_all_default_parameters'->'required' as value; + value +------- + false +(1 row) + +-- the request body is required when at least one of the parameters has a default value +select get_openapi_document('{test}')->'components'->'requestBodies'->'rpc.has_one_default_parameter'->'required' as value; + value +------- + true +(1 row) + +-- does not define a request body for functions without parameters +select get_openapi_document('{test}')->'components'->'requestBodies' ? 'rps.has_no_parameters' as value; + value +------- + f +(1 row) + diff --git a/test/expected/schemas.out b/test/expected/schemas.out index e0686ff..1aa6746 100644 --- a/test/expected/schemas.out +++ b/test/expected/schemas.out @@ -716,6 +716,353 @@ select jsonb_pretty(get_openapi_document('{test}')->'components'->'schemas'->'rp (1 row) +-- Function parameters (referenced by POST request bodies) +-- maps sql types to OpenAPI types correctly +select jsonb_pretty(get_openapi_document('{test}')->'components'->'schemas'->'rpc.args.openapi_types'); + jsonb_pretty +----------------------------------------------- + { + + "type": "object", + + "required": [ + + "a_character_varying", + + "a_character", + + "a_text", + + "a_boolean", + + "a_smallint", + + "a_integer", + + "a_bigint", + + "a_numeric", + + "a_real", + + "a_double_precision", + + "a_json", + + "a_jsonb", + + "a_text_arr", + + "a_int_arr", + + "a_bool_arr", + + "a_char_arr", + + "a_varchar_arr", + + "a_bigint_arr", + + "a_numeric_arr", + + "a_json_arr", + + "a_jsonb_arr" + + ], + + "properties": { + + "a_json": { + + "type": "object", + + "format": "json" + + }, + + "a_real": { + + "type": "number", + + "format": "real" + + }, + + "a_text": { + + "type": "string", + + "format": "text" + + }, + + "a_jsonb": { + + "type": "object", + + "format": "jsonb" + + }, + + "a_bigint": { + + "type": "integer", + + "format": "bigint" + + }, + + "a_boolean": { + + "type": "boolean", + + "format": "boolean" + + }, + + "a_int_arr": { + + "type": "array", + + "items": { + + "type": "integer", + + "format": "integer" + + }, + + "format": "integer[]" + + }, + + "a_integer": { + + "type": "integer", + + "format": "integer" + + }, + + "a_numeric": { + + "type": "number", + + "format": "numeric" + + }, + + "a_bool_arr": { + + "type": "array", + + "items": { + + "type": "boolean", + + "format": "boolean" + + }, + + "format": "boolean[]" + + }, + + "a_char_arr": { + + "type": "array", + + "items": { + + "type": "string", + + "format": "character" + + }, + + "format": "character[]" + + }, + + "a_json_arr": { + + "type": "array", + + "items": { + + "type": "object", + + "format": "json" + + }, + + "format": "json[]" + + }, + + "a_smallint": { + + "type": "integer", + + "format": "smallint" + + }, + + "a_text_arr": { + + "type": "array", + + "items": { + + "type": "string", + + "format": "text" + + }, + + "format": "text[]" + + }, + + "a_character": { + + "type": "string", + + "format": "character" + + }, + + "a_jsonb_arr": { + + "type": "array", + + "items": { + + "type": "object", + + "format": "jsonb" + + }, + + "format": "jsonb[]" + + }, + + "a_bigint_arr": { + + "type": "array", + + "items": { + + "type": "integer", + + "format": "bigint" + + }, + + "format": "bigint[]" + + }, + + "a_numeric_arr": { + + "type": "array", + + "items": { + + "type": "number", + + "format": "numeric" + + }, + + "format": "numeric[]" + + }, + + "a_varchar_arr": { + + "type": "array", + + "items": { + + "type": "string", + + "format": "character varying"+ + }, + + "format": "character varying[]" + + }, + + "a_double_precision": { + + "type": "number", + + "format": "double precision" + + }, + + "a_character_varying": { + + "type": "string", + + "format": "character varying" + + } + + } + + } +(1 row) + +-- detects functions with composite types parameters +select jsonb_pretty(get_openapi_document('{test}')->'components'->'schemas'->'rpc.args.get_attribute'); + jsonb_pretty +---------------------------------------------------------------- + { + + "type": "object", + + "required": [ + + "loc" + + ], + + "properties": { + + "loc": { + + "$ref": "#/components/schemas/types.attribute_arg"+ + } + + } + + } +(1 row) + +-- detects functions with IN parameters +select jsonb_pretty(get_openapi_document('{test}')->'components'->'schemas'->'rpc.args.has_in_parameters'); + jsonb_pretty +---------------------------------------------------------------- + { + + "type": "object", + + "required": [ + + "x", + + "y", + + "z" + + ], + + "properties": { + + "o": { + + "type": "integer", + + "format": "integer" + + }, + + "x": { + + "type": "integer", + + "format": "integer" + + }, + + "y": { + + "type": "array", + + "items": { + + "type": "string", + + "format": "text" + + }, + + "format": "text[]" + + }, + + "z": { + + "$ref": "#/components/schemas/types.attribute_ref"+ + } + + } + + } +(1 row) + +-- detects functions with INOUT parameters +select jsonb_pretty(get_openapi_document('{test}')->'components'->'schemas'->'rpc.args.has_inout_parameters'); + jsonb_pretty +---------------------------------------------------------------- + { + + "type": "object", + + "required": [ + + "x", + + "y", + + "z" + + ], + + "properties": { + + "o": { + + "type": "integer", + + "format": "integer" + + }, + + "x": { + + "type": "integer", + + "format": "integer" + + }, + + "y": { + + "type": "array", + + "items": { + + "type": "string", + + "format": "text" + + }, + + "format": "text[]" + + }, + + "z": { + + "$ref": "#/components/schemas/types.attribute_ref"+ + } + + } + + } +(1 row) + +-- detects functions with VARIADIC parameters +select jsonb_pretty(get_openapi_document('{test}')->'components'->'schemas'->'rpc.args.has_variadic_parameter'); + jsonb_pretty +------------------------------------- + { + + "type": "object", + + "required": [ + + "x", + + "y" + + ], + + "properties": { + + "x": { + + "type": "integer", + + "format": "integer" + + }, + + "y": { + + "type": "array", + + "items": { + + "type": "integer", + + "format": "integer"+ + }, + + "format": "integer[]" + + } + + } + + } +(1 row) + +-- detects functions with default parameters +select jsonb_pretty(get_openapi_document('{test}')->'components'->'schemas'->'rpc.args.has_all_default_parameters'); + jsonb_pretty +--------------------------------- + { + + "type": "object", + + "properties": { + + "a": { + + "type": "integer", + + "format": "integer"+ + }, + + "b": { + + "type": "integer", + + "format": "integer"+ + } + + } + + } +(1 row) + +select jsonb_pretty(get_openapi_document('{test}')->'components'->'schemas'->'rpc.args.has_one_default_parameter'); + jsonb_pretty +--------------------------------- + { + + "type": "object", + + "required": [ + + "a" + + ], + + "properties": { + + "a": { + + "type": "integer", + + "format": "integer"+ + }, + + "b": { + + "type": "integer", + + "format": "integer"+ + } + + } + + } +(1 row) + +-- ignores functions without parameters +select get_openapi_document('{test}')->'components'->'schemas' ? 'rpc.args.has_no_parameters' as value; + value +------- + f +(1 row) + +-- ignores functions with single unnamed parameters +select get_openapi_document('{test}')->'components'->'schemas' ? 'rpc.args.single_unnamed_json_param' as value; + value +------- + f +(1 row) + +select get_openapi_document('{test}')->'components'->'schemas' ? 'rpc.args.single_unnamed_jsonb_param' as value; + value +------- + f +(1 row) + +select get_openapi_document('{test}')->'components'->'schemas' ? 'rpc.args.single_unnamed_text_param' as value; + value +------- + f +(1 row) + +select get_openapi_document('{test}')->'components'->'schemas' ? 'rpc.args.single_unnamed_xml_param' as value; + value +------- + f +(1 row) + +select get_openapi_document('{test}')->'components'->'schemas' ? 'rpc.args.single_unnamed_bytea_param' as value; + value +------- + f +(1 row) + -- Common -- defines all the available prefer headers select key, jsonb_pretty(value) diff --git a/test/fixtures.sql b/test/fixtures.sql index 81bd2f3..b249146 100644 --- a/test/fixtures.sql +++ b/test/fixtures.sql @@ -105,6 +105,31 @@ create view test.non_auto_updatable as -- Functions for testing +create function test.openapi_types( + "a_character_varying" character varying, + "a_character" character(1), + "a_text" text, + "a_boolean" boolean, + "a_smallint" smallint, + "a_integer" integer, + "a_bigint" bigint, + "a_numeric" numeric, + "a_real" real, + "a_double_precision" double precision, + "a_json" json, + "a_jsonb" jsonb, + "a_text_arr" text[], + "a_int_arr" int[], + "a_bool_arr" boolean[], + "a_char_arr" char[], + "a_varchar_arr" varchar[], + "a_bigint_arr" bigint[], + "a_numeric_arr" numeric[], + "a_json_arr" json[], + "a_jsonb_arr" jsonb[] +) +returns int language sql as 'select 1'; + create function test.get_products_by_size(s types.size) returns setof test.products stable language sql as $$ @@ -224,10 +249,15 @@ $$ select ($1, $2); $$; +create function test.has_all_default_parameters(a int default 1, b int default 2) returns void stable language sql as 'select $1 + $2'; +create function test.has_one_default_parameter(a int, b int default 2) returns void stable language sql as 'select $1 + $2'; +create function test.has_no_parameters() returns void stable language sql as ''; + create function test.single_unnamed_json_param(json) returns json stable language sql as 'select $1'; create function test.single_unnamed_jsonb_param(jsonb) returns jsonb stable language sql as 'select $1'; create function test.single_unnamed_text_param(text) returns text stable language sql as 'select $1'; create function test.single_unnamed_xml_param(xml) returns xml stable language sql as 'select $1'; +create function test.single_unnamed_bytea_param(bytea) returns bytea stable language sql as 'select $1'; create function test.single_unnamed_unrecognized_param(int) returns int stable language sql as 'select $1'; create function test.unnamed_params(int, numeric) returns numeric stable language sql as 'select $2 + $1'; diff --git a/test/sql/parameters.sql b/test/sql/parameters.sql index 8cf1421..6eca55d 100644 --- a/test/sql/parameters.sql +++ b/test/sql/parameters.sql @@ -18,6 +18,21 @@ select key, jsonb_pretty(value) from jsonb_each(get_openapi_document('{test}')->'components'->'parameters') where key like 'rowFilter.big_products.%'; +-- shows composite type columns as parameters on `RETURNS ` functions +select key, jsonb_pretty(value) +from jsonb_each(get_openapi_document('{test}')->'components'->'parameters') +where key like 'rowFilter.types.attribute_ret.%'; + +-- shows table arguments as parameters on `RETURNS TABLE` functions +select key, jsonb_pretty(value) +from jsonb_each(get_openapi_document('{test}')->'components'->'parameters') +where key like 'rowFilter.rpc.returns_table.%'; + +-- shows inout/out arguments as parameters on functions with INOUT/OUT parameters +select key, jsonb_pretty(value) +from jsonb_each(get_openapi_document('{test}')->'components'->'parameters') +where key like 'rowFilter.rpc.returns_inout_out.%'; + -- shows `IN` function arguments as RPC parameters select key, jsonb_pretty(value) from jsonb_each(get_openapi_document('{test}')->'components'->'parameters') diff --git a/test/sql/paths.sql b/test/sql/paths.sql index 5d1cb89..d75fae5 100644 --- a/test/sql/paths.sql +++ b/test/sql/paths.sql @@ -309,3 +309,104 @@ select get_openapi_document('{test}')->'paths' ? '/rpc/unnamed_params' as value; -- does not show a function with named and unnamed parameters select get_openapi_document('{test}')->'paths' ? '/rpc/named_and_unnamed_params' as value; + +-- POST operation object + +-- shows the function name as tag +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/get_products_by_size'->'post'->'tags'); + +-- uses a reference for the 200 HTTP code response +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/get_products_by_size'->'post'->'responses'->'200'); + +-- uses a reference for the 206 HTTP code response on `RETURNS SET OF` functions +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/get_products_by_size'->'post'->'responses'->'206'); + +-- uses a reference for error responses +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/get_products_by_size'->'post'->'responses'->'default'); + +-- uses a reference for request body when the function has a single unnamed parameter (json, jsonb, text, xml, bytea) +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/single_unnamed_json_param'->'post'->'requestBody'->'$ref'); +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/single_unnamed_jsonb_param'->'post'->'requestBody'->'$ref'); +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/single_unnamed_text_param'->'post'->'requestBody'->'$ref'); +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/single_unnamed_xml_param'->'post'->'requestBody'->'$ref'); +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/single_unnamed_bytea_param'->'post'->'requestBody'->'$ref'); + +-- uses a reference for the request body when the function has at least one no-default parameter +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/has_one_default_parameter'->'post'->'requestBody'->'$ref'); +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/has_all_default_parameters'->'post'->'requestBody'->'$ref'); + +-- does not use a reference for the request body when the function has no parameters +select get_openapi_document('{test}')->'paths'->'/rpc/has_no_parameters'->'post' ? 'requestBody' as value; + +-- shows the first line of the comment on the function as summary +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/get_products_by_size'->'post'->'summary'); + +-- shows the second line of the comment on the function as description +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/get_products_by_size'->'post'->'description'); + +-- uses references for columns as query parameters on `RETURNS
` functions +select value +from jsonb_array_elements(get_openapi_document('{test}')->'paths'->'/rpc/get_products_by_size'->'post'->'parameters') +where value->>'$ref' like '#/components/parameters/rowFilter.products.%' +order by value->>'$ref'; + +-- uses references for composite type columns as query parameters on `RETURNS ` functions +select value +from jsonb_array_elements(get_openapi_document('{test}')->'paths'->'/rpc/get_attribute'->'post'->'parameters') +where value->>'$ref' like '#/components/parameters/rowFilter.types.attribute_ret.%' +order by value->>'$ref'; + +-- uses references for table arguments as query parameters on `RETURNS TABLE` functions +select value +from jsonb_array_elements(get_openapi_document('{test}')->'paths'->'/rpc/returns_table'->'post'->'parameters') +where value->>'$ref' like '#/components/parameters/rowFilter.rpc.returns_table.%' +order by value->>'$ref'; + +-- uses references for inout/out arguments as query parameters on functions with INOUT/OUT parameters +select value +from jsonb_array_elements(get_openapi_document('{test}')->'paths'->'/rpc/returns_inout_out'->'post'->'parameters') +where value->>'$ref' like '#/components/parameters/rowFilter.rpc.returns_inout_out.%' +order by value->>'$ref'; + +-- uses references for common parameters on `RETURNS
` functions +select value +from jsonb_array_elements(get_openapi_document('{test}')->'paths'->'/rpc/get_products_by_size'->'post'->'parameters') +where value->>'$ref' not like '#/components/parameters/rowFilter.products.%'; + +-- uses references for common parameters on `RETURNS ` functions +select value +from jsonb_array_elements(get_openapi_document('{test}')->'paths'->'/rpc/get_attribute'->'post'->'parameters') +where value->>'$ref' not like '#/components/parameters/rowFilter.types.attribute_ret.%'; + +-- uses references for common parameters on `RETURNS TABLE` functions +select value +from jsonb_array_elements(get_openapi_document('{test}')->'paths'->'/rpc/returns_table'->'post'->'parameters') +where value->>'$ref' not like '#/components/parameters/rowFilter.rpc.returns_table.%'; + +-- uses references for common parameters on functions with INOUT/OUT parameters +select value +from jsonb_array_elements(get_openapi_document('{test}')->'paths'->'/rpc/returns_inout_out'->'post'->'parameters') +where value->>'$ref' not like '#/components/parameters/rowFilter.rpc.returns_inout_out.%'; + +-- does not use a reference for the 206 HTTP code response on functions that do not return `SET OF` +select get_openapi_document('{test}')->'paths'->'/rpc/get_attribute'->'post'->'responses' ? '206' as value; + +-- does not use a reference for common parameters (except for prefer headers) on functions that do not return composite types +select value +from jsonb_array_elements(get_openapi_document('{test}')->'paths'->'/rpc/returns_simple_type'->'post'->'parameters') +where value->>'$ref' not like '#/components/parameters/rpcParam.returns_simple_type.%'; + +-- shows a function with a single unnamed parameter of accepted types +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/single_unnamed_json_param'->'post'->'tags'); +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/single_unnamed_jsonb_param'->'post'->'tags'); +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/single_unnamed_text_param'->'post'->'tags'); +select jsonb_pretty(get_openapi_document('{test}')->'paths'->'/rpc/single_unnamed_xml_param'->'post'->'tags'); + +-- does not show a function with a single unnamed parameter of non-accepted types +select get_openapi_document('{test}')->'paths' ? '/rpc/single_unnamed_unrecognized_param' as value; + +-- does not show a function with unnamed parameters +select get_openapi_document('{test}')->'paths' ? '/rpc/unnamed_params' as value; + +-- does not show a function with named and unnamed parameters +select get_openapi_document('{test}')->'paths' ? '/rpc/named_and_unnamed_params' as value; diff --git a/test/sql/reqbodies.sql b/test/sql/reqbodies.sql index 2ccc9c6..66daeb8 100644 --- a/test/sql/reqbodies.sql +++ b/test/sql/reqbodies.sql @@ -22,3 +22,36 @@ select jsonb_pretty(get_openapi_document('{test}')->'components'->'requestBodies -- does not define a request body for non auto-updatable views select get_openapi_document('{test}')->'components'->'requestBodies' ? 'non_auto_updatable' as value; + +-- Functions + +-- defines an application/json request body +select jsonb_pretty(get_openapi_document('{test}')->'components'->'requestBodies'->'rpc.get_products_by_size'->'content'->'application/json'); + +-- defines an application/x-www-form-urlencoded request body +select jsonb_pretty(get_openapi_document('{test}')->'components'->'requestBodies'->'rpc.get_products_by_size'->'content'->'application/x-www-form-urlencoded'); + +-- defines a text/csv request body +select jsonb_pretty(get_openapi_document('{test}')->'components'->'requestBodies'->'rpc.get_products_by_size'->'content'->'text/csv'); + +-- defines only one application/json request body for a single unnamed json/jsonb parameter +select jsonb_pretty(get_openapi_document('{test}')->'components'->'requestBodies'->'rpc.single_unnamed_json_param'->'content'); +select jsonb_pretty(get_openapi_document('{test}')->'components'->'requestBodies'->'rpc.single_unnamed_jsonb_param'->'content'); + +-- defines only one text/pain request body for a single unnamed text parameter +select jsonb_pretty(get_openapi_document('{test}')->'components'->'requestBodies'->'rpc.single_unnamed_text_param'->'content'); + +-- defines only one text/xml request body for a single unnamed xml parameter +select jsonb_pretty(get_openapi_document('{test}')->'components'->'requestBodies'->'rpc.single_unnamed_xml_param'->'content'); + +-- defines only one application/octect-stream request body for a single unnamed bytea parameter +select jsonb_pretty(get_openapi_document('{test}')->'components'->'requestBodies'->'rpc.single_unnamed_bytea_param'->'content'); + +-- the request body is not required when all the parameters have default values +select get_openapi_document('{test}')->'components'->'requestBodies'->'rpc.has_all_default_parameters'->'required' as value; + +-- the request body is required when at least one of the parameters has a default value +select get_openapi_document('{test}')->'components'->'requestBodies'->'rpc.has_one_default_parameter'->'required' as value; + +-- does not define a request body for functions without parameters +select get_openapi_document('{test}')->'components'->'requestBodies' ? 'rps.has_no_parameters' as value; diff --git a/test/sql/schemas.sql b/test/sql/schemas.sql index c8997b1..b3d1a43 100644 --- a/test/sql/schemas.sql +++ b/test/sql/schemas.sql @@ -143,6 +143,37 @@ select jsonb_pretty(get_openapi_document('{test}')->'components'->'schemas'->'rp -- ignores a function outside of the exposed schemas select jsonb_pretty(get_openapi_document('{test}')->'components'->'schemas'->'rpc.private.secret_function'); +-- Function parameters (referenced by POST request bodies) + +-- maps sql types to OpenAPI types correctly +select jsonb_pretty(get_openapi_document('{test}')->'components'->'schemas'->'rpc.args.openapi_types'); + +-- detects functions with composite types parameters +select jsonb_pretty(get_openapi_document('{test}')->'components'->'schemas'->'rpc.args.get_attribute'); + +-- detects functions with IN parameters +select jsonb_pretty(get_openapi_document('{test}')->'components'->'schemas'->'rpc.args.has_in_parameters'); + +-- detects functions with INOUT parameters +select jsonb_pretty(get_openapi_document('{test}')->'components'->'schemas'->'rpc.args.has_inout_parameters'); + +-- detects functions with VARIADIC parameters +select jsonb_pretty(get_openapi_document('{test}')->'components'->'schemas'->'rpc.args.has_variadic_parameter'); + +-- detects functions with default parameters +select jsonb_pretty(get_openapi_document('{test}')->'components'->'schemas'->'rpc.args.has_all_default_parameters'); +select jsonb_pretty(get_openapi_document('{test}')->'components'->'schemas'->'rpc.args.has_one_default_parameter'); + +-- ignores functions without parameters +select get_openapi_document('{test}')->'components'->'schemas' ? 'rpc.args.has_no_parameters' as value; + +-- ignores functions with single unnamed parameters +select get_openapi_document('{test}')->'components'->'schemas' ? 'rpc.args.single_unnamed_json_param' as value; +select get_openapi_document('{test}')->'components'->'schemas' ? 'rpc.args.single_unnamed_jsonb_param' as value; +select get_openapi_document('{test}')->'components'->'schemas' ? 'rpc.args.single_unnamed_text_param' as value; +select get_openapi_document('{test}')->'components'->'schemas' ? 'rpc.args.single_unnamed_xml_param' as value; +select get_openapi_document('{test}')->'components'->'schemas' ? 'rpc.args.single_unnamed_bytea_param' as value; + -- Common -- defines all the available prefer headers