feat(18.1-08): detached admin bulk actions run outside the framework transaction

- pact.AdminBulkAction.Detached (json:"-"): opt-in; records loaded through the list scope without row locks, no write transaction in the context
- cabana BulkAction: detached branch with the same empty, partial-selection, failure and message rules; lockScoped is loadScoped with lock, the locked path unchanged
- docs: admin-controllers bulk route list, pact and cabana READMEs
This commit is contained in:
Jakub Zych
2026-10-16 12:47:46 +02:00
parent f217c73ef8
commit fc2a397d9b
5 changed files with 69 additions and 10 deletions

View File

@@ -281,18 +281,29 @@ type HasAdminActions interface {
// keys or literal text: Label is the menu item, Confirm the confirmation
// question, and an empty Confirm means the framework's default confirm text.
// Permissions are checked in addition to the controller's
// RequiredPermissions.
// RequiredPermissions. Detached opts an action out of the framework
// transaction (see the field).
type AdminBulkAction struct {
Name string
Label string
Confirm string
Permissions []string
Run func(ctx context.Context, in AdminBulkActionInput) (AdminBulkActionResult, error) `json:"-"`
// Detached runs Run outside the framework transaction. The selected
// records are loaded through the same ListExtendQuery scope, with the
// same empty and partial-selection rules, but without row locks, and
// the context carries no write transaction (cabana.TxFromContext
// reports none); Run opens the short transactions it needs. An action
// that calls an external service sets it, so that no transaction or row
// lock is held across the call. What such an action saved before a
// later failure stays saved.
Detached bool `json:"-"`
}
// AdminBulkActionInput is what the framework hands an AdminBulkAction.
// Records are pointers to the controller's model, loaded and row-locked
// through the controller's ListExtendQuery scope and ordered by primary key.
// through the controller's ListExtendQuery scope and ordered by primary key
// (loaded without row locks for a Detached action).
// A bulk action never receives ids, so an id list can never become an
// unscoped lookup.
type AdminBulkActionInput struct {