< Summary

Information
Class: Chronicis.Api.Services.LinkSyncService
Assembly: Chronicis.Api
File(s): /home/runner/work/chronicis/chronicis/src/Chronicis.Api/Services/LinkSyncService.cs
Line coverage
100%
Covered lines: 34
Uncovered lines: 0
Coverable lines: 34
Total lines: 78
Line coverage: 100%
Branch coverage
100%
Covered branches: 4
Total branches: 4
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
SyncLinksAsync()100%44100%

File(s)

/home/runner/work/chronicis/chronicis/src/Chronicis.Api/Services/LinkSyncService.cs

#LineLine coverage
 1using Chronicis.Api.Data;
 2using Chronicis.Shared.Models;
 3using Microsoft.EntityFrameworkCore;
 4
 5namespace Chronicis.Api.Services;
 6
 7/// <summary>
 8/// Synchronizes wiki links in the database based on article content.
 9/// Uses a delete-then-insert strategy for simplicity.
 10/// </summary>
 11public class LinkSyncService : ILinkSyncService
 12{
 13    private readonly ChronicisDbContext _context;
 14    private readonly ILinkParser _linkParser;
 15    private readonly ILogger<LinkSyncService> _logger;
 16
 1217    public LinkSyncService(
 1218        ChronicisDbContext context,
 1219        ILinkParser linkParser,
 1220        ILogger<LinkSyncService> logger)
 21    {
 1222        _context = context;
 1223        _linkParser = linkParser;
 1224        _logger = logger;
 1225    }
 26
 27    /// <summary>
 28    /// Synchronizes the ArticleLink table for the given article.
 29    /// Removes all existing links for this article and creates new ones based on the body content.
 30    /// </summary>
 31    /// <param name="sourceArticleId">The ID of the article whose links should be synced.</param>
 32    /// <param name="body">The article body containing wiki links to parse.</param>
 33    public async Task SyncLinksAsync(Guid sourceArticleId, string? body)
 34    {
 35        // Step 1: Delete all existing links where this article is the source
 1436        var existingLinks = await _context.ArticleLinks
 1437            .Where(al => al.SourceArticleId == sourceArticleId)
 1438            .ToListAsync();
 39
 1440        var removedCount = existingLinks.Count;
 41
 1442        if (existingLinks.Any())
 43        {
 344            _context.ArticleLinks.RemoveRange(existingLinks);
 45        }
 46
 47        // Step 2: Parse new links from body
 1448        var parsedLinks = _linkParser.ParseLinks(body);
 49
 50        // Step 3: Create new ArticleLink entities
 2751        var newLinks = parsedLinks.Select(pl => new ArticleLink
 2752        {
 2753            Id = Guid.NewGuid(),
 2754            SourceArticleId = sourceArticleId,
 2755            TargetArticleId = pl.TargetArticleId,
 2756            DisplayText = pl.DisplayText,
 2757            Position = pl.Position,
 2758            CreatedAt = DateTime.UtcNow
 2759        }).ToList();
 60
 1461        var addedCount = newLinks.Count;
 62
 1463        if (newLinks.Any())
 64        {
 1065            await _context.ArticleLinks.AddRangeAsync(newLinks);
 66        }
 67
 68        // Step 4: Save all changes in a single transaction
 1469        await _context.SaveChangesAsync();
 70
 71        // Step 5: Log metrics
 1472        _logger.LogDebug(
 1473            "Synced links for article {ArticleId}: {RemovedCount} removed, {AddedCount} added",
 1474            sourceArticleId,
 1475            removedCount,
 1476            addedCount);
 1477    }
 78}