Skip to content

控制台「筛选」把整个 FilterGroup 对象存进 view 的 filter(spec 声明的是 ViewFilterRule[])—— 真浏览器实测 422,#5114 热修修不到 #5159

Description

@xuyushun441-sys

发现于 #5114 热修的 dogfood 浏览器端到端复核(真实 /_console bundle,objectui 固定 pin f5bc4c78,Playwright 驱动)。

#5114 是同一个「保存筛选条件」动作上的两个独立缺陷,叠在同一份 body 上。#5114 的重开修掉里层那个,修不到这一条。 写下来免得下一个人以为热修合并后控制台就通了 —— 它不通。

实测:真浏览器里点一次「Filter → Add filter」

showcase 起在自有端口,真实登录,/_console/apps/showcase_app/showcase_task,点工具栏 FilterAdd filter:

PUT /api/v1/meta/view/showcase_task.default  ->  422
{"error":"[invalid_metadata] view/showcase_task.default failed spec validation: root: Invalid input",
 "code":"INVALID_METADATA","issues":[{"path":"","message":"Invalid input","code":"invalid_union"}]}

抓下来的 body 里,filter 是这个:

"filter": {
  "id": "root",
  "logic": "and",
  "conditions": [
    { "id": "712135fb-58c5-4be4-a611-1925181509b0", "field": "title", "operator": "equals", "value": "" }
  ]
}

FilterGroup 对象,不是 ViewFilterRule[]ListViewSchema.filter 声明的是 z.array(ViewFilterRuleSchema)

两个缺陷是叠着的 —— 把抓下来的真 body 三变体回放

同一份 body,只改 filter,分别打到 修前(origin/main)/ 修后(#5114 热修)两个真实运行的 server:

变体 修前 修后(#5114)
① 原样(FilterGroup 对象) 422 invalid_union 422 invalid_union
② 拆掉分组 → rule[],保留 UI 的 id 422 ACCEPTED
③ 拆掉分组 → rule[],去掉 id ACCEPTED ACCEPTED

读法:外层类型错(本 issue)挡在前面;把它拆开之后,下一道就是 #5114 那个 id #5114 的重开让 ② 从 422 翻成通过 —— 必要,但不充分。外层不修,控制台保存筛选条件仍然 422。

产出端链路(objectui,固定 pin 上逐跳可查)

  1. components/src/custom/filter-builder.tsx —— handleChange(newGroup: FilterGroup)onChange?.(newGroup),交出的是分组对象;addCondition 给每行盖 id: crypto.randomUUID()(:228)
  2. plugin-list/src/ListView.tsx:2155-2161 —— onChange={(newFilters) => { setCurrentFilters(newFilters); onFilterChange?.(newFilters) }},原样上抛
  3. app-shell/src/views/ObjectView.tsx:1592-1595 —— onFilterChange={(filter) => { persistViewPatch(viewDef.id, viewDef, { filter }); … }}(同源另一处::1337)
  4. persistViewPatch(ObjectView.tsx:242)→ dataSource.updateViewConfig(objectName, viewId, { ...baseViewDef, ...merged })
  5. data-objectstack/src/index.ts:2608client.meta.saveItem('view', viewId, merged)PUT /api/v1/meta/view/:name

对照:Studio 侧那条路今天是好的 —— app-shell/src/views/metadata-admin/widgets.tsx:1727-1732handle 在写回前把每行压成 {field, operator, value}(顺带剥了 id),所以走 Studio 视图编辑器配筛选不会 422。缺的是运行时工具栏这条路的同一半转换。

处方倾向(不代劳裁定)

按 Prime Directive #12 契约优先,修产出端,⛔ 不要把 ListViewSchema.filter 放宽成 union([rule[], FilterGroup]):

  • A(推荐):运行时工具栏在 persistViewPatch 之前把 FilterGroup 折成 ViewFilterRule[] —— 这个转换 Studio 侧已经写好了(widgets.tsxhandle),反方向也有(plugin-view/src/config/view-config-utils.tsparseSpecFilter/toFilterGroup);缺的就是写方向的这一半。代价小,且把「一个视图的 filter 在库里只有一种形状」这条守住。
  • B:如果分组语义(logic: 'or'、嵌套组)确实要进协议,那是一次有意的 spec 扩展(带 ADR + 迁移 + 读端全线更新),不是靠消费端容忍。

选 B 之外的任何「放宽 schema」都会让 filter 在库里同时有两种形状,所有读端都得两边都认 —— 正是本仓一直在还的那类债。

⚠️ 一并注意:logic: 'or' 今天没有任何落盘表示。修 A 之后 and 能正确折平,or 会被静默降级成 and —— 那是「声明 ≠ 生效」的另一面,建议在修 A 时一并判定(要么折平时对 or 显式报错,要么走 B)。

关联

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions