feat(app): add L1+L2 caching to UserManagement (Auth0 API) (#293)#297
Merged
Conversation
…ession - Merge decision inbox into .squad/decisions.md (decisions 30-31) - Decision 30: Dark Mode Base Text Colours Must Contrast Against Dark Backgrounds (Legolas) - Decision 31: CSS Visual Regressions and Test/Visual Finding Arbitration (Aragorn) - Update Legolas history: UI regression review findings (Sprint 16 fan-out) - Update Gimli history: Blazor UI regression testing (Issue #292 coverage + visual findings) - Update Aragorn history: Issue triage (#292) and PR #295 arbitration workflow Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…und 1) - Closed duplicate issue #294, merged PR #295 with all 19 CI checks green - Sprint-stamped issues #293 and #296 with title normalization - Removed go:needs-research from #293 (sufficient context), kept for #296 (Auth investigation needed) - Decision #32: PR self-authored approval pattern and Sprint triage workflow Co-authored-by: Boromir <lead-organizer@squad.local> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Create UserManagementCacheService with two-tier cache (30s L1 IMemoryCache, 2min L2 IDistributedCache/Redis) following the existing BlogPostCacheService pattern. The ManageRoles page was making N+1 Auth0 Management API calls on every page load — this reduces API usage to once per 2 minutes under normal usage. Cache invalidation: AssignRole + RemoveRole handlers invalidate the users cache so role changes are reflected within the next cache miss. Closes #293 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
🏗️ PR Added to Squad Triage QueueThis PR has been labeled with Next steps:
|
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a two-tier cache layer (L1 IMemoryCache + L2 IDistributedCache/Redis) around Auth0 Management API calls used by the UserManagement feature, reducing repeated expensive calls when navigating to the roles management UI.
Changes:
- Registered
AddUserManagementCaching()inProgram.csand added DI wiring viaCachingServiceExtensions. - Introduced
IUserManagementCacheService,UserManagementCacheService, and cache key constants for users+roles and available roles. - Updated
UserManagementHandlerquery handlers to read through cache and command handlers to invalidate cached user lists after role mutations.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Web/Program.cs | Registers the new UserManagement cache service. |
| src/Web/Infrastructure/Caching/CachingServiceExtensions.cs | Adds AddUserManagementCaching() DI extension. |
| src/Web/Infrastructure/Caching/IUserManagementCacheService.cs | Defines the two-tier cache abstraction for UserManagement DTOs. |
| src/Web/Infrastructure/Caching/UserManagementCacheKeys.cs | Adds cache key constants for Redis/L1. |
| src/Web/Infrastructure/Caching/UserManagementCacheService.cs | Implements L1+L2 cache read-through + invalidation. |
| src/Web/Features/UserManagement/UserManagementHandler.cs | Uses cache on reads and invalidates after role assign/remove. |
| .squad/decisions/aragorn-pr295-merge.md | Adds a decision doc (appears unrelated to this PR’s caching scope). |
| .squad/decisions.md | Appends multiple decisions (largely about UI/CSS/PR #295 process). |
| .squad/agents/legolas/history.md | Adds/updates history notes (contains contradictory statements). |
| .squad/agents/gimli/history.md | Adds history notes (contains inconsistent test counts). |
| .squad/agents/aragorn/history.md | Adds history notes (includes future-dated entry). |
| request.UserId, | ||
| new AssignUserRolesRequestContent { Roles = [request.RoleId] }, | ||
| cancellationToken: cancellationToken).ConfigureAwait(false); | ||
| await cache.InvalidateUsersAsync(CancellationToken.None).ConfigureAwait(false); |
Comment on lines
114
to
120
| var client = await GetManagementClientAsync(cancellationToken).ConfigureAwait(false); | ||
| await client.Users.Roles.DeleteAsync( | ||
| request.UserId, | ||
| new DeleteUserRolesRequestContent { Roles = [request.RoleId] }, | ||
| cancellationToken: cancellationToken).ConfigureAwait(false); | ||
| await cache.InvalidateUsersAsync(CancellationToken.None).ConfigureAwait(false); | ||
| return Result.Ok(); |
Comment on lines
+1
to
+12
| # Decision #32: PR #295 Squash Merge & Sprint 19 Triage Pattern | ||
|
|
||
| **Date:** 2026-05-15 | ||
| **Author:** Aragorn | ||
| **Stakeholders:** Boromir (work-check), Legolas (UI), Gimli (testing) | ||
| **Status:** Ready for Scribe merge | ||
|
|
||
| ## Context | ||
|
|
||
| PR #295 (`squad/291-input-css-fine-tuning` → `dev`) completed dark-mode colour fixes, PageHeadingComponent introduction, and button variant consolidation. All 19 CI checks were green; Copilot automated review commented (no changes requested); all 6 inline threads resolved. | ||
|
|
||
| Simultaneously, issue #294 (exact duplicate of #293) and two other Sprint 19 issues (#293, #296) required triage, sprint-stamping, and label cleanup. |
Comment on lines
+2270
to
+2276
| ### 27. Button Variant Colour Palette Strategy | ||
|
|
||
| **Date:** 2025-07 | ||
| **Author:** Legolas (Frontend) | ||
| **Issue:** #292 | ||
| **Status:** ✅ Implemented | ||
|
|
| Then each variant only declares its colour-specific overrides. This is idiomatic Tailwind v4 component authoring. | ||
|
|
||
| **Fixed vs theme-relative colour palette:** `.btn-primary` / `.btn-secondary` use `var(--primary-*)` theme tokens so they adapt to colour-theme switches. `.btn-warning` (amber) and `.btn-destructive` (red) use fixed Tailwind palette classes — these colours carry semantic meaning that should NOT shift when the user picks a different theme. | ||
| **Fixed colour palette — all four variants:** All button variants use fixed Tailwind palette classes, not `var(--primary-*)` theme tokens. `.btn-primary` is green, `.btn-secondary` is blue, `.btn-warning` is amber, and `.btn-destructive` is red. None shift when the user picks a different colour theme — the palette is intentionally static to give each variant a clear, invariant semantic meaning. |
| - Covered destructive + secondary actions in `ConfirmDeleteDialog`. | ||
| - Covered primary + secondary actions in the blog list, create page, and edit page. | ||
| - Updated issue #292 title to include the Sprint 19 prefix so the branch work respected squad issue hygiene. | ||
| - Re-ran `dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj -c Release --nologo` before and after the change; final result: 73 passing tests. |
Aragorn architecture decision record covering PostAuthor value object, domain change to BlogPost.Create, MongoDB OwnsOne mapping, BlogPostDto flat author fields, Create.razor auth-state approach, and work breakdown for Sam/Legolas/Gimli. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…erTests UserManagementHandler gained a required IUserManagementCacheService constructor parameter in this PR but UserManagementHandlerTests was not updated, causing CS7036 compiler errors and breaking Squad CI and Test Suite builds. - Added _cache field (pass-through NSubstitute mock) - Updated constructor to pass cache to UserManagementHandler - Added BuildPassThroughCache() helper that delegates GetOrFetchUsersAsync and GetOrFetchRolesAsync through to the factory so existing config-missing and HTTP-failure assertions remain valid - Threaded BuildPassThroughCache() through all five static builder helpers Closes #293 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## dev #297 +/- ##
==========================================
- Coverage 87.20% 82.80% -4.41%
==========================================
Files 45 46 +1
Lines 1118 1192 +74
Branches 137 145 +8
==========================================
+ Hits 975 987 +12
- Misses 93 147 +54
- Partials 50 58 +8
🚀 New features to boost your workflow:
|
mpaulosky
pushed a commit
that referenced
this pull request
May 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds two-tier caching (L1 IMemoryCache 30s + L2 IDistributedCache/Redis 2min) to the UserManagement page, following the existing
BlogPostCacheServicepattern.Problem
The ManageRoles.razor page made expensive Auth0 Management API calls on every navigation — including an N+1 pattern (one call per user to fetch their roles).
Solution
UserManagementCacheKeys— cache key constantsIUserManagementCacheService— cache abstractionUserManagementCacheService— L1+L2 implementationUserManagementHandlerto cache reads + invalidate on mutationsAddUserManagementCaching()in Program.csTesting
Closes #293
Working as Sam (Backend Developer)