Skip to content

Commit

Permalink
Adds PATCH requests for tables and views
Browse files Browse the repository at this point in the history
  • Loading branch information
laurenceisla authored Jan 31, 2024
1 parent c1e4572 commit c0c1352
Show file tree
Hide file tree
Showing 7 changed files with 337 additions and 92 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ SQL functions to build the OpenAPI output of a PostgREST instance.
- [ ] Tables and Views
- [x] GET
- [x] POST
- [ ] PUT
- [x] PATCH
- [ ] DELETE
- [ ] Functions
- [ ] GET
Expand Down
21 changes: 13 additions & 8 deletions sql/components.sql
Original file line number Diff line number Diff line change
Expand Up @@ -535,12 +535,12 @@ $$;
create or replace function oas_build_response_objects_from_tables(schemas text[])
returns jsonb language sql as
$$
select jsonb_object_agg(x.get, x.get_response) ||
jsonb_object_agg(x.post, x.post_response)
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 'get.' || table_name as get,
select 'notEmpty.' || table_name as not_empty,
oas_response_object(
description := 'GET media types for ' || table_name,
description := 'Media types when response body is not empty for ' || table_name,
content := jsonb_build_object(
'application/json',
oas_media_type_object(
Expand All @@ -565,11 +565,11 @@ from (
)
)
)
) as get_response,
'post.' || table_name as post,
) as not_empty_response,
'mayBeEmpty.' || table_name as may_be_empty,
case when insertable then
oas_response_object(
description := 'POST media types for ' || table_name,
description := 'Media types when response body could be empty or not for ' || table_name,
content := jsonb_build_object(
'application/json',
oas_media_type_object(
Expand Down Expand Up @@ -616,7 +616,7 @@ from (
)
)
)
end as post_response
end as may_be_empty_response
from postgrest_get_all_tables(schemas)
where table_schema = any(schemas)
) as x
Expand All @@ -643,6 +643,11 @@ select jsonb_build_object(
)
)
)
),
'empty',
oas_response_object(
description := 'No media types when response body is empty'
-- Does not specify a "content": https://swagger.io/docs/specification/describing-responses/#empty
)
);
$$;
Expand Down
40 changes: 35 additions & 5 deletions sql/paths.sql
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ from (
),
responses := jsonb_build_object(
'200',
oas_build_reference_to_responses('get.' || table_name, 'OK'),
oas_build_reference_to_responses('notEmpty.' || table_name, 'OK'),
'206',
oas_build_reference_to_responses('get.' || table_name, 'Partial Content'),
oas_build_reference_to_responses('notEmpty.' || table_name, 'Partial Content'),
'default',
oas_build_reference_to_responses('defaultError', 'Error')
)
Expand All @@ -54,19 +54,49 @@ from (
),
responses := jsonb_build_object(
'201',
oas_build_reference_to_responses('post.' || table_name, 'Created'),
oas_build_reference_to_responses('mayBeEmpty.' || table_name, 'Created'),
'default',
oas_build_reference_to_responses('defaultError', 'Error')
)
)
end,
patch :=
case when updatable then
oas_operation_object(
description := table_description,
tags := array[table_name],
requestBody := oas_build_reference_to_request_bodies(table_name),
parameters := jsonb_agg(
oas_build_reference_to_parameters(format('rowFilter.%1$s.%2$s', table_name, column_name))
) ||
jsonb_build_array(
oas_build_reference_to_parameters('select'),
oas_build_reference_to_parameters('columns'),
oas_build_reference_to_parameters('order'),
oas_build_reference_to_parameters('limit'),
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('preferPatch')
),
responses := jsonb_build_object(
'200',
oas_build_reference_to_responses('notEmpty.' || table_name, 'OK'),
'204',
oas_build_reference_to_responses('empty', 'No Content'),
'default',
oas_build_reference_to_responses('defaultError', 'Error')
)
)
end
) as oas_path_item
from (
select table_schema, table_name, table_description, insertable, unnest(all_cols) as column_name
select table_schema, table_name, table_description, insertable, updatable, unnest(all_cols) as column_name
from postgrest_get_all_tables(schemas)
) _
where table_schema = any(schemas)
group by table_schema, table_name, table_description, insertable
group by table_schema, table_name, table_description, insertable, updatable
) x;
$$;

