Loading users to Fluxor state now with Redis caching.

This commit is contained in:
Rhywden
2026-08-21 17:03:29 +02:00
parent b1a62e83f5
commit 75145ad284
5 changed files with 77 additions and 20 deletions
+15 -13
View File
@@ -1,17 +1,23 @@
@page "/" @page "/"
@inject Wollmilchsau.Services.Ldap.ILdapService LdapService @inherits Fluxor.Blazor.Web.Components.FluxorComponent
@inject Fluxor.IDispatcher Dispatcher
@inject Fluxor.IState<Wollmilchsau.Store.AdminStore.AdminState> AdminState
@inject IJSRuntime JsRuntime @inject IJSRuntime JsRuntime
<PageTitle>LDAP Users</PageTitle> <PageTitle>LDAP Users</PageTitle>
<button class="btn btn-primary" @onclick="DownloadCsvAsync" disabled="@(users is null)">Export CSV</button> <button class="btn btn-primary" @onclick="DownloadCsvAsync" disabled="@(!Users.Any())">Export CSV</button>
<h1>LDAP Users</h1> <h1>LDAP Users</h1>
@if (users is null) @if (!AdminState.Value.LdapUsersLoaded)
{ {
<p><em>Loading users...</em></p> <p><em>Loading users...</em></p>
} }
else if (!Users.Any())
{
<p>No LDAP users found.</p>
}
else else
{ {
<table class="table"> <table class="table">
@@ -25,7 +31,7 @@ else
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@foreach (var user in users) @foreach (var user in Users)
{ {
<tr> <tr>
<td>@user.Username</td> <td>@user.Username</td>
@@ -40,21 +46,17 @@ else
} }
@code { @code {
private List<Wollmilchsau.Models.LdapUser>? users; private IEnumerable<Wollmilchsau.Models.LdapUser> Users => AdminState.Value.LdapUsers.Values;
protected override async Task OnInitializedAsync() protected override void OnInitialized()
{ {
users = []; base.OnInitialized();
Dispatcher.Dispatch(new Wollmilchsau.Store.AdminStore.GetAllLdapUsers());
await foreach (var user in LdapService.GetAllUsers())
{
users.Add(user);
}
} }
private Task DownloadCsvAsync() private Task DownloadCsvAsync()
{ {
var csv = string.Join("\r\n", users!.Select(user => string.Join(';', var csv = string.Join("\r\n", Users.Select(user => string.Join(';',
user.GivenName ?? string.Empty, user.GivenName ?? string.Empty,
user.Surname ?? string.Empty, user.Surname ?? string.Empty,
user.Classroom ?? string.Empty, user.Classroom ?? string.Empty,
@@ -1,6 +1,9 @@
namespace Wollmilchsau.Store.AdminStore; using Wollmilchsau.Models;
namespace Wollmilchsau.Store.AdminStore;
public sealed record GetAllMoodleUsers(); public sealed record GetAllMoodleUsers();
public sealed record GetAllElementUsers(); public sealed record GetAllElementUsers();
public sealed record GetAllLdapUsers(); public sealed record GetAllLdapUsers();
public sealed record SetLdapUsers(IReadOnlyCollection<LdapUser> Users);
public sealed record GetAllMsolUsers(); public sealed record GetAllMsolUsers();
+41 -2
View File
@@ -1,5 +1,44 @@
namespace Wollmilchsau.Store.AdminStore; using System.Text.Json;
using Fluxor;
using Microsoft.Extensions.Caching.Distributed;
using Wollmilchsau.Models;
using Wollmilchsau.Services.Ldap;
public class AdminEffects namespace Wollmilchsau.Store.AdminStore;
public sealed class AdminEffects(ILdapService ldapService, IDistributedCache cache)
{ {
private const string LdapUsersCacheKey = "admin:ldap-users";
[EffectMethod]
public async Task HandleGetAllLdapUsers(GetAllLdapUsers action, IDispatcher dispatcher)
{
var cachedUsers = await cache.GetStringAsync(LdapUsersCacheKey);
IReadOnlyCollection<LdapUser> users;
if (cachedUsers is not null)
{
users = JsonSerializer.Deserialize<LdapUser[]>(cachedUsers)
?? throw new InvalidOperationException("The cached LDAP users could not be deserialized.");
}
else
{
var loadedUsers = new List<LdapUser>();
await foreach (var user in ldapService.GetAllUsers())
{
loadedUsers.Add(user);
}
users = loadedUsers;
await cache.SetStringAsync(
LdapUsersCacheKey,
JsonSerializer.Serialize(users),
new DistributedCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromMinutes(10)
});
}
dispatcher.Dispatch(new SetLdapUsers(users));
}
} }
+14 -2
View File
@@ -1,5 +1,17 @@
namespace Wollmilchsau.Store.AdminStore; using System.Collections.Immutable;
using Fluxor;
public class AdminReducers namespace Wollmilchsau.Store.AdminStore;
public static class AdminReducers
{ {
[ReducerMethod]
public static AdminState ReduceSetLdapUsers(AdminState state, SetLdapUsers action)
{
return state with
{
LdapUsers = action.Users.ToImmutableDictionary(user => user.Username, StringComparer.OrdinalIgnoreCase),
LdapUsersLoaded = true
};
}
} }
+2 -1
View File
@@ -10,6 +10,7 @@ public sealed record AdminState
public AdminState() { } public AdminState() { }
public ImmutableDictionary<Guid, ElementUser> ElementUsers { get; init; } = []; public ImmutableDictionary<Guid, ElementUser> ElementUsers { get; init; } = [];
public ImmutableDictionary<Guid, MoodleUser> MoodleUsers { get; init; } = []; public ImmutableDictionary<Guid, MoodleUser> MoodleUsers { get; init; } = [];
public ImmutableDictionary<Guid, LdapUser> LdapUsers { get; init; } = []; public ImmutableDictionary<string, LdapUser> LdapUsers { get; init; } = [];
public bool LdapUsersLoaded { get; init; }
public ImmutableDictionary<Guid, MsolUser> MsolUsers { get; init; } = []; public ImmutableDictionary<Guid, MsolUser> MsolUsers { get; init; } = [];
} }