Skip to content

spec(view): canonicalize ViewFilterRule operator vocabulary (enum + legacy aliases) and deprecate legacy string sort #3368

Description

@os-zhuang

Background

ViewFilterRuleSchema.operator is currently a free-form z.string(), so Studio (ObjectUI SchemaForm) cannot render an operator dropdown, and invalid operators pass validation silently. PR #3363 attempted to fix this by hard-switching to a 19-value z.enum, but that approach breaks existing data and consumers (see "Why the previous attempt was rejected" below). This issue tracks a redesign.

Paired UI work lives in objectui (field-type-aware operator/value widgets); this spec change is its blocking dependency.

Problem

The operator vocabulary actually in use today is wider than any single canonical list:

  • ObjectUI's Studio FilterBuilderWidget writes shorthand operators to stored view metadata: gt, gte, lt, lte (see FB_TO_SPEC in packages/app-shell/src/views/metadata-admin/widgets.tsx).
  • ObjectUI's runtime accepts shorthands: eq, ne, neq, gt, lt, gte, lte, nin (see specOperatorToAst in packages/plugin-list/src/UserFilters.tsx).
  • Existing object definitions in this repo use isNull / isNotNull (plugin-sharing) and relative-date operators like this_quarter (documented in the schema's own JSDoc).

A strict enum containing only long-form snake_case names invalidates all of the above, including data ObjectUI's own editor writes today.

Requirements

  1. Define the canonical operator vocabulary (proposed: long-form snake_case — equals, not_equals, contains, not_contains, starts_with, ends_with, greater_than, less_than, greater_than_or_equal, less_than_or_equal, in, not_in, is_empty, is_not_empty, is_null, is_not_null, before, after, between).
  2. Accept legacy aliases on parseeq, ne, neq, gt, lt, gte, lte, nin, isNull, isNotNull, isEmpty, isNotEmpty, camelCase forms (notEquals, greaterThan, …) — normalized to the canonical form (e.g. via z.preprocess/transform), so existing stored metadata keeps validating. Alternatively ship a migration; either way, no silent breakage.
  3. Decide the fate of relative-date operators (this_quarter, etc.). They are documented in the current schema JSDoc; removing them is a product decision, not a cleanup. If removed, provide a replacement (e.g. relative-date value tokens) or an explicit deprecation.
  4. Deprecate legacy string sort separately. ListViewSchema.sort accepting "field desc" is a documented compatibility form, and the task.view.ts fixture using it is intentional live regression coverage for objectui#2601 ("schema.sort.map is not a function"). Removal must be its own deprecation-cycle change — keep the union (optionally with a deprecation note) for now, and do not delete the regression fixture until the string form is actually dropped.
  5. Fixture updates must be semantics-preserving. E.g. rewriting isNullis_empty changes meaning unless the data layer treats them identically; use the alias mapping instead of rewording filters.

Acceptance criteria

  • ViewFilterRuleSchema.operator exposes an enum (so JSON-schema consumers like SchemaForm get enum choices) while still parsing all legacy aliases listed above.
  • ObjectSchema definitions in this repo (plugin-sharing, examples) validate unchanged, without semantic rewrites.
  • ListViewSchema.sort string form still parses; its removal is tracked in a separate deprecation issue.
  • Spec tests cover: canonical values, each legacy alias normalizing correctly, and rejection of genuinely unknown operators.
  • Changelog/migration note for downstream consumers (ObjectUI runtime + Studio).

Prior art

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions