Repository navigation
fix(react): mark the field a unique-index refusal names in create/edit forms - #27043
Conversation
…rors A unique-index refusal (409 UNIQUE_VIOLATION) names its one field on a single string `field`, not a list, so extractFieldErrors answered null and create/edit dialogs showed a banner and a toast instead of marking the input. Read that field (details.field, or field on a hand-rolled error) and return one entry carrying the server's sentence; without `field` the answer stays null and the banner stays. Claude-Session: https://claude.ai/code/session_01JQwxbZRo35mpCPrKXVXevY Co-authored-by: Claude <noreply@anthropic.com>
… mark Claude-Session: https://claude.ai/code/session_01JQwxbZRo35mpCPrKXVXevY Co-authored-by: Claude <noreply@anthropic.com>
席位核对ACCEPT · 席位意见:只认
|
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 40655321196 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Requested by Warren · project thread
Fixes #26891
Clause-②: no
Metadata-protocol: none
维护者速读
@object-ui/react的extractFieldErrors现在识别 409UNIQUE_VIOLATION里单个field,返回一条带服务端句子的字段错误;表单因此把重复值标在对应输入框下。code为UNIQUE_VIOLATION且带字符串field时生效;无field(复合索引、索引名)照旧走横幅。回滚 = revert 本 PR。ui/packages/react/src/utils/error-message.ts的extractFieldErrors新分支;form-server-field-errors.test.tsx新增的 unique-index 两例。pnpm --dir ui exec vitest run packages/react/src/utils/error-message.test.ts packages/components/src/renderers/form/__tests__/form-server-field-errors.test.tsx,预期全绿;在 console 新建一个 SKU 重复的产品,SKU 输入框下出现 "A record with this SKU already exists",无 toast。What changes
extractFieldErrors(@object-ui/react): when no field-error list is present and the refusal's code isUNIQUE_VIOLATION(read fromcode, thendetails.code, the same pairisPermissionErrorreads), the single stringfield(details.fieldon the raw@objectstack/clienterror,fieldon a hand-rolled one) becomes oneWriteFieldErrorwhose message is the server's sentence (extractWriteErrorMessage). Withoutfieldthe answer staysnull.form.tsx: its existingextractFieldErrorsthenform.setErrorpath marks the input, and a fully attributed refusal shows no banner and no toast.@object-ui/reactpatch.Why
The server half shipped in #26867: the 409
UNIQUE_VIOLATIONbody is{ error, code, developerMessage?, field?, object? }, witherrornaming the field by its label.@objectstack/clientparks that flat body onerr.details, andnormaliseClientError(@object-ui/data-objectstack) passes a unique 409 through unchanged. The last hop read only arrays (validationErrors,details.fields,fields), so it answerednulland the dialog showed the generic banner and toast.Side effect, measured:
InlineEditSaveBar(@object-ui/plugin-detail) calls the same helper, so on the atomic save path a unique refusal withfieldis now attributed to that field (labelled in the bar and published as the per-field hint) instead of the generic text; in callback mode it was already attributed to the in-flight field. Its suites stay green (below).Tests
error-message.test.ts: a unique refusal withfieldyields one entry carrying the server's sentence (assertscodeandhttpStatus409 on the refusal); a hand-rolled one with top-levelfield; withoutfieldit answersnull; anINVALID_FIELDcarryingfieldstill answersnull.error-message.normalisation-boundary.test.ts: the refusal survivesnormaliseClientErrorwithcode,httpStatusand the field entry intact.form-server-field-errors.test.tsx: a rejected submit marks theskuinput (aria-invalid, sentence under it, no toast); withoutfieldthe toast carries the generic sentence and no input is marked.Acceptance notes
ApiDataSource, which embeds the body as a JSON tail in the message string, is not read forfield; the ObjectStack adapter is the console's path.Verification
Tree:
a17ebf4eon base7eb613c3.Tests and ablation
scripts/ablation-replace.mjs(unique branch replaced byreturn null): exactly the four new positive tests red (two helper, one boundary, one form render), 42 passed; restored, blob equals HEAD,git diff HEADempty. Tests resolve@object-ui/reacttosrcthrough the ui vitest alias, so no dist leg.packages/plugin-detail/src/__tests__/,packages/react/,apps/console/src/pages/settings/,apps/console/src/pages/system/: Test Files 385 passed, 1 skipped; Tests 3607 passed, 18 skipped.type-checkfor@object-ui/reactand@object-ui/components: exit 0. eslint on the four files: 0 errors.Derived gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 34 families, each run, all exit 0, includingnode scripts/check-sdui-manifest.mjsandpnpm --dir ui exec vitest run scripts/.--ranreconciliation: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN.Human Minimum
packages/specholds no runtime logic.pnpm lintpasses. (eslint on the touched files: 0 errors; the repo-wide run is CI's)main, one pull request.packages/spectouched: its generated artifacts are regenerated and committed. (not touched)content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code