feat(cabana): per-request options for plugin field types

- pact.FormFieldType gains RequestOptions, called with the application's
  database once per form schema request for each type the form uses
- the form schema route merges the answer into a copy of each field's
  typeOptions; a fields.yaml key wins, the compiled schema never changes
- an error logs one warning naming the type and serves the fields.yaml
  options only
- TestFieldTypeRequestOptions covers every screen, the precedence, the
  call count, the error path, unchanged schemas, form extension fields
  and the authenticated handler; a runnable example backs the docs
- the typeOptions description, the regenerated OpenAPI document and
  schema.d.ts, the field types page and the pact and cabana READMEs
  document it
This commit is contained in:
Jakub Zych
2026-11-04 08:40:09 +01:00
parent 561a56c768
commit 4cd35c9d66
12 changed files with 416 additions and 12 deletions

View File

@@ -857,6 +857,9 @@ func (s *service) formSchema(w http.ResponseWriter, r *http.Request) {
WriteError(w, http.StatusInternalServerError, "error", msgServerError)
return
}
// A plugin field type's per-request options (pact.FormFieldType.
// RequestOptions) join its fields.yaml options in kept only.
mergeRequestOptions(r.Context(), s.requestDB(r.Context()), kept)
view.Fields = kept
view.Assets = s.controllerAssets(cc)
if codes := s.enabledContentLocales(r.Context()); len(codes) > 0 {
@@ -873,6 +876,16 @@ func (s *service) formSchema(w http.ResponseWriter, r *http.Request) {
})
}
// requestDB is the application's database bound to ctx, nil when none is
// configured.
func (s *service) requestDB(ctx context.Context) *gorm.DB {
db, err := s.db()
if err != nil {
return nil
}
return db.WithContext(ctx)
}
// schemaFieldHider is implemented by cabana's own admin controllers that
// hide some fields from some administrators (the Administrators screen's
// is_superuser for non-superusers). hiddenSchemaFields names the form