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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions