Skip to content

Commit c0c1352

Browse files
authored
Adds PATCH requests for tables and views
1 parent c1e4572 commit c0c1352

File tree

7 files changed

+337
-92
lines changed

7 files changed

+337
-92
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ SQL functions to build the OpenAPI output of a PostgREST instance.
1515
- [ ] Tables and Views
1616
- [x] GET
1717
- [x] POST
18-
- [ ] PUT
18+
- [x] PATCH
1919
- [ ] DELETE
2020
- [ ] Functions
2121
- [ ] GET

sql/components.sql

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -535,12 +535,12 @@ $$;
535535
create or replace function oas_build_response_objects_from_tables(schemas text[])
536536
returns jsonb language sql as
537537
$$
538-
select jsonb_object_agg(x.get, x.get_response) ||
539-
jsonb_object_agg(x.post, x.post_response)
538+
select jsonb_object_agg(x.not_empty, x.not_empty_response) ||
539+
jsonb_object_agg(x.may_be_empty, x.may_be_empty_response)
540540
from (
541-
select 'get.' || table_name as get,
541+
select 'notEmpty.' || table_name as not_empty,
542542
oas_response_object(
543-
description := 'GET media types for ' || table_name,
543+
description := 'Media types when response body is not empty for ' || table_name,
544544
content := jsonb_build_object(
545545
'application/json',
546546
oas_media_type_object(
@@ -565,11 +565,11 @@ from (
565565
)
566566
)
567567
)
568-
) as get_response,
569-
'post.' || table_name as post,
568+
) as not_empty_response,
569+
'mayBeEmpty.' || table_name as may_be_empty,
570570
case when insertable then
571571
oas_response_object(
572-
description := 'POST media types for ' || table_name,
572+
description := 'Media types when response body could be empty or not for ' || table_name,
573573
content := jsonb_build_object(
574574
'application/json',
575575
oas_media_type_object(
@@ -616,7 +616,7 @@ from (
616616
)
617617
)
618618
)
619-
end as post_response
619+
end as may_be_empty_response
620620
from postgrest_get_all_tables(schemas)
621621
where table_schema = any(schemas)
622622
) as x
@@ -643,6 +643,11 @@ select jsonb_build_object(
643643
)
644644
)
645645
)
646+
),
647+
'empty',
648+
oas_response_object(
649+
description := 'No media types when response body is empty'
650+
-- Does not specify a "content": https://swagger.io/docs/specification/describing-responses/#empty
646651
)
647652
);
648653
$$;

sql/paths.sql

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ from (
3434
),
3535
responses := jsonb_build_object(
3636
'200',
37-
oas_build_reference_to_responses('get.' || table_name, 'OK'),
37+
oas_build_reference_to_responses('notEmpty.' || table_name, 'OK'),
3838
'206',
39-
oas_build_reference_to_responses('get.' || table_name, 'Partial Content'),
39+
oas_build_reference_to_responses('notEmpty.' || table_name, 'Partial Content'),
4040
'default',
4141
oas_build_reference_to_responses('defaultError', 'Error')
4242
)
@@ -54,19 +54,49 @@ from (
5454
),
5555
responses := jsonb_build_object(
5656
'201',
57-
oas_build_reference_to_responses('post.' || table_name, 'Created'),
57+
oas_build_reference_to_responses('mayBeEmpty.' || table_name, 'Created'),
58+
'default',
59+
oas_build_reference_to_responses('defaultError', 'Error')
60+
)
61+
)
62+
end,
63+
patch :=
64+
case when updatable then
65+
oas_operation_object(
66+
description := table_description,
67+
tags := array[table_name],
68+
requestBody := oas_build_reference_to_request_bodies(table_name),
69+
parameters := jsonb_agg(
70+
oas_build_reference_to_parameters(format('rowFilter.%1$s.%2$s', table_name, column_name))
71+
) ||
72+
jsonb_build_array(
73+
oas_build_reference_to_parameters('select'),
74+
oas_build_reference_to_parameters('columns'),
75+
oas_build_reference_to_parameters('order'),
76+
oas_build_reference_to_parameters('limit'),
77+
oas_build_reference_to_parameters('or'),
78+
oas_build_reference_to_parameters('and'),
79+
oas_build_reference_to_parameters('not.or'),
80+
oas_build_reference_to_parameters('not.and'),
81+
oas_build_reference_to_parameters('preferPatch')
82+
),
83+
responses := jsonb_build_object(
84+
'200',
85+
oas_build_reference_to_responses('notEmpty.' || table_name, 'OK'),
86+
'204',
87+
oas_build_reference_to_responses('empty', 'No Content'),
5888
'default',
5989
oas_build_reference_to_responses('defaultError', 'Error')
6090
)
6191
)
6292
end
6393
) as oas_path_item
6494
from (
65-
select table_schema, table_name, table_description, insertable, unnest(all_cols) as column_name
95+
select table_schema, table_name, table_description, insertable, updatable, unnest(all_cols) as column_name
6696
from postgrest_get_all_tables(schemas)
6797
) _
6898
where table_schema = any(schemas)
69-
group by table_schema, table_name, table_description, insertable
99+
group by table_schema, table_name, table_description, insertable, updatable
70100
) x;
71101
$$;
72102

