refactor: consolidate principal-by-resource listing into membership#1567
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 ignored due to path filters (1)
📒 Files selected for processing (31)
📝 WalkthroughWalkthroughThis PR refactors member listing across the codebase from policy-based role queries to membership-service-driven principal retrieval. It adds ChangesMembership Service Migration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 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. Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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 |
Manual API test resultsExercised all 13 modified handlers against a local Frontier build of this branch. Test fixture: one org (
24/24 PASS. role-name-to-ID resolution in |
Coverage Report for CI Build 25717610172Coverage increased (+0.06%) to 42.048%Details
Uncovered Changes
Coverage Regressions12 previously-covered lines in 3 files lost coverage.
Coverage Stats
💛 - Coveralls |
There was a problem hiding this comment.
Pull request overview
Refactors principal/member listing across org/project/group/user handlers to use a single membership entrypoint for “principals that hold policies on a resource”, and removes redundant service methods and dependencies.
Changes:
- Introduces
membership.ListPrincipalsByResource(...)with optional principal-type filtering, role-ID filtering, and optional role enrichment. - Rewires multiple Connect API handlers and tests to use membership listing +
*.GetByIDshydration, removing handler-level role enrichment viapolicyService.ListRoles. - Removes now-redundant
user/projectlisting APIs and updates mocks/wiring accordingly (including event billing onboarding lookup).
Reviewed changes
Copilot reviewed 30 out of 31 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| test/e2e/regression/api_test.go | Updates org user listing request to use RoleFilters instead of PermissionFilter. |
| internal/api/v1beta1connect/user.go | Routes org/group-filtered user listings through membership-based lookup + GetByIDs. |
| internal/api/v1beta1connect/project_test.go | Updates project handler tests to mock membership + hydration instead of project service list methods. |
| internal/api/v1beta1connect/project.go | Refactors project member/admin/service-user/group listing to membership-based lookup and membership-provided role enrichment. |
| internal/api/v1beta1connect/organization_test.go | Updates org admin/user listing tests to use membership listing and role resolution. |
| internal/api/v1beta1connect/organization.go | Replaces org admin/user listing implementation with membership-based lookup and role-ID filtering. |
| internal/api/v1beta1connect/mocks/user_service.go | Removes ListByOrg/ListByGroup mock methods no longer used by handler layer. |
| internal/api/v1beta1connect/mocks/project_service.go | Removes ListUsers/ListServiceUsers/ListGroups mock methods no longer used by handler layer. |
| internal/api/v1beta1connect/mocks/policy_service.go | Removes handler-layer ListRoles mock method no longer used by Connect handlers. |
| internal/api/v1beta1connect/mocks/membership_service.go | Adds ListPrincipalsByResource mock for Connect handler tests. |
| internal/api/v1beta1connect/mocks/group_service.go | Adds GetByIDs mock method for group hydration. |
| internal/api/v1beta1connect/interfaces.go | Updates handler dependency interfaces to align with refactor (adds membership listing, removes old list APIs, adds GroupService.GetByIDs). |
| internal/api/v1beta1connect/group_test.go | Updates group handler tests to use membership listing + GetByIDs hydration and role resolution. |
| internal/api/v1beta1connect/group.go | Refactors group member listing, group members expansion, and admin checks to membership-based lookup. |
| core/user/service_test.go | Updates user service tests for new constructor signature and removed org/group list behavior. |
| core/user/service.go | Removes policy/role dependencies and deletes org/group list helpers from user service. |
| core/user/filter.go | Removes OrgID/GroupID from user filter. |
| core/project/service_test.go | Removes tests for now-deleted project list helper methods. |
| core/project/service.go | Removes ListUsers/ListServiceUsers/ListGroups from project service. |
| core/membership/service_test.go | Adds unit tests for ListPrincipalsByResource behavior (validation, filters, enrichment, error wrapping). |
| core/membership/service.go | Adds ListPrincipalsByResource implementation, plus MemberFilter/Member types and PolicyService.ListRoles dependency. |
| core/membership/mocks/policy_service.go | Extends membership policy mock to include ListRoles. |
| core/membership/errors.go | Adds ErrInvalidResourceType. |
| core/event/service_test.go | Updates billing onboarding tests to use membership-based org-admin email lookup and new mocks. |
| core/event/service.go | Switches onboarding admin email lookup to membership listing + role resolution + user hydration. |
| core/event/mocks/user_service.go | Updates event user service mock to GetByIDs. |
| core/event/mocks/role_service.go | Adds new role service mock for event service. |
| core/event/mocks/membership_service.go | Adds new membership service mock for event service. |
| cmd/serve.go | Updates service wiring to new constructors (user service, event service). |
| Makefile | Updates PROTON_COMMIT pin. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
6d4dc3e to
6943a73
Compare
|
Trying to create a generic method can cause some unwanted performance issues, specifically because of parameter explosion and N+1 queries kind of issues. This happens because we are thinking of the new service method as a generic one but that powers other non generic rpc like ListOrganizationUsers etc. Hence we need to get details of a user separately. I don't think making this refactor will actually give us a lot of value. |
Manual API test results (post-refactor)Tested all changed handlers against a local Frontier build of this branch (
22/22 PASS. Key behavioral changes verified:
|
d7feecd to
5e047ca
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
core/membership/service.go (1)
965-972: ⚡ Quick winAvoid an unnecessary second policy read on empty results.
At Line 969, the enrichment query runs even when no principals matched in the first query. Early-returning avoids a redundant DB call on empty-resource paths.
Proposed patch
// deduplicate by (principalID, principalType) preserving order memberIndex := make(map[string]int, len(policies)) members := make([]Member, 0, len(policies)) for _, pol := range policies { key := pol.PrincipalType + "\x00" + pol.PrincipalID if _, ok := memberIndex[key]; ok { continue } memberIndex[key] = len(members) members = append(members, Member{ PrincipalID: pol.PrincipalID, PrincipalType: pol.PrincipalType, }) } + + if len(members) == 0 { + return members, nil + } // fetch all policies for the resource (without role filtering) to get // the complete set of roles per principal in a single query roleFlt := fltinternal/api/v1beta1connect/project.go (1)
230-245: ⚡ Quick winConsider extracting role transformation logic to reduce duplication.
The role-pair construction pattern appears three times with identical structure:
- Lines 230-245 (ListProjectUsers)
- Lines 296-311 (ListProjectServiceUsers)
- Lines 362-378 (ListProjectGroups)
Each block does:
rolesPb := utils.Filter(utils.Map(m.Roles, func(r role.Role) *frontierv1beta1.Role { pb, err := transformRoleToPB(r) if err != nil { errorLogger.LogTransformError(...) return nil } return &pb }), func(r *frontierv1beta1.Role) bool { return r != nil })Consider extracting this to a helper function to improve maintainability:
func (h *ConnectHandler) transformMemberRolesToPB(ctx context.Context, request interface{}, handlerName string, m membership.Member, errorLogger *ErrorLogger) []*frontierv1beta1.Role { return utils.Filter(utils.Map(m.Roles, func(r role.Role) *frontierv1beta1.Role { pb, err := transformRoleToPB(r) if err != nil { errorLogger.LogTransformError(ctx, request, handlerName, r.ID, err) return nil } return &pb }), func(r *frontierv1beta1.Role) bool { return r != nil }) }Also applies to: 296-311, 362-378
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: e760c090-006e-4ca7-98f5-4e0b46fcd88a
⛔ Files ignored due to path filters (1)
proto/v1beta1/frontier.pb.gois excluded by!**/*.pb.go,!proto/**
📒 Files selected for processing (31)
Makefilecmd/serve.gocore/event/mocks/membership_service.gocore/event/mocks/role_service.gocore/event/mocks/user_service.gocore/event/service.gocore/event/service_test.gocore/membership/errors.gocore/membership/mocks/role_service.gocore/membership/service.gocore/membership/service_test.gocore/project/service.gocore/project/service_test.gocore/user/filter.gocore/user/service.gocore/user/service_test.gointernal/api/v1beta1connect/errors.gointernal/api/v1beta1connect/group.gointernal/api/v1beta1connect/group_test.gointernal/api/v1beta1connect/interfaces.gointernal/api/v1beta1connect/mocks/group_service.gointernal/api/v1beta1connect/mocks/membership_service.gointernal/api/v1beta1connect/mocks/policy_service.gointernal/api/v1beta1connect/mocks/project_service.gointernal/api/v1beta1connect/mocks/user_service.gointernal/api/v1beta1connect/organization.gointernal/api/v1beta1connect/organization_test.gointernal/api/v1beta1connect/project.gointernal/api/v1beta1connect/project_test.gointernal/api/v1beta1connect/user.gotest/e2e/regression/api_test.go
💤 Files with no reviewable changes (8)
- internal/api/v1beta1connect/errors.go
- core/project/service_test.go
- internal/api/v1beta1connect/mocks/user_service.go
- core/user/filter.go
- internal/api/v1beta1connect/mocks/policy_service.go
- core/project/service.go
- core/user/service.go
- internal/api/v1beta1connect/mocks/project_service.go
…ListOrganizationUsers
5e047ca to
9980f45
Compare
Summary
membership.ListPrincipalsByResource(ctx, resourceID, resourceType, filter)as the single entrypoint for listing principals (users, service users, groups) that hold policies on a resource, with optional principal-type / role-ID filtering and role enrichment.ListOrganizationAdmins,ListOrganizationUsers,ListProjectAdmins,ListProjectUsers,ListProjectServiceUsers,ListProjectGroups,ListGroupUsers,ListOrganizationGroups(with members),GetGroup(with members),RemoveGroupUser,ListUsers/ListAllUsers(org/group filters) — to call the new method and hydrate entities via*.GetByIDs.user.ListByOrg,user.ListByGroup,user.getUserIDsFromPolicies,project.ListUsers,project.ListServiceUsers,project.ListGroups, and the handler-level direct calls topolicyService.ListRolesfor role enrichment. Drops thePolicyService/RoleServicedeps fromuser.Serviceand theOrgID/GroupIDfields fromuser.Filter.event.Service(billing onboarding) switches fromuserService.ListByOrgto the membership method to look up the first org admin's email.Test plan
go build ./...golangci-lint run ./...(0 issues)go test ./...green across core/membership, core/user, core/project, core/event, internal/api/v1beta1connectcore/membership/service_test.gocover resource-type validation, principal-type filter, role-ID filter, role enrichment, dedup, empty results, and error wrappinggo test ./test/e2e/regressionpasses end-to-end (organization, user, serviceuser suites)