From 1694f8e3a9f3b8f593bfc6a6482485aef68b5614 Mon Sep 17 00:00:00 2001 From: andy Date: Tue, 12 Oct 2021 20:23:25 +0100 Subject: [PATCH] added tokenfactoryprovider --- .gitignore | 2 ++ .vscode/launch.json | 15 ++++---- .vscode/tasks.json | 6 ++-- Selector.CLI/Options.cs | 5 +-- Selector.CLI/Program.cs | 1 + Selector.CLI/WatcherService.cs | 6 +++- Selector.CLI/appsettings.json | 2 +- .../ConfigFactory/ISpotifyConfigFactory.cs | 10 ++++++ .../RefreshTokenFactory.cs | 2 +- .../CachingRefreshTokenFactoryProvider.cs | 35 +++++++++++++++++++ .../IRefreshTokenFactoryProvider.cs | 11 ++++++ .../RefreshTokenFactoryProvider.cs | 33 +++++++++++++++++ Selector/Spotify/ISpotifyClientFactory.cs | 13 ------- 13 files changed, 113 insertions(+), 28 deletions(-) create mode 100644 Selector/Spotify/ConfigFactory/ISpotifyConfigFactory.cs rename Selector/Spotify/{ => ConfigFactory}/RefreshTokenFactory.cs (95%) create mode 100644 Selector/Spotify/FactoryProvider/CachingRefreshTokenFactoryProvider.cs create mode 100644 Selector/Spotify/FactoryProvider/IRefreshTokenFactoryProvider.cs create mode 100644 Selector/Spotify/FactoryProvider/RefreshTokenFactoryProvider.cs delete mode 100644 Selector/Spotify/ISpotifyClientFactory.cs diff --git a/.gitignore b/.gitignore index 72de34f..64ac0f6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +appsettings.Development.json + # User-specific files *.rsuser *.suo diff --git a/.vscode/launch.json b/.vscode/launch.json index a91c9cd..be9b76b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,19 +1,20 @@ { + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { - // Use IntelliSense to find out which attributes exist for C# debugging - // Use hover for the description of the existing attributes - // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md "name": ".NET Core Launch (console)", "type": "coreclr", "request": "launch", "preLaunchTask": "build", - // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/Selector.Tests/bin/Debug/net5.0/Selector.Tests.dll", + "program": "${workspaceFolder}/Selector.CLI/bin/Debug/net5.0/Selector.CLI.dll", + "env": { + "DOTNET_ENVIRONMENT": "Development" + }, "args": [], - "cwd": "${workspaceFolder}/Selector.Tests", - // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console + "cwd": "${workspaceFolder}/Selector.CLI", "console": "internalConsole", "stopAtEntry": false }, diff --git a/.vscode/tasks.json b/.vscode/tasks.json index b55186a..d4b5bd8 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -7,7 +7,7 @@ "type": "process", "args": [ "build", - "${workspaceFolder}/Selector.Tests/Selector.Tests.csproj", + "${workspaceFolder}/Selector.CLI/Selector.CLI.csproj", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary" ], @@ -19,7 +19,7 @@ "type": "process", "args": [ "publish", - "${workspaceFolder}/Selector.Tests/Selector.Tests.csproj", + "${workspaceFolder}/Selector.CLI/Selector.CLI.csproj", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary" ], @@ -32,7 +32,7 @@ "args": [ "watch", "run", - "${workspaceFolder}/Selector.Tests/Selector.Tests.csproj", + "${workspaceFolder}/Selector.CLI/Selector.CLI.csproj", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary" ], diff --git a/Selector.CLI/Options.cs b/Selector.CLI/Options.cs index 61ff915..8e49e3b 100644 --- a/Selector.CLI/Options.cs +++ b/Selector.CLI/Options.cs @@ -1,5 +1,4 @@ - -using System.Collections.Generic; +using System.Collections.Generic; namespace Selector.CLI { @@ -40,7 +39,9 @@ namespace Selector.CLI public string RefreshKey { get; set; } public int PollPeriod { get; set; } = 5000; public WatcherType Type { get; set; } = WatcherType.Player; +#nullable enable public string? PlaylistUri { get; set; } +#nullable disable } enum WatcherType diff --git a/Selector.CLI/Program.cs b/Selector.CLI/Program.cs index 9420053..9eadee6 100644 --- a/Selector.CLI/Program.cs +++ b/Selector.CLI/Program.cs @@ -29,6 +29,7 @@ namespace Selector.CLI // SERVICES //services.AddTransient(); //services.AddTransient(); + services.AddSingleton(); switch(context.Configuration.GetValue("selector:equality")) { diff --git a/Selector.CLI/WatcherService.cs b/Selector.CLI/WatcherService.cs index dc670ce..8fb6e03 100644 --- a/Selector.CLI/WatcherService.cs +++ b/Selector.CLI/WatcherService.cs @@ -15,13 +15,17 @@ namespace Selector.CLI { private readonly ILogger Logger; private readonly RootOptions Config; + private readonly IRefreshTokenFactoryProvider TokenFactoryProvider; private Dictionary Watchers { get; set; } = new(); - public WatcherService(ILogger logger, IOptions config) + public WatcherService(IRefreshTokenFactoryProvider tokenFactoryProvider, ILogger logger, IOptions config) { Logger = logger; Config = config.Value; + TokenFactoryProvider = tokenFactoryProvider; + + TokenFactoryProvider.Initialise(Config.ClientId, Config.ClientSecret); } public Task StartAsync(CancellationToken cancellationToken) diff --git a/Selector.CLI/appsettings.json b/Selector.CLI/appsettings.json index c066b15..14d3195 100644 --- a/Selector.CLI/appsettings.json +++ b/Selector.CLI/appsettings.json @@ -1,7 +1,7 @@ { "Selector": { "ClientId": "", - "ClientSecret" "", + "ClientSecret": "", "Equality": "uri", "Watcher": { "Instances": [ diff --git a/Selector/Spotify/ConfigFactory/ISpotifyConfigFactory.cs b/Selector/Spotify/ConfigFactory/ISpotifyConfigFactory.cs new file mode 100644 index 0000000..6bb792d --- /dev/null +++ b/Selector/Spotify/ConfigFactory/ISpotifyConfigFactory.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using SpotifyAPI.Web; + +namespace Selector +{ + public interface ISpotifyConfigFactory + { + public Task GetConfig(); + } +} diff --git a/Selector/Spotify/RefreshTokenFactory.cs b/Selector/Spotify/ConfigFactory/RefreshTokenFactory.cs similarity index 95% rename from Selector/Spotify/RefreshTokenFactory.cs rename to Selector/Spotify/ConfigFactory/RefreshTokenFactory.cs index 22cc4e6..45a4a1f 100644 --- a/Selector/Spotify/RefreshTokenFactory.cs +++ b/Selector/Spotify/ConfigFactory/RefreshTokenFactory.cs @@ -8,7 +8,7 @@ namespace Selector /// /// Get config from a refresh token /// - public class RefreshTokenFactory : ISpotifyClientFactory + public class RefreshTokenFactory : ISpotifyConfigFactory { private string ClientId { get; set; } private string ClientSecret { get; set; } diff --git a/Selector/Spotify/FactoryProvider/CachingRefreshTokenFactoryProvider.cs b/Selector/Spotify/FactoryProvider/CachingRefreshTokenFactoryProvider.cs new file mode 100644 index 0000000..e5c9469 --- /dev/null +++ b/Selector/Spotify/FactoryProvider/CachingRefreshTokenFactoryProvider.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using SpotifyAPI.Web; + + +namespace Selector +{ + public class CachingRefreshTokenFactoryProvider : RefreshTokenFactoryProvider + { + protected Dictionary Configs = new(); + + public RefreshTokenFactory GetUserConfig(string userId) => Configs.ContainsKey(userId) ? Configs[userId] : null; + + new public async Task GetFactory(string refreshToken) + { + var configProvider = await base.GetFactory(refreshToken); + var newConfig = await configProvider.GetConfig(); + + var client = new SpotifyClient(newConfig); + var userDetails = await client.UserProfile.Current(); + + if(Configs.ContainsKey(userDetails.Id)) + { + return Configs[userDetails.Id]; + } + else + { + Configs[userDetails.Id] = configProvider; + return configProvider; + } + } + } +} diff --git a/Selector/Spotify/FactoryProvider/IRefreshTokenFactoryProvider.cs b/Selector/Spotify/FactoryProvider/IRefreshTokenFactoryProvider.cs new file mode 100644 index 0000000..11ba334 --- /dev/null +++ b/Selector/Spotify/FactoryProvider/IRefreshTokenFactoryProvider.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; + +namespace Selector +{ + public interface IRefreshTokenFactoryProvider + { + public void Initialise(string clientId, string clientSecret); + public bool Initialised { get; } + public Task GetFactory(string refreshToken); + } +} diff --git a/Selector/Spotify/FactoryProvider/RefreshTokenFactoryProvider.cs b/Selector/Spotify/FactoryProvider/RefreshTokenFactoryProvider.cs new file mode 100644 index 0000000..9ff2546 --- /dev/null +++ b/Selector/Spotify/FactoryProvider/RefreshTokenFactoryProvider.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using SpotifyAPI.Web; + + +namespace Selector +{ + /// + /// Get config from a refresh token + /// + public class RefreshTokenFactoryProvider : IRefreshTokenFactoryProvider + { + protected string ClientId { get; set; } + protected string ClientSecret { get; set; } + + public void Initialise(string clientId, string clientSecret){ + ClientId = clientId; + ClientSecret = clientSecret; + } + + public bool Initialised => string.IsNullOrWhiteSpace(ClientId) || string.IsNullOrWhiteSpace(ClientSecret); + + public Task GetFactory(string refreshToken) + { + if(!Initialised) throw new InvalidOperationException("Factory not initialised"); + if(string.IsNullOrEmpty(refreshToken)) throw new ArgumentException("Null or empty refresh key provided"); + + return Task.FromResult(new RefreshTokenFactory(ClientId, ClientSecret, refreshToken)); + } + } +} diff --git a/Selector/Spotify/ISpotifyClientFactory.cs b/Selector/Spotify/ISpotifyClientFactory.cs deleted file mode 100644 index 5b45dae..0000000 --- a/Selector/Spotify/ISpotifyClientFactory.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using SpotifyAPI.Web; - -namespace Selector -{ - public interface ISpotifyClientFactory - { - public Task GetConfig(); - } -}