[FA-misc] Saga seems to work, fixed a UserNovelDataService bug

This commit is contained in:
gamer147
2026-01-28 12:11:06 -05:00
parent 579e05b853
commit ec967770d3
34 changed files with 1341 additions and 97 deletions

View File

@@ -1,5 +1,6 @@
using FictionArchive.Common.Enums;
using FictionArchive.Service.NovelService.Contracts;
using FictionArchive.Service.NovelService.Models;
using FictionArchive.Service.NovelService.Models.Configuration;
using FictionArchive.Service.NovelService.Models.Enums;
using FictionArchive.Service.NovelService.Models.Images;
@@ -12,6 +13,7 @@ using HtmlAgilityPack;
using MassTransit;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using NodaTime;
namespace FictionArchive.Service.NovelService.Services;
@@ -22,14 +24,16 @@ public class NovelUpdateService
private readonly IEnumerable<ISourceAdapter> _sourceAdapters;
private readonly IPublishEndpoint _publishEndpoint;
private readonly NovelUpdateServiceConfiguration _novelUpdateServiceConfiguration;
private readonly IClock _clock;
public NovelUpdateService(NovelServiceDbContext dbContext, ILogger<NovelUpdateService> logger, IEnumerable<ISourceAdapter> sourceAdapters, IPublishEndpoint publishEndpoint, IOptions<NovelUpdateServiceConfiguration> novelUpdateServiceConfiguration)
public NovelUpdateService(NovelServiceDbContext dbContext, ILogger<NovelUpdateService> logger, IEnumerable<ISourceAdapter> sourceAdapters, IPublishEndpoint publishEndpoint, IOptions<NovelUpdateServiceConfiguration> novelUpdateServiceConfiguration, IClock clock)
{
_dbContext = dbContext;
_logger = logger;
_sourceAdapters = sourceAdapters;
_publishEndpoint = publishEndpoint;
_novelUpdateServiceConfiguration = novelUpdateServiceConfiguration.Value;
_clock = clock;
}
#region Helper Methods
@@ -299,7 +303,7 @@ public class NovelUpdateService
#endregion
public async Task<Novel> ImportNovel(string novelUrl)
public async Task<Novel> ImportNovel(Guid importId, string novelUrl)
{
// Step 1: Get metadata from source adapter
NovelMetadata? metadata = null;
@@ -417,10 +421,24 @@ public class NovelUpdateService
}
}
// Count chapters that need pulling
var chaptersNeedingPull = novel.Volumes
.SelectMany(v => v.Chapters)
.Where(c => c.Body?.Texts == null || !c.Body.Texts.Any())
.ToList();
// Publish metadata imported event for saga
await _publishEndpoint.Publish<INovelMetadataImported>(new NovelMetadataImported(
importId,
novel.Id,
chaptersNeedingPull.Count
));
// Publish cover image event if needed
if (shouldPublishCoverEvent && novel.CoverImage != null && metadata.CoverImage != null)
{
await _publishEndpoint.Publish<IFileUploadRequestCreated>(new FileUploadRequestCreated(
importId,
novel.CoverImage.Id,
$"Novels/{novel.Id}/Images/cover.jpg",
metadata.CoverImage.Data));
@@ -429,13 +447,14 @@ public class NovelUpdateService
// Publish chapter pull events for chapters without body content
foreach (var volume in novel.Volumes)
{
var chaptersNeedingPull = volume.Chapters
var volumeChaptersNeedingPull = volume.Chapters
.Where(c => c.Body?.Texts == null || !c.Body.Texts.Any())
.ToList();
foreach (var chapter in chaptersNeedingPull)
foreach (var chapter in volumeChaptersNeedingPull)
{
await _publishEndpoint.Publish<IChapterPullRequested>(new ChapterPullRequested(
importId,
novel.Id,
volume.Id,
chapter.Order));
@@ -445,7 +464,7 @@ public class NovelUpdateService
return novel;
}
public async Task<Chapter> PullChapterContents(uint novelId, uint volumeId, uint chapterOrder)
public async Task<(Chapter chapter, int imageCount)> PullChapterContents(Guid importId, uint novelId, uint volumeId, uint chapterOrder)
{
var novel = await _dbContext.Novels.Where(novel => novel.Id == novelId)
.Include(novel => novel.Volumes)
@@ -512,12 +531,13 @@ public class NovelUpdateService
{
var data = rawChapter.ImageData.FirstOrDefault(img => img.Url == image.OriginalPath);
await _publishEndpoint.Publish<IFileUploadRequestCreated>(new FileUploadRequestCreated(
importId,
image.Id,
$"Novels/{novel.Id}/Images/Chapter-{chapter.Id}/{imgCount++}.jpg",
data.Data));
}
return chapter;
return (chapter, chapter.Images.Count);
}
public async Task UpdateImage(Guid imageId, string newUrl)
@@ -548,16 +568,34 @@ public class NovelUpdateService
await _dbContext.SaveChangesAsync();
}
public async Task<NovelUpdateRequested> QueueNovelImport(string novelUrl)
public async Task<NovelImportRequested> QueueNovelImport(string novelUrl)
{
var importNovelRequestEvent = new NovelUpdateRequested(novelUrl);
await _publishEndpoint.Publish<INovelUpdateRequested>(importNovelRequestEvent);
var importId = Guid.NewGuid();
var activeImport = new ActiveImport
{
ImportId = importId,
NovelUrl = novelUrl,
StartedAt = _clock.GetCurrentInstant()
};
try
{
await _dbContext.ActiveImports.AddAsync(activeImport);
await _dbContext.SaveChangesAsync();
}
catch (DbUpdateException)
{
throw new InvalidOperationException($"An import is already in progress for {novelUrl}");
}
var importNovelRequestEvent = new NovelImportRequested(importId, novelUrl);
await _publishEndpoint.Publish<INovelImportRequested>(importNovelRequestEvent);
return importNovelRequestEvent;
}
public async Task<ChapterPullRequested> QueueChapterPull(uint novelId, uint volumeId, uint chapterOrder)
public async Task<ChapterPullRequested> QueueChapterPull(Guid importId, uint novelId, uint volumeId, uint chapterOrder)
{
var chapterPullEvent = new ChapterPullRequested(novelId, volumeId, chapterOrder);
var chapterPullEvent = new ChapterPullRequested(importId, novelId, volumeId, chapterOrder);
await _publishEndpoint.Publish<IChapterPullRequested>(chapterPullEvent);
return chapterPullEvent;
}