feat(10-02): filter choices and a fully typed admin API proven on the wire

- pact.FilterOptions on the model serves a scope filter's choices; a scope
  filter whose model lacks it fails activation (D-27)
- GET /{vendor}/{plugin}/{controller}/filters/{scope}/options answers a
  declared scope filter behind the controller permission with localized
  {value, label} choices, 404 otherwise
- Every admin route documents a typed success schema, and protected routes
  document 401, 403 and 404 (422 on writes); SuccessEnvelope is gone and
  logout writes a typed AdminLogoutData
- jsonScalar and fieldContext decode their served shapes
- TestPhase10OpenAPIConformance calls every inventoried route through the
  assembled router on PostgreSQL and decodes each body into its documented
  type with unknown fields disallowed, checking admin.json's schema ref
- The SPA aliases every new schema type; Tailwind no longer scans the
  generated API files, so API changes do not churn boardwalk/dist
This commit is contained in:
Jakub Zych
2026-09-27 16:27:42 +02:00
parent c87148a34f
commit 9f296b0484
16 changed files with 1653 additions and 49 deletions

View File

@@ -35,6 +35,17 @@
],
"type": "object"
},
"cabana.AdminLogoutData": {
"properties": {
"status": {
"type": "string"
}
},
"required": [
"status"
],
"type": "object"
},
"cabana.AdminProfile": {
"properties": {
"email": {
@@ -106,6 +117,35 @@
],
"type": "object"
},
"cabana.BulkResult": {
"properties": {
"deleted": {
"type": "integer"
}
},
"required": [
"deleted"
],
"type": "object"
},
"cabana.Envelope-array_cabana_FilterOption": {
"properties": {
"data": {
"items": {
"$ref": "#/components/schemas/cabana.FilterOption"
},
"type": "array"
},
"meta": {
"$ref": "#/components/schemas/cabana.SuccessMeta"
}
},
"required": [
"data",
"meta"
],
"type": "object"
},
"cabana.Envelope-array_cabana_NavigationEntry": {
"properties": {
"data": {
@@ -124,6 +164,24 @@
],
"type": "object"
},
"cabana.Envelope-array_cabana_SettingsEntry": {
"properties": {
"data": {
"items": {
"$ref": "#/components/schemas/cabana.SettingsEntry"
},
"type": "array"
},
"meta": {
"$ref": "#/components/schemas/cabana.SuccessMeta"
}
},
"required": [
"data",
"meta"
],
"type": "object"
},
"cabana.Envelope-cabana_AdminLoginData": {
"properties": {
"data": {
@@ -139,6 +197,21 @@
],
"type": "object"
},
"cabana.Envelope-cabana_AdminLogoutData": {
"properties": {
"data": {
"$ref": "#/components/schemas/cabana.AdminLogoutData"
},
"meta": {
"$ref": "#/components/schemas/cabana.SuccessMeta"
}
},
"required": [
"data",
"meta"
],
"type": "object"
},
"cabana.Envelope-cabana_AdminProfile": {
"properties": {
"data": {
@@ -154,6 +227,21 @@
],
"type": "object"
},
"cabana.Envelope-cabana_BulkResult": {
"properties": {
"data": {
"$ref": "#/components/schemas/cabana.BulkResult"
},
"meta": {
"$ref": "#/components/schemas/cabana.SuccessMeta"
}
},
"required": [
"data",
"meta"
],
"type": "object"
},
"cabana.Envelope-cabana_FormView": {
"properties": {
"data": {
@@ -199,6 +287,21 @@
],
"type": "object"
},
"cabana.Envelope-cabana_RelationMutationResult": {
"properties": {
"data": {
"$ref": "#/components/schemas/cabana.RelationMutationResult"
},
"meta": {
"$ref": "#/components/schemas/cabana.SuccessMeta"
}
},
"required": [
"data",
"meta"
],
"type": "object"
},
"cabana.Envelope-cabana_RelationSchema": {
"properties": {
"data": {
@@ -214,6 +317,21 @@
],
"type": "object"
},
"cabana.Envelope-cabana_SettingsResult": {
"properties": {
"data": {
"$ref": "#/components/schemas/cabana.SettingsResult"
},
"meta": {
"$ref": "#/components/schemas/cabana.SuccessMeta"
}
},
"required": [
"data",
"meta"
],
"type": "object"
},
"cabana.ErrorBody": {
"properties": {
"code": {
@@ -245,6 +363,21 @@
],
"type": "object"
},
"cabana.FilterOption": {
"properties": {
"label": {
"type": "string"
},
"value": {
"type": "string"
}
},
"required": [
"label",
"value"
],
"type": "object"
},
"cabana.FormField": {
"properties": {
"attributes": {
@@ -893,6 +1026,17 @@
],
"type": "object"
},
"cabana.RelationMutationResult": {
"properties": {
"linked": {
"type": "integer"
},
"removed": {
"type": "integer"
}
},
"type": "object"
},
"cabana.RelationOption": {
"properties": {
"label": {
@@ -979,16 +1123,61 @@
],
"type": "object"
},
"cabana.SuccessEnvelope": {
"cabana.SettingsEntry": {
"properties": {
"data": {},
"meta": {
"$ref": "#/components/schemas/cabana.SuccessMeta"
"category": {
"type": "string"
},
"code": {
"type": "string"
},
"description": {
"type": "string"
},
"icon": {
"type": "string"
},
"keywords": {
"items": {
"type": "string"
},
"type": "array"
},
"label": {
"type": "string"
},
"model": {
"type": "string"
},
"order": {
"type": "integer"
}
},
"required": [
"category",
"code",
"description",
"icon",
"keywords",
"label",
"model",
"order"
],
"type": "object"
},
"cabana.SettingsResult": {
"properties": {
"data": {
"additionalProperties": {},
"type": "object"
},
"exists": {
"type": "boolean"
}
},
"required": [
"data",
"meta"
"exists"
],
"type": "object"
},
@@ -1115,7 +1304,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.SuccessEnvelope"
"$ref": "#/components/schemas/cabana.Envelope-cabana_AdminLogoutData"
}
}
},
@@ -1130,6 +1319,26 @@
}
},
"description": "Unauthorized"
},
"403": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Forbidden"
},
"404": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Not Found"
}
},
"security": [
@@ -1165,6 +1374,26 @@
}
},
"description": "Unauthorized"
},
"403": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Forbidden"
},
"404": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Not Found"
}
},
"security": [
@@ -1281,6 +1510,16 @@
}
},
"description": "Forbidden"
},
"404": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Not Found"
}
},
"security": [
@@ -1301,7 +1540,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.SuccessEnvelope"
"$ref": "#/components/schemas/cabana.Envelope-array_cabana_SettingsEntry"
}
}
},
@@ -1326,6 +1565,16 @@
}
},
"description": "Forbidden"
},
"404": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Not Found"
}
},
"security": [
@@ -1357,7 +1606,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.SuccessEnvelope"
"$ref": "#/components/schemas/cabana.Envelope-cabana_SettingsResult"
}
}
},
@@ -1421,7 +1670,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.SuccessEnvelope"
"$ref": "#/components/schemas/cabana.Envelope-cabana_SettingsResult"
}
}
},
@@ -1447,6 +1696,16 @@
},
"description": "Forbidden"
},
"404": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Not Found"
},
"422": {
"content": {
"application/json": {
@@ -1487,7 +1746,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.SuccessEnvelope"
"$ref": "#/components/schemas/cabana.Envelope-cabana_FormView"
}
}
},
@@ -1637,6 +1896,16 @@
},
"description": "Forbidden"
},
"404": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Not Found"
},
"422": {
"content": {
"application/json": {
@@ -1720,6 +1989,16 @@
},
"description": "Forbidden"
},
"404": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Not Found"
},
"422": {
"content": {
"application/json": {
@@ -1778,7 +2057,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.SuccessEnvelope"
"$ref": "#/components/schemas/cabana.Envelope-cabana_BulkResult"
}
}
},
@@ -1804,6 +2083,26 @@
},
"description": "Forbidden"
},
"404": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Not Found"
},
"409": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Conflict"
},
"422": {
"content": {
"application/json": {
@@ -1954,6 +2253,100 @@
]
}
},
"/{vendor}/{plugin}/{controller}/filters/{scope}/options": {
"get": {
"description": "Choices of a declared scope filter of the controller's list, from the model's FilterOptions. {scope} is the filter name used as filter[\u003cscope\u003e]; labels are localized.",
"parameters": [
{
"description": "Vendor",
"in": "path",
"name": "vendor",
"required": true,
"schema": {
"type": "string"
}
},
{
"description": "Plugin",
"in": "path",
"name": "plugin",
"required": true,
"schema": {
"type": "string"
}
},
{
"description": "Controller",
"in": "path",
"name": "controller",
"required": true,
"schema": {
"type": "string"
}
},
{
"description": "Filter name",
"in": "path",
"name": "scope",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.Envelope-array_cabana_FilterOption"
}
}
},
"description": "OK"
},
"401": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Unauthorized"
},
"403": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Forbidden"
},
"404": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Not Found"
}
},
"security": [
{
"BackendBearer": []
}
],
"summary": "Filter scope options",
"tags": [
"admin"
]
}
},
"/{vendor}/{plugin}/{controller}/schema/form": {
"get": {
"parameters": [
@@ -2260,7 +2653,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.SuccessEnvelope"
"$ref": "#/components/schemas/cabana.Envelope-cabana_BulkResult"
}
}
},
@@ -2295,6 +2688,16 @@
}
},
"description": "Not Found"
},
"422": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Unprocessable Entity"
}
},
"security": [
@@ -2554,7 +2957,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.SuccessEnvelope"
"$ref": "#/components/schemas/cabana.ListEnvelope-array_cabana_AdminRecord"
}
}
},
@@ -2579,6 +2982,26 @@
}
},
"description": "Forbidden"
},
"404": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Not Found"
},
"422": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Unprocessable Entity"
}
},
"security": [
@@ -2646,7 +3069,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.SuccessEnvelope"
"$ref": "#/components/schemas/cabana.ListEnvelope-array_cabana_AdminRecord"
}
}
},
@@ -2671,6 +3094,26 @@
}
},
"description": "Forbidden"
},
"404": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Not Found"
},
"422": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Unprocessable Entity"
}
},
"security": [
@@ -2738,7 +3181,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.SuccessEnvelope"
"$ref": "#/components/schemas/cabana.Envelope-cabana_RelationMutationResult"
}
}
},
@@ -2764,6 +3207,16 @@
},
"description": "Forbidden"
},
"404": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Not Found"
},
"422": {
"content": {
"application/json": {
@@ -2840,7 +3293,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.SuccessEnvelope"
"$ref": "#/components/schemas/cabana.Envelope-cabana_RelationMutationResult"
}
}
},
@@ -2866,6 +3319,16 @@
},
"description": "Forbidden"
},
"404": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/cabana.ErrorEnvelope"
}
}
},
"description": "Not Found"
},
"422": {
"content": {
"application/json": {