Expand Down
207 changes: 177 additions & 30 deletions test/expected/paths.out
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,21 @@ select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/products'->'get

-- uses a reference for the 200 HTTP code response
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/products'->'get'->'responses'->'200');
jsonb_pretty
----------------------------------------------------
{ +
"$ref": "#/components/responses/get.products",+
"description": "OK" +
jsonb_pretty
---------------------------------------------------------
{ +
"$ref": "#/components/responses/notEmpty.products",+
"description": "OK" +
}
(1 row)

-- uses a reference for the 206 HTTP code response
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/products'->'get'->'responses'->'206');
jsonb_pretty
----------------------------------------------------
{ +
"$ref": "#/components/responses/get.products",+
"description": "Partial Content" +
jsonb_pretty
---------------------------------------------------------
{ +
"$ref": "#/components/responses/notEmpty.products",+
"description": "Partial Content" +
}
(1 row)

Expand Down Expand Up @@ -115,11 +115,11 @@ select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/products'->'pos

-- uses a reference for the 201 HTTP code response
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/products'->'post'->'responses'->'201');
jsonb_pretty
-----------------------------------------------------
{ +
"$ref": "#/components/responses/post.products",+
"description": "Created" +
jsonb_pretty
-----------------------------------------------------------
{ +
"$ref": "#/components/responses/mayBeEmpty.products",+
"description": "Created" +
}
(1 row)

Expand Down Expand Up @@ -151,6 +151,77 @@ where value->>'$ref' like '#/components/parameters/%';
{"$ref": "#/components/parameters/preferPost"}
(3 rows)

-- PATCH operation object
-- shows the table name as tag
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/products'->'patch'->'tags');
jsonb_pretty
----------------
[ +
"products"+
]
(1 row)

-- uses a reference for the 200 HTTP code response
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/products'->'patch'->'responses'->'200');
jsonb_pretty
---------------------------------------------------------
{ +
"$ref": "#/components/responses/notEmpty.products",+
"description": "OK" +
}
(1 row)

-- uses a reference for the 204 HTTP code response
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/products'->'patch'->'responses'->'204');
jsonb_pretty
---------------------------------------------
{ +
"$ref": "#/components/responses/empty",+
"description": "No Content" +
}
(1 row)

-- uses a reference for error responses
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/products'->'patch'->'responses'->'default');
jsonb_pretty
----------------------------------------------------
{ +
"$ref": "#/components/responses/defaultError",+
"description": "Error" +
}
(1 row)

-- uses references for columns as query parameters
select value
from jsonb_array_elements(postgrest_openapi_spec('{test}')->'paths'->'/products'->'patch'->'parameters')
where value->>'$ref' like '#/components/parameters/rowFilter.products.%';
value
--------------------------------------------------------------------
{"$ref": "#/components/parameters/rowFilter.products.id"}
{"$ref": "#/components/parameters/rowFilter.products.code"}
{"$ref": "#/components/parameters/rowFilter.products.name"}
{"$ref": "#/components/parameters/rowFilter.products.description"}
{"$ref": "#/components/parameters/rowFilter.products.attr"}
{"$ref": "#/components/parameters/rowFilter.products.size"}
(6 rows)

-- uses references for common parameters
select value
from jsonb_array_elements(postgrest_openapi_spec('{test}')->'paths'->'/products'->'patch'->'parameters')
where value->>'$ref' not like '#/components/parameters/rowFilter.products.%';
value
-------------------------------------------------
{"$ref": "#/components/parameters/select"}
{"$ref": "#/components/parameters/columns"}
{"$ref": "#/components/parameters/order"}
{"$ref": "#/components/parameters/limit"}
{"$ref": "#/components/parameters/or"}
{"$ref": "#/components/parameters/and"}
{"$ref": "#/components/parameters/not.or"}
{"$ref": "#/components/parameters/not.and"}
{"$ref": "#/components/parameters/preferPatch"}
(9 rows)

-- Views
-- GET operation object
-- shows the table name as tag
Expand All @@ -165,21 +236,21 @@ select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->

-- uses a reference for the 200 HTTP code response
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->'get'->'responses'->'200');
jsonb_pretty
--------------------------------------------------------
{ +
"$ref": "#/components/responses/get.big_products",+
"description": "OK" +
jsonb_pretty
-------------------------------------------------------------
{ +
"$ref": "#/components/responses/notEmpty.big_products",+
"description": "OK" +
}
(1 row)

-- uses a reference for the 206 HTTP code response
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->'get'->'responses'->'206');
jsonb_pretty
--------------------------------------------------------
{ +
"$ref": "#/components/responses/get.big_products",+
"description": "Partial Content" +
jsonb_pretty
-------------------------------------------------------------
{ +
"$ref": "#/components/responses/notEmpty.big_products",+
"description": "Partial Content" +
}
(1 row)

Expand Down Expand Up @@ -242,11 +313,11 @@ select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->

-- uses a reference for the 201 HTTP code response
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->'post'->'responses'->'201');
jsonb_pretty
---------------------------------------------------------
{ +
"$ref": "#/components/responses/post.big_products",+
"description": "Created" +
jsonb_pretty
---------------------------------------------------------------
{ +
"$ref": "#/components/responses/mayBeEmpty.big_products",+
"description": "Created" +
}
(1 row)

Expand Down Expand Up @@ -285,3 +356,79 @@ select postgrest_openapi_spec('{test}')->'paths'->'/non_auto_updatable' ? 'post'
f
(1 row)

-- PATCH operation object
-- shows the table name as tag
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->'patch'->'tags');
jsonb_pretty
--------------------
[ +
"big_products"+
]
(1 row)

-- uses a reference for the 200 HTTP code response
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->'patch'->'responses'->'200');
jsonb_pretty
-------------------------------------------------------------
{ +
"$ref": "#/components/responses/notEmpty.big_products",+
"description": "OK" +
}
(1 row)

-- uses a reference for the 204 HTTP code response
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->'patch'->'responses'->'204');
jsonb_pretty
---------------------------------------------
{ +
"$ref": "#/components/responses/empty",+
"description": "No Content" +
}
(1 row)

