Skip to content

refactor(store): wave B.10 — user_group repo (#263)#264

Merged
PaulDotterer merged 1 commit into
mainfrom
feat/263-usergroup-repo
May 15, 2026
Merged

refactor(store): wave B.10 — user_group repo (#263)#264
PaulDotterer merged 1 commit into
mainfrom
feat/263-usergroup-repo

Conversation

@PaulDotterer

@PaulDotterer PaulDotterer commented May 15, 2026

Copy link
Copy Markdown
Contributor

Summary

`UserGroupRepo` — read side of `user_groups_projection` + `user_group_members_projection`. Branches off main directly.

Methods

  • `Get`, `GetByName`, `GetWithMembers` (with `ActualMemberCount` for SCIM reconciliation)
  • `List(filter)`, `Count`
  • `ListForUser`, `ListMemberIDs`, `ListMembers`

`MaintenanceWindow` stays as `json.RawMessage` at the boundary per the JSONB normalize plan.

Call sites migrated

  • `internal/api/user_group_handler.go` — ~20 sites + `userGroupToProto` signature shifted to `store.UserGroup`
  • `internal/scim/groups_helpers.go` — 2 sites
  • `internal/search/index.go` — 1 site

Non-goals

  • `GetUserGroupRoles` / `UserGroupHasRole` — Role-domain queries by group ID, will be added to RoleRepo in a follow-up.
  • `EvaluateDynamicUserGroup` / `EvaluateQueuedDynamicUserGroups` / `ValidateUserGroupQuery` — call PL/pgSQL, move with Wave C (dynamic-query interpreter port).
  • Write-side projector queries — stay on `Queries()` per pattern.

Acceptance

  • Build / vet / staticcheck / gofmt: clean.
  • Tests pass: `TestCreateUserGroup`, `TestListUserGroups`, `TestUpdateUserGroup`, `TestSCIMGroup*` (55s).

Part of #242. Closes #263.

Summary by CodeRabbit

  • Refactor
    • Reworked backend user-group data handling and storage wiring, centralizing group reads, listings, and membership access behind a unified repository layer to improve consistency and maintainability. No changes to user-facing behavior.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 15, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2903423f-ef8a-4044-ba0f-0fa5e5f65681

📥 Commits

Reviewing files that changed from the base of the PR and between b355052 and ad30608.

📒 Files selected for processing (9)
  • internal/api/assignment_handler.go
  • internal/api/user_group_handler.go
  • internal/scim/groups_helpers.go
  • internal/scim/groups_mutate.go
  • internal/search/index.go
  • internal/store/postgres/user_group.go
  • internal/store/postgres/wire.go
  • internal/store/repos.go
  • internal/store/user_group.go
✅ Files skipped from review due to trivial changes (2)
  • internal/store/postgres/wire.go
  • internal/store/user_group.go
🚧 Files skipped from review as they are similar to previous changes (7)
  • internal/api/assignment_handler.go
  • internal/search/index.go
  • internal/scim/groups_mutate.go
  • internal/scim/groups_helpers.go
  • internal/store/repos.go
  • internal/store/postgres/user_group.go
  • internal/api/user_group_handler.go

📝 Walkthrough

Walkthrough

Adds a store-layer UserGroupRepo (contracts + Postgres implementation), wires it into Repos, and migrates user-group read and membership queries across API handlers, SCIM, assignment validation, and search indexing to use the new repo methods.

Changes

User Group Repository Abstraction

Layer / File(s) Summary
Store contracts and data types
internal/store/user_group.go
Introduces UserGroup, UserGroupWithMembers, UserGroupMember, ListUserGroupsFilter, and the UserGroupRepo interface for read-side user-group operations.
PostgreSQL repository implementation
internal/store/postgres/user_group.go
Implements UserGroupRepo delegating to sqlc Queries: Get, GetByName, GetWithMembers, List, Count, ListForUser, ListMemberIDs, ListMembers. Centralizes projection mapping via userGroupFromRow and wraps/normalizes SQL errors.
Repository wiring
internal/store/repos.go, internal/store/postgres/wire.go
Adds UserGroup UserGroupRepo field to Repos and wires NewUserGroup(q) into NewRepos.
API handler migrations
internal/api/user_group_handler.go
Replaces many h.store.Queries() call sites with h.store.Repos().UserGroup calls (create-time name check, gets, member lists, paginated List/Count, update/delete/maintenance checks, dynamic evaluation, session bumping). Updates userGroupToProto to accept store.UserGroup.
Peripheral handler migrations
internal/api/assignment_handler.go, internal/scim/groups_helpers.go, internal/scim/groups_mutate.go, internal/search/index.go
Switches assignment target validation, SCIM group reconciliation/build and members replace flow, and search warmup to use Repos().UserGroup methods for group and member reads.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • #265: Adds a UserGroupRepo and migrates call sites to repo methods — directly aligned with the repository addition and handler migrations in this PR.

Possibly related PRs

Poem

🐰 I hopped through code, a tidy trail I paved,
New repo burrowed where raw queries braved.
Groups now lined up, members counted and kept,
Handlers call neatly — no stray rows left unwept.
Hooray for tidy reads and tests that behave!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: introducing a UserGroupRepo as part of wave B.10 of a systematic store refactoring.
Linked Issues check ✅ Passed The PR fulfills all primary objectives from #263: UserGroupRepo interface with required methods (Get, GetByName, GetWithMembers, List, Count, ListForUser, ListMemberIDs, ListMembers), UserGroupWithMembers projection, MaintenanceWindow as json.RawMessage, and migrated call sites across three files.
Out of Scope Changes check ✅ Passed All changes directly support UserGroupRepo implementation and migration. No unrelated modifications detected; non-goals (role queries, dynamic evaluation, write projectors) are appropriately excluded per requirements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/263-usergroup-repo

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
internal/store/postgres/user_group.go (1)

44-56: ⚡ Quick win

Consolidate UserGroup mapping to reduce drift risk.

GetWithMembers duplicates the same base store.UserGroup field mapping used elsewhere. Extracting a shared mapper for common fields will prevent silent divergence when new fields are added.

Also applies to: 120-132

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/store/postgres/user_group.go` around lines 44 - 56, GetWithMembers
duplicates the base store.UserGroup field mapping, which risks divergence;
extract a single reusable mapper (e.g., a function named mapUserGroupRow or
newUserGroupFromRow) that accepts the DB row type and returns store.UserGroup,
then call that mapper inside GetWithMembers and other places (also update the
code at the other occurrence around lines 120-132) so shared fields like ID,
Name, Description, MemberCount, CreatedAt, CreatedBy, UpdatedAt, IsDynamic,
DynamicQuery, and MaintenanceWindow are populated in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/store/postgres/user_group.go`:
- Around line 44-56: GetWithMembers duplicates the base store.UserGroup field
mapping, which risks divergence; extract a single reusable mapper (e.g., a
function named mapUserGroupRow or newUserGroupFromRow) that accepts the DB row
type and returns store.UserGroup, then call that mapper inside GetWithMembers
and other places (also update the code at the other occurrence around lines
120-132) so shared fields like ID, Name, Description, MemberCount, CreatedAt,
CreatedBy, UpdatedAt, IsDynamic, DynamicQuery, and MaintenanceWindow are
populated in one place.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 81284143-3eca-4771-b5ea-53abb2cede0c

📥 Commits

Reviewing files that changed from the base of the PR and between 8e6bf70 and b355052.

📒 Files selected for processing (9)
  • internal/api/assignment_handler.go
  • internal/api/user_group_handler.go
  • internal/scim/groups_helpers.go
  • internal/scim/groups_mutate.go
  • internal/search/index.go
  • internal/store/postgres/user_group.go
  • internal/store/postgres/wire.go
  • internal/store/repos.go
  • internal/store/user_group.go

UserGroupRepo covers the read side of user_groups_projection and
user_group_members_projection:

  Get / GetByName / GetWithMembers / List / Count /
  ListForUser / ListMemberIDs / ListMembers

UserGroupWithMembers pairs the projection row with the JOIN-computed
ActualMemberCount so SCIM handlers can reconcile against projection
lag. MaintenanceWindow stays as json.RawMessage at the boundary per
the JSONB normalize plan in #242.

Call sites migrated:
- internal/api/user_group_handler.go — ~20 sites + userGroupToProto
  signature shifted to store.UserGroup
- internal/scim/groups_helpers.go — 2 sites
- internal/search/index.go — 1 site (group reindex sweep)

Non-goals:
- GetUserGroupRoles / UserGroupHasRole — Role-domain queries by
  group ID, will be added to RoleRepo in a follow-up wave.
- EvaluateDynamicUserGroup / EvaluateQueuedDynamicUserGroups /
  ValidateUserGroupQuery — call PL/pgSQL, move with Wave C
  (dynamic-query interpreter port).
- Write-side projector queries — stay on Queries() per pattern.

Closes #263.
@PaulDotterer PaulDotterer force-pushed the feat/263-usergroup-repo branch from b355052 to ad30608 Compare May 15, 2026 21:18
@PaulDotterer PaulDotterer merged commit 23b916c into main May 15, 2026
5 checks passed
@PaulDotterer PaulDotterer deleted the feat/263-usergroup-repo branch May 15, 2026 21:34
PaulDotterer added a commit that referenced this pull request Jul 2, 2026
Was a pseudo-version (a3146410, the #264 merge) — repin to the tagged
v0.5.3 release so the server rc consumes a clean, tagged SDK. v0.5.3 is
a superset (adds the #265 SyncActionsResult facade field the server
does not use); no server code change, go.mod/go.sum only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

wave B.10: user_group repo

1 participant