From 1f0e8840c1d55083940743a7c24f0bd51b11592d Mon Sep 17 00:00:00 2001 From: Max Holleman Date: Thu, 1 Jun 2023 15:55:15 +0200 Subject: [PATCH] Reworking db & Syndication implementation --- SharpRss/DbAccess.cs | 49 +++++++++--------- .../{GroupModel.cs => CategoryModel.cs} | 4 +- SharpRss/Models/FeedItemModel.cs | 2 + SharpRss/Models/FeedModel.cs | 3 +- SharpRss/Services/RssService.cs | 51 ++++++++++++++++--- WebSharpRSS/Bootstrapper.cs | 6 --- WebSharpRSS/Models/TreeItemData.cs | 4 +- WebSharpRSS/Settings.json | 12 ----- WebSharpRSS/Shared/SideGuide.razor | 2 +- 9 files changed, 75 insertions(+), 58 deletions(-) rename SharpRss/Models/{GroupModel.cs => CategoryModel.cs} (87%) delete mode 100644 WebSharpRSS/Settings.json diff --git a/SharpRss/DbAccess.cs b/SharpRss/DbAccess.cs index 79c011c..f578d9a 100644 --- a/SharpRss/DbAccess.cs +++ b/SharpRss/DbAccess.cs @@ -21,9 +21,8 @@ namespace SharpRss private const string FeedItemTable = "feed_item_data"; // Groups - public static async Task> GetGroupsAsync(string? groupId = null) + public static async Task> GetGroupsAsync(string? groupId = null) { - CheckInitialized(); await using SqliteConnection dbc = new SqliteConnection(ConnectionString); dbc.Open(); await using SqliteCommand cmd = new SqliteCommand(groupId != null ? $"SELECT * FROM {GroupTable} WHERE id=@gId;" : $"SELECT * FROM {GroupTable}", dbc) @@ -34,7 +33,7 @@ namespace SharpRss } }; await using SqliteDataReader reader = await cmd.ExecuteReaderAsync(); - HashSet groups = new HashSet(); + HashSet groups = new HashSet(); await using SqliteCommand cmdFeedCount = new SqliteCommand($"SELECT COUNT(*) FROM {FeedTable} WHERE group_id=@groupId", dbc); while (reader.Read()) { @@ -43,7 +42,7 @@ namespace SharpRss using SqliteDataReader countReader = await cmdFeedCount.ExecuteReaderAsync(); int count = countReader.Read() ? countReader.GetInt32(0) : 0; - groups.Add(new GroupModel() + groups.Add(new CategoryModel() { Name = reader["name"].ToString(), FeedCount = count, @@ -54,7 +53,7 @@ namespace SharpRss } return groups; } - public static async Task SetGroupAsync(GroupModel groupModel) + public static async Task SetGroupAsync(CategoryModel groupModel) { bool result = false; await using SqliteConnection dbc = new SqliteConnection(ConnectionString); @@ -74,7 +73,7 @@ namespace SharpRss result = true; return result; } - public static async Task RemoveGroupAsync(GroupModel groupModel) + public static async Task RemoveGroupAsync(CategoryModel groupModel) { bool result = false; await using SqliteConnection dbc = new SqliteConnection(ConnectionString); @@ -93,6 +92,7 @@ namespace SharpRss return result; } // Feeds + /// /// /// @@ -115,14 +115,15 @@ namespace SharpRss feeds.Add(await ReaderToFeedModel(reader)); return feeds; } - public static async Task GetFeedAsync(string feedId) + public static async Task GetFeedAsync(string url) { await using SqliteConnection dbc = new SqliteConnection(ConnectionString); dbc.Open(); FeedModel? feed = null; - await using SqliteCommand cmd = new SqliteCommand($"SELECT * FROM {FeedTable} WHERE id=@id", dbc) + //TODO: Use dapper to simplify this query. + await using SqliteCommand cmd = new SqliteCommand($"SELECT * FROM {FeedTable} WHERE url=@Url", dbc) { - Parameters = { new SqliteParameter("id", feedId) } + Parameters = { new SqliteParameter("Url", url) } }; await using SqliteDataReader reader = await cmd.ExecuteReaderAsync(); if (reader.Read()) @@ -241,7 +242,7 @@ namespace SharpRss }; if (feedItemModel is { FeedId: { } }) { - FeedModel? feedModel = await GetFeedAsync(feedItemModel.FeedId); + FeedModel? feedModel = await GetFeedAsync(feedItemModel.FeedUrl); feedItemModel.Feed = feedModel; } feedItems.Add(feedItemModel); @@ -303,9 +304,9 @@ namespace SharpRss result = true; return result; } - public static async Task GetGroupFromFeedItemAsync(FeedItemModel feedItem) + public static async Task GetGroupFromFeedItemAsync(FeedItemModel feedItem) { - GroupModel? result = null; + CategoryModel? result = null; await using SqliteConnection dbc = new SqliteConnection(ConnectionString); dbc.Open(); await using SqliteCommand cmd = new SqliteCommand($"SELECT * FROM {GroupTable} WHERE id=(SELECT group_id FROM {FeedTable} WHERE id=@fId)", dbc) @@ -316,7 +317,7 @@ namespace SharpRss } }; await using SqliteDataReader reader = await cmd.ExecuteReaderAsync(); - HashSet? groups = null; + HashSet? groups = null; if (reader.Read()) groups = await GetGroupsAsync(reader["group_id"].ToString()); if (groups != null && groups.Any()) @@ -348,10 +349,6 @@ namespace SharpRss } //=== - private static void CheckInitialized() - { - if (!_isInitialized) throw new TypeInitializationException(nameof(DbAccess), null); - } public static async void Initialize() { if (_isInitialized) return; @@ -359,17 +356,17 @@ namespace SharpRss HashSet failed = new HashSet(); await using SqliteConnection dbc = new SqliteConnection(ConnectionString); dbc.Open(); - Log.Verbose("Checking table: {Table}", GroupTable); - var queryResponse = await dbc.QueryAsync($"CREATE TABLE IF NOT EXISTS {GroupTable} (name STRING NOT NULL, hex_color STRING NOT NULL, icon STRING, id STRING PRIMARY KEY)"); - if (queryResponse.Any()) failed.Add("category_data"); + Log.Verbose("Checking table: {Table}", "category"); + var queryResponse = await dbc.QueryAsync("CREATE TABLE IF NOT EXISTS category (name STRING NOT NULL, hex_color STRING NOT NULL, icon STRING, id STRING PRIMARY KEY)"); + if (queryResponse.Any()) failed.Add("category"); - Log.Verbose("Checking table: {Table}", FeedTable); - queryResponse = await dbc.QueryAsync($"CREATE TABLE IF NOT EXISTS {FeedTable} (id STRING PRIMARY KEY, url STRING NOT NULL, title STRING, group_id STRING, feed_type STRING, description STRING, language STRING, copyright STRING, date_added INT, last_updated INT, image_url STRING, original_document STRING)"); - if (queryResponse.Any()) failed.Add("feed_data"); + Log.Verbose("Checking table: {Table}", "feed"); + queryResponse = await dbc.QueryAsync($"CREATE TABLE IF NOT EXISTS feed (url STRING PRIMARY KEY, title STRING, group_id STRING, feed_type STRING, description STRING, language STRING, copyright STRING, date_added INT, last_updated INT, image_url STRING, original_document STRING)"); + if (queryResponse.Any()) failed.Add("feed"); - Log.Verbose("Checking table: {Table}", FeedItemTable); - queryResponse = await dbc.QueryAsync($"CREATE TABLE IF NOT EXISTS {FeedItemTable} (id STRING PRIMARY KEY, feed_id STRING, read INT, title STRING, description STRING, link STRING, last_updated INT, publishing_date INT, author STRING, categories STRING, content STRING)"); - if (queryResponse.Any()) failed.Add("feed_item_data"); + Log.Verbose("Checking table: {Table}", "feed_item"); + queryResponse = await dbc.QueryAsync($"CREATE TABLE IF NOT EXISTS feed_item (id STRING PRIMARY KEY, feed_id STRING, read INT, title STRING, description STRING, link STRING, last_updated INT, publishing_date INT, author STRING, categories STRING, content STRING)"); + if (queryResponse.Any()) failed.Add("feed_item"); if (failed.Any()) { diff --git a/SharpRss/Models/GroupModel.cs b/SharpRss/Models/CategoryModel.cs similarity index 87% rename from SharpRss/Models/GroupModel.cs rename to SharpRss/Models/CategoryModel.cs index d6678e9..3a6e736 100644 --- a/SharpRss/Models/GroupModel.cs +++ b/SharpRss/Models/CategoryModel.cs @@ -3,9 +3,9 @@ using ToolQit; namespace SharpRss.Models { - public class GroupModel + public class CategoryModel { - public GroupModel() + public CategoryModel() { HexColor = Utilities.GenerateRandomHexColor(); Id = Guid.NewGuid().ToString(); diff --git a/SharpRss/Models/FeedItemModel.cs b/SharpRss/Models/FeedItemModel.cs index 7cad094..506e196 100644 --- a/SharpRss/Models/FeedItemModel.cs +++ b/SharpRss/Models/FeedItemModel.cs @@ -6,7 +6,9 @@ namespace SharpRss.Models { public FeedModel? Feed { get; set; } public string? Id { get; set; } = string.Empty; + // FeedId will be removed public string? FeedId { get; set; } = string.Empty; + public string FeedUrl { get; set; } = string.Empty; public bool Read { get; set; } public string? Type { get; set; } = string.Empty; public string? Title { get; set; } = string.Empty; diff --git a/SharpRss/Models/FeedModel.cs b/SharpRss/Models/FeedModel.cs index 296f9d3..8fdb172 100644 --- a/SharpRss/Models/FeedModel.cs +++ b/SharpRss/Models/FeedModel.cs @@ -7,9 +7,8 @@ namespace SharpRss.Models public FeedModel(string rssUrl) { Url = rssUrl; - Id = Guid.NewGuid().ToString(); } - public GroupModel? Group { get; set; } + public CategoryModel? Group { get; set; } public string? Id { get; set; } public string? Url { get; set; } public string? Title { get; set; } = string.Empty; diff --git a/SharpRss/Services/RssService.cs b/SharpRss/Services/RssService.cs index e935851..ce7a7d9 100644 --- a/SharpRss/Services/RssService.cs +++ b/SharpRss/Services/RssService.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Threading.Tasks; using Argotic.Common; using Argotic.Syndication; +using Microsoft.Data.Sqlite; using Serilog; using SharpRss.Models; @@ -26,14 +27,50 @@ namespace SharpRss.Services items.UnionWith(await GetUngroupedFeedsAsync()); return items; } - public async Task CreateGroupAsync(GroupModel group) => await DbAccess.SetGroupAsync(group); - public async Task> GetGroupsAsync() => await DbAccess.GetGroupsAsync(); + public async Task CreateGroupAsync(CategoryModel group) => await DbAccess.SetGroupAsync(group); + public async Task> GetGroupsAsync() => await DbAccess.GetGroupsAsync(); - //TODO: Need to rework this implementation!!! - public async Task AddFeedsAsync(string[]? rssUrls, GroupModel? group = null) + //TODO: Rework this! + // Subscribe to a feed. + public async Task AddSubscriptionAsync(string url, CategoryModel? group = null) { + // Check for valid feed url + bool validate = SyndicationDiscoveryUtility.UriExists(new Uri(url)); + if (!validate) return false; + // Check if feed exists in db + FeedModel? dbFeed = await DbAccess.GetFeedAsync(url); + if (dbFeed == null) + { + GenericSyndicationFeed genFeed = new GenericSyndicationFeed(); + switch (genFeed.Format) + { + case SyndicationContentFormat.Rss: + RssFeed rssFeed = (RssFeed)genFeed.Resource; + break; + case SyndicationContentFormat.Atom: + AtomFeed atomFeed = (AtomFeed)genFeed.Resource; + break; + default: + Log.Information("Feed implementation missing!"); + break; + } + } + // Update feed if newer + // Update/fetch items + return false; } + private static FeedModel FromResource(ISyndicationResource resource) + { + FeedModel model = new FeedModel(""); + switch (resource) + { + case RssFeed rssFeed: + //TODO: From feed to model + break; + } + return model; + } public async Task UpdateFeeds() { @@ -122,14 +159,14 @@ namespace SharpRss.Services throw; }*/ var groups = await GetGroupsAsync(); - GroupModel testGroup = groups.Single(x => x.Name == "News"); - await AddFeedsAsync(new[] + CategoryModel testGroup = groups.Single(x => x.Name == "News"); + /*await AddFeedsAsync(new[] { "https://www.nu.nl/rss/Algemeen", "https://www.nu.nl/rss/Economie", "https://www.nu.nl/rss/Sport", "http://news.google.com/?output=atom" - }, testGroup); + }, testGroup);*/ } public void Dispose() diff --git a/WebSharpRSS/Bootstrapper.cs b/WebSharpRSS/Bootstrapper.cs index 21df60b..55cac56 100644 --- a/WebSharpRSS/Bootstrapper.cs +++ b/WebSharpRSS/Bootstrapper.cs @@ -18,12 +18,6 @@ namespace WebSharpRSS //paths.Set("FaviconResolveUrl", "https://icons.duckduckgo.com/ip3/{0}.ico", false); paths.Set("FaviconResolveUrl", "http://www.google.com/s2/favicons?domain={0}", false); paths.Set("LogPath", Path.Combine(Environment.CurrentDirectory, "logs", "log_.json"), false); - - var dbSql = dataCon["SQL"]; - dbSql.Set("Host", "localhost", false); - dbSql.Set("Port", "6969", false); - dbSql.Set("Username", "sharpUser", false); - dbSql.Set("Password", "sh@rP@s$", false); } private static LoggerConfiguration? _configuration; diff --git a/WebSharpRSS/Models/TreeItemData.cs b/WebSharpRSS/Models/TreeItemData.cs index 620779b..76b80f9 100644 --- a/WebSharpRSS/Models/TreeItemData.cs +++ b/WebSharpRSS/Models/TreeItemData.cs @@ -8,7 +8,7 @@ namespace WebSharpRSS.Models { public class TreeItemData { - public TreeItemData(GroupModel groupModel) + public TreeItemData(CategoryModel groupModel) { GroupModel = groupModel; Title = groupModel.Name; @@ -23,7 +23,7 @@ namespace WebSharpRSS.Models if (FeedModel.Url == null) return; FaviconUrl = string.Format(Caretaker.Settings["Paths"].GetString("FaviconResolveUrl"), new Uri(FeedModel.Url).Host); } - public readonly GroupModel? GroupModel; + public readonly CategoryModel? GroupModel; public readonly FeedModel? FeedModel; public HashSet? Children { get; set; } diff --git a/WebSharpRSS/Settings.json b/WebSharpRSS/Settings.json deleted file mode 100644 index 2cb5c33..0000000 --- a/WebSharpRSS/Settings.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "Paths": { - "FaviconResolveUrl": "http://www.google.com/s2/favicons?domain={0}", - "LogPath": "/home/max/GitHub/SharpRSS/WebSharpRSS/logs/log_.json" - }, - "SQL": { - "Host": "localhost", - "Port": "6969", - "Username": "sharpUser", - "Password": "sh@rP@s$" - } -} \ No newline at end of file diff --git a/WebSharpRSS/Shared/SideGuide.razor b/WebSharpRSS/Shared/SideGuide.razor index 45ffde3..0f2ec49 100644 --- a/WebSharpRSS/Shared/SideGuide.razor +++ b/WebSharpRSS/Shared/SideGuide.razor @@ -76,5 +76,5 @@ StateHasChanged(); Log.Verbose("Guide initialized!"); } - private HashSet ModelToTreeItem(HashSet model) => model.Select(x => x is GroupModel model ? new TreeItemData(model) : x is FeedModel feedModel ? new TreeItemData(feedModel) : throw new ArgumentException("Item arg is invalid!")).ToHashSet(); + private HashSet ModelToTreeItem(HashSet model) => model.Select(x => x is CategoryModel model ? new TreeItemData(model) : x is FeedModel feedModel ? new TreeItemData(feedModel) : throw new ArgumentException("Item arg is invalid!")).ToHashSet(); } \ No newline at end of file