test/expected/paths.out

Lines changed: 177 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,21 @@ select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/products'->'get
4343

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

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

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

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

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

154+
-- PATCH operation object
155+
-- shows the table name as tag
156+
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/products'->'patch'->'tags');
157+
jsonb_pretty
158+
----------------
159+
[ +
160+
"products"+
161+
]
162+
(1 row)
163+
164+
-- uses a reference for the 200 HTTP code response
165+
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/products'->'patch'->'responses'->'200');
166+
jsonb_pretty
167+
---------------------------------------------------------
168+
{ +
169+
"$ref": "#/components/responses/notEmpty.products",+
170+
"description": "OK" +
171+
}
172+
(1 row)
173+
174+
-- uses a reference for the 204 HTTP code response
175+
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/products'->'patch'->'responses'->'204');
176+
jsonb_pretty
177+
---------------------------------------------
178+
{ +
179+
"$ref": "#/components/responses/empty",+
180+
"description": "No Content" +
181+
}
182+
(1 row)
183+
184+
-- uses a reference for error responses
185+
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/products'->'patch'->'responses'->'default');
186+
jsonb_pretty
187+
----------------------------------------------------
188+
{ +
189+
"$ref": "#/components/responses/defaultError",+
190+
"description": "Error" +
191+
}
192+
(1 row)
193+
194+
-- uses references for columns as query parameters
195+
select value
196+
from jsonb_array_elements(postgrest_openapi_spec('{test}')->'paths'->'/products'->'patch'->'parameters')
197+
where value->>'$ref' like '#/components/parameters/rowFilter.products.%';
198+
value
199+
--------------------------------------------------------------------
200+
{"$ref": "#/components/parameters/rowFilter.products.id"}
201+
{"$ref": "#/components/parameters/rowFilter.products.code"}
202+
{"$ref": "#/components/parameters/rowFilter.products.name"}
203+
{"$ref": "#/components/parameters/rowFilter.products.description"}
204+
{"$ref": "#/components/parameters/rowFilter.products.attr"}
205+
{"$ref": "#/components/parameters/rowFilter.products.size"}
206+
(6 rows)
207+
208+
-- uses references for common parameters
209+
select value
210+
from jsonb_array_elements(postgrest_openapi_spec('{test}')->'paths'->'/products'->'patch'->'parameters')
211+
where value->>'$ref' not like '#/components/parameters/rowFilter.products.%';
212+
value
213+
-------------------------------------------------
214+
{"$ref": "#/components/parameters/select"}
215+
{"$ref": "#/components/parameters/columns"}
216+
{"$ref": "#/components/parameters/order"}
217+
{"$ref": "#/components/parameters/limit"}
218+
{"$ref": "#/components/parameters/or"}
219+
{"$ref": "#/components/parameters/and"}
220+
{"$ref": "#/components/parameters/not.or"}
221+
{"$ref": "#/components/parameters/not.and"}
222+
{"$ref": "#/components/parameters/preferPatch"}
223+
(9 rows)
224+
154225
-- Views
155226
-- GET operation object
156227
-- shows the table name as tag
@@ -165,21 +236,21 @@ select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->
165236

