fix: Properly sync IncludeCustomFormatWhenRenaming

json-serializing-nullable-fields-issue
Robert Dailey 8 months ago
parent ef7aa64c5f
commit 9a26348d26

@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Fixed
- If the guide data for "Include Custom Format when Renaming" is set to "true", it now syncs that
correctly instead of always setting to "false" (#213).
## [5.4.0] - 2023-09-11
### Added

@ -8,10 +8,10 @@ namespace Recyclarr.TrashLib.Guide.CustomFormat;
public class CustomFormatLoader : ICustomFormatLoader
{
private readonly BulkJsonLoader _loader;
private readonly ServiceJsonLoader _loader;
private readonly ICustomFormatCategoryParser _categoryParser;
public CustomFormatLoader(BulkJsonLoader loader, ICustomFormatCategoryParser categoryParser)
public CustomFormatLoader(ServiceJsonLoader loader, ICustomFormatCategoryParser categoryParser)
{
_loader = loader;
_categoryParser = categoryParser;

@ -8,13 +8,15 @@ namespace Recyclarr.TrashLib.Json;
public record LoadedJsonObject<T>(IFileInfo File, T Obj);
public class BulkJsonLoader
public class BulkJsonLoader : IBulkJsonLoader
{
private readonly ILogger _log;
private readonly JsonSerializerSettings _serializerSettings;
public BulkJsonLoader(ILogger log)
public BulkJsonLoader(ILogger log, JsonSerializerSettings serializerSettings)
{
_log = log;
_serializerSettings = serializerSettings;
}
public ICollection<T> LoadAllFilesAtPaths<T>(
@ -31,9 +33,9 @@ public class BulkJsonLoader
return convertedObservable.ToEnumerable().ToList();
}
private static T ParseJson<T>(string guideData, string fileName)
private T ParseJson<T>(string guideData, string fileName)
{
var obj = JsonConvert.DeserializeObject<T>(guideData, GlobalJsonSerializerSettings.Guide);
var obj = JsonConvert.DeserializeObject<T>(guideData, _serializerSettings);
if (obj is null)
{
throw new JsonSerializationException($"Unable to parse JSON at file {fileName}");

@ -0,0 +1,21 @@
using System.IO.Abstractions;
using Newtonsoft.Json;
namespace Recyclarr.TrashLib.Json;
public class GuideJsonLoader : IBulkJsonLoader
{
private readonly IBulkJsonLoader _loader;
public GuideJsonLoader(Func<JsonSerializerSettings, IBulkJsonLoader> jsonLoaderFactory)
{
_loader = jsonLoaderFactory(GlobalJsonSerializerSettings.Guide);
}
public ICollection<T> LoadAllFilesAtPaths<T>(
IEnumerable<IDirectoryInfo> jsonPaths,
Func<IObservable<LoadedJsonObject<T>>, IObservable<T>>? extra = null)
{
return _loader.LoadAllFilesAtPaths(jsonPaths, extra);
}
}

@ -0,0 +1,10 @@
using System.IO.Abstractions;
namespace Recyclarr.TrashLib.Json;
public interface IBulkJsonLoader
{
ICollection<T> LoadAllFilesAtPaths<T>(
IEnumerable<IDirectoryInfo> jsonPaths,
Func<IObservable<LoadedJsonObject<T>>, IObservable<T>>? extra = null);
}

@ -0,0 +1,22 @@
using Autofac;
using Newtonsoft.Json;
namespace Recyclarr.TrashLib.Json;
public class JsonAutofacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
base.Load(builder);
builder.Register<Func<JsonSerializerSettings, IBulkJsonLoader>>(c =>
{
return settings => new BulkJsonLoader(c.Resolve<ILogger>(), settings);
});
// Decorators for BulkJsonLoader. We do not use RegisterDecorator() here for these reasons:
// - We consume the BulkJsonLoader as a delegate factory, not by instance
// - We do not want all implementations of BulkJsonLoader to be decorated, only a specific implementation.
builder.RegisterType<GuideJsonLoader>();
builder.RegisterType<ServiceJsonLoader>();
}
}

@ -0,0 +1,21 @@
using System.IO.Abstractions;
using Newtonsoft.Json;
namespace Recyclarr.TrashLib.Json;
public class ServiceJsonLoader : IBulkJsonLoader
{
private readonly IBulkJsonLoader _loader;
public ServiceJsonLoader(Func<JsonSerializerSettings, IBulkJsonLoader> jsonLoaderFactory)
{
_loader = jsonLoaderFactory(GlobalJsonSerializerSettings.Services);
}
public ICollection<T> LoadAllFilesAtPaths<T>(
IEnumerable<IDirectoryInfo> jsonPaths,
Func<IObservable<LoadedJsonObject<T>>, IObservable<T>>? extra = null)
{
return _loader.LoadAllFilesAtPaths(jsonPaths, extra);
}
}

@ -25,14 +25,15 @@ public class TrashLibAutofacModule : Module
// Needed for Autofac.Extras.Ordering
builder.RegisterSource<OrderedRegistrationSource>();
builder.RegisterType<SettingsProvider>().As<ISettingsProvider>().SingleInstance();
builder.RegisterModule<VersionControlAutofacModule>();
builder.RegisterModule<RepoAutofacModule>();
builder.RegisterModule<CompatibilityAutofacModule>();
builder.RegisterModule<ApiServicesAutofacModule>();
builder.RegisterModule<JsonAutofacModule>();
builder.RegisterType<SettingsProvider>().As<ISettingsProvider>().SingleInstance();
builder.RegisterType<ServiceRequestBuilder>().As<IServiceRequestBuilder>();
builder.RegisterType<FlurlClientFactory>().As<IFlurlClientFactory>().SingleInstance();
builder.RegisterType<BulkJsonLoader>();
}
private static void CommonRegistrations(ContainerBuilder builder)

@ -0,0 +1,68 @@
using System.Diagnostics.CodeAnalysis;
using System.IO.Abstractions;
using Recyclarr.TrashLib.Json;
using Recyclarr.TrashLib.Models;
using Recyclarr.TrashLib.TestLibrary;
namespace Recyclarr.TrashLib.Tests.Json;
[TestFixture]
[Parallelizable(ParallelScope.All)]
public class BulkJsonLoaderIntegrationTest : TrashLibIntegrationFixture
{
[SuppressMessage("ReSharper", "NotAccessedPositionalProperty.Local")]
private sealed record TestJsonObject(string TrashId, int TrashScore, string Name);
[Test]
public void Guide_deserialize_works()
{
var sut = Resolve<GuideJsonLoader>();
const string jsonData =
"""
{
"trash_id": "90cedc1fea7ea5d11298bebd3d1d3223",
"trash_score": "-10000",
"name": "TheName"
}
""";
Fs.AddFile(Fs.CurrentDirectory().File("file.json"), new MockFileData(jsonData));
var result = sut.LoadAllFilesAtPaths<TestJsonObject>(new[] {Fs.CurrentDirectory()});
result.Should().BeEquivalentTo(new[]
{
new TestJsonObject("90cedc1fea7ea5d11298bebd3d1d3223", -10000, "TheName")
});
}
[Test]
public void Service_deserialize_works()
{
var sut = Resolve<ServiceJsonLoader>();
const string jsonData =
"""
{
"id": 22,
"name": "FUNi",
"includeCustomFormatWhenRenaming": true
}
""";
Fs.AddFile(Fs.CurrentDirectory().File("file.json"), new MockFileData(jsonData));
var result = sut.LoadAllFilesAtPaths<CustomFormatData>(new[] {Fs.CurrentDirectory()});
result.Should().BeEquivalentTo(new[]
{
new CustomFormatData
{
Id = 22,
Name = "FUNi",
IncludeCustomFormatWhenRenaming = true
}
});
}
}

@ -1,37 +0,0 @@
using System.Diagnostics.CodeAnalysis;
using System.IO.Abstractions;
using Recyclarr.TrashLib.Json;
namespace Recyclarr.TrashLib.Tests.Json;
[TestFixture]
[Parallelizable(ParallelScope.All)]
public class BulkJsonLoaderTest
{
[SuppressMessage("ReSharper", "NotAccessedPositionalProperty.Local")]
private sealed record TestJsonObject(string TrashId, int TrashScore, string Name);
[Test, AutoMockData]
public void Deserialize_works(
[Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs,
BulkJsonLoader sut)
{
var jsonData =
"""
{
"trash_id": "90cedc1fea7ea5d11298bebd3d1d3223",
"trash_score": "-10000",
"name": "TheName"
}
""";
fs.AddFile(fs.CurrentDirectory().File("file.json"), new MockFileData(jsonData));
var result = sut.LoadAllFilesAtPaths<TestJsonObject>(new[] {fs.CurrentDirectory()});
result.Should().BeEquivalentTo(new[]
{
new TestJsonObject("90cedc1fea7ea5d11298bebd3d1d3223", -10000, "TheName")
});
}
}
Loading…
Cancel
Save