mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-10-27 14:57:15 +00:00
47ca9e521a
* fix(server): refuse a collection schema that repeats or empties a field key or option (TASK-3539, PR B) Lead ruling: a repeated or empty field key or select option is a broken schema, and the web renders fields and options as lists keyed by them, so one repeat blanked the view (BUG-3538). PR A made the web render such a schema; this stops new ones being written. - Collection create and update refuse, with 400 validation_error naming it, a schema in which a field has no key, two fields share a key, or a select field lists an empty option or the same option twice (models.ValidateSchemaKeysAndOptions, beside ValidateAbandonedOptions). WRITE check only: a stored schema with a repeat still reads, and an update that sends no schema still applies. Live census on :7777: 0 of 213 collections affected. - Dynamic Client Registration registers a repeated redirect URI once (it is unauthenticated, and the connected-apps page lists URIs keyed by value). - ListWorkspaceTags groups tags as TEXT: on SQLite json_each kept [1] and ["1"] apart while both scanned to "1". - Pins that convention commands and surfaces are deduped on read: they are already (normalizeItemConventionMetadata); the census verdict that said otherwise was wrong. Not done, deliberately: refusing repeated app-manifest event names or companion field keys. appmanifest.Parse also re-reads a STORED manifest on upgrade, so a new refusal there would fail a read of an installed app; PR A made both web lists render repeats. ToolSurfaceVersion 0.82 -> 0.83: pad_collection create/update refuse calls they used to accept (the v0.43 / v0.48 grounds). docs/api.md states the 400. * fix(server): hold companion and template schemas to the key/option rule (TASK-3539) Codex round 1: writers that bypass the collection handlers could still store a schema that repeats or empties a key or option. - An app's companion collection schemas are checked in the install/upgrade preview, on the NEW manifest beside the reserved-key check, and refused 422 invalid_manifest. Not in appmanifest.Parse, which also re-reads an installed app's stored manifest. - Every template and default collection schema the repo ships is checked by a test (they seed through the store, not the handlers). - The legacy-schema test also reads and creates items in a collection stored with a repeat. Workspace and artifact import, and the library option widening (which already dedupes), are unchanged: refuse on write only. * docs(mcp): the v0.83 entry in docs/mcp.md's version history (TASK-3539) Codex round 2: the current-version labels said v0.83, the history began at v0.82. * docs(mcp): docs/mcp.md's intro names the current catalog, v0.83 (TASK-3539) Codex round 3: the intro still said v0.81, stale since before this change. * fix(server): the option check covers select and multi_select only (TASK-3539) Codex round 4: it refused empty or repeated options on every field type, but only a select or multi_select's options are a rendered list. Another type can carry a legacy options list (the old field DSL put a third part there), and refusing it would block an unrelated schema save. Tested both ways. * test(collections): the template schema check is independent of the validator (TASK-3539) Codex round 5: calling ValidateSchemaKeysAndOptions from the template test was circular. It now uses its own check. Also records, at the write, why the library option widening is not held to the validator: it only appends missing words (never a repeat), and refusing would fail an item create over a legacy schema.
47 lines
1.8 KiB
Go
47 lines
1.8 KiB
Go
package models
|
|
|
|
import "fmt"
|
|
|
|
// ValidateSchemaKeysAndOptions refuses a collection schema in which a field
|
|
// has no key, two fields share a key, or a select or multi_select field's
|
|
// options include an empty value or the same value twice (TASK-3539). Every client renders a
|
|
// collection's fields and options as lists keyed by those values, and a
|
|
// repeated key throws during render and blanks the view (BUG-3538); a field
|
|
// key named twice is also ambiguous for every reader that looks one up.
|
|
//
|
|
// It is a WRITE check: called on collection create and update, never on a
|
|
// read, an import or a migration, so a schema already stored with a repeat
|
|
// keeps working (the web normalises it for rendering) until someone edits it,
|
|
// and the error then names exactly what to fix. Values compare exactly, as
|
|
// stored.
|
|
func ValidateSchemaKeysAndOptions(schema CollectionSchema) error {
|
|
seen := make(map[string]bool, len(schema.Fields))
|
|
for i, f := range schema.Fields {
|
|
if f.Key == "" {
|
|
return fmt.Errorf("field %d (label %q) has no key", i+1, f.Label)
|
|
}
|
|
if seen[f.Key] {
|
|
return fmt.Errorf("field key %q is defined more than once", f.Key)
|
|
}
|
|
seen[f.Key] = true
|
|
// Options are a list the clients render only for a select or a
|
|
// multi_select. Another type can carry a legacy `options` list (the old
|
|
// field DSL put a third part there for every type), and refusing that
|
|
// would block an unrelated schema save.
|
|
if f.Type != "select" && f.Type != "multi_select" {
|
|
continue
|
|
}
|
|
options := make(map[string]bool, len(f.Options))
|
|
for _, o := range f.Options {
|
|
if o == "" {
|
|
return fmt.Errorf("field %q has an empty option", f.Key)
|
|
}
|
|
if options[o] {
|
|
return fmt.Errorf("field %q lists the option %q more than once", f.Key, o)
|
|
}
|
|
options[o] = true
|
|
}
|
|
}
|
|
return nil
|
|
}
|