diff --git a/JSMR.Application/Scanning/Ports/IVoiceWorkScannerRepository.cs b/JSMR.Application/Scanning/Ports/IVoiceWorkScannerRepository.cs new file mode 100644 index 0000000..bb08e15 --- /dev/null +++ b/JSMR.Application/Scanning/Ports/IVoiceWorkScannerRepository.cs @@ -0,0 +1,7 @@ +using JSMR.Application.Enums; + +namespace JSMR.Application.Scanning.Ports; +public interface IVoiceWorkScannerRepository +{ + public IVoiceWorksScanner? GetScanner(Locale locale); +} \ No newline at end of file diff --git a/JSMR.Application/Scanning/Ports/IVoiceWorkUpdaterRepository.cs b/JSMR.Application/Scanning/Ports/IVoiceWorkUpdaterRepository.cs new file mode 100644 index 0000000..69efdef --- /dev/null +++ b/JSMR.Application/Scanning/Ports/IVoiceWorkUpdaterRepository.cs @@ -0,0 +1,8 @@ +using JSMR.Application.Enums; + +namespace JSMR.Application.Scanning.Ports; + +public interface IVoiceWorkUpdaterRepository +{ + public IVoiceWorkUpdater? GetUpdater(Locale locale); +} \ No newline at end of file diff --git a/JSMR.Application/Scanning/ScanVoiceWorksHandler.cs b/JSMR.Application/Scanning/ScanVoiceWorksHandler.cs index 94875c3..dfeab61 100644 --- a/JSMR.Application/Scanning/ScanVoiceWorksHandler.cs +++ b/JSMR.Application/Scanning/ScanVoiceWorksHandler.cs @@ -3,20 +3,20 @@ using JSMR.Application.Integrations.DLSite.Models; using JSMR.Application.Integrations.Ports; using JSMR.Application.Scanning.Contracts; using JSMR.Application.Scanning.Ports; -using Microsoft.Extensions.DependencyInjection; namespace JSMR.Application.Scanning; public sealed class ScanVoiceWorksHandler( - IServiceProvider serviceProvider, + IVoiceWorkScannerRepository scannerRepository, + IVoiceWorkUpdaterRepository updaterRepository, IDLSiteClient dlsiteClient, ISpamCircleCache spamCircleCache, IVoiceWorkSearchUpdater searchUpdater) { public async Task HandleAsync(ScanVoiceWorksRequest request, CancellationToken cancellationToken) { - IVoiceWorksScanner? scanner = serviceProvider.GetKeyedService(request.Locale); - IVoiceWorkUpdater? updater = serviceProvider.GetKeyedService(request.Locale); + IVoiceWorksScanner? scanner = scannerRepository.GetScanner(request.Locale); + IVoiceWorkUpdater? updater = updaterRepository.GetUpdater(request.Locale); if (scanner is null || updater is null) return new(); diff --git a/JSMR.Infrastructure/DI/InfrastructureServiceCollectionExtensions.cs b/JSMR.Infrastructure/DI/InfrastructureServiceCollectionExtensions.cs index 26c8535..1b73d4c 100644 --- a/JSMR.Infrastructure/DI/InfrastructureServiceCollectionExtensions.cs +++ b/JSMR.Infrastructure/DI/InfrastructureServiceCollectionExtensions.cs @@ -47,9 +47,12 @@ public static class InfrastructureServiceCollectionExtensions services.AddKeyedScoped(Locale.Japanese); services.AddKeyedScoped(Locale.English); + services.AddSingleton(); services.AddKeyedScoped(Locale.Japanese); services.AddKeyedScoped(Locale.English); + services.AddSingleton(); + services.AddScoped(); //services.AddKeyedScoped(Locale.Japanese); diff --git a/JSMR.Infrastructure/Ingestion/VoiceWorkUpdater.cs b/JSMR.Infrastructure/Ingestion/VoiceWorkUpdater.cs index d551643..3e366c0 100644 --- a/JSMR.Infrastructure/Ingestion/VoiceWorkUpdater.cs +++ b/JSMR.Infrastructure/Ingestion/VoiceWorkUpdater.cs @@ -35,6 +35,18 @@ public class VoiceWorkUpdater(AppDbContext dbContext, ITimeProvider timeProvider await dbContext.SaveChangesAsync(cancellationToken); + foreach (VoiceWorkIngest ingest in ingests) + { + VoiceWorkUpsertResult result = upsertContext.Results[ingest.ProductId]; + + if (result.Status is VoiceWorkUpsertStatus.Skipped) + continue; + + VoiceWork voiceWork = upsertContext.VoiceWorks[ingest.ProductId]; + + result.VoiceWorkId = voiceWork.VoiceWorkId; + } + return [.. upsertContext.Results.Select(x => x.Value)]; } diff --git a/JSMR.Infrastructure/Ingestion/VoiceWorkUpdaterRepository.cs b/JSMR.Infrastructure/Ingestion/VoiceWorkUpdaterRepository.cs new file mode 100644 index 0000000..bb0ff2c --- /dev/null +++ b/JSMR.Infrastructure/Ingestion/VoiceWorkUpdaterRepository.cs @@ -0,0 +1,13 @@ +using JSMR.Application.Enums; +using JSMR.Application.Scanning.Ports; +using Microsoft.Extensions.DependencyInjection; + +namespace JSMR.Infrastructure.Ingestion; + +public class VoiceWorkUpdaterRepository(IServiceProvider serviceProvider) : IVoiceWorkUpdaterRepository +{ + public IVoiceWorkUpdater? GetUpdater(Locale locale) + { + return serviceProvider.GetKeyedService(locale); + } +} \ No newline at end of file diff --git a/JSMR.Infrastructure/Scanning/VoiceWorkScannerRepository.cs b/JSMR.Infrastructure/Scanning/VoiceWorkScannerRepository.cs new file mode 100644 index 0000000..4d44f12 --- /dev/null +++ b/JSMR.Infrastructure/Scanning/VoiceWorkScannerRepository.cs @@ -0,0 +1,13 @@ +using JSMR.Application.Enums; +using JSMR.Application.Scanning.Ports; +using Microsoft.Extensions.DependencyInjection; + +namespace JSMR.Infrastructure.Scanning; + +public class VoiceWorkScannerRepository(IServiceProvider serviceProvider) : IVoiceWorkScannerRepository +{ + public IVoiceWorksScanner? GetScanner(Locale locale) + { + return serviceProvider.GetKeyedService(locale); + } +} \ No newline at end of file diff --git a/JSMR.Tests/Ingestion/IngestTestFactory.cs b/JSMR.Tests/Ingestion/IngestTestFactory.cs new file mode 100644 index 0000000..b7c36e7 --- /dev/null +++ b/JSMR.Tests/Ingestion/IngestTestFactory.cs @@ -0,0 +1,32 @@ +using JSMR.Application.Scanning.Contracts; +using JSMR.Domain.Enums; +using JSMR.Domain.ValueObjects; +using JSMR.Tests.Ingestion.Search; + +namespace JSMR.Tests.Ingestion; + +internal class IngestTestFactory +{ + public static VoiceWorkIngest Create(SearchRelatedParameters searchRelatedParameters) + { + return new() + { + MakerId = searchRelatedParameters.MakerId, + MakerName = searchRelatedParameters.MakerName, + ProductId = searchRelatedParameters.ProductId, + Title = searchRelatedParameters.Title, + Description = searchRelatedParameters.Description, + Tags = searchRelatedParameters.Tags, + Creators = searchRelatedParameters.Creators, + WishlistCount = 100, + Downloads = 0, + HasTrial = false, + HasDLPlay = false, + AgeRating = AgeRating.AllAges, + HasImage = false, + SupportedLanguages = [SupportedLanguage.Japanese], + SalesDate = null, + ExpectedDate = new DateOnly(2025, 1, 1) + }; + } +} \ No newline at end of file diff --git a/JSMR.Tests/Ingestion/Japanese/JapaneseIngestionTestsBase.cs b/JSMR.Tests/Ingestion/Japanese/JapaneseIngestionTestsBase.cs index cfc7a37..af42160 100644 --- a/JSMR.Tests/Ingestion/Japanese/JapaneseIngestionTestsBase.cs +++ b/JSMR.Tests/Ingestion/Japanese/JapaneseIngestionTestsBase.cs @@ -1,5 +1,7 @@ -using JSMR.Application.Scanning.Contracts; +using JSMR.Application.Enums; +using JSMR.Application.Scanning.Contracts; using JSMR.Application.Scanning.Ports; +using JSMR.Domain.Entities; using JSMR.Infrastructure.Common.Time; using JSMR.Infrastructure.Data; using JSMR.Infrastructure.Ingestion; @@ -25,6 +27,13 @@ public abstract class JapaneseIngestionTestsBase(MariaDbContainerFixture contain return await updater.UpsertAsync(ingests, TestContext.Current.CancellationToken); } + protected static async Task UpdateSearchAsync(AppDbContext dbContext, int[] voiceWorkIds) + { + VoiceWorkSearchUpdater updater = new(dbContext); + + await updater.UpdateAsync(voiceWorkIds, TestContext.Current.CancellationToken); + } + protected static DateTime TokyoLocalToUtc(int year, int month, int day, int hour, int minute, int second) { var tokyo = TimeZoneInfo.FindSystemTimeZoneById( diff --git a/JSMR.Tests/Ingestion/Search/Insert_Into_Search_Tests.cs b/JSMR.Tests/Ingestion/Search/Insert_Into_Search_Tests.cs new file mode 100644 index 0000000..502b051 --- /dev/null +++ b/JSMR.Tests/Ingestion/Search/Insert_Into_Search_Tests.cs @@ -0,0 +1,69 @@ +using JSMR.Application.Scanning.Contracts; +using JSMR.Domain.Entities; +using JSMR.Infrastructure.Data; +using JSMR.Tests.Fixtures; +using JSMR.Tests.Ingestion.Japanese; +using Microsoft.EntityFrameworkCore; +using Shouldly; + +namespace JSMR.Tests.Ingestion.Search; + +internal record SearchRelatedParameters( + string ProductId, + string MakerId, + string Title, + string Description, + string MakerName, + string[] Tags, + string[] Creators +); + +public class Insert_Into_Search_Tests(MariaDbContainerFixture container) : JapaneseIngestionTestsBase(container) +{ + [Fact] + public async Task Insert_Into_Search_And_Update() + { + await using AppDbContext dbContext = await GetAppDbContextAsync(); + + // PART 1 - Insert + SearchRelatedParameters parameters = new( + ProductId: "RJ1", + MakerId: "RG1", + Title: "Title", + Description: "Description", + MakerName: "Maker", + Tags: ["Tag 1", "Tag 2"], + Creators: ["Creator 1"] + ); + + VoiceWorkIngest ingest = IngestTestFactory.Create(parameters); + + await UpsertAndVerify(dbContext, ingest, "RJ1 RG1 Title Description Maker Tag 1 Tag 2 Creator 1"); + + // PART 2 - Update + SearchRelatedParameters updateParameters = parameters with + { + Title = "Updated Title", + Tags = ["Tag 1", "Tag 2", "Tag 3"] + }; + + VoiceWorkIngest updatedIngest = IngestTestFactory.Create(updateParameters); + + await UpsertAndVerify(dbContext, updatedIngest, "RJ1 RG1 Updated Title Description Maker Tag 1 Tag 2 Tag 3 Creator 1"); + } + + private static async Task UpsertAndVerify(AppDbContext dbContext, VoiceWorkIngest ingest, string searchText) + { + await UpsertAsync(dbContext, TokyoLocalToUtc(2025, 01, 15, 00, 00, 00), [ingest]); + + VoiceWork? voiceWork = await dbContext.VoiceWorks.SingleAsync(v => v.ProductId == ingest.ProductId, TestContext.Current.CancellationToken); + voiceWork.ShouldNotBeNull(); + + await UpdateSearchAsync(dbContext, [voiceWork.VoiceWorkId]); + + VoiceWorkSearch voiceWorkSearch = await dbContext.VoiceWorkSearches.SingleAsync(v => v.VoiceWorkId == voiceWork.VoiceWorkId, TestContext.Current.CancellationToken); + voiceWorkSearch.ShouldNotBeNull(); + + voiceWorkSearch.SearchText.ShouldBe(searchText); + } +} \ No newline at end of file