Add the get group by id endpoint #47

Merged
rob merged 3 commits from feat/get-group-endpoint into main 2026-08-05 02:33:19 +00:00
Owner

Implements task 75: GET /api/groups/{groupId}, authenticated, returning the group and its roster to a member.

  • Refusal is 404 for a non-member, never 403 (403 stays for an accepted member whose role falls short, which can't arise on this read). Reuses RequireGroupCapability(GroupCapability.Member)GroupScopedEndpointAuthorisationTests covers the new route automatically since it matches /api/groups/{groupId}.
  • Roster shows display name, role and invitation status — never email. Pending invitees are included, distinguished by status, so an Owner can see who they've invited without a second endpoint. Reasoning and alternatives in ADR-0045 (took the next free number; checked main and every open branch, none claims 0045+).
  • GroupDetailResponse(GroupResponse Group, IReadOnlyList<GroupMemberResponse> Members) reuses GroupResponse and the wire GroupRole from PR #44 rather than duplicating fields.
  • Personal groups: confirmed by test that the roster is exactly the owner.

New repository methods: GroupRepository.FindByIdAsync, GroupMembershipRepository.ListForGroupAsync.

Implements task 75: `GET /api/groups/{groupId}`, authenticated, returning the group and its roster to a member. - Refusal is 404 for a non-member, never 403 (403 stays for an accepted member whose role falls short, which can't arise on this read). Reuses `RequireGroupCapability(GroupCapability.Member)` — `GroupScopedEndpointAuthorisationTests` covers the new route automatically since it matches `/api/groups/{groupId}`. - Roster shows display name, role and invitation status — never email. Pending invitees are included, distinguished by status, so an Owner can see who they've invited without a second endpoint. Reasoning and alternatives in ADR-0045 (took the next free number; checked `main` and every open branch, none claims 0045+). - `GroupDetailResponse(GroupResponse Group, IReadOnlyList<GroupMemberResponse> Members)` reuses `GroupResponse` and the wire `GroupRole` from PR #44 rather than duplicating fields. - Personal groups: confirmed by test that the roster is exactly the owner. New repository methods: `GroupRepository.FindByIdAsync`, `GroupMembershipRepository.ListForGroupAsync`.
Add the get group by id endpoint
All checks were successful
CI / build (pull_request) Successful in 2m0s
5b8c8ab7a2
Renumber the member list ADR to 0046
All checks were successful
CI / build (pull_request) Successful in 1m52s
61366a8f09
rob left a comment
No description provided.
Verdict: mergeable Verified against the branch head (61366a8), not just the diff: - Mutated `SelectMembersForGroup` to select `u.email` and plumbed it through `GroupMemberForResponse`/`GroupMemberResponse`/the endpoint mapping: `GetGroup_TheRoster_NeverCarriesAnEmailAddress` reddens correctly, so the wire-level guard is real, not just a DTO-shape assumption. - `GroupMembershipRepository.FindAsync` (used by `RequireGroupCapability`) treats a pending row as "row exists, doesn't satisfy the policy" → 403, per ADR-0041's own pre-existing, deliberate rule (`InsufficientRoleReason`: "a pending invitee received an invitation naming it... 403 reveals nothing they did not already have"). The PR's `GetGroup_APendingInviteeReadingItThemselves_RespondsWith403ProblemDetails` is correct as written — a pending invitee is *not* refused with 404. Flagging this because it's the opposite of what I went in expecting from the ticket text alone; the ticket's "403 remains for a member whose role is insufficient" is imprecise next to ADR-0041's actual, broader "any row, pending or accepted" rule, but the code matches the ADR, which is the one already-accepted, cross-cutting decision here. - 404-vs-404 indistinguishability, personal-group-roster-is-owner-only, and reuse of `GroupResponse`/`PagedResponse<T>`/wire `GroupRole` (no parallel types) all confirmed by inspection and a full local test run (`dotnet test`, 10.0.100, all green) plus a targeted mutation, not just by reading the tests. - No stale `0045` references anywhere in the branch; ADR index in `docs/adr/README.md` stays in numeric order with 0045 correctly absent. Nothing to change.
Merge remote-tracking branch 'origin/main' into feat/get-group-endpoint
All checks were successful
CI / build (pull_request) Successful in 2m6s
379241665a
# Conflicts:
#	docs/adr/README.md
rob merged commit 6eaa84a8ed into main 2026-08-05 02:33:19 +00:00
rob deleted branch feat/get-group-endpoint 2026-08-05 02:33:19 +00:00
rob referenced this pull request from a commit 2026-08-05 02:33:20 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rob/PlaceMark!47
No description provided.