diff --git a/src/LinkDotNet.Blog.Web/Features/Admin/Settings/SettingsPage.razor b/src/LinkDotNet.Blog.Web/Features/Admin/Settings/SettingsPage.razor index 3d511107..ff5cb02c 100644 --- a/src/LinkDotNet.Blog.Web/Features/Admin/Settings/SettingsPage.razor +++ b/src/LinkDotNet.Blog.Web/Features/Admin/Settings/SettingsPage.razor @@ -47,7 +47,7 @@ private void RunVisitTransformer() { - InstantJobRegistry.AddInstantJob(); + InstantJobRegistry.AddInstantJob(); ToastService.ShowInfo("Transformer was started."); } } diff --git a/src/LinkDotNet.Blog.Web/Features/TransformBlogPostRecordsService.cs b/src/LinkDotNet.Blog.Web/Features/TransformBlogPostRecordsJob.cs similarity index 89% rename from src/LinkDotNet.Blog.Web/Features/TransformBlogPostRecordsService.cs rename to src/LinkDotNet.Blog.Web/Features/TransformBlogPostRecordsJob.cs index 4ea62d96..208f4cb8 100644 --- a/src/LinkDotNet.Blog.Web/Features/TransformBlogPostRecordsService.cs +++ b/src/LinkDotNet.Blog.Web/Features/TransformBlogPostRecordsJob.cs @@ -10,19 +10,19 @@ namespace LinkDotNet.Blog.Web.Features; -public sealed partial class TransformBlogPostRecordsService : IJob +public sealed partial class TransformBlogPostRecordsJob : IJob { private static readonly SemaphoreSlim Semaphore = new(1, 1); private readonly IRepository blogPostRepository; private readonly IRepository userRecordRepository; private readonly IRepository blogPostRecordRepository; - private readonly ILogger logger; + private readonly ILogger logger; - public TransformBlogPostRecordsService( + public TransformBlogPostRecordsJob( IRepository blogPostRepository, IRepository userRecordRepository, IRepository blogPostRecordRepository, - ILogger logger) + ILogger logger) { this.blogPostRepository = blogPostRepository; this.userRecordRepository = userRecordRepository; @@ -110,8 +110,14 @@ private async Task TransformRecordsAsync() var userRecords = await userRecordRepository.GetAllAsync( filter: r => r.UrlClicked.StartsWith("blogPost/")); - var newBlogPostRecords = GetBlogPostRecords(blogPosts, userRecords); - var oldBlogPostRecords = await blogPostRecordRepository.GetAllAsync(); + var newBlogPostRecords = GetBlogPostRecords(blogPosts, userRecords).ToArray(); + if (newBlogPostRecords.Length == 0) + { + return; + } + + var earliestDate = newBlogPostRecords.MinBy(r => r.DateClicked).DateClicked; + var oldBlogPostRecords = await blogPostRecordRepository.GetAllAsync(f => f.DateClicked >= earliestDate); var mergedRecords = MergeRecords(newBlogPostRecords, oldBlogPostRecords); @@ -123,10 +129,10 @@ private async Task TransformRecordsAsync() LogDeletedUserRecords(); } - [LoggerMessage(Level = LogLevel.Information, Message = $"{nameof(TransformBlogPostRecordsService)} is starting")] + [LoggerMessage(Level = LogLevel.Information, Message = $"{nameof(TransformBlogPostRecordsJob)} is starting")] private partial void LogTransformStarted(); - [LoggerMessage(Level = LogLevel.Information, Message = $"{nameof(TransformBlogPostRecordsService)} is stopping")] + [LoggerMessage(Level = LogLevel.Information, Message = $"{nameof(TransformBlogPostRecordsJob)} is stopping")] private partial void LogTransformStopped(); [LoggerMessage(Level = LogLevel.Information, Message = "Deleting {RecordCount} records from UserRecord-Table")] diff --git a/src/LinkDotNet.Blog.Web/RegistrationExtensions/BackgroundServiceRegistrationExtensions.cs b/src/LinkDotNet.Blog.Web/RegistrationExtensions/BackgroundServiceRegistrationExtensions.cs index fc1d8f8c..108304a0 100644 --- a/src/LinkDotNet.Blog.Web/RegistrationExtensions/BackgroundServiceRegistrationExtensions.cs +++ b/src/LinkDotNet.Blog.Web/RegistrationExtensions/BackgroundServiceRegistrationExtensions.cs @@ -18,6 +18,6 @@ public static void AddBackgroundServices(this IServiceCollection services) services.AddNCronJob(p => p.TimerInterval = TimeSpan.FromSeconds(30)); services.AddCronJob(p => p.CronExpression = "* * * * *"); - services.AddCronJob(p => p.CronExpression = "0 * * * *"); + services.AddCronJob(p => p.CronExpression = "0 * * * *"); } } diff --git a/tests/LinkDotNet.Blog.IntegrationTests/Web/Features/TransformBlogPostRecordsServiceTests.cs b/tests/LinkDotNet.Blog.IntegrationTests/Web/Features/TransformBlogPostRecordsJobTests.cs similarity index 66% rename from tests/LinkDotNet.Blog.IntegrationTests/Web/Features/TransformBlogPostRecordsServiceTests.cs rename to tests/LinkDotNet.Blog.IntegrationTests/Web/Features/TransformBlogPostRecordsJobTests.cs index 24679e9c..a845de04 100644 --- a/tests/LinkDotNet.Blog.IntegrationTests/Web/Features/TransformBlogPostRecordsServiceTests.cs +++ b/tests/LinkDotNet.Blog.IntegrationTests/Web/Features/TransformBlogPostRecordsJobTests.cs @@ -12,24 +12,24 @@ namespace LinkDotNet.Blog.IntegrationTests.Web.Features; -public class TransformBlogPostRecordsServiceTests : SqlDatabaseTestBase +public class TransformBlogPostRecordsJobTests : SqlDatabaseTestBase { - private readonly TransformBlogPostRecordsService sut; + private readonly TransformBlogPostRecordsJob sut; private readonly IRepository blogPostRecordRepository; private readonly IRepository userRecordRepository; - public TransformBlogPostRecordsServiceTests() + public TransformBlogPostRecordsJobTests() { blogPostRecordRepository = new Repository(DbContextFactory, Substitute.For>>()); userRecordRepository = new Repository(DbContextFactory, Substitute.For>>()); - sut = new TransformBlogPostRecordsService( + sut = new TransformBlogPostRecordsJob( Repository, userRecordRepository, blogPostRecordRepository, - Substitute.For>()); + Substitute.For>()); } [Fact] @@ -78,4 +78,38 @@ public async Task ShouldTransformRecords() post3Record.Should().NotBeNull(); post3Record.Clicks.Should().Be(1); } + + [Fact] + public async Task ShouldMergeRecordsWhenThereAreAlreadyEntries() + { + // Arrange + var someDate = new DateOnly(2023, 08, 13); + var blogPost = new BlogPostBuilder().WithUpdatedDate(someDate.ToDateTime(default)).Build(); + + await Repository.StoreAsync(blogPost); + List userRecords = + [ + new() { Id = "A", DateClicked = someDate, UrlClicked = $"blogPost/{blogPost.Id}" }, + new() { Id = "B", DateClicked = someDate, UrlClicked = $"blogPost/{blogPost.Id}" }, + new() { Id = "C", DateClicked = someDate.AddDays(1), UrlClicked = $"blogPost/{blogPost.Id}" }, + ]; + await userRecordRepository.StoreBulkAsync(userRecords); + + List blogPostRecords = + [ + new() { BlogPostId = blogPost.Id, DateClicked = someDate.AddDays(-1), Clicks = 1 }, + new() { BlogPostId = blogPost.Id, DateClicked = someDate, Clicks = 1 }, + ]; + await blogPostRecordRepository.StoreBulkAsync(blogPostRecords); + + // Act + await sut.RunAsync(new(null), default); + + // Assert + var records = await blogPostRecordRepository.GetAllAsync(); + records.Count.Should().Be(3); + records[0].Clicks.Should().Be(1); + records[1].Clicks.Should().Be(3); + records[2].Clicks.Should().Be(1); + } }