feat(12.1-01): declared record actions with an applicability rule
- pact.HasAdminRecordActions with AdminRecordAction (Applies, Run)
- config_form.yaml recordActions, compiled fail-loud
- show response meta.actions lists the permitted actions that apply
- POST .../{controller}/{id}/actions/{action}: record loaded and locked
through the form scope; 404 out of scope, 409 when it does not apply
- RecordActions.vue with confirm and request flow (mounted by plan 02)
- roster fixture, smoke tests, OpenAPI, TS types, READMEs, docs
This commit is contained in:
@@ -72,6 +72,11 @@ func compileExtension(pluginID string, cc *CompiledController, fsys fs.FS) error
|
||||
return fmt.Errorf("cabana: admin controller %s/%s: %w", pluginID, id, err)
|
||||
}
|
||||
cc.BulkActions = bulkActions
|
||||
recordActions, err := compileRecordActions(cc.Controller)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cabana: admin controller %s/%s: %w", pluginID, id, err)
|
||||
}
|
||||
cc.RecordActions = recordActions
|
||||
if err := compileClientAssets(pluginID, cc, fsys); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -81,6 +86,21 @@ func compileExtension(pluginID string, cc *CompiledController, fsys fs.FS) error
|
||||
if cc.Form == nil {
|
||||
return nil
|
||||
}
|
||||
// Every recordActions name of config_form.yaml must be a record action
|
||||
// the controller registers, with a label (D-10).
|
||||
formFile := cc.Form.configPath
|
||||
if formFile == "" {
|
||||
formFile = "config_form.yaml"
|
||||
}
|
||||
for _, name := range cc.Form.recordActions {
|
||||
action, ok := recordActions[name]
|
||||
if !ok {
|
||||
return bootErr(pluginID, id, formFile, fmt.Errorf("recordActions: unsupported action %s (want a record action the controller registers)", name))
|
||||
}
|
||||
if strings.TrimSpace(action.Label) == "" {
|
||||
return bootErr(pluginID, id, formFile, fmt.Errorf("recordActions: action %s needs a label", name))
|
||||
}
|
||||
}
|
||||
file := cc.Form.fieldsPath
|
||||
if file == "" {
|
||||
file = "fields.yaml"
|
||||
@@ -308,3 +328,29 @@ func compileBulkActions(ctl pact.AdminController) (map[string]pact.AdminBulkActi
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// compileRecordActions collects a controller's registered record actions into
|
||||
// their own namespace; create and delete stay reserved.
|
||||
func compileRecordActions(ctl pact.AdminController) (map[string]pact.AdminRecordAction, error) {
|
||||
out := map[string]pact.AdminRecordAction{}
|
||||
src, ok := ctl.(pact.HasAdminRecordActions)
|
||||
if !ok || src == nil {
|
||||
return out, nil
|
||||
}
|
||||
for _, action := range src.AdminRecordActions() {
|
||||
if !identifier(action.Name) {
|
||||
return nil, fmt.Errorf("record action name %q is not an identifier", action.Name)
|
||||
}
|
||||
if builtinToolbarActions[action.Name] {
|
||||
return nil, fmt.Errorf("record action %s uses a reserved built-in name (create, delete)", action.Name)
|
||||
}
|
||||
if _, dup := out[action.Name]; dup {
|
||||
return nil, fmt.Errorf("duplicate record action %s", action.Name)
|
||||
}
|
||||
if action.Run == nil {
|
||||
return nil, fmt.Errorf("record action %s has no Run function", action.Name)
|
||||
}
|
||||
out[action.Name] = action
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user