refactor(membership): make SetGroupMemberRole an upsert#1603
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Coverage Report for CI Build 25780601827Coverage decreased (-0.003%) to 42.288%Details
Uncovered Changes
Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
SetGroupMemberRole now adds a new member when the principal has no existing group policy, and changes the role otherwise. New adds validate that the principal is a member of the parent organization. The min-owner constraint still applies to demotions. This unifies add and role-change into one public RPC so the SDK can use a single mutation for group membership writes, eliminating the need to expose AddGroupMember as a proto RPC. - Drops ErrNotMember from the SetGroupMemberRole path; ErrNotOrgMember surfaces instead when an upsert-add targets a non-org-member. - Handler error mapping updated; new ErrNotOrgMember Connect mapping. - New unit tests cover both upsert paths: add (with org-member check) and the existing change paths remain intact. AddGroupMember (service-only) is unchanged and continues to be used internally by OnGroupCreated, where the creator is always a fresh add. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Make explicit that both paths use the requested role: add with role on a new add, replace existing role with the requested role on a change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bb23f69 to
e774125
Compare
Manual Testing Results - SetGroupMemberRole UpsertTested the new upsert behavior where Test Environment
New Upsert Add Behavior
Unhappy Path (Constraints Preserved)
Summary
The upsert behavior works correctly:
|
After the SetGroupMemberRole upsert in #1603, AddGroupMember and the add-path of SetGroupMemberRole did the same work. The three callers (OnGroupCreated, AddGroupUsers handler, invitation accept) are all fresh-add sites that don't need the strict ErrAlreadyMember signal — they're either guaranteed-new (group create) or already pre-filtered (invitation accept) or fine with idempotent semantics (AddGroupUsers). Switching them to SetGroupMemberRole makes the writes idempotent and removes a near-duplicate function. The "added" vs "role_changed" audit distinction is preserved by SetGroupMemberRole's own branching, so audit semantics are unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After the SetGroupMemberRole upsert in #1603, AddGroupMember and the add-path of SetGroupMemberRole did the same work. The three callers (OnGroupCreated, AddGroupUsers handler, invitation accept) are all fresh-add sites that don't need the strict ErrAlreadyMember signal — they're either guaranteed-new (group create) or already pre-filtered (invitation accept) or fine with idempotent semantics (AddGroupUsers). Switching them to SetGroupMemberRole makes the writes idempotent and removes a near-duplicate function. The "added" vs "role_changed" audit distinction is preserved by SetGroupMemberRole's own branching, so audit semantics are unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces three legacy addGroupUsers call sites with the upsert
setGroupMemberRole RPC, which adds the principal as a group member with
the requested role. The app_group_member role UUID is resolved once
from listRoles({scopes: ["app/group"]}) and passed down to each
add-member component.
- add-member-menu.tsx (views-new): receives roles from team-details-view,
derives memberRoleId, swaps the addGroupUsers mutation for
setGroupMemberRole with PERMISSIONS.UserPrincipal + memberRoleId.
- team-members.tsx (legacy view): same swap; AddMemberDropdown now takes
roles as a prop and derives memberRoleId internally.
- invite-team-member-dialog.tsx: drops the two-step
addGroupUsers + createPolicy workaround entirely. The dialog already
collects the chosen role from the user; we now hand that role directly
to setGroupMemberRole in one call.
Depends on #1603 being deployed to production
(SetGroupMemberRole upsert) — before that, the new SDK calls would hit
the strict-set rejection for not-yet-members.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
SetGroupMemberRolenow upserts: it adds the principal as a group member if they don't already have a group policy, or changes their role if they do. New adds validate that the principal is a member of the parent organization. The min-owner constraint still guards demotions.This unifies add and role-change into a single public RPC so the SDK can use one mutation for group membership writes, removing the need to expose
AddGroupMemberas a proto RPC.Why
While planning the SDK migration off the legacy
AddGroupUsersRPC, we hit the question: do we expose a newAddGroupMemberRPC, or relaxSetGroupMemberRoleto upsert? Going with upsert because:ErrNotMember) wasn't load-bearing for any caller.AddGroupMember(service-only) is unchanged and continues to be used internally byOnGroupCreatedfor the group-create owner add, where strict semantics make sense.Test plan
go build ./...go test ./core/membership/... ./internal/api/v1beta1connect/...gofmt -lcleanFollow-ups
AddGroupUsers→ loopsSetGroupMemberRole.AddGroupUsershandler to membership, deleteAddMember/AddUsers/addMemberPolicyfromcore/group/service.go.