diff --git a/src/TrashLib.Tests/Radarr/CustomFormat/Guide/GithubCustomFormatJsonRequesterTest.cs b/src/TrashLib.Tests/Radarr/CustomFormat/Guide/GithubCustomFormatJsonRequesterTest.cs deleted file mode 100644 index 2aa9a90a..00000000 --- a/src/TrashLib.Tests/Radarr/CustomFormat/Guide/GithubCustomFormatJsonRequesterTest.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using FluentAssertions; -using Newtonsoft.Json.Linq; -using NUnit.Framework; -using TrashLib.Radarr.CustomFormat.Guide; - -namespace TrashLib.Tests.Radarr.CustomFormat.Guide -{ - [TestFixture] - [Parallelizable(ParallelScope.All)] - public class GithubCustomFormatJsonRequesterTest - { - [Test] - public async Task Requesting_json_from_github_works() - { - var requester = new GithubCustomFormatJsonRequester(); - - var jsonList = (await requester.GetCustomFormatJson()).ToList(); - - Action act = () => JObject.Parse(jsonList.First()); - - // As of the time this test was written, there are around 58 custom format JSON files. - // This number can fluctuate over time, but I'm only interested in verifying we get a handful - // of files in the response. - jsonList.Should().HaveCountGreaterOrEqualTo(5); - - act.Should().NotThrow(); - } - } -} diff --git a/src/TrashLib.Tests/Radarr/CustomFormat/Processors/GuideProcessorTest.cs b/src/TrashLib.Tests/Radarr/CustomFormat/Processors/GuideProcessorTest.cs index 3d5298ba..0e8dad86 100644 --- a/src/TrashLib.Tests/Radarr/CustomFormat/Processors/GuideProcessorTest.cs +++ b/src/TrashLib.Tests/Radarr/CustomFormat/Processors/GuideProcessorTest.cs @@ -2,8 +2,8 @@ using System.Diagnostics.CodeAnalysis; using Common; using FluentAssertions; -using Flurl.Http.Testing; using Newtonsoft.Json.Linq; +using NSubstitute; using NUnit.Framework; using Serilog; using TestLibrary.FluentAssertions; @@ -55,25 +55,18 @@ namespace TrashLib.Tests.Radarr.CustomFormat.Processors public void Guide_processor_behaves_as_expected_with_normal_guide_data() { var ctx = new Context(); - var guideProcessor = - new GuideProcessor(ctx.Logger, new GithubCustomFormatJsonRequester(), - () => new TestGuideProcessorSteps()); + var guideService = Substitute.For(); + var guideProcessor = new GuideProcessor(ctx.Logger, guideService, () => new TestGuideProcessorSteps()); - // simulate guide data - using var testHttp = new HttpTest(); - testHttp.RespondWithJson(new[] + // Simulate custom format JSON data from local Git repository + guideService.GetCustomFormatJson().Returns(new[] { - new {name = "ImportableCustomFormat1.json", type = "file", download_url = "http://not_real/file.json"}, - new {name = "ImportableCustomFormat2.json", type = "file", download_url = "http://not_real/file.json"}, - new {name = "NoScore.json", type = "file", download_url = "http://not_real/file.json"}, - new {name = "WontBeInConfig.json", type = "file", download_url = "http://not_real/file.json"} + ctx.Data.ReadData("ImportableCustomFormat1.json"), + ctx.Data.ReadData("ImportableCustomFormat2.json"), + ctx.Data.ReadData("NoScore.json"), + ctx.Data.ReadData("WontBeInConfig.json") }); - testHttp.RespondWithJson(ctx.ReadJson("ImportableCustomFormat1.json")); - testHttp.RespondWithJson(ctx.ReadJson("ImportableCustomFormat2.json")); - testHttp.RespondWithJson(ctx.ReadJson("NoScore.json")); - testHttp.RespondWithJson(ctx.ReadJson("WontBeInConfig.json")); - // Simulate user config in YAML var config = new List { diff --git a/src/TrashLib/Config/IResourcePaths.cs b/src/TrashLib/Config/IResourcePaths.cs new file mode 100644 index 00000000..0d9fa753 --- /dev/null +++ b/src/TrashLib/Config/IResourcePaths.cs @@ -0,0 +1,7 @@ +namespace TrashLib.Config +{ + public interface IResourcePaths + { + string RepoPath { get; } + } +} diff --git a/src/TrashLib/Config/ServiceConfiguration.cs b/src/TrashLib/Config/ServiceConfiguration.cs index 964d19cf..1fec1b6b 100644 --- a/src/TrashLib/Config/ServiceConfiguration.cs +++ b/src/TrashLib/Config/ServiceConfiguration.cs @@ -2,7 +2,7 @@ { public abstract class ServiceConfiguration : IServiceConfiguration { - public string BaseUrl { get; init; } = ""; - public string ApiKey { get; init; } = ""; + public string BaseUrl { get; set; } = ""; + public string ApiKey { get; set; } = ""; } } diff --git a/src/TrashLib/Radarr/CustomFormat/Guide/GithubCustomFormatJsonRequester.cs b/src/TrashLib/Radarr/CustomFormat/Guide/GithubCustomFormatJsonRequester.cs deleted file mode 100644 index e2c66ef9..00000000 --- a/src/TrashLib/Radarr/CustomFormat/Guide/GithubCustomFormatJsonRequester.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Flurl.Http; -using Flurl.Http.Configuration; -using JetBrains.Annotations; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; - -namespace TrashLib.Radarr.CustomFormat.Guide -{ - internal class GithubCustomFormatJsonRequester : IRadarrGuideService - { - private readonly ISerializer _flurlSerializer; - - public GithubCustomFormatJsonRequester() - { - // In addition to setting the naming strategy, this also serves as a mechanism to avoid inheriting the - // global Flurl serializer setting: MissingMemberHandling. We do not want missing members to error out - // since we're only deserializing a subset of the github response object. - _flurlSerializer = new NewtonsoftJsonSerializer(new JsonSerializerSettings - { - ContractResolver = new DefaultContractResolver - { - NamingStrategy = new SnakeCaseNamingStrategy() - } - }); - } - - public async Task> GetCustomFormatJson() - { - var response = await "https://api.github.com/repos/TRaSH-/Guides/contents/docs/json/radarr" - .WithHeader("User-Agent", "Trash Updater") - .ConfigureRequest(settings => settings.JsonSerializer = _flurlSerializer) - .GetJsonAsync>(); - - var tasks = response - .Where(o => o.Type == "file" && o.Name.EndsWith(".json")) - .Select(o => DownloadJsonContents(o.DownloadUrl)); - - return await Task.WhenAll(tasks); - } - - private async Task DownloadJsonContents(string jsonUrl) - { - return await jsonUrl.GetStringAsync(); - } - - [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] - private record RepoContentEntry - { - public string Name { get; init; } = default!; - public string Type { get; init; } = default!; - public string DownloadUrl { get; init; } = default!; - } - } -} diff --git a/src/TrashLib/Radarr/CustomFormat/Guide/LocalRepoCustomFormatJsonParser.cs b/src/TrashLib/Radarr/CustomFormat/Guide/LocalRepoCustomFormatJsonParser.cs new file mode 100644 index 00000000..f08498cb --- /dev/null +++ b/src/TrashLib/Radarr/CustomFormat/Guide/LocalRepoCustomFormatJsonParser.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.IO; +using System.IO.Abstractions; +using System.Linq; +using System.Threading.Tasks; +using LibGit2Sharp; +using TrashLib.Config; + +namespace TrashLib.Radarr.CustomFormat.Guide +{ + internal class LocalRepoCustomFormatJsonParser : IRadarrGuideService + { + private readonly IFileSystem _fileSystem; + private readonly string _repoPath; + + public LocalRepoCustomFormatJsonParser(IFileSystem fileSystem, IResourcePaths paths) + { + _fileSystem = fileSystem; + _repoPath = paths.RepoPath; + } + + public async Task> GetCustomFormatJson() + { + await Task.Run(() => + { + if (!Repository.IsValid(_repoPath)) + { + _fileSystem.Directory.Delete(_repoPath, true); + Repository.Clone("https://github.com/TRaSH-/Guides.git", _repoPath, new CloneOptions + { + RecurseSubmodules = false + }); + } + + using var repo = new Repository(_repoPath); + Commands.Checkout(repo, "master", new CheckoutOptions + { + CheckoutModifiers = CheckoutModifiers.Force + }); + + var origin = repo.Network.Remotes["origin"]; + Commands.Fetch(repo, origin.Name, origin.FetchRefSpecs.Select(s => s.Specification), null, ""); + + repo.Reset(ResetMode.Hard, repo.Branches["origin/master"].Tip); + }); + + var jsonDir = Path.Combine(_repoPath, "docs/json/radarr"); + var tasks = _fileSystem.Directory.GetFiles(jsonDir, "*.json") + .Select(async f => await _fileSystem.File.ReadAllTextAsync(f)); + + return await Task.WhenAll(tasks); + } + } +} diff --git a/src/TrashLib/Radarr/RadarrAutofacModule.cs b/src/TrashLib/Radarr/RadarrAutofacModule.cs index 026df56f..27a58a9d 100644 --- a/src/TrashLib/Radarr/RadarrAutofacModule.cs +++ b/src/TrashLib/Radarr/RadarrAutofacModule.cs @@ -37,13 +37,12 @@ namespace TrashLib.Radarr // Custom Format Support builder.RegisterType().As(); - builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); // Guide Processor - builder.RegisterType() - .As< - IGuideProcessor>(); // todo: register as singleton to avoid parsing guide multiple times when using 2 or more instances in config + // todo: register as singleton to avoid parsing guide multiple times when using 2 or more instances in config + builder.RegisterType().As(); builder.RegisterAggregateService(); builder.RegisterType().As(); builder.RegisterType().As(); diff --git a/src/TrashLib/TrashLib.csproj b/src/TrashLib/TrashLib.csproj index 8730575b..ccee4322 100644 --- a/src/TrashLib/TrashLib.csproj +++ b/src/TrashLib/TrashLib.csproj @@ -9,6 +9,7 @@ +