166237
-- uses a reference for the 200 HTTP code response
167238
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->'get'->'responses'->'200');
168-
jsonb_pretty
169-
--------------------------------------------------------
170-
{ +
171-
"$ref": "#/components/responses/get.big_products",+
172-
"description": "OK" +
239+
jsonb_pretty
240+
-------------------------------------------------------------
241+
{ +
242+
"$ref": "#/components/responses/notEmpty.big_products",+
243+
"description": "OK" +
173244
}
174245
(1 row)
175246

176247
-- uses a reference for the 206 HTTP code response
177248
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->'get'->'responses'->'206');
178-
jsonb_pretty
179-
--------------------------------------------------------
180-
{ +
181-
"$ref": "#/components/responses/get.big_products",+
182-
"description": "Partial Content" +
249+
jsonb_pretty
250+
-------------------------------------------------------------
251+
{ +
252+
"$ref": "#/components/responses/notEmpty.big_products",+
253+
"description": "Partial Content" +
183254
}
184255
(1 row)
185256

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

243314
-- uses a reference for the 201 HTTP code response
244315
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->'post'->'responses'->'201');
245-
jsonb_pretty
246-
---------------------------------------------------------
247-
{ +
248-
"$ref": "#/components/responses/post.big_products",+
249-
"description": "Created" +
316+
jsonb_pretty
317+
---------------------------------------------------------------
318+
{ +
319+
"$ref": "#/components/responses/mayBeEmpty.big_products",+
320+
"description": "Created" +
250321
}
251322
(1 row)
252323

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

359+
-- PATCH operation object
360+
-- shows the table name as tag
361+
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->'patch'->'tags');
362+
jsonb_pretty
363+
--------------------
364+
[ +
365+
"big_products"+
366+
]
367+
(1 row)
368+
369+
-- uses a reference for the 200 HTTP code response
370+
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->'patch'->'responses'->'200');
371+
jsonb_pretty
372+
-------------------------------------------------------------
373+
{ +
374+
"$ref": "#/components/responses/notEmpty.big_products",+
375+
"description": "OK" +
376+
}
377+
(1 row)
378+
379+
-- uses a reference for the 204 HTTP code response
380+
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->'patch'->'responses'->'204');
381+
jsonb_pretty
382+
---------------------------------------------
383+
{ +
384+
"$ref": "#/components/responses/empty",+
385+
"description": "No Content" +
386+
}
387+
(1 row)
388+
389+
-- uses a reference for error responses
390+
select jsonb_pretty(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->'patch'->'responses'->'default');
391+
jsonb_pretty
392+
----------------------------------------------------
393+
{ +
394+
"$ref": "#/components/responses/defaultError",+
395+
"description": "Error" +
396+
}
397+
(1 row)
398+
399+
-- uses references for columns as query parameters
400+
select value
401+
from jsonb_array_elements(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->'patch'->'parameters')
402+
where value->>'$ref' like '#/components/parameters/rowFilter.big_products.%';
403+
value
404+
-----------------------------------------------------------------
405+
{"$ref": "#/components/parameters/rowFilter.big_products.id"}
406+
{"$ref": "#/components/parameters/rowFilter.big_products.code"}
407+
{"$ref": "#/components/parameters/rowFilter.big_products.name"}
408+
{"$ref": "#/components/parameters/rowFilter.big_products.size"}
409+
(4 rows)
410+
411+
-- uses references for common parameters
412+
select value
413+
from jsonb_array_elements(postgrest_openapi_spec('{test}')->'paths'->'/big_products'->'patch'->'parameters')
414+
where value->>'$ref' not like '#/components/parameters/rowFilter.big_products.%';
415+
value
416+
-------------------------------------------------
417+
{"$ref": "#/components/parameters/select"}
418+
{"$ref": "#/components/parameters/columns"}
419+
{"$ref": "#/components/parameters/order"}
420+
{"$ref": "#/components/parameters/limit"}
421+
{"$ref": "#/components/parameters/or"}
422+
{"$ref": "#/components/parameters/and"}
423+
{"$ref": "#/components/parameters/not.or"}
424+
{"$ref": "#/components/parameters/not.and"}
425+
{"$ref": "#/components/parameters/preferPatch"}
426+
(9 rows)
427+
428+
-- does not show a PATCH operation object for non auto-updatable views
429+
select postgrest_openapi_spec('{test}')->'paths'->'/non_auto_updatable' ? 'patch' as value;
430+
value
431+
-------
432+
f
433+
(1 row)
434+

0 commit comments

Comments
 (0)