From 1714e3f911de2f923a26332b55e7f3f541769a91 Mon Sep 17 00:00:00 2001 From: andy Date: Wed, 27 Oct 2021 23:00:01 +0100 Subject: [PATCH] adding cache, ICache. RedisOptions and registering services in CLI and web app --- Selector.CLI/Options.cs | 10 ++++++++++ Selector.CLI/Program.cs | 19 +++++++++++++++++++ Selector.CLI/Selector.CLI.csproj | 2 +- Selector.Cache/ICache.cs | 24 ++++++++++++++++++++++++ Selector.Cache/JsonSerialiser.cs | 19 +++++++++++++++++++ Selector.Cache/Key.cs | 15 +++++++++++++++ Selector.Cache/Model.cs | 11 +++++++++++ Selector.Cache/RedisCache.cs | 25 +++++++++++++++++++++++++ Selector.Cache/Selector.Cache.csproj | 8 +++++++- Selector.Tests/Selector.Tests.csproj | 2 +- Selector.Web/Options.cs | 16 ++++++++++++++-- Selector.Web/Selector.Web.csproj | 2 +- Selector.Web/Startup.cs | 21 +++++++++++++++++++++ 13 files changed, 168 insertions(+), 6 deletions(-) create mode 100644 Selector.Cache/ICache.cs create mode 100644 Selector.Cache/JsonSerialiser.cs create mode 100644 Selector.Cache/Key.cs create mode 100644 Selector.Cache/Model.cs create mode 100644 Selector.Cache/RedisCache.cs diff --git a/Selector.CLI/Options.cs b/Selector.CLI/Options.cs index 258239f..37840ab 100644 --- a/Selector.CLI/Options.cs +++ b/Selector.CLI/Options.cs @@ -9,6 +9,7 @@ namespace Selector.CLI config.GetSection(RootOptions.Key).Bind(options); config.GetSection(FormatKeys( new[] { RootOptions.Key, WatcherOptions.Key})).Bind(options.WatcherOptions); config.GetSection(FormatKeys( new[] { RootOptions.Key, DatabaseOptions.Key})).Bind(options.DatabaseOptions); + config.GetSection(FormatKeys( new[] { RootOptions.Key, RedisOptions.Key})).Bind(options.RedisOptions); } public static RootOptions ConfigureOptions(IConfiguration config) @@ -35,6 +36,7 @@ namespace Selector.CLI public string ClientSecret { get; set; } public WatcherOptions WatcherOptions { get; set; } = new(); public DatabaseOptions DatabaseOptions { get; set; } = new(); + public RedisOptions RedisOptions { get; set; } = new(); public EqualityChecker Equality { get; set; } = EqualityChecker.Uri; } @@ -78,4 +80,12 @@ namespace Selector.CLI public bool Enabled { get; set; } = false; public string ConnectionString { get; set; } } + + class RedisOptions + { + public const string Key = "Redis"; + + public bool Enabled { get; set; } = false; + public string ConnectionString { get; set; } + } } diff --git a/Selector.CLI/Program.cs b/Selector.CLI/Program.cs index 6ec9540..3f1c9a7 100644 --- a/Selector.CLI/Program.cs +++ b/Selector.CLI/Program.cs @@ -8,6 +8,8 @@ using Microsoft.EntityFrameworkCore; using NLog.Extensions.Logging; using Selector.Model; +using Selector.Cache; +using StackExchange.Redis; namespace Selector.CLI { @@ -51,6 +53,23 @@ namespace Selector.CLI ); } + if (config.RedisOptions.Enabled) + { + Console.WriteLine("> Configuring Redis..."); + + if(string.IsNullOrWhiteSpace(config.RedisOptions.ConnectionString)) + { + Console.WriteLine("> No Redis configuration string provided, exiting..."); + Environment.Exit(1); + } + + var connMulti = ConnectionMultiplexer.Connect(config.RedisOptions.ConnectionString); + services.AddSingleton(connMulti); + services.AddSingleton(connMulti.GetDatabase()); + + services.AddSingleton, RedisCache>(); + } + switch (config.Equality) { case EqualityChecker.Uri: diff --git a/Selector.CLI/Selector.CLI.csproj b/Selector.CLI/Selector.CLI.csproj index e5ac509..8e51ddd 100644 --- a/Selector.CLI/Selector.CLI.csproj +++ b/Selector.CLI/Selector.CLI.csproj @@ -12,7 +12,7 @@ - + diff --git a/Selector.Cache/ICache.cs b/Selector.Cache/ICache.cs new file mode 100644 index 0000000..e6d9803 --- /dev/null +++ b/Selector.Cache/ICache.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Selector.Cache +{ + public interface ICache + { + public Task Get(TKey key); + public Task Set(TKey key, string value); + } + + /// + /// Is this unnecessary? + /// + /// + /// + public interface ICacheSerialiser + { + public Task Write(TKey key, T obj, ICache cache); + public Task Read(TKey key, ICache cache); + } +} diff --git a/Selector.Cache/JsonSerialiser.cs b/Selector.Cache/JsonSerialiser.cs new file mode 100644 index 0000000..02bf513 --- /dev/null +++ b/Selector.Cache/JsonSerialiser.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +using SpotifyAPI.Web; + +namespace Selector.Cache +{ + public static class JsonSerialiser + { + public static async Task Read(this ICache cache, string key) + => JsonSerializer.Deserialize(await cache.Get(key)); + + public static async Task Write(this ICache cache, string key, T obj) + => await cache.Set(key, JsonSerializer.Serialize(obj)); + } +} diff --git a/Selector.Cache/Key.cs b/Selector.Cache/Key.cs new file mode 100644 index 0000000..09ed02d --- /dev/null +++ b/Selector.Cache/Key.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Selector.Cache +{ + public class Key + { + public const string CurrentlyPlayingName = "CurrentlyPlaying"; + + public static string CurrentlyPlaying(string user) => Namespace(new[] { user, CurrentlyPlayingName }); + + public static string Namespace(string[] args) => string.Join(":", args); + } +} diff --git a/Selector.Cache/Model.cs b/Selector.Cache/Model.cs new file mode 100644 index 0000000..c7d297d --- /dev/null +++ b/Selector.Cache/Model.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Selector.Cache +{ + public class CurrentPlaying + { + + } +} diff --git a/Selector.Cache/RedisCache.cs b/Selector.Cache/RedisCache.cs new file mode 100644 index 0000000..3e7ecb9 --- /dev/null +++ b/Selector.Cache/RedisCache.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + +using StackExchange.Redis; + +namespace Selector.Cache +{ + public class RedisCache : ICache + { + private readonly IDatabaseAsync Db; + + public RedisCache( + IDatabaseAsync db + ) { + Db = db; + } + + public async Task Get(string key) => (await Db.StringGetAsync(key)).ToString(); + + public async Task Set(string key, string value) => await Db.StringSetAsync(key, value); + } +} diff --git a/Selector.Cache/Selector.Cache.csproj b/Selector.Cache/Selector.Cache.csproj index 0c5d768..936630d 100644 --- a/Selector.Cache/Selector.Cache.csproj +++ b/Selector.Cache/Selector.Cache.csproj @@ -1,13 +1,19 @@ - netstandard2.1 + net5.0 true latest + + + + + + diff --git a/Selector.Tests/Selector.Tests.csproj b/Selector.Tests/Selector.Tests.csproj index 64152cf..15dfd93 100644 --- a/Selector.Tests/Selector.Tests.csproj +++ b/Selector.Tests/Selector.Tests.csproj @@ -8,7 +8,7 @@ - + diff --git a/Selector.Web/Options.cs b/Selector.Web/Options.cs index c794f68..218cd94 100644 --- a/Selector.Web/Options.cs +++ b/Selector.Web/Options.cs @@ -3,11 +3,12 @@ using Microsoft.Extensions.Configuration; namespace Selector.Web { - public static class OptionsHelper { + static class OptionsHelper { public static void ConfigureOptions(RootOptions options, IConfiguration config) { config.GetSection(RootOptions.Key).Bind(options); - } + config.GetSection(FormatKeys(new[] { RootOptions.Key, RedisOptions.Key })).Bind(options.RedisOptions); + } public static RootOptions ConfigureOptions(IConfiguration config) { @@ -35,5 +36,16 @@ namespace Selector.Web /// Spotify callback for authentication /// public string SpotifyCallback { get; set; } + + public RedisOptions RedisOptions { get; set; } = new(); + + } + + public class RedisOptions + { + public const string Key = "Redis"; + + public bool Enabled { get; set; } = false; + public string ConnectionString { get; set; } } } diff --git a/Selector.Web/Selector.Web.csproj b/Selector.Web/Selector.Web.csproj index 1130f3a..db9949e 100644 --- a/Selector.Web/Selector.Web.csproj +++ b/Selector.Web/Selector.Web.csproj @@ -27,7 +27,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/Selector.Web/Startup.cs b/Selector.Web/Startup.cs index 13e8ea9..313abbf 100644 --- a/Selector.Web/Startup.cs +++ b/Selector.Web/Startup.cs @@ -13,8 +13,11 @@ using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Authorization; using Microsoft.EntityFrameworkCore; +using StackExchange.Redis; + using Selector.Model; using Selector.Model.Authorisation; +using Selector.Cache; namespace Selector.Web { @@ -34,6 +37,7 @@ namespace Selector.Web { OptionsHelper.ConfigureOptions(options, Configuration); }); + var config = OptionsHelper.ConfigureOptions(Configuration); services.AddRazorPages().AddRazorRuntimeCompilation(); services.AddControllers(); @@ -92,6 +96,23 @@ namespace Selector.Web services.AddScoped(); services.AddSingleton(); + + if (config.RedisOptions.Enabled) + { + Console.WriteLine("> Configuring Redis..."); + + if (string.IsNullOrWhiteSpace(config.RedisOptions.ConnectionString)) + { + Console.WriteLine("> No Redis configuration string provided, exiting..."); + Environment.Exit(1); + } + + var connMulti = ConnectionMultiplexer.Connect(config.RedisOptions.ConnectionString); + services.AddSingleton(connMulti); + services.AddSingleton(connMulti.GetDatabase()); + + services.AddSingleton, RedisCache>(); + } } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.