feat(17-01): tree reorder page and singleton admin controllers
- cabana: config_reorder.yaml (title, nameFrom, modelClass, parentColumn,
sortColumn, maxDepth, partition) with fail-loud boot checks and the
built-in reorder toolbar button; list schema carries reorder
- GET/POST {controller}/reorder: scoped set (ListExtendQuery plus the
partition) locked FOR UPDATE, 409 reorder_stale on any set difference,
422 for duplicates, foreign parents, cycles and maxDepth, both columns
written without model hooks
- pact.AdminSingleton: GET {controller}/singleton (first or create, form
scope checked), form schema singleton, list/create/bulk/delete 404,
no config_list.yaml needed
- swag, inventories, token classes, CSRF walk, conformance cases, backend
lang reorder.stale and load_failed, reorder and singleton smoke tests, docs
This commit is contained in:
306
admin/src/api/schema.d.ts
vendored
306
admin/src/api/schema.d.ts
vendored
@@ -1887,6 +1887,180 @@ export interface paths {
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/{vendor}/{plugin}/{controller}/reorder": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/**
|
||||
* Load a reorder tree
|
||||
* @description The records of a controller with config_reorder.yaml, scoped by the list scope (ListExtendQuery) and, when the page has a partition, by one partition: `partition` picks it (an id among `partition.options`; another id answers 404) and defaults to the first option. Nodes carry the id, parent_id (null at the top), sort_order and the nameFrom column as name, ordered by the sort column and then the id. A controller without a reorder page answers 404.
|
||||
*/
|
||||
get: {
|
||||
parameters: {
|
||||
query?: {
|
||||
/** @description Partition id */
|
||||
partition?: number;
|
||||
};
|
||||
header?: never;
|
||||
path: {
|
||||
/** @description Vendor */
|
||||
vendor: string;
|
||||
/** @description Plugin */
|
||||
plugin: string;
|
||||
/** @description Controller */
|
||||
controller: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description OK */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.Envelope-cabana_AdminReorderTree"];
|
||||
};
|
||||
};
|
||||
/** @description Unauthorized */
|
||||
401: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Forbidden */
|
||||
403: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Not Found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Unprocessable Entity */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
put?: never;
|
||||
/**
|
||||
* Save a reorder tree
|
||||
* @description Saves the parent and sort columns of every record of the scoped set in one transaction that locks the set (ListExtendQuery plus the partition). `nodes` must list exactly the records of that set: any difference (a record added, removed or moved to another partition meanwhile) answers 409 `reorder_stale` and writes nothing. A duplicate id, a parent outside the set, a cycle and a depth above maxDepth answer 422 `validation_failed` on `nodes`; a missing partition on a partitioned page is a 422 on `partition`. The answer is the new tree.
|
||||
*/
|
||||
post: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
/** @description Vendor */
|
||||
vendor: string;
|
||||
/** @description Plugin */
|
||||
plugin: string;
|
||||
/** @description Controller */
|
||||
controller: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
/** @description The partition and every node with its new parent and sort value */
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.AdminReorderRequest"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description OK */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.Envelope-cabana_AdminReorderTree"];
|
||||
};
|
||||
};
|
||||
/** @description Unauthorized */
|
||||
401: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Forbidden */
|
||||
403: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Not Found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Conflict */
|
||||
409: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Request Entity Too Large */
|
||||
413: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Unprocessable Entity */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/{vendor}/{plugin}/{controller}/schema/form": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
@@ -2102,6 +2276,79 @@ export interface paths {
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/{vendor}/{plugin}/{controller}/singleton": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/**
|
||||
* Open a singleton record
|
||||
* @description The id of the one record a pact.AdminSingleton controller edits, created by the controller's SingletonRecord when it does not exist yet. The admin opens that record's form. The form schema of such a controller carries `singleton: true`, and its list, create, bulk and delete routes answer 404. A controller that is not a singleton answers 404.
|
||||
*/
|
||||
get: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
/** @description Vendor */
|
||||
vendor: string;
|
||||
/** @description Plugin */
|
||||
plugin: string;
|
||||
/** @description Controller */
|
||||
controller: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description OK */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.Envelope-cabana_AdminSingletonResult"];
|
||||
};
|
||||
};
|
||||
/** @description Unauthorized */
|
||||
401: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Forbidden */
|
||||
403: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
/** @description Not Found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["cabana.ErrorEnvelope"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/{vendor}/{plugin}/{controller}/toolbar/{action}": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
@@ -5113,11 +5360,43 @@ export interface components {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
};
|
||||
"cabana.AdminReorderNode": {
|
||||
id: number;
|
||||
name: string;
|
||||
parent_id: number;
|
||||
sort_order: number;
|
||||
};
|
||||
"cabana.AdminReorderNodeInput": {
|
||||
id: number;
|
||||
parent_id: number;
|
||||
sort_order: number;
|
||||
};
|
||||
"cabana.AdminReorderPartition": {
|
||||
label: string;
|
||||
options: components["schemas"]["cabana.AdminReorderPartitionOption"][];
|
||||
value: number;
|
||||
};
|
||||
"cabana.AdminReorderPartitionOption": {
|
||||
label: string;
|
||||
value: number;
|
||||
};
|
||||
"cabana.AdminReorderRequest": {
|
||||
nodes: components["schemas"]["cabana.AdminReorderNodeInput"][];
|
||||
partition: number;
|
||||
};
|
||||
"cabana.AdminReorderTree": {
|
||||
nodes: components["schemas"]["cabana.AdminReorderNode"][];
|
||||
partition: components["schemas"]["cabana.AdminReorderPartition"];
|
||||
title: string;
|
||||
};
|
||||
"cabana.AdminRoleSummary": {
|
||||
code: string;
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
"cabana.AdminSingletonResult": {
|
||||
id: number;
|
||||
};
|
||||
"cabana.BulkAction": {
|
||||
confirm?: string;
|
||||
label?: string;
|
||||
@@ -5194,6 +5473,14 @@ export interface components {
|
||||
data: components["schemas"]["cabana.AdminRecord"];
|
||||
meta: components["schemas"]["cabana.SuccessMeta"];
|
||||
};
|
||||
"cabana.Envelope-cabana_AdminReorderTree": {
|
||||
data: components["schemas"]["cabana.AdminReorderTree"];
|
||||
meta: components["schemas"]["cabana.SuccessMeta"];
|
||||
};
|
||||
"cabana.Envelope-cabana_AdminSingletonResult": {
|
||||
data: components["schemas"]["cabana.AdminSingletonResult"];
|
||||
meta: components["schemas"]["cabana.SuccessMeta"];
|
||||
};
|
||||
"cabana.Envelope-cabana_BulkActionResult": {
|
||||
data: components["schemas"]["cabana.BulkActionResult"];
|
||||
meta: components["schemas"]["cabana.SuccessMeta"];
|
||||
@@ -5484,6 +5771,12 @@ export interface components {
|
||||
* them onto its routes.
|
||||
*/
|
||||
redirects: components["schemas"]["cabana.FormRedirects"];
|
||||
/**
|
||||
* @description Singleton is true when the controller edits exactly one record
|
||||
* (pact.AdminSingleton, D-11): the admin opens it through the singleton
|
||||
* route instead of a list.
|
||||
*/
|
||||
singleton?: boolean;
|
||||
};
|
||||
"cabana.LangBundle": {
|
||||
[key: string]: components["schemas"]["cabana.MessageForms"];
|
||||
@@ -5550,6 +5843,14 @@ export interface components {
|
||||
};
|
||||
total: number;
|
||||
};
|
||||
"cabana.ListReorder": {
|
||||
partition?: components["schemas"]["cabana.ListReorderPartition"];
|
||||
title: string;
|
||||
};
|
||||
"cabana.ListReorderPartition": {
|
||||
column: string;
|
||||
label?: string;
|
||||
};
|
||||
"cabana.ListSchema": {
|
||||
/** @description Assets are the controller's plugin JS and CSS URLs (pact.AdminClientAssets). */
|
||||
assets: components["schemas"]["cabana.ControllerAssets"];
|
||||
@@ -5573,6 +5874,11 @@ export interface components {
|
||||
perPageOptions: number[];
|
||||
recordUrl?: string;
|
||||
recordsPerPage: number;
|
||||
/**
|
||||
* @description Reorder is set when the controller has a tree reorder page
|
||||
* (config_reorder.yaml, D-09).
|
||||
*/
|
||||
reorder?: components["schemas"]["cabana.ListReorder"];
|
||||
rowActions: components["schemas"]["cabana.RowAction"][];
|
||||
searchPrompt?: string;
|
||||
searchTerm: string;
|
||||
|
||||
Reference in New Issue
Block a user