-- uses a reference for error responses
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->'patch'->'responses'->'default');
jsonb_pretty
----------------------------------------------------
{ +
"$ref": "#/components/responses/defaultError",+
"description": "Error" +
}
(1 row)

-- uses references for columns as query parameters
select value
from jsonb_array_elements(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->'patch'->'parameters')
where value->>'$ref' like '#/components/parameters/rowFilter.big_products.%';
value
-----------------------------------------------------------------
{"$ref": "#/components/parameters/rowFilter.big_products.id"}
{"$ref": "#/components/parameters/rowFilter.big_products.code"}
{"$ref": "#/components/parameters/rowFilter.big_products.name"}
{"$ref": "#/components/parameters/rowFilter.big_products.size"}
(4 rows)

-- uses references for common parameters
select value
from jsonb_array_elements(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->'patch'->'parameters')
where value->>'$ref' not like '#/components/parameters/rowFilter.big_products.%';
value
-------------------------------------------------
{"$ref": "#/components/parameters/select"}
{"$ref": "#/components/parameters/columns"}
{"$ref": "#/components/parameters/order"}
{"$ref": "#/components/parameters/limit"}
{"$ref": "#/components/parameters/or"}
{"$ref": "#/components/parameters/and"}
{"$ref": "#/components/parameters/not.or"}
{"$ref": "#/components/parameters/not.and"}
{"$ref": "#/components/parameters/preferPatch"}
(9 rows)

-- does not show a PATCH operation object for non auto-updatable views
select postgrest_openapi_spec('{test}')->'paths'->'/non_auto_updatable' ? 'patch' as value;
value
-------
f
(1 row)

Loading

0 comments on commit c0c1352

Please sign in to comment.