Skip to content

feat(app): add L1+L2 caching to UserManagement (Auth0 API) (#293)#297

Merged
mpaulosky merged 6 commits into
devfrom
squad/293-member-roles-caching
May 11, 2026
Merged

feat(app): add L1+L2 caching to UserManagement (Auth0 API) (#293)#297
mpaulosky merged 6 commits into
devfrom
squad/293-member-roles-caching

Conversation

@mpaulosky

Copy link
Copy Markdown
Owner

Summary

Adds two-tier caching (L1 IMemoryCache 30s + L2 IDistributedCache/Redis 2min) to the UserManagement page, following the existing BlogPostCacheService pattern.

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 constants
  • IUserManagementCacheService — cache abstraction
  • UserManagementCacheService — L1+L2 implementation
  • Updated UserManagementHandler to cache reads + invalidate on mutations
  • Registered AddUserManagementCaching() in Program.cs

Testing

  • Architecture tests pass (16/16)
  • Web.Tests.Bunit tests pass (84/84)
  • Build clean (0 errors)

Closes #293

Working as Sam (Backend Developer)

Boromir and others added 4 commits May 11, 2026 10:45
…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>
@mpaulosky mpaulosky added the enhancement New feature or request label May 11, 2026
Copilot AI review requested due to automatic review settings May 11, 2026 18:45
@github-actions github-actions Bot added the squad Squad triage inbox — Lead will assign to a member label May 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🏗️ PR Added to Squad Triage Queue

This PR has been labeled with squad and added to the triage queue.

Next steps:

  • The squad Lead will review and assign to an appropriate team member
  • A squad:member label will be added after triage

If you know which squad member should handle this, you can add the appropriate squad:member label yourself.

@github-actions

github-actions Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor

Test Results Summary

356 tests  ±0   355 ✅ ±0   19s ⏱️ -1s
  6 suites ±0     1 💤 ±0 
  6 files   ±0     0 ❌ ±0 

Results for commit 6d93c77. ± Comparison against base commit 2ba0886.

♻️ This comment has been updated with latest results.

Copilot AI 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.

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() in Program.cs and added DI wiring via CachingServiceExtensions.
  • Introduced IUserManagementCacheService, UserManagementCacheService, and cache key constants for users+roles and available roles.
  • Updated UserManagementHandler query 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 thread .squad/decisions.md
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.
Boromir and others added 2 commits May 11, 2026 11:52
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

codecov Bot commented May 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 13.04348% with 80 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.80%. Comparing base (2ba0886) to head (6d93c77).

Files with missing lines Patch % Lines
...frastructure/Caching/UserManagementCacheService.cs 0.00% 48 Missing and 8 partials ⚠️
...b/Features/UserManagement/UserManagementHandler.cs 35.29% 15 Missing and 7 partials ⚠️
...Infrastructure/Caching/CachingServiceExtensions.cs 0.00% 2 Missing ⚠️
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     
Files with missing lines Coverage Δ
...Infrastructure/Caching/CachingServiceExtensions.cs 50.00% <0.00%> (-50.00%) ⬇️
...b/Features/UserManagement/UserManagementHandler.cs 54.54% <35.29%> (+3.11%) ⬆️
...frastructure/Caching/UserManagementCacheService.cs 0.00% <0.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@mpaulosky
mpaulosky merged commit 4b5d0d2 into dev May 11, 2026
19 checks passed
@mpaulosky
mpaulosky deleted the squad/293-member-roles-caching branch May 11, 2026 19:17
mpaulosky pushed a commit that referenced this pull request May 11, 2026
- Aragorn: PR #297 merge review, PR #295 merge patterns, ADR #296
- Legolas: Create.razor auth auto-fill patterns

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
mpaulosky pushed a commit that referenced this pull request May 11, 2026
- Aragorn: PR #297 merge review, PR #295 merge patterns, ADR #296
- Legolas: Create.razor auth auto-fill patterns

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
mpaulosky pushed a commit that referenced this pull request May 11, 2026
Summary: PRs #297, #298, #301, #302 merged. Issues #293, #296, #299,
#300 all closed. Sprint 19 complete.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request squad Squad triage inbox — Lead will assign to a member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Sprint 19] feat(app): add caching to MemberRoles page

2 participants