Repository navigation
Commit 81d00a4
fix(rest)!: an async import job for an unregistered object is refused with 404 OBJECT_NOT_FOUND before any job is created (#27711)
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F
<!-- claude-projects-attribution: {"github_login":"os-sales"} -->
_Requested by **Sales** · [project
thread](https://claude.ai/code/project/chan_01D5bWFY3rFzgJcytD5gD4Uu?thread=cmsg_01D5bWFY3rFzgJcytD5gD4UuPMZ8BHWtPf6nBTp51qSM64)_
Fixes #27501
Scope: ask (a) only, per triage 6498786930. Ask (b), "a job whose every
row failed does not read `succeeded`", was split to the decision card
#27676 and is not built here: the job's terminal status is unchanged.
Clause-②: no (narrowing)
Metadata-protocol: none
Skills-docs: none (no published skill names the async import job door's
object handling)
## 维护者速读
- 改了什么:`POST /data/OBJ/import/jobs` 在对象未注册时直接返回 404 `OBJECT_NOT_FOUND`,与
`POST /data/OBJ` 的应答信封完全一致;不再建 `sys_import_job`,也不启动后台任务。
- 为什么改:之前拼错对象名会得到 201、一个任务 id,随后任务显示
`succeeded`,只有逐行错误里写着找不到对象;自动化导入方会把这当成成功。
- 风险与回滚:只收窄一个入口;已注册对象行为不变。对象元数据读取本身出错时不拦截(与 API
暴露闸门同样放行),逐行写入仍按数据入口应答。回滚即还原 `data-action-routes.ts` 的一处调用。
- 先看:`packages/rest/src/data-action-routes.ts` 的
`assertImportObjectExists`
及其调用点;`packages/rest/src/import-job-integration.test.ts` 末尾新增的 describe。
- 两分钟验证:`pnpm --filter @objectstack/rest exec vitest run
src/import-job-integration.test.ts`,预期 15/15 通过。
- 需要你做的:无,席位落地。
## What changes
- `POST /data/:object/import/jobs` now reads the object by name, the
same read the API-exposure gate makes (the protocol's execution face
where it has one, else `getMetaItem`), right after that gate and before
the body is parsed. If no object is behind the name, it throws
`objectNotFoundError(objectName)` from `@objectstack/core`. That is the
one factory the protocol's existence gate and the engine's verbs throw,
and the route's `handleRouteError` classifies it. The answer is 404
`OBJECT_NOT_FOUND` with the same body `POST /data/:object` gives the
same name. No new code is minted.
- No `sys_import_job` row is written and no worker is scheduled for a
refused request.
- A read refused `OBJECT_PACKAGE_DISABLED` propagates (the protocol's
by-name read documents that refusal; this was not measured here). Any
other thrown read fails open and logs at `warn`, as the exposure gate's
`loadObjectItem` does: such a read says nothing about existence, and
each row's data call still answers for the object. Two existing
real-protocol harnesses whose metadata store is not provisioned
(`import-row-schema-drift-20701`, `import-row-dropped-fields-20701`)
need this; with the fault propagated they answered 503.
- Changeset: `@objectstack/rest` patch, with a BREAKING note and the
ADR-0087 `not-required (no-migration-prescription)` marker.
## Tests
Two cases in `packages/rest/src/import-job-integration.test.ts`. They
use the real `RestServer` route, the real `ObjectQL` engine with
`ObjectStackProtocolImplementation`, and SQLite `:memory:`.
- An unregistered object → 404 and `OBJECT_NOT_FOUND`. The body is
`toEqual` the data door's (`POST /api/v1/data/:object`) body for the
same name. `sys_import_job` holds 0 rows, counted at the engine before
and after.
- Control: a registered object → 201, `{object:'task', status:'pending',
total:1}`, and exactly one `sys_import_job` row once the job finishes.
## Acceptance notes
- Out of scope: the synchronous door `POST /data/:object/import` has the
same gap. It runs no existence check, answers 200, and reports
`OBJECT_NOT_FOUND` per row (read from the handler; the card's
reproduction step 3 measured it). Triage did not name it, so it is not
changed here. It is reported to the seat.
- The pins drive the route handler registered on a real `RestServer`,
not an HTTP transport. This is the harness the file's existing cases
use.
## Verification
Final head `bb1e1d2f` (merged with `origin/main`).
<details><summary>Red before the fix (commit fcde8d5, test
only)</summary>
```
× answers 404 OBJECT_NOT_FOUND, the data door's answer, and persists no job row
AssertionError: expected 201 to be 404 // Object.is equality
Tests 1 failed | 14 passed (15)
```
</details>
<details><summary>Ablation on the final head
(scripts/ablation-replace.mjs)</summary>
```
ablation-replace: ok mutation landed: anchor 1 -> 0, blob e436474 -> 01857e27771d
× answers 404 OBJECT_NOT_FOUND, the data door's answer, and persists no job row
AssertionError: expected 201 to be 404 // Object.is equality
Tests 1 failed | 14 passed (15)
ablation-replace: ok restored: blob == HEAD (e436474) and `git diff HEAD` is empty
```
The ablation removed the `await assertImportObjectExists(...)` call. The
suite imports `./rest-server` from source, so no dist rebuild was
involved.
</details>
<details><summary>Package suites and gates</summary>
Read at head `bb1e1d2f`, except where a line names another sha.
- `pnpm --filter @objectstack/rest test`: exit 0, 340/340 files, 6668
passed, 326 skipped. Run at 06dd5e1; the commit after it changes only
the new test's row-count helper, and that file re-ran 15/15 at bb1e1d2.
- `pnpm --filter @objectstack/rest typecheck`: exit 0 (06dd5e1, after a
declaration build of the `@objectstack/rest...` closure).
- `eslint packages/rest --no-inline-config`: exit 0, 0 errors. The 2
warnings are in an untouched file.
- `dispatch-gates --commands` derived 59 families. 57 exited 0.
`check:query-options-erasure` was red at first (the new test's `as any`
options cast grew the test surface 236 → 237); it was fixed in bb1e1d2
and re-ran green at 236. `check:dual-build-cjs-loads` exited 3,
PREREQUISITE NOT MET: it reads every package's dist and needs a full
`pnpm build`, so it is NOT MEASURED locally and the CI `Lint & Repo
Gates` job carries it. `--ran`: 59 derived, 59 run, 0 NOT-MEASURED, 0
UNRUN.
- The test-reading gates re-ran green at bb1e1d2:
cross-package-test-inputs, test-source-alias, engine-double-contract,
nul-bytes, rest-log-spy-declared and doc-authoring.
- Dogfood battery (`(narrowing)` arm) at bb1e1d2: os-verify-lock
VERDICT command-exit 0 (held 36m11s), closure build exit 0, 352 files
passed and 3 skipped, 2844 tests passed and 20 skipped, no failures
(os-dev-report 6508232970 on #27501).
- No open PR touches `data-action-routes.ts` or
`import-job-integration.test.ts` (REST read of all 29 open PRs' files).
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01L6bhgUCizoYWQVJWjhdR6F)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01L6bhgUCizoYWQVJWjhdR6F
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 74aae1e commit 81d00a4
3 files changed
Lines changed: 115 additions & 1 deletion
File tree
- .changeset
- packages/rest/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
80 | 80 | | |
81 | 81 | | |
82 | 82 | | |
83 | | - | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
84 | 86 | | |
85 | 87 | | |
86 | 88 | | |
| |||
308 | 310 | | |
309 | 311 | | |
310 | 312 | | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
311 | 349 | | |
312 | 350 | | |
313 | 351 | | |
| |||
592 | 630 | | |
593 | 631 | | |
594 | 632 | | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
595 | 641 | | |
596 | 642 | | |
597 | 643 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
127 | 127 | | |
128 | 128 | | |
129 | 129 | | |
| 130 | + | |
130 | 131 | | |
131 | 132 | | |
132 | 133 | | |
| |||
460 | 461 | | |
461 | 462 | | |
462 | 463 | | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
0 commit comments