feat: migrate SDK to use SetProjectMemberRole and RemoveProjectMember RPCs#1508
feat: migrate SDK to use SetProjectMemberRole and RemoveProjectMember RPCs#1508whoAbhishekSah wants to merge 3 commits intomainfrom
Conversation
… RPCs Replace policy-based member management (createPolicyForProject, listPolicies, deletePolicy, createPolicy) with the new atomic RPCs. React SDK: - project-members.tsx: addMember/addTeam use SetProjectMemberRole - project-member-columns.tsx: updateRole uses SetProjectMemberRole - remove-project-member-dialog.tsx: uses RemoveProjectMember - add-service-account-dialog.tsx: uses SetProjectMemberRole - manage-service-user-projects-dialog.tsx: uses both RPCs Admin SDK: - use-add-project-members.tsx: uses SetProjectMemberRole - remove-member.tsx: uses RemoveProjectMember - assign-role.tsx: uses SetProjectMemberRole Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 1 minutes and 34 seconds. ⌛ 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 (1)
📝 WalkthroughWalkthroughThis PR implements the migration from policy-based member management to role-based member management across multiple SDK files. It replaces client-side policy CRUD operations (listPolicies, deletePolicy, createPolicy) with direct role-assignment mutations (setProjectMemberRole, removeProjectMember) for projects and service users. Role IDs are resolved via listRoles queries, and the proton dependency is updated. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~28 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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 |
…ions Fetch project-scoped roles via listRoles and resolve the role UUID before calling SetProjectMemberRole. Consistent with the role change dropdown which already uses UUIDs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AddMemberDropdown is a separate child component that needs the roles array to resolve the viewer role UUID. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Manual Testing — PASSTested locally with client demo app on clean Frontier + SpiceDB.
All flows use |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
web/sdk/admin/views/organizations/details/projects/members/assign-role.tsx (1)
88-107:⚠️ Potential issue | 🔴 Critical
setProjectMemberRoleonly preserves the last role in this loop.This RPC replaces the member’s project role set; it does not append to it. Iterating
assignedRolesArrhere means each call overwrites the previous one, so only the last role survives whileonRoleUpdatestill reports every selected role locally.Suggested fix
const onSubmit = async (data: FormData) => { try { const assignedRolesArr = Array.from(data.roleIds); + if (assignedRolesArr.length !== 1) { + throw new Error("Project members can only have one direct project role"); + } - for (const roleId of assignedRolesArr) { - await setProjectMemberRole( - create(SetProjectMemberRoleRequestSchema, { - projectId, - principalId: user?.id || "", - principalType: "app/user", - roleId, - }), - ); - } + await setProjectMemberRole( + create(SetProjectMemberRoleRequestSchema, { + projectId, + principalId: user?.id || "", + principalType: "app/user", + roleId: assignedRolesArr[0], + }), + );web/sdk/react/views/projects/details/remove-project-member-dialog.tsx (1)
20-25:⚠️ Potential issue | 🟠 MajorMake
memberTyperequired instead of defaulting to'user'.This makes missing caller updates look valid.
web/sdk/react/views/projects/details/project-detail-page.tsx:255-260still opens the dialog withoutmemberType, so removing a team member will continue to sendapp/userand fail. Making the prop required forces the remaining call sites to pass the correct principal type.Suggested fix
export interface RemoveProjectMemberDialogProps { open: boolean; onOpenChange?: (value: boolean) => void; projectId: string; memberId: string; - memberType?: 'user' | 'group'; + memberType: 'user' | 'group'; } @@ export const RemoveProjectMemberDialog = ({ open, onOpenChange, projectId, memberId, - memberType = 'user' + memberType }: RemoveProjectMemberDialogProps) => {Also applies to: 32-33, 52-57
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 96b266ff-0062-46b1-bc94-f27ec9d10147
⛔ Files ignored due to path filters (1)
web/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (9)
web/sdk/admin/views/organizations/details/projects/members/assign-role.tsxweb/sdk/admin/views/organizations/details/projects/members/remove-member.tsxweb/sdk/admin/views/organizations/details/projects/use-add-project-members.tsxweb/sdk/package.jsonweb/sdk/react/views/api-keys/details/manage-service-user-projects-dialog.tsxweb/sdk/react/views/api-keys/list/add-service-account-dialog.tsxweb/sdk/react/views/projects/details/project-member-columns.tsxweb/sdk/react/views/projects/details/project-members.tsxweb/sdk/react/views/projects/details/remove-project-member-dialog.tsx
| const ownerRoleId = useMemo( | ||
| () => rolesData?.roles?.find(r => r.name === PERMISSIONS.RoleProjectOwner)?.id ?? '', | ||
| [rolesData] | ||
| ); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
sed -n '157,224p' web/sdk/react/views/api-keys/details/manage-service-user-projects-dialog.tsxRepository: raystack/frontier
Length of output: 2157
onAccessChange closes over a stale ownerRoleId.
The callback uses ownerRoleId at line 200 and line 206, but ownerRoleId is missing from the dependency array at line 223. When the dialog opens and listRoles populates the role ID, onAccessChange still holds the stale empty string from initial render, causing "Project owner role not found" errors on subsequent project access changes.
Suggested fix
- [serviceUserId, setProjectMemberRole, removeProjectMember]
+ [serviceUserId, ownerRoleId, setProjectMemberRole, removeProjectMember]| const ownerRoleId = useMemo( | ||
| () => rolesData?.roles?.find(r => r.name === PERMISSIONS.RoleProjectOwner)?.id ?? '', | ||
| [rolesData] | ||
| ); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
sed -n '111,209p' web/sdk/react/views/api-keys/list/add-service-account-dialog.tsxRepository: raystack/frontier
Length of output: 3022
Validate and include ownerRoleId in the useCallback dependency array.
The validation of ownerRoleId occurs after the service user is created (line ~147), which can leave an orphaned user if the roles query is slow or fails. Additionally, the dependency array (lines 201-209) omits ownerRoleId despite using it in the handler, causing the callback to close over the initial empty string even after listRoles resolves.
Suggested fix
const onSubmit = useCallback(
async (data: FormData) => {
if (!orgId) return;
try {
+ if (!ownerRoleId) {
+ throw new Error('Project owner role not found');
+ }
+
const serviceUserResponse = await createServiceUser(
create(CreateServiceUserRequestSchema, {
orgId,
body: create(ServiceUserRequestBodySchema, {
title: data.title
})
})
);
const serviceUserId = serviceUserResponse.serviceuser?.id;
if (!serviceUserId) return;
- if (!ownerRoleId) throw new Error('Project owner role not found');
await setProjectMemberRole(
create(SetProjectMemberRoleRequestSchema, {
projectId: data.project_id,
principalId: serviceUserId,
principalType: PERMISSIONS.ServiceUserPrincipal,
roleId: ownerRoleId
})
);
@@
[
orgId,
+ ownerRoleId,
createServiceUser,
setProjectMemberRole,
createServiceUserToken,
queryClient,
transport,
Summary
Replace policy-based project member management with the new atomic RPCs across React SDK and Admin SDK.
Before: SDK manipulated policies directly (
createPolicyForProject,listPolicies → deletePolicy x N → createPolicy)After: SDK uses
SetProjectMemberRoleandRemoveProjectMemberInconsistency found and fixed
The "Add member" flow was passing role names (e.g.,
app_project_viewer) as theroleId, while the "Change role" dropdown was passing role UUIDs from the fetched roles list. The backendSetProjectMemberRoleexpects a UUID.Fix: All flows now fetch project-scoped roles via
listRolesand resolve the role UUID before calling the RPC. This makes add member, role change, and service account assignment all consistent.Net result: simpler code, no client-side policy orchestration.
Closes #1462
Test plan
🤖 Generated with Claude Code