Skip to content

Commit cf4bd9a

Browse files
committed
fix(plugin-sharing): withdraw through SharingService.revoke; bound the test double
The user-scoped withdrawal reuses the sharing service's revoke, as the whole-rule pass does, so the change adds no new engine write call site. The position-graph equivalence double applies the caller's limit. Claude-Session: https://claude.ai/code/session_01KqkTQNLcxVc588eZpJmp1r Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8ecf4bc commit cf4bd9a

2 files changed

Lines changed: 9 additions & 11 deletions

File tree

‎packages/plugins/plugin-sharing/src/position-graph.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ describe('PositionGraphService.holdsPosition — the user-scoped twin of the exp
119119
async find(object: string, options: any) {
120120
const f = options?.filter ?? options?.where ?? {};
121121
const rows: any[] = object === 'sys_user_position' ? ASSIGN : object === 'sys_member' ? MEMBER : [];
122-
return rows.filter((r) => Object.entries(f).every(([k, v]) => r[k] === v));
122+
return rows.filter((r) => Object.entries(f).every(([k, v]) => r[k] === v)).slice(0, options?.limit ?? rows.length);
123123
},
124124
} as any;
125125
const USERS = ['u_a', 'u_b', 'u_global', 'u_expired', 'u_pending', 'u_mixed', 'u_role_a', 'u_role_b', 'u_none'];

‎packages/plugins/plugin-sharing/src/sharing-rule-service.ts‎

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,9 +1644,10 @@ export class SharingRuleService implements ISharingRuleService {
16441644
* with how many OTHER users hold the recipient, so a bulk import of N rows
16451645
* costs N user-scoped passes, never N whole-rule passes.
16461646
*
1647-
* Withdrawal is one set-based delete, scoped by rule and recipient, for the
1648-
* reason {@link revokeRuleGrantsForRetiredRecipients} gives: under a system
1649-
* context `SharingService.revoke` is itself a plain delete by id.
1647+
* Withdrawal goes through `SharingService.revoke`, one grant at a time, as
1648+
* the whole-rule pass's own withdrawal does ({@link reconcile}); a user holds
1649+
* at most one grant per matched record, so the count is bounded by the
1650+
* rule's matched set.
16501651
*
16511652
* A `field` rule has no rule-wide recipient to check a user against; its
16521653
* recipients live on each record and are re-derived by that record's write
@@ -1676,13 +1677,10 @@ export class SharingRuleService implements ISharingRuleService {
16761677
for (const row of (existing ?? [])) held.set(String((row as any).record_id), row);
16771678

16781679
if (!(await this.ruleReachesUser(rule, userId, {}))) {
1679-
if (held.size === 0) return pass;
1680-
await this.engine.delete('sys_record_share', {
1681-
where: scope,
1682-
multi: true,
1683-
context: SYSTEM_CTX,
1684-
} as any);
1685-
pass.grantsRevoked = held.size;
1680+
for (const [, row] of held.entries()) {
1681+
await this.sharing.revoke(String(row.id), SYSTEM_CTX);
1682+
pass.grantsRevoked += 1;
1683+
}
16861684
return pass;
16871685
}
16881686

0 commit comments

Comments
 (0)