feat(cabana): widget action payload and data channel (quick-261006-eyj)
- pact.AdminActionInput.Payload (json.RawMessage) carries the widget's own JSON value untouched; pact.AdminActionResult.Data is passed through as data - cabana decodes payload with a 64 KiB cap (422 on body), refuses it on the toolbar and record routes, and embeds Data once encoded with a 256 KiB cap (opaque 500 when larger or unencodable); fill stays filtered - root .swaggo overrides json.RawMessage so swag keeps record_id and values; admin.json and schema.d.ts regenerated (payload?: unknown, data?: unknown) - TestWidgetPayloadAndData covers pass-through, cap, refusal and data 500 - cabana and pact READMEs, partials-and-widgets and admin-spa docs updated
This commit is contained in:
@@ -35,7 +35,7 @@ A form whose schema carries `preview` has a read-only record screen at `<control
|
||||
|
||||
## Types from OpenAPI
|
||||
|
||||
The admin API is described by swag annotations in cabana. `scripts/check-admin-openapi.sh` generates the OpenAPI document (`admin/openapi/admin.json`) from them and the SPA's TypeScript types (`admin/src/api/schema.d.ts`) from the document, so the SPA's API client is checked against the server's shapes at compile time. `--check` fails when either committed file is out of date:
|
||||
The admin API is described by swag annotations in cabana. `scripts/check-admin-openapi.sh` generates the OpenAPI document (`admin/openapi/admin.json`) from them and the SPA's TypeScript types (`admin/src/api/schema.d.ts`) from the document, so the SPA's API client is checked against the server's shapes at compile time. Type overrides for swag live in the root `.swaggo` (it documents `json.RawMessage` as an untyped value, which swag cannot resolve on its own). `--check` fails when either committed file is out of date:
|
||||
|
||||
```sh
|
||||
scripts/check-admin-openapi.sh --check
|
||||
|
||||
@@ -70,7 +70,7 @@ lookup:
|
||||
- `action` names an action the controller registers through `pact.HasAdminActions`.
|
||||
- `fill` lists the writable scalar fields of the same form the action may write back.
|
||||
|
||||
The SPA posts the widget's values to `.../widgets/{field}`. cabana checks the CSRF header, the controller's and the action's permissions and the record scope, then calls the action's `Run` with a `pact.AdminActionInput`. The answer's `pact.AdminActionResult` carries a message and the fill values; keys outside `fill` and values that are not scalars are dropped before the response is written. An action may return a `cabana.ValidationError` to answer 422 on a field.
|
||||
The SPA posts `record_id`, the fill snapshot `values` and, when the element supplied one, `payload` to `.../widgets/{field}`. cabana checks the CSRF header, the controller's and the action's permissions and the record scope, then calls the action's `Run` with a `pact.AdminActionInput`. The action reads the payload from the `Payload` field as raw JSON (any value up to 64 KiB, exactly the bytes the client sent, nil when absent) and decodes it itself; it must treat any ids inside the payload as untrusted input and re-check them against its own scope, because the framework never uses the payload to select the record. The answer's `pact.AdminActionResult` carries a message and the fill values; keys outside `fill` and values that are not scalars are dropped before the response is written. It may also carry `Data`, any JSON value up to 256 KiB encoded, which is passed through as `data` and is not filtered like `fill` (a larger or unencodable value is a 500). Toolbar and record actions refuse a payload. An action may return a `cabana.ValidationError` to answer 422 on a field.
|
||||
|
||||
## Toolbar actions
|
||||
|
||||
|
||||
Reference in New Issue
Block a user