Repository navigation
fix(core): interpolate api action targets before a registered api handler - #26826
Conversation
A registered `api` handler (the console's) replaces `executeAPI`, the only
caller of `interpolateTarget` on the api path, so `${ctx.recordId}` went out
literally. The runner now hands the handler an interpolated target through
the same substitution and encoding a url target uses.
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jTDpMW8KaXLg1DFU6f
…angeset Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jTDpMW8KaXLg1DFU6f
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jTDpMW8KaXLg1DFU6f
席位核对ACCEPT · seat
Landing: no contract review is owed (no published schema, no governed text, |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 40560750659 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Requested by Steedos · project thread
Fixes #26722
Clause-②: no
Metadata-protocol: none
维护者速读
type: "api"的动作,目标地址里的${ctx.recordId}等占位符现在会先被替换成真实值再发请求,和url动作用的是同一套替换与编码。executeAPI,请求带着字面占位符发出(…/$%7Bctx.recordId%7D),记录级 PATCH/POST 动作在每条记录上都打到不存在的记录。${占位符」的动作;不带占位符的动作原对象原样交给处理器。回滚即 revert 本 PR。ui/packages/core/src/actions/ActionRunner.ts中dispatchCollected的已注册处理器分支;ActionRunner.apiHandlerTarget-26722.test.ts。pnpm --dir ui exec vitest run packages/core/src/actions/__tests__/ActionRunner.apiHandlerTarget-26722.test.ts,预期 7 个用例全绿。What changes
ActionRunner.dispatchCollectednow runsinterpolateTargeton anapiaction's stringtargetbefore handing the action to a registeredapihandler. It is the same call the built-inexecuteAPIandexecuteUrlmake, so an api target gets the same scopes (ctx,param) and the same per-valueencodeURIComponentas a url target. There is no second interpolator and no change inapp-shell.executeAPIalready interpolated, but the Console registers its ownapihandler (useConsoleActionRuntime'sapiHandler, wired throughActionProvider'shandlers), and that handler replacesexecuteAPIentirely. The handler only filled{field}row tokens, so${ctx.recordId}reachedauthFetchliterally.flow,script,modal) still gets its target exactly as authored.interpolateTargetnow name the new caller.Why
The spec documents
${ctx.recordId}for api targets, and a url action with the same token already resolved. Record-level API actions could not be built without code because they failed on every record.Tests
New
ui/packages/core/src/actions/__tests__/ActionRunner.apiHandlerTarget-26722.test.ts(7 cases):/api/v1/data/gapdata_item/gd_001;a%2Fb%20c);flowhandler still gets its target as authored;Acceptance notes
${ctx.recordId}on a list row action resolves from the runner's context (the page record), not from the clicked row. That is how url targets behave today too, so it is unchanged here. Row actions keep using the{field}row tokens the Console's handler already fills.Verification
Ablation (one-time, on head 333eef5, mutation restored)
With
actionType === 'api'replaced byfalsein the new branch (viascripts/ablation-replace.mjs, anchor 1 → 0, blob restored to HEAD's), the new file reads 3 failed / 4 passed: the PATCH, POST and encoding cases go red, and the built-in, url, flow and same-object controls stay green.Suites and type-check (head 333eef5)
pnpm --dir ui exec vitest run packages/core/ --maxWorkers=1under the verify lock: Test Files 215 passed (215), Tests 4112 passed, 27 skipped.pnpm --filter @object-ui/core run type-check: exit 0.pnpm --dir ui exec vitest run scripts/ --maxWorkers=2under the verify lock: Test Files 178 passed, 2 skipped (180); Tests 5065 passed, 2 skipped.pnpm check:new-line-citations(fromui/): 0 new citations.eslinton both changed files: 0 errors; the 15 warnings are existingno-explicit-anylines the diff does not touch.app-shellandcomponentssuites were not run locally. Agit grepover ui tests finds no test outsidepackages/corethat registers an api handler and dispatches a target carrying a${token, and untokenized actions reach the handler as the same object. CI's Test UI runs those suites.Derived gates (head 333eef5)
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 34 commands. 33 ran locally, each exit 0, and with the scripts suite--ranreconciles all of them (Run reconciliation — 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN). The 34th,pnpm --dir ui exec vitest run scripts/, is in the suites block above.Generated by Claude Code