< Summary

Information
Class: GistBackend.Types.RssFeed
Assembly: GistBackend
File(s): /home/runner/work/the-gist-of-it-sec/the-gist-of-it-sec/backend/GistBackend/Types/RssFeed.cs
Line coverage
82%
Covered lines: 33
Uncovered lines: 7
Coverable lines: 40
Total lines: 69
Line coverage: 82.5%
Branch coverage
50%
Covered branches: 5
Total branches: 10
Branch coverage: 50%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
get_AllowedCategories()100%210%
get_ForbiddenCategories()100%210%
get_SyndicationFeed()100%11100%
get_Id()100%11100%
get_Title()100%11100%
get_Entries()100%11100%
CheckForSponsoredContent(...)100%210%
CheckForPaywall(...)100%210%
ParseFeedAsync()50%2280%
ParseEntries(...)50%6687.5%
ToRssFeedInfo()50%22100%
SyndicationItemToRssEntry(...)100%11100%

File(s)

/home/runner/work/the-gist-of-it-sec/the-gist-of-it-sec/backend/GistBackend/Types/RssFeed.cs

#LineLine coverage
 1using System.Net;
 2using System.ServiceModel.Syndication;
 3using System.Xml;
 4using GistBackend.Exceptions;
 5using GistBackend.Utils;
 6
 7namespace GistBackend.Types;
 8
 3139public abstract record RssFeed()
 10{
 11    public abstract Uri RssUrl { get; }
 12    public abstract Language Language { get; }
 13    public abstract FeedType Type { get; }
 014    public virtual IEnumerable<string>? AllowedCategories => null;
 015    public virtual IEnumerable<string>? ForbiddenCategories => null;
 14016    private SyndicationFeed? SyndicationFeed { get; set; }
 30217    public int? Id { get; set; }
 22518    public string? Title { get; set; }
 9319    public IEnumerable<RssEntry>? Entries { get; private set; }
 20    public abstract string ExtractText(string content);
 21
 022    public virtual bool CheckForSponsoredContent(string content) => false;
 023    public virtual bool CheckForPaywall(string content) => false;
 24
 25    public async Task ParseFeedAsync(HttpClient httpClient, CancellationToken ct)
 26    {
 3627        var response = await httpClient.GetAsync(RssUrl, ct);
 3628        if (response.StatusCode != HttpStatusCode.OK)
 29        {
 030            throw new ParsingFeedException(
 031                $"Failed to fetch RSS feed from {RssUrl}, status code: {response.StatusCode}");
 32        }
 3633        var feedContent = await response.Content.ReadAsStringAsync(ct);
 3634        using var stringReader = new StringReader(feedContent);
 3635        using var xmlReader = XmlReader.Create(stringReader);
 3636        SyndicationFeed = SyndicationFeed.Load(xmlReader);
 3637        Title = SyndicationFeed.Title.Text;
 3638    }
 39
 40    public void ParseEntries(int feedId)
 41    {
 3442        if (SyndicationFeed is null || Title is null)
 043            throw new InvalidOperationException($"{nameof(SyndicationFeed)} is null, need to parse feed first");
 3444        Id = feedId;
 3445        Entries = SyndicationFeed.Items.Select(SyndicationItemToRssEntry)
 3446            .FilterForAllowedCategories(AllowedCategories)
 3447            .FilterForForbiddenCategories(ForbiddenCategories)
 3448            .FilterPaywallEntries(Title ?? "");
 3449    }
 50
 51    public RssFeedInfo ToRssFeedInfo()
 52    {
 1353        return Title is null
 1354            ? throw new ArgumentNullException($"{nameof(Title)} is null, need to parse feed first")
 1355            : new RssFeedInfo(Title, RssUrl, Language, Type) { Id = Id };
 56    }
 57
 58    private RssEntry SyndicationItemToRssEntry(SyndicationItem item) =>
 18759        new(
 18760            item.Id.Trim(),
 18761            Id!.Value,
 18762            item.ExtractAuthor(),
 18763            WebUtility.HtmlDecode(item.Title.Text.Trim()),
 18764            item.PublishDate.UtcDateTime,
 18765            item.ExtractUpdated(),
 18766            item.ExtractUrl(),
 18767            item.ExtractCategories()
 18768        );
 69}