From 39274165cbf115bb7502a8cda8be11b2a54d1ff5 Mon Sep 17 00:00:00 2001 From: Brian Bicknell Date: Thu, 11 Sep 2025 00:26:11 -0400 Subject: [PATCH] Update spam circle cache after updating circle status. --- .../UpdateCircleStatusHandler.cs | 15 ++++++++-- .../UpdateCircleStatusResponse.cs | 2 +- .../Common/Caching/ICacheObject.cs | 7 +++++ .../Common/Caching/ISpamCircleCache.cs | 6 ++++ .../{ICacheObject.cs => CacheObject.cs} | 28 ------------------- .../Caching/SpamCircleCache.cs | 23 +++++++++++++++ .../Data/Repositories/Circles/CircleWriter.cs | 19 ++++++++++++- .../Scanning/EnglishVoiceWorksScanner.cs | 3 +- .../Scanning/JapaneseVoiceWorksScanner.cs | 2 +- .../Scanning/VoiceWorksScanner.cs | 2 +- 10 files changed, 70 insertions(+), 37 deletions(-) create mode 100644 JSMR.Application/Common/Caching/ICacheObject.cs create mode 100644 JSMR.Application/Common/Caching/ISpamCircleCache.cs rename JSMR.Infrastructure/Caching/{ICacheObject.cs => CacheObject.cs} (52%) create mode 100644 JSMR.Infrastructure/Caching/SpamCircleCache.cs diff --git a/JSMR.Application/Circles/Commands/UpdateCircleStatus/UpdateCircleStatusHandler.cs b/JSMR.Application/Circles/Commands/UpdateCircleStatus/UpdateCircleStatusHandler.cs index 7c11c5f..6dfcc56 100644 --- a/JSMR.Application/Circles/Commands/UpdateCircleStatus/UpdateCircleStatusHandler.cs +++ b/JSMR.Application/Circles/Commands/UpdateCircleStatus/UpdateCircleStatusHandler.cs @@ -1,11 +1,20 @@ using JSMR.Application.Circles.Ports; +using JSMR.Application.Common.Caching; namespace JSMR.Application.Circles.Commands.UpdateCircleStatus; -public class UpdateCircleStatusHandler(ICircleWriter writer) +public class UpdateCircleStatusHandler(ICircleWriter writer, ISpamCircleCache spamCircleCache) { - public Task HandleAsync(UpdateCircleStatusRequest request, CancellationToken cancellationToken = default) + public async Task HandleAsync(UpdateCircleStatusRequest request, CancellationToken cancellationToken = default) { - return writer.UpdateStatusAsync(request, cancellationToken); + UpdateCircleStatusResponse response = await writer.UpdateStatusAsync(request, cancellationToken); + + bool wasChangedToSpam = response.OldStatus is not Contracts.CircleStatus.Spam && response.NewStatus is Contracts.CircleStatus.Spam; + bool wasChangedFromSpam = response.OldStatus is Contracts.CircleStatus.Spam && response.NewStatus is not Contracts.CircleStatus.Spam; + + if (wasChangedToSpam || wasChangedFromSpam) + await spamCircleCache.RefreshAsync(cancellationToken); + + return response; } } \ No newline at end of file diff --git a/JSMR.Application/Circles/Commands/UpdateCircleStatus/UpdateCircleStatusResponse.cs b/JSMR.Application/Circles/Commands/UpdateCircleStatus/UpdateCircleStatusResponse.cs index f8e785f..6808acd 100644 --- a/JSMR.Application/Circles/Commands/UpdateCircleStatus/UpdateCircleStatusResponse.cs +++ b/JSMR.Application/Circles/Commands/UpdateCircleStatus/UpdateCircleStatusResponse.cs @@ -2,4 +2,4 @@ namespace JSMR.Application.Circles.Commands.UpdateCircleStatus; -public sealed record UpdateCircleStatusResponse(int CircleId, CircleStatus CircleStatus); \ No newline at end of file +public sealed record UpdateCircleStatusResponse(int CircleId, CircleStatus OldStatus, CircleStatus NewStatus); \ No newline at end of file diff --git a/JSMR.Application/Common/Caching/ICacheObject.cs b/JSMR.Application/Common/Caching/ICacheObject.cs new file mode 100644 index 0000000..c4162df --- /dev/null +++ b/JSMR.Application/Common/Caching/ICacheObject.cs @@ -0,0 +1,7 @@ +namespace JSMR.Application.Common.Caching; + +public interface ICacheObject +{ + Task GetAsync(CancellationToken cancellationToken = default); + Task RefreshAsync(CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/JSMR.Application/Common/Caching/ISpamCircleCache.cs b/JSMR.Application/Common/Caching/ISpamCircleCache.cs new file mode 100644 index 0000000..ee4b122 --- /dev/null +++ b/JSMR.Application/Common/Caching/ISpamCircleCache.cs @@ -0,0 +1,6 @@ +namespace JSMR.Application.Common.Caching; + +public interface ISpamCircleCache : ICacheObject +{ + +} \ No newline at end of file diff --git a/JSMR.Infrastructure/Caching/ICacheObject.cs b/JSMR.Infrastructure/Caching/CacheObject.cs similarity index 52% rename from JSMR.Infrastructure/Caching/ICacheObject.cs rename to JSMR.Infrastructure/Caching/CacheObject.cs index 60bbb63..bab65e8 100644 --- a/JSMR.Infrastructure/Caching/ICacheObject.cs +++ b/JSMR.Infrastructure/Caching/CacheObject.cs @@ -4,12 +4,6 @@ using Microsoft.EntityFrameworkCore; namespace JSMR.Infrastructure.Caching; -public interface ICacheObject -{ - Task GetAsync(CancellationToken cancellationToken = default); - Task RefreshAsync(CancellationToken cancellationToken = default); -} - public abstract class CacheObject(ICache cache) : ICacheObject { protected abstract string Key { get; } @@ -30,26 +24,4 @@ public abstract class CacheObject(ICache cache) : ICacheObject return cacheObject; } -} - -public interface ISpamCircleCache : ICacheObject -{ - -} - -public class SpamCircleCache(IDbContextFactory contextFactory, ICache cache) : CacheObject(cache), ISpamCircleCache -{ - protected override string Key => "SpamCircles"; - - protected override async Task FetchAsync(CancellationToken cancellationToken = default) - { - using var context = contextFactory.CreateDbContext(); - - return await context.Circles - .AsNoTracking() - .Where(circle => circle.Spam) - .Select(circle => circle.MakerId) - .Distinct() - .ToArrayAsync(cancellationToken); - } } \ No newline at end of file diff --git a/JSMR.Infrastructure/Caching/SpamCircleCache.cs b/JSMR.Infrastructure/Caching/SpamCircleCache.cs new file mode 100644 index 0000000..99c62de --- /dev/null +++ b/JSMR.Infrastructure/Caching/SpamCircleCache.cs @@ -0,0 +1,23 @@ +using JSMR.Application.Common.Caching; +using JSMR.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; + +namespace JSMR.Infrastructure.Caching; + +public class SpamCircleCache(IDbContextFactory contextFactory, ICache cache) + : CacheObject(cache), ISpamCircleCache +{ + protected override string Key => "SpamCircles"; + + protected override async Task FetchAsync(CancellationToken cancellationToken = default) + { + using var context = contextFactory.CreateDbContext(); + + return await context.Circles + .AsNoTracking() + .Where(circle => circle.Spam) + .Select(circle => circle.MakerId) + .Distinct() + .ToArrayAsync(cancellationToken); + } +} \ No newline at end of file diff --git a/JSMR.Infrastructure/Data/Repositories/Circles/CircleWriter.cs b/JSMR.Infrastructure/Data/Repositories/Circles/CircleWriter.cs index e076462..dcb9447 100644 --- a/JSMR.Infrastructure/Data/Repositories/Circles/CircleWriter.cs +++ b/JSMR.Infrastructure/Data/Repositories/Circles/CircleWriter.cs @@ -11,6 +11,7 @@ public class CircleWriter(AppDbContext context) : ICircleWriter public async Task UpdateStatusAsync(UpdateCircleStatusRequest request, CancellationToken cancellationToken = default) { Circle circle = await GetCircleAsync(request.CircleId, cancellationToken); + CircleStatus oldStatus = GetCircleStatus(circle); switch (request.CircleStatus) { @@ -38,7 +39,23 @@ public class CircleWriter(AppDbContext context) : ICircleWriter await context.SaveChangesAsync(cancellationToken); - return new UpdateCircleStatusResponse(request.CircleId, request.CircleStatus); + CircleStatus newStatus = GetCircleStatus(circle); + + return new UpdateCircleStatusResponse(request.CircleId, oldStatus, newStatus); + } + + private static CircleStatus GetCircleStatus(Circle circle) + { + if (circle.Favorite) + return CircleStatus.Favorite; + + if (circle.Blacklisted) + return CircleStatus.Blacklisted; + + if (circle.Spam) + return CircleStatus.Spam; + + return CircleStatus.Neutral; } private async Task GetCircleAsync(int circleId, CancellationToken cancellationToken) diff --git a/JSMR.Infrastructure/Scanning/EnglishVoiceWorksScanner.cs b/JSMR.Infrastructure/Scanning/EnglishVoiceWorksScanner.cs index 66a5e2e..ae96e70 100644 --- a/JSMR.Infrastructure/Scanning/EnglishVoiceWorksScanner.cs +++ b/JSMR.Infrastructure/Scanning/EnglishVoiceWorksScanner.cs @@ -1,5 +1,4 @@ -using JSMR.Application.Scanning; -using JSMR.Infrastructure.Caching; +using JSMR.Application.Common.Caching; using JSMR.Infrastructure.Common.Locales; using JSMR.Infrastructure.Common.SupportedLanguages; using JSMR.Infrastructure.Http; diff --git a/JSMR.Infrastructure/Scanning/JapaneseVoiceWorksScanner.cs b/JSMR.Infrastructure/Scanning/JapaneseVoiceWorksScanner.cs index 35ece96..3d0c7a9 100644 --- a/JSMR.Infrastructure/Scanning/JapaneseVoiceWorksScanner.cs +++ b/JSMR.Infrastructure/Scanning/JapaneseVoiceWorksScanner.cs @@ -1,4 +1,4 @@ -using JSMR.Infrastructure.Caching; +using JSMR.Application.Common.Caching; using JSMR.Infrastructure.Common.Locales; using JSMR.Infrastructure.Common.SupportedLanguages; using JSMR.Infrastructure.Http; diff --git a/JSMR.Infrastructure/Scanning/VoiceWorksScanner.cs b/JSMR.Infrastructure/Scanning/VoiceWorksScanner.cs index 4bf25a5..0d3e6ea 100644 --- a/JSMR.Infrastructure/Scanning/VoiceWorksScanner.cs +++ b/JSMR.Infrastructure/Scanning/VoiceWorksScanner.cs @@ -1,8 +1,8 @@ using HtmlAgilityPack; +using JSMR.Application.Common.Caching; using JSMR.Application.Scanning; using JSMR.Application.Scanning.Contracts; using JSMR.Application.Scanning.Ports; -using JSMR.Infrastructure.Caching; using JSMR.Infrastructure.Common.Locales; using JSMR.Infrastructure.Common.SupportedLanguages; using JSMR.Infrastructure.Http;