< Summary

Information
Class: Chronicis.Client.Services.ArticleApiService
Assembly: Chronicis.Client
File(s): /home/runner/work/chronicis/chronicis/src/Chronicis.Client/Services/ArticleApiService.cs
Line coverage
100%
Covered lines: 3
Uncovered lines: 0
Coverable lines: 3
Total lines: 151
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

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

File(s)

/home/runner/work/chronicis/chronicis/src/Chronicis.Client/Services/ArticleApiService.cs

#LineLine coverage
 1using Chronicis.Shared.DTOs;
 2
 3namespace Chronicis.Client.Services;
 4
 5/// <summary>
 6/// Service for communicating with the Article API.
 7/// Uses HttpClientExtensions for consistent error handling and logging.
 8/// </summary>
 9public class ArticleApiService : IArticleApiService
 10{
 11    private readonly HttpClient _http;
 12    private readonly ILogger<ArticleApiService> _logger;
 13
 14    public ArticleApiService(HttpClient http, ILogger<ArticleApiService> logger)
 15    {
 516        _http = http;
 517        _logger = logger;
 518    }
 19
 20    public async Task<List<ArticleTreeDto>> GetRootArticlesAsync(Guid? worldId = null)
 21    {
 22        var url = worldId.HasValue
 23            ? $"articles?worldId={worldId.Value}"
 24            : "articles";
 25
 26        return await _http.GetListAsync<ArticleTreeDto>(url, _logger, "root articles");
 27    }
 28
 29    public async Task<List<ArticleTreeDto>> GetAllArticlesAsync(Guid? worldId = null)
 30    {
 31        var url = worldId.HasValue
 32            ? $"articles/all?worldId={worldId.Value}"
 33            : "articles/all";
 34
 35        return await _http.GetListAsync<ArticleTreeDto>(url, _logger, "all articles");
 36    }
 37
 38    public async Task<List<ArticleTreeDto>> GetChildrenAsync(Guid parentId)
 39    {
 40        return await _http.GetListAsync<ArticleTreeDto>(
 41            $"articles/{parentId}/children",
 42            _logger,
 43            $"children for article {parentId}");
 44    }
 45
 46    public async Task<ArticleDto?> GetArticleDetailAsync(Guid id)
 47    {
 48        return await _http.GetEntityAsync<ArticleDto>(
 49            $"articles/{id}",
 50            _logger,
 51            $"article {id}");
 52    }
 53
 54    public async Task<ArticleDto?> GetArticleAsync(Guid id) => await GetArticleDetailAsync(id);
 55
 56    public async Task<ArticleDto?> GetArticleByPathAsync(string path)
 57    {
 58        var encodedPath = string.Join("/", path.Split('/').Select(Uri.EscapeDataString));
 59
 60        return await _http.GetEntityAsync<ArticleDto>(
 61            $"articles/by-path/{encodedPath}",
 62            _logger,
 63            $"article at path '{path}'");
 64    }
 65
 66    public async Task<ArticleDto?> CreateArticleAsync(ArticleCreateDto dto)
 67    {
 68        return await _http.PostEntityAsync<ArticleDto>(
 69            "articles",
 70            dto,
 71            _logger,
 72            "article");
 73    }
 74
 75    public async Task<ArticleDto?> UpdateArticleAsync(Guid id, ArticleUpdateDto dto)
 76    {
 77        return await _http.PutEntityAsync<ArticleDto>(
 78            $"articles/{id}",
 79            dto,
 80            _logger,
 81            $"article {id}");
 82    }
 83
 84    public async Task<bool> DeleteArticleAsync(Guid id)
 85    {
 86        return await _http.DeleteEntityAsync(
 87            $"articles/{id}",
 88            _logger,
 89            $"article {id}");
 90    }
 91
 92    public async Task<bool> MoveArticleAsync(Guid articleId, Guid? newParentId, Guid? newSessionId = null)
 93    {
 94        var moveDto = new ArticleMoveDto
 95        {
 96            NewParentId = newParentId,
 97            NewSessionId = newSessionId
 98        };
 99
 100        return await _http.PutBoolAsync(
 101            $"articles/{articleId}/move",
 102            moveDto,
 103            _logger,
 104            $"move article {articleId}");
 105    }
 106
 107    public async Task<List<ArticleSearchResultDto>> SearchArticlesAsync(string query)
 108    {
 109        if (string.IsNullOrWhiteSpace(query))
 110            return new List<ArticleSearchResultDto>();
 111
 112        var results = await _http.GetEntityAsync<GlobalSearchResultsDto>(
 113            $"articles/search?query={Uri.EscapeDataString(query)}",
 114            _logger,
 115            $"search results for '{query}'");
 116
 117        if (results == null)
 118            return new List<ArticleSearchResultDto>();
 119
 120        // Combine all match types into a single list
 121        var allResults = new List<ArticleSearchResultDto>();
 122        allResults.AddRange(results.TitleMatches);
 123        allResults.AddRange(results.BodyMatches);
 124        allResults.AddRange(results.HashtagMatches);
 125        return allResults;
 126    }
 127
 128    public async Task<List<ArticleSearchResultDto>> SearchArticlesByTitleAsync(string query)
 129    {
 130        if (string.IsNullOrWhiteSpace(query))
 131            return new List<ArticleSearchResultDto>();
 132
 133        var results = await _http.GetEntityAsync<GlobalSearchResultsDto>(
 134            $"articles/search?query={Uri.EscapeDataString(query)}",
 135            _logger,
 136            $"title search results for '{query}'");
 137
 138        return results?.TitleMatches ?? new List<ArticleSearchResultDto>();
 139    }
 140
 141    public async Task<ArticleDto?> UpdateAliasesAsync(Guid articleId, string aliases)
 142    {
 143        var dto = new ArticleAliasesUpdateDto { Aliases = aliases ?? string.Empty };
 144
 145        return await _http.PutEntityAsync<ArticleDto>(
 146            $"articles/{articleId}/aliases",
 147            dto,
 148            _logger,
 149            $"aliases for article {articleId}");
 150    }
 151}