diff --git a/sql/components.sql b/sql/components.sql index a14fe86..b7580f4 100644 --- a/sql/components.sql +++ b/sql/components.sql @@ -18,6 +18,7 @@ create or replace function oas_build_component_schemas(schemas text[]) 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_headers() $$; @@ -28,6 +29,10 @@ with recursive all_rels as ( select * from postgrest_get_all_tables_and_composite_types() ), +all_funcs as ( + select * + from postgrest_get_all_functions(schemas) +), recursive_rels_in_schema as ( select * from all_rels @@ -35,10 +40,11 @@ recursive_rels_in_schema as ( -- All the tables or views in the exposed schemas (table_schema = any(schemas) and (is_table or is_view)) -- All the composite types or tables that are present in function arguments - -- TODO: tweak postgrest_get_all_functions() or use another CTE for a more performant filter - or table_oid in ( - select unnest(composite_args_ret) - from postgrest_get_all_functions(schemas) + or exists ( + select 1 + from all_funcs + where return_type_composite_relid = table_oid + or argument_composite_relid = table_oid ) union -- Tables may have columns with composite or table types, so we recursively @@ -51,14 +57,14 @@ recursive_rels_in_schema as ( all_tables_and_composite_types as ( select table_schema, - table_name, + table_full_name, table_description, is_composite, array_agg(column_name order by column_position) filter (where not column_is_nullable) AS required_cols, jsonb_object_agg( column_name, - case when column_item_data_type is null and column_composite_relid <> 0 then - oas_build_reference_to_schemas(column_data_type) + case when column_item_data_type is null and column_is_composite then + oas_build_reference_to_schemas(column_composite_full_name) else oas_schema_object( description := column_description, @@ -71,8 +77,8 @@ all_tables_and_composite_types as ( case when column_item_data_type is null then null - when column_composite_relid <> 0 then - oas_build_reference_to_schemas(column_item_data_type) + when column_is_composite then + oas_build_reference_to_schemas(column_composite_full_name) else oas_schema_object( type := postgrest_pgtype_to_oastype(column_item_data_type), @@ -83,11 +89,11 @@ all_tables_and_composite_types as ( end order by column_position ) as columns from recursive_rels_in_schema - group by table_schema, table_name, table_description, is_composite + group by table_schema, table_full_name, table_description, is_composite ) select jsonb_object_agg(x.component_name, x.oas_schema) from ( - select case when is_composite then table_schema || '.' else '' end || table_name as component_name, + select table_full_name as component_name, oas_schema_object( description := table_description, properties := coalesce(columns, '{}'), @@ -98,6 +104,82 @@ from ( ) x; $$; +create or replace function oas_build_component_schemas_from_functions_return_types(schemas text[]) +returns jsonb language sql stable as +$$ +with all_functions_returning_table_out_simple_types as ( + -- Build Component Schemas for functions that RETURNS simple types or RETURNS TABLE or with INOUT/OUT arguments + select *, not (return_type_is_out or return_type_is_table) as return_type_is_simple + from postgrest_get_all_functions(schemas) + where return_type_is_out or return_type_is_table or not return_type_is_composite +), +aggregated_function_returns as ( + select + function_schema, + function_full_name, + function_description, + return_type_is_simple, + -- Build objects for functions with RETURNS TABLE or with INOUT/OUT arguments + case when not return_type_is_simple then + 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 + ) filter ( where argument_is_table or argument_is_out or argument_is_inout) + -- For functions with RETURNS build the Schema according to the type + else + oas_schema_object( + type := postgrest_pgtype_to_oastype(return_type_name), + format := return_type_name::text, + items := + case when return_type_item_name is not null then + oas_schema_object( + type := postgrest_pgtype_to_oastype(return_type_item_name), + format := return_type_item_name::text + ) + end + ) + end as arguments + from all_functions_returning_table_out_simple_types + group by function_schema, function_name, function_full_name, function_description, return_type_is_simple, return_type_name, return_type_item_name +) +select jsonb_object_agg(x.component_name, x.oas_schema) +from ( + select + 'rpc.' || function_full_name as component_name, + case when not return_type_is_simple then + oas_schema_object( + description := function_description, + properties := coalesce(arguments, '{}'), + type := 'object' + ) + else + arguments + end as oas_schema + from + aggregated_function_returns +) x; +$$; + create or replace function oas_build_component_schemas_headers() returns jsonb language sql stable as $$ @@ -221,6 +303,7 @@ create or replace function oas_build_component_parameters(schemas text[]) 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_common() || oas_build_component_parameters_headers_common(); $$; @@ -230,7 +313,7 @@ returns jsonb language sql stable as $$ select jsonb_object_agg(x.param_name, x.param_schema) from ( - select format('rowFilter.%1$s.%2$s', table_name, column_name) as param_name, + select format('rowFilter.%1$s.%2$s', table_full_name, column_name) as param_name, oas_parameter_object( name := column_name, "in" := 'query', @@ -239,7 +322,7 @@ from ( ) ) as param_schema from ( - select table_schema, is_table, is_view, table_name, column_name + 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) @@ -247,6 +330,44 @@ from ( ) x; $$; +create or replace function oas_build_component_parameters_query_params_from_function_args(schemas text[]) +returns jsonb language sql stable as +$$ +select jsonb_object_agg(x.param_name, x.param_schema) +from ( + select format('rpcParam.%1$s.%2$s', function_full_name, argument_name) as param_name, + oas_parameter_object( + name := argument_name, + "in" := 'query', + required := argument_is_required, + schema := + case when argument_is_variadic then + oas_schema_object( + type := 'array', + items := oas_schema_object( + type := argument_oastype, + format := argument_type_name::text + ) + ) + else + oas_schema_object( + type := argument_oastype, + format := argument_type_name::text + ) + end + ) as param_schema + from ( + select function_full_name, argument_name, argument_is_required, argument_type_name, argument_is_variadic, + -- In query parameters, "array" and "object" types have a different format, so we use a generic "string" + case when postgrest_pgtype_to_oastype(argument_type_name) = any ('{array,object}') then 'string' else postgrest_pgtype_to_oastype(argument_type_name) end as argument_oastype + from postgrest_get_all_functions(schemas) + where argument_input_qty > 0 + and argument_name <> '' + and (argument_is_in or argument_is_inout or argument_is_variadic) + ) _ +) x; +$$; + create or replace function oas_build_component_parameters_query_params_common() returns jsonb language sql stable as $$ @@ -574,6 +695,7 @@ create or replace function oas_build_response_objects(schemas text[]) returns jsonb language sql stable as $$ select oas_build_response_objects_from_tables(schemas) || + oas_build_response_objects_from_function_return_types(schemas) || oas_build_response_objects_common(); $$; @@ -583,24 +705,24 @@ $$ select jsonb_object_agg(x.not_empty, x.not_empty_response) || jsonb_object_agg(x.may_be_empty, x.may_be_empty_response) from ( - select 'notEmpty.' || table_name as not_empty, + select 'notEmpty.' || table_full_name as not_empty, oas_response_object( - description := 'Media types when response body is not empty for ' || table_name, + description := 'Media types when response body is not empty for ' || table_full_name, content := jsonb_build_object( 'application/json', oas_media_type_object( schema := oas_schema_object( type := 'array', - items := oas_build_reference_to_schemas(table_name) + items := oas_build_reference_to_schemas(table_full_name) ) ), 'application/vnd.pgrst.object+json', oas_media_type_object( - schema := oas_build_reference_to_schemas(table_name) + schema := oas_build_reference_to_schemas(table_full_name) ), 'application/vnd.pgrst.object+json;nulls=stripped', oas_media_type_object( - schema := oas_build_reference_to_schemas(table_name) + schema := oas_build_reference_to_schemas(table_full_name) ), 'text/csv', oas_media_type_object( @@ -611,10 +733,10 @@ from ( ) ) ) as not_empty_response, - 'mayBeEmpty.' || table_name as may_be_empty, + 'mayBeEmpty.' || table_full_name as may_be_empty, case when insertable then oas_response_object( - description := 'Media types when response body could be empty or not for ' || table_name, + description := 'Media types when response body could be empty or not for ' || table_full_name, content := jsonb_build_object( 'application/json', oas_media_type_object( @@ -622,7 +744,7 @@ from ( oneof := jsonb_build_array( oas_schema_object( type := 'array', - items := oas_build_reference_to_schemas(table_name) + items := oas_build_reference_to_schemas(table_full_name) ), oas_schema_object( type := 'string' @@ -634,7 +756,7 @@ from ( oas_media_type_object( schema := oas_schema_object( oneOf := jsonb_build_array( - oas_build_reference_to_schemas(table_name), + oas_build_reference_to_schemas(table_full_name), oas_schema_object( type := 'string' ) @@ -645,7 +767,7 @@ from ( oas_media_type_object( schema := oas_schema_object( oneOf := jsonb_build_array( - oas_build_reference_to_schemas(table_name), + oas_build_reference_to_schemas(table_full_name), oas_schema_object( type := 'string' ) @@ -665,7 +787,63 @@ from ( from postgrest_get_all_tables_and_composite_types() where table_schema = any(schemas) and (is_table or is_view) - group by table_schema, table_name, insertable, is_table, is_view + group by table_schema, table_full_name, insertable, is_table, is_view +) as x +$$; + +create or replace function oas_build_response_objects_from_function_return_types(schemas text[]) +returns jsonb language sql stable as +$$ +select jsonb_object_agg(x.not_empty, x.not_empty_response) +from ( + select 'rpc.' || function_full_name as not_empty, + oas_response_object( + description := 'Media types for RPC ' || function_full_name, + content := jsonb_build_object( + 'application/json', + case when return_type_is_set then + oas_media_type_object( + schema := oas_schema_object( + type := 'array', + items := return_type_reference_schema + ) + ) + else + oas_media_type_object( + schema := return_type_reference_schema + ) + end, + 'application/vnd.pgrst.object+json', + oas_media_type_object( + schema := return_type_reference_schema + ), + 'application/vnd.pgrst.object+json;nulls=stripped', + oas_media_type_object( + schema := return_type_reference_schema + ), + 'text/csv', + oas_media_type_object( + schema := oas_schema_object( + type := 'string', + format := 'csv' + ) + ) + ) + ) as not_empty_response + from ( + select *, + -- Build the reference either to the table/composite return type or the non-composite return type + case when return_type_is_composite then + oas_build_reference_to_schemas(return_type_composite_full_name) + else + oas_build_reference_to_schemas('rpc.' || function_full_name) + end as return_type_reference_schema + from ( + select function_full_name, return_type_is_set, return_type_is_composite, return_type_composite_full_name + from postgrest_get_all_functions(schemas) + group by function_full_name, return_type_is_set, return_type_is_composite, return_type_composite_full_name + ) _ + ) _ ) as x $$; @@ -710,28 +888,28 @@ $$; create or replace function oas_build_request_bodies_from_tables(schemas text[]) returns jsonb language sql stable as $$ -select jsonb_object_agg(x.table_name, x.oas_req_body) +select jsonb_object_agg(x.table_full_name, x.oas_req_body) from ( select - table_name, + table_full_name, oas_request_body_object( - description := table_name, + description := table_full_name, content := jsonb_build_object( 'application/json', oas_media_type_object( "schema" := oas_schema_object( oneOf := jsonb_build_array( - oas_build_reference_to_schemas(table_name), + oas_build_reference_to_schemas(table_full_name), oas_schema_object( type := 'array', - items := oas_build_reference_to_schemas(table_name) + items := oas_build_reference_to_schemas(table_full_name) ) ) ) ), 'application/x-www-form-urlencoded', oas_media_type_object( - "schema" := oas_build_reference_to_schemas(table_name) + "schema" := oas_build_reference_to_schemas(table_full_name) ), 'text/csv', oas_media_type_object( @@ -746,7 +924,7 @@ from ( where table_schema = any(schemas) and (is_table or is_view) and insertable - group by table_schema, table_name, insertable + group by table_schema, table_full_name, insertable ) as x; $$; diff --git a/sql/paths.sql b/sql/paths.sql index 7b94174..63fb27b 100644 --- a/sql/paths.sql +++ b/sql/paths.sql @@ -4,7 +4,8 @@ create or replace function oas_build_paths(schemas text[]) returns jsonb language sql stable as $$ select oas_build_path_item_root() || - oas_build_path_items_from_tables(schemas); + oas_build_path_items_from_tables(schemas) || + oas_build_path_items_from_functions(schemas); $$; create or replace function oas_build_path_items_from_tables(schemas text[]) @@ -19,7 +20,7 @@ from ( description := (postgrest_unfold_comment(table_description))[2], tags := array[table_name], parameters := jsonb_agg( - oas_build_reference_to_parameters(format('rowFilter.%1$s.%2$s', table_name, column_name)) + oas_build_reference_to_parameters(format('rowFilter.%1$s.%2$s', table_full_name, column_name)) ) || jsonb_build_array( oas_build_reference_to_parameters('select'), @@ -35,9 +36,9 @@ from ( ), responses := jsonb_build_object( '200', - oas_build_reference_to_responses('notEmpty.' || table_name, 'OK'), + oas_build_reference_to_responses('notEmpty.' || table_full_name, 'OK'), '206', - oas_build_reference_to_responses('notEmpty.' || table_name, 'Partial Content'), + oas_build_reference_to_responses('notEmpty.' || table_full_name, 'Partial Content'), 'default', oas_build_reference_to_responses('defaultError', 'Error') ) @@ -48,7 +49,7 @@ from ( summary := (postgrest_unfold_comment(table_description))[1], description := (postgrest_unfold_comment(table_description))[2], tags := array[table_name], - requestBody := oas_build_reference_to_request_bodies(table_name), + requestBody := oas_build_reference_to_request_bodies(table_full_name), parameters := jsonb_build_array( oas_build_reference_to_parameters('select'), oas_build_reference_to_parameters('columns'), @@ -56,7 +57,7 @@ from ( ), responses := jsonb_build_object( '201', - oas_build_reference_to_responses('mayBeEmpty.' || table_name, 'Created'), + oas_build_reference_to_responses('mayBeEmpty.' || table_full_name, 'Created'), 'default', oas_build_reference_to_responses('defaultError', 'Error') ) @@ -68,9 +69,9 @@ from ( summary := (postgrest_unfold_comment(table_description))[1], description := (postgrest_unfold_comment(table_description))[2], tags := array[table_name], - requestBody := oas_build_reference_to_request_bodies(table_name), + requestBody := oas_build_reference_to_request_bodies(table_full_name), parameters := jsonb_agg( - oas_build_reference_to_parameters(format('rowFilter.%1$s.%2$s', table_name, column_name)) + oas_build_reference_to_parameters(format('rowFilter.%1$s.%2$s', table_full_name, column_name)) ) || jsonb_build_array( oas_build_reference_to_parameters('select'), @@ -85,7 +86,7 @@ from ( ), responses := jsonb_build_object( '200', - oas_build_reference_to_responses('notEmpty.' || table_name, 'OK'), + oas_build_reference_to_responses('notEmpty.' || table_full_name, 'OK'), '204', oas_build_reference_to_responses('empty', 'No Content'), 'default', @@ -100,7 +101,7 @@ from ( description := (postgrest_unfold_comment(table_description))[2], tags := array[table_name], parameters := jsonb_agg( - oas_build_reference_to_parameters(format('rowFilter.%1$s.%2$s', table_name, column_name)) + oas_build_reference_to_parameters(format('rowFilter.%1$s.%2$s', table_full_name, column_name)) ) || jsonb_build_array( oas_build_reference_to_parameters('select'), @@ -114,7 +115,7 @@ from ( ), responses := jsonb_build_object( '200', - oas_build_reference_to_responses('notEmpty.' || table_name, 'OK'), + oas_build_reference_to_responses('notEmpty.' || table_full_name, 'OK'), '204', oas_build_reference_to_responses('empty', 'No Content'), 'default', @@ -124,13 +125,77 @@ from ( end ) as oas_path_item from ( - select table_schema, table_name, table_description, insertable, updatable, deletable, column_name + select table_schema, table_name, table_full_name, table_description, insertable, updatable, deletable, column_name from postgrest_get_all_tables_and_composite_types() where table_schema = any(schemas) and (is_table or is_view) order by table_schema, table_name, column_position ) _ - group by table_schema, table_name, table_description, insertable, updatable, deletable + group by table_schema, table_name, table_full_name, table_description, insertable, updatable, deletable +) x; +$$; + +create or replace function oas_build_path_items_from_functions(schemas text[]) +returns jsonb language sql stable as +$$ +select jsonb_object_agg(x.path, x.oas_path_item) +from ( + select '/rpc/' || function_name as path, + oas_path_item_object( + get :=oas_operation_object( + summary := (postgrest_unfold_comment(function_description))[1], + description := (postgrest_unfold_comment(function_description))[2], + tags := array['(rpc) ' || function_name], + parameters := + coalesce( + jsonb_agg( + oas_build_reference_to_parameters(format('rpcParam.%1$s.%2$s', function_full_name, argument_name)) + ) filter ( where argument_name <> '' and (argument_is_in or argument_is_inout or argument_is_variadic)), + '[]' + ) || + 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('range'), + oas_build_reference_to_parameters('preferGet') + ) + else + jsonb_build_array( + oas_build_reference_to_parameters('preferGet') + ) + 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) + ) _ + 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 ) x; $$; diff --git a/sql/postgrest.sql b/sql/postgrest.sql index 922ee64..c1d00da 100644 --- a/sql/postgrest.sql +++ b/sql/postgrest.sql @@ -11,12 +11,15 @@ returns table ( column_character_maximum_length integer, column_enums text[], column_composite_relid oid, + column_composite_full_name text, + column_is_composite bool, column_position int, column_is_pk bool, table_oid oid, table_namespace oid, table_schema text, table_name text, + table_full_name text, table_description text, is_table bool, is_view bool, @@ -85,13 +88,17 @@ WITH RECURSIVE )::integer AS column_character_maximum_length, (SELECT array_agg(enumlabel ORDER BY enumsortorder) FROM pg_enum WHERE enumtypid = COALESCE(COALESCE(bt_arr.oid, bt.oid), COALESCE(t_arr.oid, t.oid))) AS column_enums, -- If the column or item is a composite type, this references the relid. - COALESCE(COALESCE(bt_arr.typrelid, t_arr.typrelid), COALESCE(bt.typrelid, t.typrelid)) AS column_composite_relid, + cc.oid AS column_composite_relid, + -- The "full name" of the composite type is `.`. We omit `.` when it belongs to the `current_schema` + COALESCE(NULLIF(cc.relnamespace::regnamespace::text, current_schema) || '.', '') || cc.relname AS column_composite_full_name, + COALESCE(cc.oid <> 0, FALSE) AS column_is_composite, a.attnum::integer AS position, tpks.position IS NOT NULL AS column_is_pk, c.oid AS table_oid, c.relnamespace AS table_namespace, n.nspname AS table_schema, c.relname AS table_name, + COALESCE(NULLIF(n.nspname, current_schema) || '.', '') || c.relname AS table_full_name, td.description AS table_description, c.relkind IN ('r','f','p') AS is_table, c.relkind IN ('v','m') AS is_view, @@ -141,6 +148,8 @@ WITH RECURSIVE ON t.typtype = 'd' AND t.typbasetype = bt.oid LEFT JOIN pg_type bt_arr ON bt.typarray = 0 AND bt.oid = bt_arr.typarray + LEFT JOIN pg_class cc + ON cc.oid = COALESCE(COALESCE(bt_arr.typrelid, t_arr.typrelid), COALESCE(bt.typrelid, t.typrelid)) LEFT JOIN pg_depend seq ON seq.refobjid = a.attrelid AND seq.refobjsubid = a.attnum and seq.deptype = 'i' LEFT JOIN tbl_pk_cols tpks @@ -154,31 +163,43 @@ WITH RECURSIVE AND not c.relispartition; $$; --- TODO: simplify the query to have only relevant info for OpenAPI --- TODO: Further optimize the query (takes ~160ms compared to ~17ms from the query in the core repo) create or replace function postgrest_get_all_functions(schemas text[]) returns table ( - proc_schema text, - proc_name text, - proc_description text, - schema text, - name text, - rettype_is_setof boolean, - rettype_is_composite boolean, - rettype_is_table boolean, - rettype_is_composite_alias boolean, - provolatile char, - hasvariadic boolean, - transaction_isolation_level text, - statement_timeout text, - composite_args_ret oid[], - required_args text[], - all_args text[], - args jsonb + argument_input_qty int, + argument_name text, + argument_reg_type oid, + argument_type_name text, + argument_item_type_name text, + argument_is_required bool, + argument_is_in bool, + argument_is_inout bool, + argument_is_out bool, + argument_is_table bool, + argument_is_variadic bool, + argument_position int, + argument_composite_relid oid, + argument_composite_full_name text, + argument_is_composite bool, + function_oid oid, + function_schema name, + function_name name, + function_full_name text, + function_description text, + return_type_name text, + return_type_item_name text, + return_type_is_set bool, + return_type_is_table bool, + return_type_is_out bool, + return_type_is_composite_alias bool, + return_type_composite_relid oid, + return_type_composite_full_name text, + return_type_is_composite bool, + is_volatile bool, + has_variadic bool ) language sql stable as $$ -- Recursively get the base types of domains - WITH + WITH base_types AS ( WITH RECURSIVE recurse AS ( @@ -201,146 +222,77 @@ $$ FROM recurse WHERE typbasetype = 0 ), - arguments AS ( + all_functions AS ( SELECT - p.oid as proc_oid, - pa.idx AS idx, - COALESCE(pa.name, '') as name, - type, - CASE type - WHEN 'bit'::regtype THEN 'bit varying' - WHEN 'bit[]'::regtype THEN 'bit varying[]' - WHEN 'character'::regtype THEN 'character varying' - WHEN 'character[]'::regtype THEN 'character varying[]' - ELSE type::regtype::text - END AS type_ignore_length, -- convert types that ignore the lenth and accept any value till maximum size - pa.idx <= (p.pronargs - p.pronargdefaults) AS is_required, - t.typarray = 0 AS is_array, - t.typtype = 'c' AS is_composite, - t.typrelid AS composite_oid, - CASE - WHEN t.typtype = 'd' THEN - CASE - WHEN nbt.nspname = 'pg_catalog'::name THEN format_type(t.typbasetype, NULL::integer) - ELSE format_type(pa.type, NULL::integer) - END - ELSE format_type(pa.type, NULL::integer) - END::text AS data_type, - t.oid AS data_type_id, - COALESCE(bt.typname, t.typname)::name AS udt_name, - CASE - WHEN t_arr.typtype = 'd' THEN - CASE - WHEN nbt_arr.nspname = 'pg_catalog'::name THEN format_type(t_arr.typbasetype, NULL::integer) - ELSE format_type(t_arr.oid, t_arr.typtypmod) - END - ELSE - CASE - WHEN nt_arr.nspname = 'pg_catalog'::name THEN format_type(t_arr.oid, NULL::integer) - ELSE format_type(t_arr.oid, t_arr.typtypmod) - END - END::text AS item_data_type, - t_arr.typtype = 'c' AS item_is_composite, - t_arr.typrelid AS item_composite_oid, - t_arr.oid AS item_data_type_id, - COALESCE(mode = 'v', FALSE) AS is_variadic + p.pronargs AS argument_input_qty, + COALESCE(pa.name, '') AS argument_name, + pa.type AS argument_reg_type, + format_type(ta.oid, NULL::integer) AS argument_type_name, + format_type(ta_arr.oid, NULL::integer) AS argument_item_type_name, + pa.idx <= (array_length(COALESCE(p.proallargtypes, p.proargtypes), 1) - p.pronargdefaults) AS argument_is_required, + COALESCE(pa.mode = 'i', TRUE) AS argument_is_in, -- if the mode IS NULL then it is an IN argument + COALESCE(pa.mode = 'b', FALSE) AS argument_is_inout, + COALESCE(pa.mode = 'o', FALSE) AS argument_is_out, + COALESCE(pa.mode = 't', FALSE) AS argument_is_table, + COALESCE(pa.mode = 'v', FALSE) AS argument_is_variadic, + pa.idx as argument_position, + -- If the argument or item is a composite type, this references the relid. + ca.oid AS argument_composite_relid, + -- The "full name" of the composite type is `.`. We omit `.` when it belongs to the `current_schema` + COALESCE(NULLIF(ca.relnamespace::regnamespace::text, current_schema) || '.', '') || ca.relname AS argument_composite_full_name, + COALESCE(ca.oid <> 0, FALSE) AS argument_is_composite, + p.oid as function_oid, + pn.nspname AS function_schema, + p.proname AS function_name, + -- 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, + 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, + COALESCE(proargmodes::text[] && '{t}', FALSE) return_type_is_table, -- If the function RETURNS TABLE + COALESCE(proargmodes::text[] && '{b,o}', FALSE) return_type_is_out, -- If the function has INOUT or OUT arguments + bt.oid <> bt.base AS return_type_is_composite_alias, + -- If the return type or item is a composite type, this references the relid. + c.oid AS return_type_composite_relid, + COALESCE(NULLIF(c.relnamespace::regnamespace::text, current_schema) || '.', '') || c.relname AS return_type_composite_full_name, + COALESCE(c.oid <> 0, FALSE) AS return_type_is_composite, + p.provolatile = 'v' AS is_volatile, + p.provariadic > 0 AS has_variadic FROM pg_proc p - CROSS JOIN unnest(proargnames, proargtypes, proargmodes) - WITH ORDINALITY AS pa (name, type, mode, idx) - JOIN (pg_type t JOIN pg_namespace nt ON t.typnamespace = nt.oid) - ON pa.type = t.oid - LEFT JOIN (pg_type bt JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid) - ON t.typtype = 'd' AND t.typbasetype = bt.oid - LEFT JOIN (pg_type t_arr JOIN pg_namespace nt_arr ON t_arr.typnamespace = nt_arr.oid) - ON t.oid = t_arr.typarray - LEFT JOIN (pg_type bt_arr JOIN pg_namespace nbt_arr ON bt_arr.typnamespace = nbt_arr.oid) - ON t_arr.typtype = 'd' AND t_arr.typbasetype = bt_arr.oid - -- TODO: Add output arguments (including "returns table") to build the response object schema - WHERE pa.type IS NOT NULL -- only input arguments - ), - arguments_agg AS ( - SELECT - info.proc_oid as oid, - array_agg(coalesce(info.composite_oid, info.item_composite_oid)) filter (where info.is_composite or info.item_is_composite) AS composite_args, - array_agg(info.name order by info.idx) filter (where info.is_required) AS required_args, - array_agg(info.name order by info.idx) AS all_args, - jsonb_object_agg( - info.name, - case when info.is_composite then - oas_build_reference_to_schemas(info.data_type) - else - oas_schema_object( - type := postgrest_pgtype_to_oastype(info.data_type), - format := info.data_type::text, - -- TODO: take default values from pg_proc.pronargdefaults - -- "default" := to_jsonb(info.arg_default), - enum := to_jsonb(enum_info.vals), - items := - case - when not info.is_array then - null - when info.item_is_composite then - oas_build_reference_to_schemas(info.item_data_type) - else - oas_schema_object( - type := postgrest_pgtype_to_oastype(info.item_data_type), - format := info.item_data_type::text - ) - end - ) - end order by info.idx - ) as args, - CASE COUNT(*) - COUNT(nullif(info.name,'')) -- number of unnamed arguments - WHEN 0 THEN true - WHEN 1 THEN COUNT(*) = 1 AND (array_agg(info.type))[1] IN ('bytea'::regtype, 'json'::regtype, 'jsonb'::regtype, 'text'::regtype, 'xml'::regtype) - ELSE false - END AS callable - FROM arguments as info - LEFT OUTER JOIN ( - SELECT - n.nspname AS s, - t.typname AS n, - array_agg(e.enumlabel ORDER BY e.enumsortorder) AS vals - FROM pg_type t - JOIN pg_enum e ON t.oid = e.enumtypid - JOIN pg_namespace n ON n.oid = t.typnamespace - GROUP BY s,n - ) AS enum_info ON info.udt_name = enum_info.n - GROUP BY proc_oid - ) - SELECT - pn.nspname AS proc_schema, - p.proname AS proc_name, - d.description AS proc_description, - tn.nspname AS schema, - COALESCE(comp.relname, t.typname) AS name, - p.proretset AS rettype_is_setof, - t.typtype = 'c' AS rettype_is_composite, - COALESCE(proargmodes::text[] @> '{t}', false) AS rettype_is_table, - -- TODO: add support for rettype out/inout - bt.oid <> bt.base as rettype_is_composite_alias, - p.provolatile, - p.provariadic > 0 as hasvariadic, - lower((regexp_split_to_array((regexp_split_to_array(iso_config, '='))[2], ','))[1]) AS transaction_isolation_level, - lower((regexp_split_to_array((regexp_split_to_array(timeout_config, '='))[2], ','))[1]) AS statement_timeout, - a.composite_args || case when t.typtype = 'c' then t.typrelid end as composite_args_ret, - a.required_args, - a.all_args, - COALESCE(a.args, '{}') AS args - FROM pg_proc p - LEFT JOIN arguments_agg a ON a.oid = p.oid - JOIN pg_namespace pn ON pn.oid = p.pronamespace - JOIN base_types bt ON bt.oid = p.prorettype - JOIN pg_type t ON t.oid = bt.base - JOIN pg_namespace tn ON tn.oid = t.typnamespace - -- TODO: Add support for functions returning array types (extra joins needed) - LEFT JOIN pg_class comp ON comp.oid = t.typrelid - LEFT JOIN pg_description as d ON d.objoid = p.oid - LEFT JOIN LATERAL unnest(proconfig) iso_config ON iso_config like 'default_transaction_isolation%' - LEFT JOIN LATERAL unnest(proconfig) timeout_config ON timeout_config like 'statement_timeout%' - WHERE t.oid <> 'trigger'::regtype AND COALESCE(a.callable, true) + LEFT JOIN LATERAL unnest(proargnames, coalesce(proallargtypes,proargtypes), proargmodes) + WITH ORDINALITY AS pa (name, type, mode, idx) ON TRUE + JOIN pg_namespace pn ON pn.oid = p.pronamespace + -- Joins relevant to the return type + JOIN base_types bt ON bt.oid = p.prorettype + JOIN pg_type t ON t.oid = bt.base + LEFT JOIN pg_type t_arr + ON t.typarray = 0 AND t.oid = t_arr.typarray + LEFT JOIN pg_class c + ON c.oid = COALESCE(t_arr.typrelid, t.typrelid) + LEFT JOIN base_types bta ON bta.oid = pa.type + LEFT JOIN pg_type ta ON ta.oid = bta.base + LEFT JOIN pg_type ta_arr + ON ta.typarray = 0 AND ta.oid = ta_arr.typarray + LEFT JOIN pg_class ca + ON ca.oid = COALESCE(ta_arr.typrelid, ta.typrelid) + LEFT JOIN pg_description as d ON d.objoid = p.oid + WHERE t.oid <> 'trigger'::regtype AND prokind = 'f' - AND pn.nspname = ANY(schemas); + AND p.pronamespace = ANY(schemas::regnamespace[]) + ) + SELECT a.* + FROM all_functions a + WHERE NOT EXISTS ( + SELECT 1 + FROM all_functions x + -- Do not include functions with unnamed arguments, unless it's a function with a single unnamed parameter of certain types + 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 x.function_oid = a.function_oid + ); $$; create or replace function postgrest_get_schema_description(schema text) diff --git a/sql/utils.sql b/sql/utils.sql index 2be2013..30afecf 100644 --- a/sql/utils.sql +++ b/sql/utils.sql @@ -8,8 +8,7 @@ select case when type like any(array['character', 'character varying', 'text']) when type like any(array['bigint', 'integer', 'smallint']) then 'integer' when type like 'boolean' then 'boolean' when type like '%[]' then 'array' - when type like 'json' then 'object' - when type like 'jsonb' then 'object' + when type like any(array['json', 'jsonb', 'record']) then 'object' else 'string' end; $$; diff --git a/test/expected/parameters.out b/test/expected/parameters.out index 1dda5fb..fba8030 100644 --- a/test/expected/parameters.out +++ b/test/expected/parameters.out @@ -367,3 +367,120 @@ where key like 'rowFilter.big_products.%'; | } (4 rows) +-- shows `IN` function arguments as RPC parameters +select key, jsonb_pretty(value) +from jsonb_each(postgrest_openapi_spec('{test}')->'components'->'parameters') +where key like 'rpcParam.has_in_parameters.%'; + key | jsonb_pretty +------------------------------+----------------------------------------- + rpcParam.has_in_parameters.o | { + + | "in": "query", + + | "name": "o", + + | "schema": { + + | "type": "integer", + + | "format": "integer" + + | }, + + | "required": false + + | } + rpcParam.has_in_parameters.x | { + + | "in": "query", + + | "name": "x", + + | "schema": { + + | "type": "integer", + + | "format": "integer" + + | }, + + | "required": true + + | } + rpcParam.has_in_parameters.y | { + + | "in": "query", + + | "name": "y", + + | "schema": { + + | "type": "string", + + | "format": "text[]" + + | }, + + | "required": true + + | } + rpcParam.has_in_parameters.z | { + + | "in": "query", + + | "name": "z", + + | "schema": { + + | "type": "string", + + | "format": "types.attribute_ref"+ + | }, + + | "required": true + + | } +(4 rows) + +-- shows `INOUT` function arguments as RPC parameters +select key, jsonb_pretty(value) +from jsonb_each(postgrest_openapi_spec('{test}')->'components'->'parameters') +where key like 'rpcParam.has_inout_parameters.%'; + key | jsonb_pretty +---------------------------------+----------------------------------------- + rpcParam.has_inout_parameters.o | { + + | "in": "query", + + | "name": "o", + + | "schema": { + + | "type": "integer", + + | "format": "integer" + + | }, + + | "required": false + + | } + rpcParam.has_inout_parameters.x | { + + | "in": "query", + + | "name": "x", + + | "schema": { + + | "type": "integer", + + | "format": "integer" + + | }, + + | "required": true + + | } + rpcParam.has_inout_parameters.y | { + + | "in": "query", + + | "name": "y", + + | "schema": { + + | "type": "string", + + | "format": "text[]" + + | }, + + | "required": true + + | } + rpcParam.has_inout_parameters.z | { + + | "in": "query", + + | "name": "z", + + | "schema": { + + | "type": "string", + + | "format": "types.attribute_ref"+ + | }, + + | "required": true + + | } +(4 rows) + +-- shows `VARIADIC` function arguments as RPC parameters +select key, jsonb_pretty(value) +from jsonb_each(postgrest_openapi_spec('{test}')->'components'->'parameters') +where key like 'rpcParam.has_variadic_parameter.%'; + key | jsonb_pretty +-----------------------------------+----------------------------------- + rpcParam.has_variadic_parameter.x | { + + | "in": "query", + + | "name": "x", + + | "schema": { + + | "type": "integer", + + | "format": "integer" + + | }, + + | "required": true + + | } + rpcParam.has_variadic_parameter.y | { + + | "in": "query", + + | "name": "y", + + | "schema": { + + | "type": "array", + + | "items": { + + | "type": "string", + + | "format": "integer[]"+ + | } + + | }, + + | "required": true + + | } +(2 rows) + diff --git a/test/expected/paths.out b/test/expected/paths.out index 3593680..519bd11 100644 --- a/test/expected/paths.out +++ b/test/expected/paths.out @@ -701,3 +701,182 @@ select postgrest_openapi_spec('{test}')->'paths'->'/non_auto_updatable' ? 'delet f (1 row) +-- Functions +-- GET operation object +-- shows the function name as tag +select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/rpc/get_products_by_size'->'get'->'tags'); + jsonb_pretty +---------------------------------- + [ + + "(rpc) get_products_by_size"+ + ] +(1 row) + +-- uses a reference for the 200 HTTP code response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/rpc/get_products_by_size'->'get'->'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(postgrest_openapi_spec('{test}')->'paths'->'/rpc/get_products_by_size'->'get'->'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(postgrest_openapi_spec('{test}')->'paths'->'/rpc/get_products_by_size'->'get'->'responses'->'default'); + jsonb_pretty +---------------------------------------------------- + { + + "$ref": "#/components/responses/defaultError",+ + "description": "Error" + + } +(1 row) + +-- shows the first line of the comment on the table as summary +select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/rpc/get_products_by_size'->'get'->'summary'); + jsonb_pretty +-------------------------------- + "Get Products By Size summary" +(1 row) + +-- shows the second line of the comment on the table as description +select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/rpc/get_products_by_size'->'get'->'description'); + jsonb_pretty +----------------------------------------------------------------- + "Get Products By Size description\nthat spans\nmultiple lines." +(1 row) + +-- uses references for common parameters on `RETURNS ` functions +select value +from jsonb_array_elements(postgrest_openapi_spec('{test}')->'paths'->'/rpc/get_attribute'->'get'->'parameters') +where value->>'$ref' not like '#/components/parameters/rpcParam.get_attribute.%'; + 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/range"} + {"$ref": "#/components/parameters/preferGet"} +(10 rows) + +-- uses references for common parameters on `RETURNS TABLE` functions +select value +from jsonb_array_elements(postgrest_openapi_spec('{test}')->'paths'->'/rpc/returns_table'->'get'->'parameters') +where value->>'$ref' not like '#/components/parameters/rpcParam.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/range"} + {"$ref": "#/components/parameters/preferGet"} +(10 rows) + +-- uses references for common parameters on functions with INOUT/OUT parameters +select value +from jsonb_array_elements(postgrest_openapi_spec('{test}')->'paths'->'/rpc/returns_inout_out'->'get'->'parameters') +where value->>'$ref' not like '#/components/parameters/rpcParam.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/range"} + {"$ref": "#/components/parameters/preferGet"} +(10 rows) + +-- does not use a reference for the 206 HTTP code response on functions that do not return `SET OF` +select postgrest_openapi_spec('{test}')->'paths'->'/rpc/get_attribute'->'get'->'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(postgrest_openapi_spec('{test}')->'paths'->'/rpc/returns_simple_type'->'get'->'parameters') +where value->>'$ref' not like '#/components/parameters/rpcParam.returns_simple_type.%'; + value +----------------------------------------------- + {"$ref": "#/components/parameters/preferGet"} +(1 row) + +-- shows a function with a single unnamed parameter of accepted types +select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/rpc/single_unnamed_json_param'->'get'->'tags'); + jsonb_pretty +--------------------------------------- + [ + + "(rpc) single_unnamed_json_param"+ + ] +(1 row) + +select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/rpc/single_unnamed_jsonb_param'->'get'->'tags'); + jsonb_pretty +---------------------------------------- + [ + + "(rpc) single_unnamed_jsonb_param"+ + ] +(1 row) + +select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/rpc/single_unnamed_text_param'->'get'->'tags'); + jsonb_pretty +--------------------------------------- + [ + + "(rpc) single_unnamed_text_param"+ + ] +(1 row) + +select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/rpc/single_unnamed_xml_param'->'get'->'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 postgrest_openapi_spec('{test}')->'paths' ? '/rpc/single_unnamed_unrecognized_param' as value; + value +------- + f +(1 row) + +-- does not show a function with unnamed parameters +select postgrest_openapi_spec('{test}')->'paths' ? '/rpc/unnamed_params' as value; + value +------- + f +(1 row) + +-- does not show a function with named and unnamed parameters +select postgrest_openapi_spec('{test}')->'paths' ? '/rpc/named_and_unnamed_params' as value; + value +------- + f +(1 row) + diff --git a/test/expected/responses.out b/test/expected/responses.out index 5a48cd6..bf944d1 100644 --- a/test/expected/responses.out +++ b/test/expected/responses.out @@ -290,3 +290,194 @@ select postgrest_openapi_spec('{test}')->'components'->'responses' ? 'mayBeEmpty f (1 row) +-- Functions +-- Returning composite types +-- defines an application/json response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.get_attribute'->'content'->'application/json'); + jsonb_pretty +------------------------------------------------------------ + { + + "schema": { + + "$ref": "#/components/schemas/types.attribute_ret"+ + } + + } +(1 row) + +-- defines an application/vnd.pgrst.object+json response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.get_attribute'->'content'->'application/vnd.pgrst.object+json'); + jsonb_pretty +------------------------------------------------------------ + { + + "schema": { + + "$ref": "#/components/schemas/types.attribute_ret"+ + } + + } +(1 row) + +-- defines an application/vnd.pgrst.object+json;nulls=stripped response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.get_attribute'->'content'->'application/vnd.pgrst.object+json;nulls=stripped'); + jsonb_pretty +------------------------------------------------------------ + { + + "schema": { + + "$ref": "#/components/schemas/types.attribute_ret"+ + } + + } +(1 row) + +-- defines a text/csv response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.get_attribute'->'content'->'text/csv'); + jsonb_pretty +--------------------------- + { + + "schema": { + + "type": "string",+ + "format": "csv" + + } + + } +(1 row) + +-- Returning SET OF composite types +-- defines an application/json response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.get_products_by_size'->'content'->'application/json'); + jsonb_pretty +----------------------------------------------------- + { + + "schema": { + + "type": "array", + + "items": { + + "$ref": "#/components/schemas/products"+ + } + + } + + } +(1 row) + +-- defines an application/vnd.pgrst.object+json response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.get_products_by_size'->'content'->'application/vnd.pgrst.object+json'); + jsonb_pretty +------------------------------------------------- + { + + "schema": { + + "$ref": "#/components/schemas/products"+ + } + + } +(1 row) + +-- defines an application/vnd.pgrst.object+json;nulls=stripped response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.get_products_by_size'->'content'->'application/vnd.pgrst.object+json;nulls=stripped'); + jsonb_pretty +------------------------------------------------- + { + + "schema": { + + "$ref": "#/components/schemas/products"+ + } + + } +(1 row) + +-- defines a text/csv response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.get_products_by_size'->'content'->'text/csv'); + jsonb_pretty +--------------------------- + { + + "schema": { + + "type": "string",+ + "format": "csv" + + } + + } +(1 row) + +-- Returning non-composite types +-- defines an application/json response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.returns_inout_out'->'content'->'application/json'); + jsonb_pretty +-------------------------------------------------------------- + { + + "schema": { + + "$ref": "#/components/schemas/rpc.returns_inout_out"+ + } + + } +(1 row) + +-- defines an application/vnd.pgrst.object+json response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.returns_inout_out'->'content'->'application/vnd.pgrst.object+json'); + jsonb_pretty +-------------------------------------------------------------- + { + + "schema": { + + "$ref": "#/components/schemas/rpc.returns_inout_out"+ + } + + } +(1 row) + +-- defines an application/vnd.pgrst.object+json;nulls=stripped response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.returns_inout_out'->'content'->'application/vnd.pgrst.object+json;nulls=stripped'); + jsonb_pretty +-------------------------------------------------------------- + { + + "schema": { + + "$ref": "#/components/schemas/rpc.returns_inout_out"+ + } + + } +(1 row) + +-- defines a text/csv response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.returns_inout_out'->'content'->'text/csv'); + jsonb_pretty +--------------------------- + { + + "schema": { + + "type": "string",+ + "format": "csv" + + } + + } +(1 row) + +-- Returning SET OF non-composite types +-- defines an application/json response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.returns_table'->'content'->'application/json'); + jsonb_pretty +-------------------------------------------------------------- + { + + "schema": { + + "type": "array", + + "items": { + + "$ref": "#/components/schemas/rpc.returns_table"+ + } + + } + + } +(1 row) + +-- defines an application/vnd.pgrst.object+json response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.returns_table'->'content'->'application/vnd.pgrst.object+json'); + jsonb_pretty +---------------------------------------------------------- + { + + "schema": { + + "$ref": "#/components/schemas/rpc.returns_table"+ + } + + } +(1 row) + +-- defines an application/vnd.pgrst.object+json;nulls=stripped response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.returns_table'->'content'->'application/vnd.pgrst.object+json;nulls=stripped'); + jsonb_pretty +---------------------------------------------------------- + { + + "schema": { + + "$ref": "#/components/schemas/rpc.returns_table"+ + } + + } +(1 row) + +-- defines a text/csv response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.returns_table'->'content'->'text/csv'); + jsonb_pretty +--------------------------- + { + + "schema": { + + "type": "string",+ + "format": "csv" + + } + + } +(1 row) + diff --git a/test/expected/schemas.out b/test/expected/schemas.out index 2719989..63390aa 100644 --- a/test/expected/schemas.out +++ b/test/expected/schemas.out @@ -287,7 +287,7 @@ select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->' (1 row) -- Functions --- Types inside arguments +-- Composite types inside arguments -- detects composite types as objects select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'types.attribute_arg'->'type'); jsonb_pretty @@ -356,7 +356,7 @@ select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->' } (1 row) --- Types inside returning values +-- Composite types inside returning values -- detects composite types as objects select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'types.attribute_ret'->'type'); jsonb_pretty @@ -425,6 +425,297 @@ select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->' } (1 row) +-- Non-composite return values +-- detects a function that returns a simple type +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_simple_type'); + jsonb_pretty +----------------------- + { + + "type": "string",+ + "format": "text" + + } +(1 row) + +-- detects a function that returns a simple array type +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_simple_type_arr'); + jsonb_pretty +--------------------------- + { + + "type": "array", + + "items": { + + "type": "string",+ + "format": "text" + + }, + + "format": "text[]" + + } +(1 row) + +-- detects a function that returns table +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_table'); + jsonb_pretty +---------------------------------------------------------------- + { + + "type": "object", + + "properties": { + + "num": { + + "type": "integer", + + "format": "integer" + + }, + + "val": { + + "type": "string", + + "format": "text" + + }, + + "comp": { + + "$ref": "#/components/schemas/types.dimension_ref"+ + } + + } + + } +(1 row) + +-- detects a function that returns table with array columns +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_table_arr'); + jsonb_pretty +-------------------------------------------------------------------- + { + + "type": "object", + + "properties": { + + "num": { + + "type": "array", + + "items": { + + "type": "integer", + + "format": "integer" + + }, + + "format": "integer[]" + + }, + + "val": { + + "type": "array", + + "items": { + + "type": "string", + + "format": "text" + + }, + + "format": "text[]" + + }, + + "comp": { + + "type": "array", + + "items": { + + "$ref": "#/components/schemas/types.dimension_ref"+ + }, + + "format": "types.dimension_ref[]" + + } + + } + + } +(1 row) + +-- detects a function that returns a record with inout arguments +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_inout'); + jsonb_pretty +------------------------------------------------------------ + { + + "type": "object", + + "properties": { + + "y": { + + "type": "string", + + "format": "text" + + }, + + "z": { + + "$ref": "#/components/schemas/types.color_ref"+ + } + + } + + } +(1 row) + +-- detects a function that returns a record with inout array arguments +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_inout_arr'); + jsonb_pretty +---------------------------------------------------------------- + { + + "type": "object", + + "properties": { + + "y": { + + "type": "array", + + "items": { + + "type": "string", + + "format": "text" + + }, + + "format": "text[]" + + }, + + "z": { + + "type": "array", + + "items": { + + "$ref": "#/components/schemas/types.color_ref"+ + }, + + "format": "types.color_ref[]" + + } + + } + + } +(1 row) + +-- detects a function that returns a record with out arguments +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_out'); + jsonb_pretty +------------------------------------------------------------ + { + + "type": "object", + + "properties": { + + "a": { + + "type": "string", + + "format": "text" + + }, + + "b": { + + "$ref": "#/components/schemas/types.color_ref"+ + } + + } + + } +(1 row) + +-- detects a function that returns a record with out array arguments +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_out_arr'); + jsonb_pretty +---------------------------------------------------------------- + { + + "type": "object", + + "properties": { + + "a": { + + "type": "array", + + "items": { + + "type": "string", + + "format": "text" + + }, + + "format": "text[]" + + }, + + "b": { + + "type": "array", + + "items": { + + "$ref": "#/components/schemas/types.color_ref"+ + }, + + "format": "types.color_ref[]" + + } + + } + + } +(1 row) + +-- detects a function that returns a record with inout and out arguments +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_inout_out'); + jsonb_pretty +------------------------------------------------------------ + { + + "type": "object", + + "properties": { + + "y": { + + "type": "string", + + "format": "text" + + }, + + "z": { + + "$ref": "#/components/schemas/types.color_ref"+ + } + + } + + } +(1 row) + +-- detects a function that returns a record with inout and out array arguments +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_inout_out_arr'); + jsonb_pretty +---------------------------------------------------------------- + { + + "type": "object", + + "properties": { + + "y": { + + "type": "array", + + "items": { + + "type": "string", + + "format": "text" + + }, + + "format": "text[]" + + }, + + "z": { + + "type": "array", + + "items": { + + "$ref": "#/components/schemas/types.color_ref"+ + }, + + "format": "types.color_ref[]" + + } + + } + + } +(1 row) + +-- detects a function that returns an unknown record as a free-form object +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_unknown_record'); + jsonb_pretty +------------------------ + { + + "type": "object", + + "format": "record"+ + } +(1 row) + +-- detects a function with a single unnamed json parameter +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.single_unnamed_json_param'); + jsonb_pretty +----------------------- + { + + "type": "object",+ + "format": "json" + + } +(1 row) + +-- detects a function with a single unnamed jsonb parameter +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.single_unnamed_jsonb_param'); + jsonb_pretty +----------------------- + { + + "type": "object",+ + "format": "jsonb"+ + } +(1 row) + +-- detects a function with a single unnamed text parameter +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.single_unnamed_text_param'); + jsonb_pretty +----------------------- + { + + "type": "string",+ + "format": "text" + + } +(1 row) + +-- detects a function with a single unnamed xml parameter +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.single_unnamed_xml_param'); + jsonb_pretty +----------------------- + { + + "type": "string",+ + "format": "xml" + + } +(1 row) + +-- ignores a function with a single unnamed unrecognized parameter +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.single_unnamed_unrecognized_param'); + jsonb_pretty +-------------- + +(1 row) + +-- ignores a function with unnamed parameters +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.single_unnamed_unrecognized_param'); + jsonb_pretty +-------------- + +(1 row) + +-- ignores a function with named and unnamed parameters +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.single_unnamed_unrecognized_param'); + jsonb_pretty +-------------- + +(1 row) + +-- ignores a function outside of the exposed schemas +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.private.secret_function'); + jsonb_pretty +-------------- + +(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 1dec36b..5471785 100644 --- a/test/fixtures.sql +++ b/test/fixtures.sql @@ -20,6 +20,10 @@ create type types.attribute_arg as (dim types.dimension_arg, colors types.color_ create type types.dimension_ret as (len numeric, wid numeric, hei numeric); create type types.color_ret as (hex char(6), def text); create type types.attribute_ret as (dim types.dimension_ret, colors types.color_ret[], other text); +-- Used to test reference to composite types +create type types.dimension_ref as (len numeric, wid numeric, hei numeric); +create type types.color_ref as (hex char(6), def text); +create type types.attribute_ref as (dim types.dimension_ret, colors types.color_ret[], other text); comment on type types.attribute is $$Attribute summary @@ -99,6 +103,8 @@ multiple lines.$$; create view test.non_auto_updatable as select 'this view is not auto updatable' as description; +-- Functions for testing + create function test.get_products_by_size(s types.size) returns setof test.products stable language sql as $$ @@ -107,12 +113,126 @@ $$ where size = s; $$; +comment on function test.get_products_by_size(s types.size) is +$$Get Products By Size summary + +Get Products By Size description +that spans +multiple lines.$$; + create function test.get_attribute(loc types.attribute_arg) returns types.attribute_ret stable language sql as $$ select ((1,2,3),array[('a','b')]::types.color_ret[],'a'); $$; +create function test.returns_simple_type() +returns text +stable language sql as +$$ + select 'text'; +$$; + +create function test.returns_simple_type_arr() +returns text[] +stable language sql as +$$ + select array['text']; +$$; + +create function test.returns_table() +returns table (num int, val text, comp types.dimension_ref) +stable language sql as +$$ + select (1,'text',(0.1,0.2,0.3)); +$$; + +create function test.returns_table_arr() +returns table (num int[], val text[], comp types.dimension_ref[]) +stable language sql as +$$ + select (array[1],array['text'],array[(0.1,0.2,0.3)]::types.dimension_ref[]); +$$; + +create function test.returns_inout(x int, inout y text, inout z types.color_ref) +returns record +stable language sql as +$$ + select ($2 || $1, $3); +$$; + +create function test.returns_inout_arr(x int, inout y text[], inout z types.color_ref[]) +returns record +stable language sql as +$$ + select (array[$2[$1]]::text[], array[$3]::types.color_ref[]); +$$; + +create function test.returns_out(x int, y text, out a text, out b types.color_ref) +returns record +stable language sql as +$$ + select ($2 || $1, ('ffffff','white')); +$$; + +create function test.returns_out_arr(x int, y text, out a text[], out b types.color_ref[]) +returns record +stable language sql as +$$ + select (array[$2 || $1]::text[], array[('ffffff','white')]::types.color_ref[]); +$$; + +create function test.returns_inout_out(x int, inout y text, out z types.color_ref) +returns record +stable language sql as +$$ + select ($2 || $1, ('ffffff','white')); +$$; + +create function test.returns_inout_out_arr(x int, inout y text[], out z types.color_ref[]) +returns record +stable language sql as +$$ + select (array[$2[$1]]::text[], array[('ffffff','white')]::types.color_ref[]); +$$; + +create function test.returns_unknown_record() +returns record +stable language sql as +$$ + select (1, 'a', ('ffffff','white')::types.color_ref); +$$; + +create function test.has_in_parameters(x int, y text[], z types.attribute_ref, o int default 0) + returns record + stable language sql as +$$ +select ($1, $2, $3, $4); +$$; + +create function test.has_inout_parameters(inout x int, inout y text[], inout z types.attribute_ref, inout o int default 0) + returns record + stable language sql as +$$ +select ($1, $2, $3, $4); +$$; + +create function test.has_variadic_parameter(x int, variadic y int[]) + returns record + stable language sql as +$$ +select ($1, $2); +$$; + +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_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'; +create function test.named_and_unnamed_params(a int, numeric) returns numeric stable language sql as 'select $2 + $1'; + create schema private; create table private.secret_table ( @@ -120,6 +240,7 @@ create table private.secret_table ( name text not null ); +create function private.secret_function() returns int stable language sql as 'select 1'; -- Sample PostgREST config -- https://postgrest.org/en/stable/references/configuration.html#in-database-configuration create schema postgrest; @@ -134,3 +255,6 @@ returns void as $$ set_config('pgrst.version', '0000.1111', false) ; $$ language sql; + +-- Emulate PostgREST search_path setting +alter database contrib_regression set search_path = test, public; diff --git a/test/sql/parameters.sql b/test/sql/parameters.sql index 2377b35..f7a913a 100644 --- a/test/sql/parameters.sql +++ b/test/sql/parameters.sql @@ -17,3 +17,18 @@ where key like 'rowFilter.products.%'; select key, jsonb_pretty(value) from jsonb_each(postgrest_openapi_spec('{test}')->'components'->'parameters') where key like 'rowFilter.big_products.%'; + +-- shows `IN` function arguments as RPC parameters +select key, jsonb_pretty(value) +from jsonb_each(postgrest_openapi_spec('{test}')->'components'->'parameters') +where key like 'rpcParam.has_in_parameters.%'; + +-- shows `INOUT` function arguments as RPC parameters +select key, jsonb_pretty(value) +from jsonb_each(postgrest_openapi_spec('{test}')->'components'->'parameters') +where key like 'rpcParam.has_inout_parameters.%'; + +-- shows `VARIADIC` function arguments as RPC parameters +select key, jsonb_pretty(value) +from jsonb_each(postgrest_openapi_spec('{test}')->'components'->'parameters') +where key like 'rpcParam.has_variadic_parameter.%'; diff --git a/test/sql/paths.sql b/test/sql/paths.sql index 0bb3a09..eebfeb3 100644 --- a/test/sql/paths.sql +++ b/test/sql/paths.sql @@ -250,3 +250,62 @@ where value->>'$ref' not like '#/components/parameters/rowFilter.big_products.%' -- does not show a DELETE operation object for non auto-updatable views select postgrest_openapi_spec('{test}')->'paths'->'/non_auto_updatable' ? 'delete' as value; + +-- Functions +-- GET operation object + +-- shows the function name as tag +select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/rpc/get_products_by_size'->'get'->'tags'); + +-- uses a reference for the 200 HTTP code response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/rpc/get_products_by_size'->'get'->'responses'->'200'); + +-- uses a reference for the 206 HTTP code response on `RETURNS SET OF` functions +select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/rpc/get_products_by_size'->'get'->'responses'->'206'); + +-- uses a reference for error responses +select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/rpc/get_products_by_size'->'get'->'responses'->'default'); + +-- shows the first line of the comment on the table as summary +select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/rpc/get_products_by_size'->'get'->'summary'); + +-- shows the second line of the comment on the table as description +select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/rpc/get_products_by_size'->'get'->'description'); + +-- uses references for common parameters on `RETURNS ` functions +select value +from jsonb_array_elements(postgrest_openapi_spec('{test}')->'paths'->'/rpc/get_attribute'->'get'->'parameters') +where value->>'$ref' not like '#/components/parameters/rpcParam.get_attribute.%'; + +-- uses references for common parameters on `RETURNS TABLE` functions +select value +from jsonb_array_elements(postgrest_openapi_spec('{test}')->'paths'->'/rpc/returns_table'->'get'->'parameters') +where value->>'$ref' not like '#/components/parameters/rpcParam.returns_table.%'; + +-- uses references for common parameters on functions with INOUT/OUT parameters +select value +from jsonb_array_elements(postgrest_openapi_spec('{test}')->'paths'->'/rpc/returns_inout_out'->'get'->'parameters') +where value->>'$ref' not like '#/components/parameters/rpcParam.returns_inout_out.%'; + +-- does not use a reference for the 206 HTTP code response on functions that do not return `SET OF` +select postgrest_openapi_spec('{test}')->'paths'->'/rpc/get_attribute'->'get'->'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(postgrest_openapi_spec('{test}')->'paths'->'/rpc/returns_simple_type'->'get'->'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(postgrest_openapi_spec('{test}')->'paths'->'/rpc/single_unnamed_json_param'->'get'->'tags'); +select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/rpc/single_unnamed_jsonb_param'->'get'->'tags'); +select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/rpc/single_unnamed_text_param'->'get'->'tags'); +select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/rpc/single_unnamed_xml_param'->'get'->'tags'); + +-- does not show a function with a single unnamed parameter of non-accepted types +select postgrest_openapi_spec('{test}')->'paths' ? '/rpc/single_unnamed_unrecognized_param' as value; + +-- does not show a function with unnamed parameters +select postgrest_openapi_spec('{test}')->'paths' ? '/rpc/unnamed_params' as value; + +-- does not show a function with named and unnamed parameters +select postgrest_openapi_spec('{test}')->'paths' ? '/rpc/named_and_unnamed_params' as value; diff --git a/test/sql/responses.sql b/test/sql/responses.sql index aa032e8..707f8c5 100644 --- a/test/sql/responses.sql +++ b/test/sql/responses.sql @@ -66,3 +66,60 @@ select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'- -- does not define an empty or non-empty response body for non auto-updatable views select postgrest_openapi_spec('{test}')->'components'->'responses' ? 'mayBeEmpty.non_auto_updatable' as value; + +-- Functions +-- Returning composite types + +-- defines an application/json response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.get_attribute'->'content'->'application/json'); + +-- defines an application/vnd.pgrst.object+json response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.get_attribute'->'content'->'application/vnd.pgrst.object+json'); + +-- defines an application/vnd.pgrst.object+json;nulls=stripped response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.get_attribute'->'content'->'application/vnd.pgrst.object+json;nulls=stripped'); + +-- defines a text/csv response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.get_attribute'->'content'->'text/csv'); + +-- Returning SET OF composite types + +-- defines an application/json response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.get_products_by_size'->'content'->'application/json'); + +-- defines an application/vnd.pgrst.object+json response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.get_products_by_size'->'content'->'application/vnd.pgrst.object+json'); + +-- defines an application/vnd.pgrst.object+json;nulls=stripped response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.get_products_by_size'->'content'->'application/vnd.pgrst.object+json;nulls=stripped'); + +-- defines a text/csv response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.get_products_by_size'->'content'->'text/csv'); + +-- Returning non-composite types + +-- defines an application/json response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.returns_inout_out'->'content'->'application/json'); + +-- defines an application/vnd.pgrst.object+json response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.returns_inout_out'->'content'->'application/vnd.pgrst.object+json'); + +-- defines an application/vnd.pgrst.object+json;nulls=stripped response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.returns_inout_out'->'content'->'application/vnd.pgrst.object+json;nulls=stripped'); + +-- defines a text/csv response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.returns_inout_out'->'content'->'text/csv'); + +-- Returning SET OF non-composite types + +-- defines an application/json response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.returns_table'->'content'->'application/json'); + +-- defines an application/vnd.pgrst.object+json response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.returns_table'->'content'->'application/vnd.pgrst.object+json'); + +-- defines an application/vnd.pgrst.object+json;nulls=stripped response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.returns_table'->'content'->'application/vnd.pgrst.object+json;nulls=stripped'); + +-- defines a text/csv response +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'responses'->'rpc.returns_table'->'content'->'text/csv'); diff --git a/test/sql/schemas.sql b/test/sql/schemas.sql index 31c8359..79cda5a 100644 --- a/test/sql/schemas.sql +++ b/test/sql/schemas.sql @@ -50,7 +50,8 @@ select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->' -- Functions --- Types inside arguments +-- Composite types inside arguments + -- detects composite types as objects select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'types.attribute_arg'->'type'); @@ -66,7 +67,7 @@ select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->' -- references a composite type column from an array composite type inside the items property select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'types.attribute_arg'->'properties'->'colors'->'items'); --- Types inside returning values +-- Composite types inside returning values -- detects composite types as objects select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'types.attribute_ret'->'type'); @@ -83,6 +84,65 @@ select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->' -- references a composite type column from an array composite type inside the items property select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'types.attribute_ret'->'properties'->'colors'->'items'); +-- Non-composite return values + +-- detects a function that returns a simple type +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_simple_type'); + +-- detects a function that returns a simple array type +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_simple_type_arr'); + +-- detects a function that returns table +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_table'); + +-- detects a function that returns table with array columns +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_table_arr'); + +-- detects a function that returns a record with inout arguments +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_inout'); + +-- detects a function that returns a record with inout array arguments +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_inout_arr'); + +-- detects a function that returns a record with out arguments +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_out'); + +-- detects a function that returns a record with out array arguments +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_out_arr'); + +-- detects a function that returns a record with inout and out arguments +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_inout_out'); + +-- detects a function that returns a record with inout and out array arguments +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_inout_out_arr'); + +-- detects a function that returns an unknown record as a free-form object +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.returns_unknown_record'); + +-- detects a function with a single unnamed json parameter +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.single_unnamed_json_param'); + +-- detects a function with a single unnamed jsonb parameter +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.single_unnamed_jsonb_param'); + +-- detects a function with a single unnamed text parameter +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.single_unnamed_text_param'); + +-- detects a function with a single unnamed xml parameter +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.single_unnamed_xml_param'); + +-- ignores a function with a single unnamed unrecognized parameter +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.single_unnamed_unrecognized_param'); + +-- ignores a function with unnamed parameters +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.single_unnamed_unrecognized_param'); + +-- ignores a function with named and unnamed parameters +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.single_unnamed_unrecognized_param'); + +-- ignores a function outside of the exposed schemas +select jsonb_pretty(postgrest_openapi_spec('{test}')->'components'->'schemas'->'rpc.private.secret_function'); + -- Common -- defines all the available prefer headers