Repository navigation
feat(console): approval requests through the standard provider:'api' door (objectui#12032) - #12043
Merged
objectstack-fleet[bot] merged 2 commits intoOct 9, 2026
Merged
Conversation
…door (objectui#12032) A1 of objectui#2763. A routed DataSource, one per inbox scope: reads of sys_approval_request go to the approvals routes through ApiDataSource, and every other resource and method to the console's own adapter. Rows come back as the approvals service serves them, so viewer and decision_progress are fields the standard ListView and RecordDetailView bind. - The list route has a closed parameter set: $top, $skip and $search become limit, offset and q; $select is dropped; every other name is refused with UNSUPPORTED_QUERY_PARAM before any request, so ListView shows its error panel rather than unscoped rows. - The get sends no scope and no QueryParams (the route reads none). - ApiDataSource.findOne resolves null only on a 404 and rejects every other failure, so the record page tells no-access and load-failed from not-found (objectui#11902 states). - approvalsApi.ts exports API_BASE so both reach the same routes. Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
…trictly (objectui#12032) Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
objectstack-fleet
Bot
deleted the
claude/issue-12032-approvals-datasource
branch
October 9, 2026 09:29
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.
Fixes #12032
Clause-②: no
A1 of the approvals rebuild (objectui#2763): approval requests now reach the standard
ListViewandRecordDetailViewthrough the existingprovider: 'api'door, withvieweranddecision_progressas ordinary fields. Built to the claim amendment on the card (comment 6077174661), which answered the dev report's three questions (comment 6077126267) with Q1 A, Q2 A and Q3 A.What changed
apps/console/src/services/approvalRequestsDataSource.ts(new).createApprovalRequestsDataSource({ host, scope })returns a routedDataSource, one per inbox scope:awaiting_mestatus=pending&approverId=every identity, comma-joinedsubmitted_by_mesubmitterId=the userallsys_approval_requestgo to/approvals/requeststhroughApiDataSource. Every other resource and method goes to the host adapter, untouched.RecordDetailViewneeds this, because it sends its side reads (thesys_userdirectory on every mount, comments, activities, history) through the same source.$topbecomeslimit,$skipbecomesoffsetand$searchbecomesq.$selectis dropped: the route has no projection, so dropping it widens columns, never rows. Every other name ($filter,$orderby,$expand,$searchFields, or anything unknown) is refused before any request with anUNSUPPORTED_QUERY_PARAMerror.ListViewclassifies that code as a rejected query and shows its error panel, never unfiltered rows.QueryParams, because the route reads none. The id is encoded into the path.vieweris on list and get rows.decision_progressis on the get only, which is the contract: single-request reads of pending requests.packages/core/src/adapters/ApiDataSource.ts. OnlyfindOne'scatchis narrowed. It answersnullon a 404 and rethrows every other failure (a 403, a 5xx, a transport error), asObjectStackAdapter.findOnedoes. The 404 is recognised from the message the class's ownrequestcomposes ("ApiDataSource: HTTP 404 …"), so no error shape changes. Before this, a refused or failed read through anyprovider: 'api'source reached the record page as "Record not found" instead of its "no access" or "could not load" state (objectui#11902).apps/console/src/services/approvalsApi.ts.API_BASEis now exported, so the new module reaches the same routes without a second spelling of where they live. This is the one helper the claim allows sharing; nothing else in the file moved..changeset/12032-approval-requests-data-source.md:patchon@object-ui/coreand@object-ui/console.There is no new package export, prop, registry type or pack key. Nothing mounts the source yet; B1 and B2 will.
Pins
apps/console/src/services/approvalRequestsDataSource.test.tsx, against a fake approvals server. The fake restates the list route's closed set verbatim (APPROVAL_REQUEST_LIST_PARAMSfrom objectstack's REST server) and its bare-row get:$top,$skip,$search, with$selectdropped andtotalfrom the envelope), and the scope and paging riding together;$filter,$orderby,$expandand$searchFieldsrefused before any request, withcodeUNSUPPORTED_QUERY_PARAMand the parameter named;ListView: a scoped list reads withlimitand no$name and draws the rows; the toolbar search reaches the route asq; and a view that needs$filter,$orderby,$expandor$searchFieldsshows the panel withdata-error-kindrejected, draws no rows and sends no request;vieweranddecision_progressas fields and no query on the wire; the id encoded; a 404 reading asnull;apps/console/src/services/approvalRequestsDataSource.recordPage.test.tsxmounts the realRecordDetailViewover the routed source, with only the transport doubled:record-access-deniedand never "Record not found";record-load-failedwith Retry;packages/core/src/adapters/__tests__/ApiDataSource.test.ts:findOnerejects on a 403, on a 500 and on a transport failure, beside the unchanged "should return null on 404" pin.Reverse verification
Both runs used objectstack's
scripts/ablation-replace.mjsin wrap mode, from commitdb70859c0. The anchor was hit once and the blob change was proven on disk. Each restore was proven: blob equal to HEAD, andgit diff HEADempty. Both runs resolve to source through the vitest alias for@object-ui/core, so no build is in the path.findOne(throw err;replaced withreturn null;). Predicted red: the three core rejection pins and the record page's 403 and 500 pins. Observed:Tests 5 failed | 32 passed (37), exactly those five. The 404 control and the found-request control stayed green.$filter($filteradded to the dropped set). Predicted red: the$filterunit refusal and theListView$filterpanel pin. Observed:Tests 2 failed | 22 passed (24), exactly those two.Local gates (all on
db70859c0, from the worktree root)pnpm exec vitest run packages/core/:Test Files 198 passed (198),Tests 3939 passed | 27 skipped (3966).pnpm exec vitest run apps/console/src/services/:Test Files 3 passed (3),Tests 37 passed (37).ApiDataSourceorresolveDataSource(15 files across components, fields, plugin-detail, plugin-gantt, plugin-kanban, plugin-list, plugin-view and react):Test Files 15 passed (15),Tests 260 passed (260).pnpm --filter @object-ui/core type-checkandpnpm --filter @object-ui/console type-check: exit 0, after building the dependency closure (turbo run build --filter='@object-ui/console^...', 34 tasks successful).pnpm exec eslinton the 6 touched files (count from--format json): 0 errors. The 11 warnings are allno-explicit-anyin pre-existing lines; the new files have none. Type-aware linting is not enabled ineslint.config.js(noprojectServiceorparserOptions.project), so this diff cannot move another file's verdict.check:control-bytes,check:test-path-roots,check:changeset-claims,check:pending-changeset-literals,check:new-line-citations,check:vi-mock-specifiers,check:vi-mock-inherit,check:vi-mock-override-shape,check:phantom-deps,check:unused-deps,check:self-importandcheck:unreferenced-sources: each exit 0.node scripts/check-changeset-presence.mjs: "6 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)".check:eager-closureand the first-load bytes, because a console production build was out of reach locally; CI's Bundle Analysis owns them. Nothing eager imports the new module. The core change is a few lines inside one method.Acceptance notes
aggregate,queryGroupHeaders,exportDownload) are refused when called forsys_approval_request, with the same code. Forwarded, they would read approval requests through the data API: outside the scope and withoutviewer. So a grouped grid or a server export on these lists fails loudly instead of widening rows. (2) The get sends none of the caller'sQueryParams, so the record page's$expand(it asks for every declared relation) is not sent. The route has no expansion and serves its own display names (submitter_name,record_title, …). Refusing$expandon the get would break every request page, becausesys_approval_requestdeclares lookups (submitter_id,organization_id). The seat may reverse either; each is pinned.sys_approval_requestall declaresortandfilter, and most show thesubmitter_idlookup. Mounted on this source, each would be refused ($orderby,$filter,$expand), as ruled. B2 authors views without them (scope comes from the source; usesubmitter_name), or a card with a pull takes the route-side option to the spec seat.viewerbut neverdecision_progress(contract), andListViewhonours aschema.dataprovider: 'api'config on the gantt view only. Both are recorded in the amendment.ApiDataSourcesection states nothing aboutfindOne's failure semantics, so none of its text became false. It was left alone because it is outside the claim's surface.Session:
https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8Generated by Claude Code