diff --git a/sql/components.sql b/sql/components.sql index 7c34279..a3a545a 100644 --- a/sql/components.sql +++ b/sql/components.sql @@ -16,7 +16,8 @@ create or replace function oas_build_component_schemas(schemas text[]) returns jsonb language sql as $$ select oas_build_component_schemas_from_tables(schemas) || - oas_build_component_schemas_from_composite_types(schemas) + oas_build_component_schemas_from_composite_types(schemas) || + oas_build_component_schemas_headers() $$; create or replace function oas_build_component_schemas_from_tables(schemas text[]) @@ -51,6 +52,123 @@ FROM ( ) x; $$; +create or replace function oas_build_component_schemas_headers() +returns jsonb language sql as +$$ +select jsonb_build_object( + 'header.preferParams', + oas_schema_object( + type := 'object', + properties := jsonb_build_object( + 'params', + oas_schema_object( + description := 'Send JSON as a single parameter', + type := 'string', + "enum" := '["single-object"]', + deprecated := true + ) + ) + ), + 'header.preferReturn', + oas_schema_object( + type:= 'object', + properties := jsonb_build_object( + 'return', + oas_schema_object( + description := 'Return information of the affected resource', + type := 'string', + "enum" := '["minimal", "headers-only", "representation"]', + "default" := 'minimal' + ) + ) + ), + 'header.preferCount', + oas_schema_object( + type:= 'object', + properties := jsonb_build_object( + 'count', + oas_schema_object( + description := 'Get the total size of the table', + type := 'string', + "enum" := '["exact", "planned", "estimated"]' + ) + ) + ), + 'header.preferResolution', + oas_schema_object( + type:= 'object', + properties := jsonb_build_object( + 'resolution', + oas_schema_object( + description := 'Handle duplicates in an upsert', + type := 'string', + "enum" := '["merge-duplicates", "ignore-duplicates"]' + ) + ) + ), + 'header.preferTx', + oas_schema_object( + type:= 'object', + properties := jsonb_build_object( + 'tx', + oas_schema_object( + description := 'Specify how to end a transaction', + type := 'string', + "enum" := '["commit", "rollback"]' + ) + ) + ), + 'header.preferMissing', + oas_schema_object( + type:= 'object', + properties := jsonb_build_object( + 'missing', + oas_schema_object( + description := 'Handle null values in bulk inserts', + type := 'string', + "enum" := '["default", "null"]', + "default" := 'null' + ) + ) + ), + 'header.preferHandling', + oas_schema_object( + type:= 'object', + properties := jsonb_build_object( + 'handling', + oas_schema_object( + description := 'How to handle invalid preferences', + type := 'string', + "enum" := '["strict", "lenient"]', + "default" := 'lenient' + ) + ) + ), + 'header.preferTimezone', + oas_schema_object( + type:= 'object', + properties := jsonb_build_object( + 'timezone', + oas_schema_object( + description := 'Specify the time zone', + type := 'string' + ) + ) + ), + 'header.preferMaxAffected', + oas_schema_object( + type:= 'object', + properties := jsonb_build_object( + 'max-affected', + oas_schema_object( + description := 'Specify the amount of resources affected', + type := 'integer' + ) + ) + ) +) +$$; + -- Parameters create or replace function oas_build_component_parameters() @@ -165,124 +283,220 @@ $$; create or replace function oas_build_component_parameters_headers () returns jsonb language sql as $$ -select jsonb_object_agg(name, param_object) from unnest( - array['preferParams', 'preferReturn', 'preferCount', 'preferResolution', 'preferTransaction', 'preferMissing', 'preferHandling', 'preferTimezone', 'preferMaxAffected', 'range'], - array[ - oas_parameter_object( - name := 'Prefer', - "in" := 'header', - description := 'Send JSON as a single parameter', - schema := oas_schema_object( - type := 'string', - "enum" := jsonb_build_array( - 'params=single-object' - ) +select jsonb_build_object( + 'preferGet', + oas_parameter_object( + name := 'Prefer', + "in" := 'header', + explode := true, + description := 'Specify a required or optional behavior for the request', + "schema" := oas_schema_object( + allOf := jsonb_build_array( + oas_reference_object('header.preferHandling'), + oas_reference_object('header.preferTimezone'), + oas_reference_object('header.preferCount') ) ), - oas_parameter_object( - name := 'Prefer', - "in" := 'header', - description := 'Return information of the affected resource', - schema := oas_schema_object( - type := 'string', - "enum" := jsonb_build_array( - 'return=minimal', - 'return=headers-only', - 'return=representation' - ) + examples := jsonb_build_object( + 'nothing', + oas_example_object( + summary := 'No preferences' + ), + 'all', + oas_example_object( + summary := 'All default preferences', + value := '{ + "handling":"lenient", + "timezone": "", + "count": "" + }' ) - ), - oas_parameter_object( - name := 'Prefer', - "in" := 'header', - description := 'Get the total size of the table', - schema := oas_schema_object( - type := 'string', - "enum" := jsonb_build_array( - 'count=exact', - 'count=planned', - 'count=estimated' - ) + ) + ), + 'preferPost', + oas_parameter_object( + name := 'Prefer', + "in" := 'header', + explode := true, + description := 'Specify a required or optional behavior for the request', + "schema" := oas_schema_object( + allOf := jsonb_build_array( + oas_reference_object('header.preferHandling'), + oas_reference_object('header.preferTimezone'), + oas_reference_object('header.preferReturn'), + oas_reference_object('header.preferCount'), + oas_reference_object('header.preferResolution'), + oas_reference_object('header.preferMissing'), + oas_reference_object('header.preferTx') ) ), - oas_parameter_object( - name := 'Prefer', - "in" := 'header', - description := 'Handle duplicates in an upsert', - schema := oas_schema_object( - type := 'string', - "enum" := jsonb_build_array( - 'resolution=merge-duplicates', - 'resolution=ignore-duplicates' - ) + examples := jsonb_build_object( + 'nothing', + oas_example_object( + summary := 'No preferences' + ), + 'all', + oas_example_object( + summary := 'All default preferences', + value := '{ + "handling": "lenient", + "timezone": "", + "return": "minimal", + "count": "", + "resolution": "", + "missing": "null", + "tx": "commit" + }' ) - ), - oas_parameter_object( - name := 'Prefer', - "in" := 'header', - description := 'Specify how to end a transaction', - schema := oas_schema_object( - type := 'string', - "enum" := jsonb_build_array( - 'tx=commit', - 'tx=rollback' - ) + ) + ), + 'preferPatch', + oas_parameter_object( + name := 'Prefer', + "in" := 'header', + explode := true, + description := 'Specify a required or optional behavior for the request', + "schema" := oas_schema_object( + allOf := jsonb_build_array( + oas_reference_object('header.preferHandling'), + oas_reference_object('header.preferTimezone'), + oas_reference_object('header.preferReturn'), + oas_reference_object('header.preferCount'), + oas_reference_object('header.preferTx'), + oas_reference_object('header.preferMaxAffected') ) ), - oas_parameter_object( - name := 'Prefer', - "in" := 'header', - description := 'Handle null values in bulk inserts', - schema := oas_schema_object( - type := 'string', - "enum" := jsonb_build_array( - 'missing=default', - 'missing=null' - ) + examples := jsonb_build_object( + 'nothing', + oas_example_object( + summary := 'No preferences' + ), + 'all', + oas_example_object( + summary := 'All default preferences', + value := '{ + "handling": "lenient", + "timezone": "", + "return": "minimal", + "count": "", + "max-affected": "", + "tx": "commit" + }' ) - ), - oas_parameter_object( - name := 'Prefer', - "in" := 'header', - description := 'Handle invalid preferences', - schema := oas_schema_object( - type := 'string', - "enum" := jsonb_build_array( - 'handling=strict', - 'handling=lenient' - ) + ) + ), + 'preferPut', + oas_parameter_object( + name := 'Prefer', + "in" := 'header', + explode := true, + description := 'Specify a required or optional behavior for the request', + "schema" := oas_schema_object( + allOf := jsonb_build_array( + oas_reference_object('header.preferHandling'), + oas_reference_object('header.preferTimezone'), + oas_reference_object('header.preferReturn'), + oas_reference_object('header.preferCount'), + oas_reference_object('header.preferTx') ) ), - oas_parameter_object( - name := 'Prefer', - "in" := 'header', - description := 'Specify the time zone', - example := '"timezone=UTC"', - schema := oas_schema_object( - -- The time zones can be queried, but there are ~500 of them. It could slow down the UIs (unverified). - type := 'string' + examples := jsonb_build_object( + 'nothing', + oas_example_object( + summary := 'No preferences' + ), + 'all', + oas_example_object( + summary := 'All default preferences', + value := '{ + "handling": "lenient", + "timezone": "", + "return": "minimal", + "count": "", + "tx": "commit" + }' + ) + ) + ), + 'preferDelete', + oas_parameter_object( + name := 'Prefer', + "in" := 'header', + explode := true, + description := 'Specify a required or optional behavior for the request', + "schema" := oas_schema_object( + allOf := jsonb_build_array( + oas_reference_object('header.preferHandling'), + oas_reference_object('header.preferTimezone'), + oas_reference_object('header.preferReturn'), + oas_reference_object('header.preferCount'), + oas_reference_object('header.preferTx'), + oas_reference_object('header.preferMaxAffected') ) ), - oas_parameter_object( - name := 'Prefer', - "in" := 'header', - description := 'Limit the number of affected resources', - example := '"max-affected=5"', - schema := oas_schema_object( - type := 'string' + examples := jsonb_build_object( + 'nothing', + oas_example_object( + summary := 'No preferences' + ), + 'all', + oas_example_object( + summary := 'All default preferences', + value := '{ + "handling": "lenient", + "timezone": "", + "return": "minimal", + "count": "", + "max-affected": "", + "tx": "commit" + }' + ) + ) + ), + 'preferPostRpc', + oas_parameter_object( + name := 'Prefer', + "in" := 'header', + explode := true, + description := 'Specify a required or optional behavior for the request', + "schema" := oas_schema_object( + allOf := jsonb_build_array( + oas_reference_object('header.preferHandling'), + oas_reference_object('header.preferTimezone'), + oas_reference_object('header.preferCount'), + oas_reference_object('header.preferTx'), + oas_reference_object('header.preferParams') ) ), - oas_parameter_object( - name := 'Range', - "in" := 'header', - description := 'For limits and pagination', - example := '"5-10"', - schema := oas_schema_object( - type := 'string' + examples := jsonb_build_object( + 'nothing', + oas_example_object( + summary := 'No preferences' + ), + 'all', + oas_example_object( + summary := 'All default preferences', + value := '{ + "handling": "lenient", + "timezone": "", + "count": "", + "tx": "commit", + "params": "" + }' ) ) - ] -) as _(name, param_object); + ), + 'range', + oas_parameter_object( + name := 'Range', + "in" := 'header', + description := 'For limits and pagination', + example := '"5-10"', + "schema" := oas_schema_object( + type := 'string' + ) + ) +); $$; -- Security Schemes diff --git a/sql/openapi.sql b/sql/openapi.sql index 007f2c2..d8963d3 100644 --- a/sql/openapi.sql +++ b/sql/openapi.sql @@ -243,3 +243,19 @@ $$ 'openIdConnectUrl', openIdConnectUrl ); $$; + +create or replace function oas_example_object( + summary text default null, + description text default null, + value jsonb default null, + externalValue text default null +) +returns jsonb language sql as +$$ + select jsonb_build_object( + 'summary', summary, + 'description', description, + 'value', value, + 'externalValue', externalValue + ); +$$; diff --git a/test/expected/parameters.out b/test/expected/parameters.out index b6cbea1..2314848 100644 --- a/test/expected/parameters.out +++ b/test/expected/parameters.out @@ -17,20 +17,267 @@ where key in ('select', 'order', 'limit', 'offset', 'on_conflict', 'columns', 'o (10 rows) -- shows common headers -select * +select key, jsonb_pretty(value) from jsonb_each(get_postgrest_openapi_spec('{public}')->'components'->'parameters') -where key in ('preferParams', 'preferReturn', 'preferCount', 'preferResolution', 'preferTransaction', 'preferMissing', 'preferHandling', 'preferTimezone', 'preferMaxAffected', 'range'); - key | value --------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - range | {"in": "header", "name": "Range", "schema": {"type": "string"}, "example": "5-10", "description": "For limits and pagination"} - preferCount | {"in": "header", "name": "Prefer", "schema": {"enum": ["count=exact", "count=planned", "count=estimated"], "type": "string"}, "description": "Get the total size of the table"} - preferParams | {"in": "header", "name": "Prefer", "schema": {"enum": ["params=single-object"], "type": "string"}, "description": "Send JSON as a single parameter"} - preferReturn | {"in": "header", "name": "Prefer", "schema": {"enum": ["return=minimal", "return=headers-only", "return=representation"], "type": "string"}, "description": "Return information of the affected resource"} - preferMissing | {"in": "header", "name": "Prefer", "schema": {"enum": ["missing=default", "missing=null"], "type": "string"}, "description": "Handle null values in bulk inserts"} - preferHandling | {"in": "header", "name": "Prefer", "schema": {"enum": ["handling=strict", "handling=lenient"], "type": "string"}, "description": "Handle invalid preferences"} - preferTimezone | {"in": "header", "name": "Prefer", "schema": {"type": "string"}, "example": "timezone=UTC", "description": "Specify the time zone"} - preferResolution | {"in": "header", "name": "Prefer", "schema": {"enum": ["resolution=merge-duplicates", "resolution=ignore-duplicates"], "type": "string"}, "description": "Handle duplicates in an upsert"} - preferMaxAffected | {"in": "header", "name": "Prefer", "schema": {"type": "string"}, "example": "max-affected=5", "description": "Limit the number of affected resources"} - preferTransaction | {"in": "header", "name": "Prefer", "schema": {"enum": ["tx=commit", "tx=rollback"], "type": "string"}, "description": "Specify how to end a transaction"} -(10 rows) +where key in ('preferGet', 'preferPost', 'preferPut', 'preferPatch', 'preferDelete', 'preferPostRpc', 'range'); + key | jsonb_pretty +---------------+------------------------------------------------------------------------------ + range | { + + | "in": "header", + + | "name": "Range", + + | "schema": { + + | "type": "string" + + | }, + + | "example": "5-10", + + | "description": "For limits and pagination" + + | } + preferGet | { + + | "in": "header", + + | "name": "Prefer", + + | "schema": { + + | "allOf": [ + + | { + + | "$ref": "#/components/schemas/header.preferHandling" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferTimezone" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferCount" + + | } + + | ] + + | }, + + | "explode": true, + + | "examples": { + + | "all": { + + | "value": { + + | "count": "", + + | "handling": "lenient", + + | "timezone": "" + + | }, + + | "summary": "All default preferences" + + | }, + + | "nothing": { + + | "summary": "No preferences" + + | } + + | }, + + | "description": "Specify a required or optional behavior for the request"+ + | } + preferPut | { + + | "in": "header", + + | "name": "Prefer", + + | "schema": { + + | "allOf": [ + + | { + + | "$ref": "#/components/schemas/header.preferHandling" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferTimezone" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferReturn" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferCount" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferTx" + + | } + + | ] + + | }, + + | "explode": true, + + | "examples": { + + | "all": { + + | "value": { + + | "tx": "commit", + + | "count": "", + + | "return": "minimal", + + | "handling": "lenient", + + | "timezone": "" + + | }, + + | "summary": "All default preferences" + + | }, + + | "nothing": { + + | "summary": "No preferences" + + | } + + | }, + + | "description": "Specify a required or optional behavior for the request"+ + | } + preferPost | { + + | "in": "header", + + | "name": "Prefer", + + | "schema": { + + | "allOf": [ + + | { + + | "$ref": "#/components/schemas/header.preferHandling" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferTimezone" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferReturn" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferCount" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferResolution" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferMissing" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferTx" + + | } + + | ] + + | }, + + | "explode": true, + + | "examples": { + + | "all": { + + | "value": { + + | "tx": "commit", + + | "count": "", + + | "return": "minimal", + + | "missing": "null", + + | "handling": "lenient", + + | "timezone": "", + + | "resolution": "" + + | }, + + | "summary": "All default preferences" + + | }, + + | "nothing": { + + | "summary": "No preferences" + + | } + + | }, + + | "description": "Specify a required or optional behavior for the request"+ + | } + preferPatch | { + + | "in": "header", + + | "name": "Prefer", + + | "schema": { + + | "allOf": [ + + | { + + | "$ref": "#/components/schemas/header.preferHandling" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferTimezone" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferReturn" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferCount" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferTx" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferMaxAffected" + + | } + + | ] + + | }, + + | "explode": true, + + | "examples": { + + | "all": { + + | "value": { + + | "tx": "commit", + + | "count": "", + + | "return": "minimal", + + | "handling": "lenient", + + | "timezone": "", + + | "max-affected": "" + + | }, + + | "summary": "All default preferences" + + | }, + + | "nothing": { + + | "summary": "No preferences" + + | } + + | }, + + | "description": "Specify a required or optional behavior for the request"+ + | } + preferDelete | { + + | "in": "header", + + | "name": "Prefer", + + | "schema": { + + | "allOf": [ + + | { + + | "$ref": "#/components/schemas/header.preferHandling" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferTimezone" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferReturn" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferCount" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferTx" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferMaxAffected" + + | } + + | ] + + | }, + + | "explode": true, + + | "examples": { + + | "all": { + + | "value": { + + | "tx": "commit", + + | "count": "", + + | "return": "minimal", + + | "handling": "lenient", + + | "timezone": "", + + | "max-affected": "" + + | }, + + | "summary": "All default preferences" + + | }, + + | "nothing": { + + | "summary": "No preferences" + + | } + + | }, + + | "description": "Specify a required or optional behavior for the request"+ + | } + preferPostRpc | { + + | "in": "header", + + | "name": "Prefer", + + | "schema": { + + | "allOf": [ + + | { + + | "$ref": "#/components/schemas/header.preferHandling" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferTimezone" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferCount" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferTx" + + | }, + + | { + + | "$ref": "#/components/schemas/header.preferParams" + + | } + + | ] + + | }, + + | "explode": true, + + | "examples": { + + | "all": { + + | "value": { + + | "tx": "commit", + + | "count": "", + + | "params": "", + + | "handling": "lenient", + + | "timezone": "" + + | }, + + | "summary": "All default preferences" + + | }, + + | "nothing": { + + | "summary": "No preferences" + + | } + + | }, + + | "description": "Specify a required or optional behavior for the request"+ + | } +(7 rows) diff --git a/test/expected/schemas.out b/test/expected/schemas.out index a76deb4..e402561 100644 --- a/test/expected/schemas.out +++ b/test/expected/schemas.out @@ -285,3 +285,125 @@ select jsonb_pretty(get_postgrest_openapi_spec('{test}')->'components'->'schemas } (1 row) +-- defines all the available prefer headers +select key, jsonb_pretty(value) +from jsonb_each(get_postgrest_openapi_spec('{public}')->'components'->'schemas') +where key like 'header.prefer%'; + key | jsonb_pretty +--------------------------+-------------------------------------------------------------------------- + header.preferTx | { + + | "type": "object", + + | "properties": { + + | "tx": { + + | "enum": [ + + | "commit", + + | "rollback" + + | ], + + | "type": "string", + + | "description": "Specify how to end a transaction" + + | } + + | } + + | } + header.preferCount | { + + | "type": "object", + + | "properties": { + + | "count": { + + | "enum": [ + + | "exact", + + | "planned", + + | "estimated" + + | ], + + | "type": "string", + + | "description": "Get the total size of the table" + + | } + + | } + + | } + header.preferParams | { + + | "type": "object", + + | "properties": { + + | "params": { + + | "enum": [ + + | "single-object" + + | ], + + | "type": "string", + + | "deprecated": true, + + | "description": "Send JSON as a single parameter" + + | } + + | } + + | } + header.preferReturn | { + + | "type": "object", + + | "properties": { + + | "return": { + + | "enum": [ + + | "minimal", + + | "headers-only", + + | "representation" + + | ], + + | "type": "string", + + | "default": "minimal", + + | "description": "Return information of the affected resource"+ + | } + + | } + + | } + header.preferMissing | { + + | "type": "object", + + | "properties": { + + | "missing": { + + | "enum": [ + + | "default", + + | "null" + + | ], + + | "type": "string", + + | "default": "null", + + | "description": "Handle null values in bulk inserts" + + | } + + | } + + | } + header.preferHandling | { + + | "type": "object", + + | "properties": { + + | "handling": { + + | "enum": [ + + | "strict", + + | "lenient" + + | ], + + | "type": "string", + + | "default": "lenient", + + | "description": "How to handle invalid preferences" + + | } + + | } + + | } + header.preferTimezone | { + + | "type": "object", + + | "properties": { + + | "timezone": { + + | "type": "string", + + | "description": "Specify the time zone" + + | } + + | } + + | } + header.preferResolution | { + + | "type": "object", + + | "properties": { + + | "resolution": { + + | "enum": [ + + | "merge-duplicates", + + | "ignore-duplicates" + + | ], + + | "type": "string", + + | "description": "Handle duplicates in an upsert" + + | } + + | } + + | } + header.preferMaxAffected | { + + | "type": "object", + + | "properties": { + + | "max-affected": { + + | "type": "integer", + + | "description": "Specify the amount of resources affected" + + | } + + | } + + | } +(9 rows) + diff --git a/test/sql/parameters.sql b/test/sql/parameters.sql index 22fb04e..be3d300 100644 --- a/test/sql/parameters.sql +++ b/test/sql/parameters.sql @@ -4,6 +4,6 @@ from jsonb_each(get_postgrest_openapi_spec('{public}')->'components'->'parameter where key in ('select', 'order', 'limit', 'offset', 'on_conflict', 'columns', 'or', 'and', 'not.or', 'not.and'); -- shows common headers -select * +select key, jsonb_pretty(value) from jsonb_each(get_postgrest_openapi_spec('{public}')->'components'->'parameters') -where key in ('preferParams', 'preferReturn', 'preferCount', 'preferResolution', 'preferTransaction', 'preferMissing', 'preferHandling', 'preferTimezone', 'preferMaxAffected', 'range'); +where key in ('preferGet', 'preferPost', 'preferPut', 'preferPatch', 'preferDelete', 'preferPostRpc', 'range'); diff --git a/test/sql/schemas.sql b/test/sql/schemas.sql index 64c623c..e5e3259 100644 --- a/test/sql/schemas.sql +++ b/test/sql/schemas.sql @@ -45,3 +45,8 @@ select jsonb_pretty(get_postgrest_openapi_spec('{test}')->'components'->'schemas -- references a composite type column from an array composite type inside the items property select jsonb_pretty(get_postgrest_openapi_spec('{test}')->'components'->'schemas'->'types.attribute'->'properties'->'colors'->'items'); + +-- defines all the available prefer headers +select key, jsonb_pretty(value) +from jsonb_each(get_postgrest_openapi_spec('{public}')->'components'->'schemas') +where key like 'header.prefer%';