feat: render the server's effective API operation set (#3391 PR-4) - #2823
Merged
Conversation
The frontend now consumes the per-object effective API operation set the server resolves (/me/permissions apiOperations, framework #3391) — never the raw apiMethods — so CRUD/Import/Export buttons match what the server will admit, and a 405 import refusal shows a dedicated message instead of a silent fallback. - core: resolveCrudAffordances(obj, effectiveApiOperations?) — optional 2nd arg intersects each affordance bit with its API operation (create/import→create/ import, edit→update, delete→delete, exportCsv→export). Omitting it leaves affordances unchanged (backward-compatible). - permissions: /me/permissions response carries per-object apiOperations; PermissionContextValue.getObjectApiOperations(object) exposes it (undefined when absent → current behavior); check() maps import→allowCreate, export→allowRead. Role-based + no-provider paths return undefined. - app-shell ObjectView: toolbar affordances intersect with effective operations (Import); the identity-import admin bypass is unaffected. - plugin-list ListView / plugin-grid ObjectGrid: Export button + handler gate on effective `export`; plugin-grid gains the @object-ui/permissions dependency. - plugin-grid ImportWizard: new isImportNotAllowed predicate (405 / OBJECT_API_METHOD_NOT_ALLOWED) at all catch sites STOPS with a dedicated grid.import.notAllowed message (10 locales + fallback), never falling back to the sync/legacy path — distinct from the 404 route-absent fallback. Tests: resolveCrudAffordances 2nd-arg matrix, getObjectApiOperations + check(import/export) mapping, isImportNotAllowed matrix (405 vs 404/unsupported). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012L8EfEa157Pe6C73qRnaJH
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
os-zhuang
marked this pull request as ready for review
July 27, 2026 03:21
This was referenced Jul 27, 2026
os-zhuang
added a commit
that referenced
this pull request
Jul 28, 2026
…e operation set (objectstack#3720) (#2889) The fourth surface objectstack#3391 left open. The toolbar (#2823), detail/form (#2832 + #2876) and related lists (#2832) all route through `resolveCrudAffordances`; the main list's row CRUD has its own resolver and none of those rounds reached it. Its gate was `operations ?? { update: !!onEdit, delete: !!onDelete }`, and ObjectView wires onEdit/onDelete unconditionally while view JSON rarely declares `operations` — so it was effectively always-on. A caller whose effective set carried neither `update` nor `delete` still got the row kebab's Edit/Delete and the bulk delete. - plugin-grid `resolveRowCrudAffordances` takes `managedBy` + `effectiveApiOperations` and resolves the object verdict through the shared `resolveCrudAffordances` policy, so the row gate is the same decision every other face makes. It also returns `objectCanDelete` — bulk delete rides `onBulkDelete`, a different callback from the row `onDelete`, so it must not be judged by whether the row handler happens to be wired. - plugin-grid `ObjectGrid` threads its existing `effectiveApiOps` (until now fed only to Export) into the row gate, and applies the delete verdict to bulk delete: the implicit `['delete']`, a declared `bulkActions: ['delete']`, and any `bulkActionDefs` entry with `operation: 'delete'`. Custom ids and non-delete operations pass through untouched. - plugin-list `ListView`'s own bulk bar (the non-grid views) drops its built-in `delete` under the same verdict. Also closes the ADR-0103 gap on this chain: the bucket lock was documented as applied upstream via the view's `operations.*`, but the all-open default meant it never was — an engine-owned system / append-only / better-auth object leaked a generic row Edit/Delete the engine rejects. A `userActions` opt-in still re-opens it. Intersection, never union. A missing effective set preserves current behavior. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
背景
#3391 的前端配套(方案里的 PR-4,objectui 侧)。框架侧 P1(spec/runtime/rest/hono)已合并(objectstack#3498),
/me/permissions现下发 per-objectapiOperations(服务端解析的有效操作集)。本 PR 让前端消费下发的 effective 结果——不读原始apiMethods、不自行派生——使按钮与服务端实际放行一致,并让导入 405 出独立文案而非静默回退。改动
resolveCrudAffordances(obj, effectiveApiOperations?)—— 新增可选第二参,逐位与 API 操作交集(create/import→create/import、edit→update、delete→delete、exportCsv→export)。省略即不变(向后兼容);空数组 = 全禁。/me/permissions响应补 per-objectapiOperations;PermissionContextValue.getObjectApiOperations(object)暴露之(缺失→undefined→回退现行为);check()补import→allowCreate、export→allowRead;角色 provider 与无 provider 路径返回undefined。ObjectView—— 工具栏 affordances 与对象 effective 操作交集(Import);平台管理员 identity-import 旁路不受影响。ListView/ plugin-gridObjectGrid—— Export 按钮与 handler 门控在 effectiveexport;plugin-grid 补@object-ui/permissionsworkspace 依赖(现缺)。ImportWizard—— 新增isImportNotAllowed(405 /OBJECT_API_METHOD_NOT_ALLOWED)谓词,在四个 catch 点(async / handleImport / sync / dry-run)先于 unsupported 判定;命中即终止并出独立文案grid.import.notAllowed(10 locale + fallback 字典),决不回退同步/legacy(它们同样 405),与 404「路由不存在→回退」语义相反。向后兼容
effective 缺失(unrestricted 对象 / 旧后端 / 无 permission provider)处处回退现行为;新旧前后端任意组合可用。
测试
resolveCrudAffordances第二参矩阵(缺失零变化 / 逐位 AND / 空数组全禁 / 不越权重开)。getObjectApiOperations读取与缺失、check('import')→allowCreate、check('export')→allowRead。isImportNotAllowed矩阵(code/status/statusCode/httpStatus/裸 405 命中;404、UNSUPPORTED_OPERATION 不命中;405 优先于 unsupported 判定)。type-check全绿;lint0 errors。关联
🤖 Generated with Claude Code
https://claude.ai/code/session_012L8EfEa157Pe6C73qRnaJH
Generated by Claude Code