From 5f8a0c2d8482955ee1fbffe5cb0c08d6dab56834 Mon Sep 17 00:00:00 2001 From: "Jeffrey T. Fritz" Date: Fri, 3 Nov 2023 13:18:47 -0400 Subject: [PATCH 1/3] Initial pass at writing an AzureQueue provider for external website submission --- .../AzureQueueConfiguration.cs | 9 +++ .../AzureQueueProvider.cs | 70 +++++++++++++++++++ .../StartAzureQueue.cs | 42 +++++++++++ .../TagzApp.Providers.AzureQueue.csproj | 18 +++++ src/TagzApp.Web/wwwroot/js/site.js | 3 + src/TagzApp.sln | 7 ++ 6 files changed, 149 insertions(+) create mode 100644 src/TagzApp.Providers.AzureQueue/AzureQueueConfiguration.cs create mode 100644 src/TagzApp.Providers.AzureQueue/AzureQueueProvider.cs create mode 100644 src/TagzApp.Providers.AzureQueue/StartAzureQueue.cs create mode 100644 src/TagzApp.Providers.AzureQueue/TagzApp.Providers.AzureQueue.csproj diff --git a/src/TagzApp.Providers.AzureQueue/AzureQueueConfiguration.cs b/src/TagzApp.Providers.AzureQueue/AzureQueueConfiguration.cs new file mode 100644 index 00000000..304d1a63 --- /dev/null +++ b/src/TagzApp.Providers.AzureQueue/AzureQueueConfiguration.cs @@ -0,0 +1,9 @@ +namespace TagzApp.Providers.AzureQueue; + +public class AzureQueueConfiguration +{ + + public string QueueConnectionString { get; set; } = string.Empty; + public bool Activated { get; set; } +} + diff --git a/src/TagzApp.Providers.AzureQueue/AzureQueueProvider.cs b/src/TagzApp.Providers.AzureQueue/AzureQueueProvider.cs new file mode 100644 index 00000000..aba0b6b1 --- /dev/null +++ b/src/TagzApp.Providers.AzureQueue/AzureQueueProvider.cs @@ -0,0 +1,70 @@ +using Azure.Storage.Queues; +using System.Text.Json; + +namespace TagzApp.Providers.AzureQueue; + +public class AzureQueueProvider : ISocialMediaProvider +{ + private const string QueueName = "tagzapp-content"; + private readonly AzureQueueConfiguration _Configuration; + private QueueClient _Client; + private SocialMediaStatus _Status = SocialMediaStatus.Unhealthy; + private string _StatusMessage = "Not started"; + + public string Id => "WEBSITE"; + public string DisplayName => "Website"; + public string Description => "Q+A submitted through a website form"; + public TimeSpan NewContentRetrievalFrequency => TimeSpan.FromSeconds(5); + + public AzureQueueProvider(AzureQueueConfiguration configuration) + { + _Configuration = configuration; + } + + + public async Task> GetContentForHashtag(Hashtag tag, DateTimeOffset since) + { + + var messageResponse = await _Client.ReceiveMessagesAsync(maxMessages: 50); + if (!messageResponse.Value.Any()) return Enumerable.Empty(); + + var outList = new List(); + + foreach (var msg in messageResponse.Value) + { + + var content = JsonSerializer.Deserialize(msg.Body.ToStream()); + if (content is not null) outList.Add(content); + + } + + return outList; + + + } + + public Task<(SocialMediaStatus Status, string Message)> GetHealth() + { + return Task.FromResult((_Status, _StatusMessage)); + } + + public async Task StartAsync() + { + + _Client = new QueueClient(_Configuration.QueueConnectionString, QueueName); + + try + { + await _Client.CreateIfNotExistsAsync(); + } catch (Exception ex) + { + _Status = SocialMediaStatus.Unhealthy; + _StatusMessage = $"Unable to start a connection to the Azure Queue: {ex.Message}"; + return; + } + + _Status = SocialMediaStatus.Healthy; + _StatusMessage = "Connected"; + + } +} diff --git a/src/TagzApp.Providers.AzureQueue/StartAzureQueue.cs b/src/TagzApp.Providers.AzureQueue/StartAzureQueue.cs new file mode 100644 index 00000000..6deedb6d --- /dev/null +++ b/src/TagzApp.Providers.AzureQueue/StartAzureQueue.cs @@ -0,0 +1,42 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using System.Text.Json; +using TagzApp.Communication; + +namespace TagzApp.Providers.AzureQueue; + +public class StartAzureQueue : BaseConfigurationProvider, IConfigureProvider +{ + + private AzureQueueConfiguration _azureQueueConfiguration; + + public StartAzureQueue(IProviderConfigurationRepository providerConfigurationRepository) : base(providerConfigurationRepository) + { + } + + public async Task RegisterServices(IServiceCollection services, CancellationToken cancellationToken = default) + { + await LoadConfigurationValuesAsync("WEBSITE", cancellationToken); + + services.AddSingleton(_azureQueueConfiguration ?? new()); + services.AddTransient(); + return services; + } + + protected override void MapConfigurationValues(ProviderConfiguration providerConfiguration) + { + + var config = providerConfiguration.ConfigurationSettings; + + if (config != null) + { + _azureQueueConfiguration = new() + { + Activated = providerConfiguration.Activated, + QueueConnectionString = config["QueueConnectionString"] ?? string.Empty + }; + } + + } + +} diff --git a/src/TagzApp.Providers.AzureQueue/TagzApp.Providers.AzureQueue.csproj b/src/TagzApp.Providers.AzureQueue/TagzApp.Providers.AzureQueue.csproj new file mode 100644 index 00000000..924ab4a7 --- /dev/null +++ b/src/TagzApp.Providers.AzureQueue/TagzApp.Providers.AzureQueue.csproj @@ -0,0 +1,18 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + diff --git a/src/TagzApp.Web/wwwroot/js/site.js b/src/TagzApp.Web/wwwroot/js/site.js index 508127b8..955057d5 100644 --- a/src/TagzApp.Web/wwwroot/js/site.js +++ b/src/TagzApp.Web/wwwroot/js/site.js @@ -319,6 +319,9 @@ case 'twitter': cssClass = 'twitter-x'; break; + case "website": + cssClass = 'globe2'; + break; case 'youtube-chat': cssClass = 'youtube'; break; diff --git a/src/TagzApp.sln b/src/TagzApp.sln index 631ba3b0..56e79803 100644 --- a/src/TagzApp.sln +++ b/src/TagzApp.sln @@ -45,6 +45,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TagzApp.Storage.Postgres.Se EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TagzApp.Providers.YouTubeChat", "TagzApp.Providers.YouTubeChat\TagzApp.Providers.YouTubeChat.csproj", "{3562DD07-36C6-4729-A0B7-5DD119AEC7CD}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagzApp.Providers.AzureQueue", "TagzApp.Providers.AzureQueue\TagzApp.Providers.AzureQueue.csproj", "{F1503058-9A18-4EB2-A009-9311655707DF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -103,6 +105,10 @@ Global {3562DD07-36C6-4729-A0B7-5DD119AEC7CD}.Debug|Any CPU.Build.0 = Debug|Any CPU {3562DD07-36C6-4729-A0B7-5DD119AEC7CD}.Release|Any CPU.ActiveCfg = Release|Any CPU {3562DD07-36C6-4729-A0B7-5DD119AEC7CD}.Release|Any CPU.Build.0 = Release|Any CPU + {F1503058-9A18-4EB2-A009-9311655707DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1503058-9A18-4EB2-A009-9311655707DF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1503058-9A18-4EB2-A009-9311655707DF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1503058-9A18-4EB2-A009-9311655707DF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -121,6 +127,7 @@ Global {95D763E5-52F5-4155-A888-C66A686BA6EB} = {370455D5-6EA6-44C1-B315-B621F7B6A954} {F0F32FC9-C435-4B1D-BB21-61B93633E307} = {370455D5-6EA6-44C1-B315-B621F7B6A954} {3562DD07-36C6-4729-A0B7-5DD119AEC7CD} = {99E0BB3F-9591-4C7A-A8EE-C54B0C3DE7A5} + {F1503058-9A18-4EB2-A009-9311655707DF} = {99E0BB3F-9591-4C7A-A8EE-C54B0C3DE7A5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F2AD77A5-5B2D-41FC-AD37-8EF8A4E54410} From 9c30e4be233c2d6770bf7e95a1decd67cd804b84 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 3 Nov 2023 17:20:22 +0000 Subject: [PATCH 2/3] Applying formatting changes through GitHub Actions --- .../AzureQueueConfiguration.cs | 18 +-- .../AzureQueueProvider.cs | 141 +++++++++--------- .../StartAzureQueue.cs | 82 +++++----- src/TagzApp.Web/wwwroot/js/site.js | 2 +- 4 files changed, 121 insertions(+), 122 deletions(-) diff --git a/src/TagzApp.Providers.AzureQueue/AzureQueueConfiguration.cs b/src/TagzApp.Providers.AzureQueue/AzureQueueConfiguration.cs index 304d1a63..ced8830f 100644 --- a/src/TagzApp.Providers.AzureQueue/AzureQueueConfiguration.cs +++ b/src/TagzApp.Providers.AzureQueue/AzureQueueConfiguration.cs @@ -1,9 +1,9 @@ -namespace TagzApp.Providers.AzureQueue; - -public class AzureQueueConfiguration -{ - - public string QueueConnectionString { get; set; } = string.Empty; - public bool Activated { get; set; } -} - +namespace TagzApp.Providers.AzureQueue; + +public class AzureQueueConfiguration +{ + + public string QueueConnectionString { get; set; } = string.Empty; + public bool Activated { get; set; } +} + diff --git a/src/TagzApp.Providers.AzureQueue/AzureQueueProvider.cs b/src/TagzApp.Providers.AzureQueue/AzureQueueProvider.cs index aba0b6b1..3d0585e6 100644 --- a/src/TagzApp.Providers.AzureQueue/AzureQueueProvider.cs +++ b/src/TagzApp.Providers.AzureQueue/AzureQueueProvider.cs @@ -1,70 +1,71 @@ -using Azure.Storage.Queues; -using System.Text.Json; - -namespace TagzApp.Providers.AzureQueue; - -public class AzureQueueProvider : ISocialMediaProvider -{ - private const string QueueName = "tagzapp-content"; - private readonly AzureQueueConfiguration _Configuration; - private QueueClient _Client; - private SocialMediaStatus _Status = SocialMediaStatus.Unhealthy; - private string _StatusMessage = "Not started"; - - public string Id => "WEBSITE"; - public string DisplayName => "Website"; - public string Description => "Q+A submitted through a website form"; - public TimeSpan NewContentRetrievalFrequency => TimeSpan.FromSeconds(5); - - public AzureQueueProvider(AzureQueueConfiguration configuration) - { - _Configuration = configuration; - } - - - public async Task> GetContentForHashtag(Hashtag tag, DateTimeOffset since) - { - - var messageResponse = await _Client.ReceiveMessagesAsync(maxMessages: 50); - if (!messageResponse.Value.Any()) return Enumerable.Empty(); - - var outList = new List(); - - foreach (var msg in messageResponse.Value) - { - - var content = JsonSerializer.Deserialize(msg.Body.ToStream()); - if (content is not null) outList.Add(content); - - } - - return outList; - - - } - - public Task<(SocialMediaStatus Status, string Message)> GetHealth() - { - return Task.FromResult((_Status, _StatusMessage)); - } - - public async Task StartAsync() - { - - _Client = new QueueClient(_Configuration.QueueConnectionString, QueueName); - - try - { - await _Client.CreateIfNotExistsAsync(); - } catch (Exception ex) - { - _Status = SocialMediaStatus.Unhealthy; - _StatusMessage = $"Unable to start a connection to the Azure Queue: {ex.Message}"; - return; - } - - _Status = SocialMediaStatus.Healthy; - _StatusMessage = "Connected"; - - } -} +using Azure.Storage.Queues; +using System.Text.Json; + +namespace TagzApp.Providers.AzureQueue; + +public class AzureQueueProvider : ISocialMediaProvider +{ + private const string QueueName = "tagzapp-content"; + private readonly AzureQueueConfiguration _Configuration; + private QueueClient _Client; + private SocialMediaStatus _Status = SocialMediaStatus.Unhealthy; + private string _StatusMessage = "Not started"; + + public string Id => "WEBSITE"; + public string DisplayName => "Website"; + public string Description => "Q+A submitted through a website form"; + public TimeSpan NewContentRetrievalFrequency => TimeSpan.FromSeconds(5); + + public AzureQueueProvider(AzureQueueConfiguration configuration) + { + _Configuration = configuration; + } + + + public async Task> GetContentForHashtag(Hashtag tag, DateTimeOffset since) + { + + var messageResponse = await _Client.ReceiveMessagesAsync(maxMessages: 50); + if (!messageResponse.Value.Any()) return Enumerable.Empty(); + + var outList = new List(); + + foreach (var msg in messageResponse.Value) + { + + var content = JsonSerializer.Deserialize(msg.Body.ToStream()); + if (content is not null) outList.Add(content); + + } + + return outList; + + + } + + public Task<(SocialMediaStatus Status, string Message)> GetHealth() + { + return Task.FromResult((_Status, _StatusMessage)); + } + + public async Task StartAsync() + { + + _Client = new QueueClient(_Configuration.QueueConnectionString, QueueName); + + try + { + await _Client.CreateIfNotExistsAsync(); + } + catch (Exception ex) + { + _Status = SocialMediaStatus.Unhealthy; + _StatusMessage = $"Unable to start a connection to the Azure Queue: {ex.Message}"; + return; + } + + _Status = SocialMediaStatus.Healthy; + _StatusMessage = "Connected"; + + } +} diff --git a/src/TagzApp.Providers.AzureQueue/StartAzureQueue.cs b/src/TagzApp.Providers.AzureQueue/StartAzureQueue.cs index 6deedb6d..871c265c 100644 --- a/src/TagzApp.Providers.AzureQueue/StartAzureQueue.cs +++ b/src/TagzApp.Providers.AzureQueue/StartAzureQueue.cs @@ -1,42 +1,40 @@ -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using System.Text.Json; -using TagzApp.Communication; - -namespace TagzApp.Providers.AzureQueue; - -public class StartAzureQueue : BaseConfigurationProvider, IConfigureProvider -{ - - private AzureQueueConfiguration _azureQueueConfiguration; - - public StartAzureQueue(IProviderConfigurationRepository providerConfigurationRepository) : base(providerConfigurationRepository) - { - } - - public async Task RegisterServices(IServiceCollection services, CancellationToken cancellationToken = default) - { - await LoadConfigurationValuesAsync("WEBSITE", cancellationToken); - - services.AddSingleton(_azureQueueConfiguration ?? new()); - services.AddTransient(); - return services; - } - - protected override void MapConfigurationValues(ProviderConfiguration providerConfiguration) - { - - var config = providerConfiguration.ConfigurationSettings; - - if (config != null) - { - _azureQueueConfiguration = new() - { - Activated = providerConfiguration.Activated, - QueueConnectionString = config["QueueConnectionString"] ?? string.Empty - }; - } - - } - -} +using Microsoft.Extensions.DependencyInjection; +using TagzApp.Communication; + +namespace TagzApp.Providers.AzureQueue; + +public class StartAzureQueue : BaseConfigurationProvider, IConfigureProvider +{ + + private AzureQueueConfiguration _azureQueueConfiguration; + + public StartAzureQueue(IProviderConfigurationRepository providerConfigurationRepository) : base(providerConfigurationRepository) + { + } + + public async Task RegisterServices(IServiceCollection services, CancellationToken cancellationToken = default) + { + await LoadConfigurationValuesAsync("WEBSITE", cancellationToken); + + services.AddSingleton(_azureQueueConfiguration ?? new()); + services.AddTransient(); + return services; + } + + protected override void MapConfigurationValues(ProviderConfiguration providerConfiguration) + { + + var config = providerConfiguration.ConfigurationSettings; + + if (config != null) + { + _azureQueueConfiguration = new() + { + Activated = providerConfiguration.Activated, + QueueConnectionString = config["QueueConnectionString"] ?? string.Empty + }; + } + + } + +} diff --git a/src/TagzApp.Web/wwwroot/js/site.js b/src/TagzApp.Web/wwwroot/js/site.js index 955057d5..d439a9d9 100644 --- a/src/TagzApp.Web/wwwroot/js/site.js +++ b/src/TagzApp.Web/wwwroot/js/site.js @@ -319,7 +319,7 @@ case 'twitter': cssClass = 'twitter-x'; break; - case "website": + case 'website': cssClass = 'globe2'; break; case 'youtube-chat': From 6cd0121df5acbcd2111a68e60efd9c6df9673f7a Mon Sep 17 00:00:00 2001 From: "Jeffrey T. Fritz" Date: Sat, 4 Nov 2023 22:52:25 -0400 Subject: [PATCH 3/3] Tested and added default user profile pic --- src/TagzApp.Common/ISocialMediaProvider.cs | 2 ++ src/TagzApp.Common/Models/Creator.cs | 2 +- .../AzureQueueConfiguration.cs | 2 ++ .../AzureQueueProvider.cs | 10 ++++++-- .../Areas/Admin/Pages/Providers.cshtml | 4 +-- src/TagzApp.Web/Pages/Overlay.cshtml | 2 +- src/TagzApp.Web/Pages/PortraitOverlay.cshtml | 2 +- src/TagzApp.Web/Pages/Waterfall.cshtml | 2 +- src/TagzApp.Web/TagzApp.Web.csproj | 1 + src/TagzApp.Web/appsettings.json | 23 ++++++++++-------- src/TagzApp.Web/wwwroot/img/user.jpg | Bin 0 -> 32345 bytes src/TagzApp.Web/wwwroot/js/site.js | 2 +- 12 files changed, 33 insertions(+), 19 deletions(-) create mode 100644 src/TagzApp.Web/wwwroot/img/user.jpg diff --git a/src/TagzApp.Common/ISocialMediaProvider.cs b/src/TagzApp.Common/ISocialMediaProvider.cs index b719e394..b8c3987d 100644 --- a/src/TagzApp.Common/ISocialMediaProvider.cs +++ b/src/TagzApp.Common/ISocialMediaProvider.cs @@ -16,6 +16,8 @@ public interface ISocialMediaProvider /// string DisplayName { get; } + virtual string DllName => DisplayName; + /// /// Provider description /// diff --git a/src/TagzApp.Common/Models/Creator.cs b/src/TagzApp.Common/Models/Creator.cs index f03a3b1d..11d04fad 100644 --- a/src/TagzApp.Common/Models/Creator.cs +++ b/src/TagzApp.Common/Models/Creator.cs @@ -20,7 +20,7 @@ public class Creator /// /// Uri to the profile of the creator /// - public required Uri ProfileUri { get; set; } + public required Uri ProfileUri { get; set; } = new Uri("/img/user.jpg", UriKind.Relative); /// /// Uri to the profile image of the creator diff --git a/src/TagzApp.Providers.AzureQueue/AzureQueueConfiguration.cs b/src/TagzApp.Providers.AzureQueue/AzureQueueConfiguration.cs index ced8830f..797e57b2 100644 --- a/src/TagzApp.Providers.AzureQueue/AzureQueueConfiguration.cs +++ b/src/TagzApp.Providers.AzureQueue/AzureQueueConfiguration.cs @@ -4,6 +4,8 @@ public class AzureQueueConfiguration { public string QueueConnectionString { get; set; } = string.Empty; + public bool Activated { get; set; } + } diff --git a/src/TagzApp.Providers.AzureQueue/AzureQueueProvider.cs b/src/TagzApp.Providers.AzureQueue/AzureQueueProvider.cs index 3d0585e6..3b316f13 100644 --- a/src/TagzApp.Providers.AzureQueue/AzureQueueProvider.cs +++ b/src/TagzApp.Providers.AzureQueue/AzureQueueProvider.cs @@ -13,6 +13,7 @@ public class AzureQueueProvider : ISocialMediaProvider public string Id => "WEBSITE"; public string DisplayName => "Website"; + public string DllName { get { return "AzureQueue"; } } public string Description => "Q+A submitted through a website form"; public TimeSpan NewContentRetrievalFrequency => TimeSpan.FromSeconds(5); @@ -25,7 +26,7 @@ public AzureQueueProvider(AzureQueueConfiguration configuration) public async Task> GetContentForHashtag(Hashtag tag, DateTimeOffset since) { - var messageResponse = await _Client.ReceiveMessagesAsync(maxMessages: 50); + var messageResponse = await _Client.ReceiveMessagesAsync(maxMessages: 10); if (!messageResponse.Value.Any()) return Enumerable.Empty(); var outList = new List(); @@ -34,7 +35,12 @@ public async Task> GetContentForHashtag(Hashtag tag, DateTi { var content = JsonSerializer.Deserialize(msg.Body.ToStream()); - if (content is not null) outList.Add(content); + if (content is not null) + { + content.HashtagSought = tag.Text.ToLowerInvariant(); + outList.Add(content); + await _Client.DeleteMessageAsync(msg.MessageId, msg.PopReceipt); + } } diff --git a/src/TagzApp.Web/Areas/Admin/Pages/Providers.cshtml b/src/TagzApp.Web/Areas/Admin/Pages/Providers.cshtml index 915f2462..6a58282a 100644 --- a/src/TagzApp.Web/Areas/Admin/Pages/Providers.cshtml +++ b/src/TagzApp.Web/Areas/Admin/Pages/Providers.cshtml @@ -12,7 +12,7 @@

Provider Management

- @foreach (var provider in Model.Providers) + @foreach (var provider in Model.Providers.OrderBy(p => p.DisplayName)) { var health = await provider.GetHealth(); @@ -41,7 +41,7 @@

Please fill in the configuration values below:

- @foreach (var property in vmUtilities.LoadViewModel(provider.DisplayName) ?? new System.Reflection.PropertyInfo[0]) + @foreach (var property in vmUtilities.LoadViewModel(provider.DllName) ?? new System.Reflection.PropertyInfo[0]) { var displayName = vmUtilities.GetDisplayName(property); displayName = string.IsNullOrWhiteSpace(displayName) ? property.Name : displayName; diff --git a/src/TagzApp.Web/Pages/Overlay.cshtml b/src/TagzApp.Web/Pages/Overlay.cshtml index 54a96356..e0502c2d 100644 --- a/src/TagzApp.Web/Pages/Overlay.cshtml +++ b/src/TagzApp.Web/Pages/Overlay.cshtml @@ -57,7 +57,7 @@ overlayDisplay.className = "begin"; overlayDisplay.innerHTML = ` - ${content.authorDisplayName} + ${content.authorDisplayName}