Summary
POST /api/v1/data/:object/import runs field-level validation only on the real write, not on the dry run. The exact same request body with dryRun:true reports the row as valid; with dryRun:false it fails. The Console import wizard surfaces the dry run as "验证数据 / 导入前在服务端逐行校验", so users are told their file is clean and then watch the import drop every row.
Version: @objectstack/* 17.0.0-rc.0.
Reproduce
- Declare a
number field with a bound:
penalty_amount: {type: 'number',label: '处罚金额',precision: 12,scale: 2,required: true,min: 0,max: 9999999.99}- In the Console list view → 导入 → upload a CSV whose row violates the bound (
处罚金额 = -500). - On the preview step click 校验数据 (the dry run).
- Then click 导入 1 行 (the real write).
Actual
Dry run — POST /api/v1/data/os_tianshun_ehr_penalty_standard/import:
{"object":"os_tianshun_ehr_penalty_standard","dryRun":true,"writeMode":"insert",
"total":1,"ok":1,"errors":0,"created":1,"updated":0,"skipped":0,
"results":[{"row":1,"ok":true,"action":"created"}]}UI shows 「全部 1 行均有效。」
Real write — same endpoint, same payload, only dryRun flipped:
{"object":"os_tianshun_ehr_penalty_standard","dryRun":false,"writeMode":"insert",
"total":1,"ok":0,"errors":1,"created":0,"updated":0,"skipped":0,
"results":[{"row":1,"ok":false,"action":"failed",
"error":"penalty_amount must be ≥ 0","code":"VALIDATION_FAILED"}]}UI shows 「已导入 0 条 / 已跳过 1 条 · 第 1 行:penalty_amount must be ≥ 0」.
Note the dry run even claims created: 1.
Expected
The dry run should run the same validateRecord pass as the write path and report ok:0, errors:1 with the same per-row error. A dry run that does not validate is worse than no dry run — it converts "your file has a problem" into a false all-clear.
Scope
Not specific to min/max — the dry run appears to cover only mapping/lookup resolution (it does catch 申请部门: 找不到匹配 "X" 的记录 and required-lookup misses). Anything enforced by validateRecord (numeric range, string length, type checks) is invisible to it, so the same false all-clear should reproduce with e.g. maxLength.
Impact
Found while adding range guards to the settlement fields of a production MES app (25 objects). The import wizard is the primary bulk-data path during implementation; a green pre-check that doesn't predict the write outcome makes the whole pre-check step untrustworthy, and reviewers can't use it as acceptance evidence.
Related: the per-row error string is hardcoded English + API field name — filed separately.
Summary
POST /api/v1/data/:object/importruns field-level validation only on the real write, not on the dry run. The exact same request body withdryRun:truereports the row as valid; withdryRun:falseit fails. The Console import wizard surfaces the dry run as "验证数据 / 导入前在服务端逐行校验", so users are told their file is clean and then watch the import drop every row.Version:
@objectstack/* 17.0.0-rc.0.Reproduce
numberfield with a bound:处罚金额 = -500).Actual
Dry run —
POST /api/v1/data/os_tianshun_ehr_penalty_standard/import:{"object":"os_tianshun_ehr_penalty_standard","dryRun":true,"writeMode":"insert", "total":1,"ok":1,"errors":0,"created":1,"updated":0,"skipped":0, "results":[{"row":1,"ok":true,"action":"created"}]}UI shows 「全部 1 行均有效。」
Real write — same endpoint, same payload, only
dryRunflipped:{"object":"os_tianshun_ehr_penalty_standard","dryRun":false,"writeMode":"insert", "total":1,"ok":0,"errors":1,"created":0,"updated":0,"skipped":0, "results":[{"row":1,"ok":false,"action":"failed", "error":"penalty_amount must be ≥ 0","code":"VALIDATION_FAILED"}]}UI shows 「已导入 0 条 / 已跳过 1 条 · 第 1 行:penalty_amount must be ≥ 0」.
Note the dry run even claims
created: 1.Expected
The dry run should run the same
validateRecordpass as the write path and reportok:0, errors:1with the same per-row error. A dry run that does not validate is worse than no dry run — it converts "your file has a problem" into a false all-clear.Scope
Not specific to
min/max— the dry run appears to cover only mapping/lookup resolution (it does catch申请部门: 找不到匹配 "X" 的记录and required-lookup misses). Anything enforced byvalidateRecord(numeric range, string length, type checks) is invisible to it, so the same false all-clear should reproduce with e.g.maxLength.Impact
Found while adding range guards to the settlement fields of a production MES app (25 objects). The import wizard is the primary bulk-data path during implementation; a green pre-check that doesn't predict the write outcome makes the whole pre-check step untrustworthy, and reviewers can't use it as acceptance evidence.
Related: the per-row
errorstring is hardcoded English + API field name — filed separately.