diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bcc74b9..a6d5c075 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,13 +11,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### FIXED - libgit2sharp PDB is no longer required with trash.exe on Windows ([#15]) +- Unexpected character error due to breaking change in Sonarr API ([#16]) [#15]: https://github.com/rcdailey/trash-updater/issues/15 +[#16]: https://github.com/rcdailey/trash-updater/issues/16 ## [1.6.3] - 2021-07-31 -### FIXED - - Fix "assembly not found" error on startup related to LibGit2Sharp (Windows only). Note that this introduces an additional file in the released ZIP files named `git2-6777db8.pdb`. This file must be next to `trash.exe`. In the future, I plan to have this extra file removed so it's just a diff --git a/src/Common/Common.csproj b/src/Common/Common.csproj index 62882e30..5f38880a 100644 --- a/src/Common/Common.csproj +++ b/src/Common/Common.csproj @@ -2,5 +2,7 @@ + + diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index cb37954e..e4a33da9 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -1,5 +1,6 @@ + @@ -21,6 +22,8 @@ + + @@ -28,6 +31,7 @@ + diff --git a/src/Trash/Command/Helpers/ServiceCommand.cs b/src/Trash/Command/Helpers/ServiceCommand.cs index 8efb0138..7566d18e 100644 --- a/src/Trash/Command/Helpers/ServiceCommand.cs +++ b/src/Trash/Command/Helpers/ServiceCommand.cs @@ -18,11 +18,13 @@ namespace Trash.Command.Helpers { public abstract class ServiceCommand : ICommand, IServiceCommand { - private readonly AutofacContractResolver _contractResolver; private readonly LoggingLevelSwitch _loggingLevelSwitch; private readonly ILogJanitor _logJanitor; - protected ServiceCommand(ILogger logger, LoggingLevelSwitch loggingLevelSwitch, ILogJanitor logJanitor) + protected ServiceCommand( + ILogger logger, + LoggingLevelSwitch loggingLevelSwitch, + ILogJanitor logJanitor) { _loggingLevelSwitch = loggingLevelSwitch; _logJanitor = logJanitor; @@ -90,7 +92,7 @@ namespace Trash.Command.Helpers Debug ? LogEventLevel.Debug : LogEventLevel.Information; } - private static void SetupHttp() + private void SetupHttp() { FlurlHttp.Configure(settings => { diff --git a/src/Trash/CompositionRoot.cs b/src/Trash/CompositionRoot.cs index 6b0396c5..01fa9b80 100644 --- a/src/Trash/CompositionRoot.cs +++ b/src/Trash/CompositionRoot.cs @@ -14,6 +14,7 @@ using TrashLib.Config; using TrashLib.Radarr; using TrashLib.Radarr.Config; using TrashLib.Sonarr; +using TrashLib.Startup; using YamlDotNet.Serialization; namespace Trash @@ -93,6 +94,8 @@ namespace Trash builder.RegisterModule(); builder.RegisterModule(); + builder.Register(_ => AutoMapperConfig.Setup()).SingleInstance(); + // builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource()); return builder.Build(); } diff --git a/src/TrashLib.Tests/Sonarr/Api/SonarrReleaseProfileCompatibilityHandlerTest.cs b/src/TrashLib.Tests/Sonarr/Api/SonarrReleaseProfileCompatibilityHandlerTest.cs new file mode 100644 index 00000000..c76a3d2d --- /dev/null +++ b/src/TrashLib.Tests/Sonarr/Api/SonarrReleaseProfileCompatibilityHandlerTest.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using AutoMapper; +using FluentAssertions; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using NSubstitute; +using NUnit.Framework; +using TrashLib.Sonarr; +using TrashLib.Sonarr.Api; +using TrashLib.Sonarr.Api.Objects; +using TrashLib.Startup; + +namespace TrashLib.Tests.Sonarr.Api +{ + [TestFixture] + [Parallelizable(ParallelScope.All)] + public class SonarrReleaseProfileCompatibilityHandlerTest + { + private class TestContext : IDisposable + { + private readonly JsonSerializerSettings _jsonSettings; + + public TestContext() + { + _jsonSettings = new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }; + + Mapper = AutoMapperConfig.Setup(); + } + + public IMapper Mapper { get; } + + public void Dispose() + { + } + + public string SerializeJson(T obj) + { + return JsonConvert.SerializeObject(obj, _jsonSettings); + } + } + + [Test] + public void Receive_v1_to_v2() + { + using var ctx = new TestContext(); + + var compat = Substitute.For(); + var dataV1 = new SonarrReleaseProfileV1 {Ignored = "one,two,three"}; + var sut = new SonarrReleaseProfileCompatibilityHandler(compat, ctx.Mapper); + + var result = sut.CompatibleReleaseProfileForReceiving(JObject.Parse(ctx.SerializeJson(dataV1))); + + _ = compat.DidNotReceive().ArraysNeededForReleaseProfileRequiredAndIgnored; + result.Should().BeEquivalentTo(new SonarrReleaseProfile + { + Ignored = new List {"one", "two", "three"} + }); + } + + [Test] + public void Receive_v2_to_v2() + { + using var ctx = new TestContext(); + + var compat = Substitute.For(); + var dataV2 = new SonarrReleaseProfile {Ignored = new List {"one", "two", "three"}}; + var sut = new SonarrReleaseProfileCompatibilityHandler(compat, ctx.Mapper); + + var result = sut.CompatibleReleaseProfileForReceiving(JObject.Parse(ctx.SerializeJson(dataV2))); + + _ = compat.DidNotReceive().ArraysNeededForReleaseProfileRequiredAndIgnored; + result.Should().BeEquivalentTo(dataV2); + } + + [Test] + public void Send_v2_to_v1() + { + using var ctx = new TestContext(); + + var compat = Substitute.For(); + compat.ArraysNeededForReleaseProfileRequiredAndIgnored.Returns(false); + + var data = new SonarrReleaseProfile {Ignored = new List {"one", "two", "three"}}; + var sut = new SonarrReleaseProfileCompatibilityHandler(compat, ctx.Mapper); + + var result = sut.CompatibleReleaseProfileForSending(data); + + result.Should().BeEquivalentTo(new SonarrReleaseProfileV1 {Ignored = "one,two,three"}); + } + + [Test] + public void Send_v2_to_v2() + { + using var ctx = new TestContext(); + + var compat = Substitute.For(); + compat.ArraysNeededForReleaseProfileRequiredAndIgnored.Returns(true); + + var data = new SonarrReleaseProfile {Ignored = new List {"one", "two", "three"}}; + var sut = new SonarrReleaseProfileCompatibilityHandler(compat, ctx.Mapper); + + var result = sut.CompatibleReleaseProfileForSending(data); + + result.Should().BeEquivalentTo(data); + } + } +} diff --git a/src/TrashLib.Tests/Sonarr/ReleaseProfileUpdaterTest.cs b/src/TrashLib.Tests/Sonarr/ReleaseProfileUpdaterTest.cs index db4e7384..54255f65 100644 --- a/src/TrashLib.Tests/Sonarr/ReleaseProfileUpdaterTest.cs +++ b/src/TrashLib.Tests/Sonarr/ReleaseProfileUpdaterTest.cs @@ -1,6 +1,7 @@ using NSubstitute; using NUnit.Framework; using Serilog; +using TrashLib.Sonarr; using TrashLib.Sonarr.Api; using TrashLib.Sonarr.Config; using TrashLib.Sonarr.ReleaseProfile; @@ -16,6 +17,7 @@ namespace TrashLib.Tests.Sonarr public IReleaseProfileGuideParser Parser { get; } = Substitute.For(); public ISonarrApi Api { get; } = Substitute.For(); public ILogger Logger { get; } = Substitute.For(); + public ISonarrCompatibility Compatibility { get; } = Substitute.For(); } [Test] @@ -23,7 +25,7 @@ namespace TrashLib.Tests.Sonarr { var context = new Context(); - var logic = new ReleaseProfileUpdater(context.Logger, context.Parser, context.Api); + var logic = new ReleaseProfileUpdater(context.Logger, context.Parser, context.Api, context.Compatibility); logic.Process(false, new SonarrConfiguration()); context.Parser.DidNotReceive().GetMarkdownData(Arg.Any()); @@ -40,7 +42,7 @@ namespace TrashLib.Tests.Sonarr ReleaseProfiles = new[] {new ReleaseProfileConfig {Type = ReleaseProfileType.Anime}} }; - var logic = new ReleaseProfileUpdater(context.Logger, context.Parser, context.Api); + var logic = new ReleaseProfileUpdater(context.Logger, context.Parser, context.Api, context.Compatibility); logic.Process(false, config); context.Parser.Received().ParseMarkdown(config.ReleaseProfiles[0], "theMarkdown"); diff --git a/src/TrashLib/Sonarr/Api/ISonarrApi.cs b/src/TrashLib/Sonarr/Api/ISonarrApi.cs index 23f06bfe..0eb70df7 100644 --- a/src/TrashLib/Sonarr/Api/ISonarrApi.cs +++ b/src/TrashLib/Sonarr/Api/ISonarrApi.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; using TrashLib.Sonarr.Api.Objects; @@ -7,7 +6,6 @@ namespace TrashLib.Sonarr.Api { public interface ISonarrApi { - Task GetVersion(); Task> GetTags(); Task CreateTag(string tag); Task> GetReleaseProfiles(); diff --git a/src/TrashLib/Sonarr/Api/ISonarrReleaseProfileCompatibilityHandler.cs b/src/TrashLib/Sonarr/Api/ISonarrReleaseProfileCompatibilityHandler.cs new file mode 100644 index 00000000..18d8bf5a --- /dev/null +++ b/src/TrashLib/Sonarr/Api/ISonarrReleaseProfileCompatibilityHandler.cs @@ -0,0 +1,11 @@ +using Newtonsoft.Json.Linq; +using TrashLib.Sonarr.Api.Objects; + +namespace TrashLib.Sonarr.Api +{ + public interface ISonarrReleaseProfileCompatibilityHandler + { + object CompatibleReleaseProfileForSending(SonarrReleaseProfile profile); + SonarrReleaseProfile CompatibleReleaseProfileForReceiving(JObject profile); + } +} diff --git a/src/TrashLib/Sonarr/Api/Mappings/SonarrApiObjectMappingProfile.cs b/src/TrashLib/Sonarr/Api/Mappings/SonarrApiObjectMappingProfile.cs new file mode 100644 index 00000000..f58cfc11 --- /dev/null +++ b/src/TrashLib/Sonarr/Api/Mappings/SonarrApiObjectMappingProfile.cs @@ -0,0 +1,27 @@ +using System; +using System.Linq; +using AutoMapper; +using JetBrains.Annotations; +using TrashLib.Sonarr.Api.Objects; + +namespace TrashLib.Sonarr.Api.Mappings +{ + [UsedImplicitly] + public class SonarrApiObjectMappingProfile : Profile + { + public SonarrApiObjectMappingProfile() + { + CreateMap() + .ForMember(d => d.Ignored, x => x.MapFrom( + s => s.Ignored.Split(',', StringSplitOptions.RemoveEmptyEntries).ToList())) + .ForMember(d => d.Required, x => x.MapFrom( + s => s.Required.Split(',', StringSplitOptions.RemoveEmptyEntries).ToList())); + + CreateMap() + .ForMember(d => d.Ignored, x => x.MapFrom( + s => string.Join(',', s.Ignored))) + .ForMember(d => d.Required, x => x.MapFrom( + s => string.Join(',', s.Required))); + } + } +} diff --git a/src/TrashLib/Sonarr/Api/Objects/SonarrReleaseProfile.cs b/src/TrashLib/Sonarr/Api/Objects/SonarrReleaseProfile.cs index b563face..4cadfc84 100644 --- a/src/TrashLib/Sonarr/Api/Objects/SonarrReleaseProfile.cs +++ b/src/TrashLib/Sonarr/Api/Objects/SonarrReleaseProfile.cs @@ -20,8 +20,12 @@ namespace TrashLib.Sonarr.Api.Objects public int Score { get; set; } } + // Retained for supporting versions of Sonarr prior to v3.0.6.1355 + // Offending change is here: + // https://github.com/Sonarr/Sonarr/blob/deed85d2f9147e6180014507ef4f5af3695b0c61/src/NzbDrone.Core/Datastore/Migration/162_release_profile_to_array.cs + // The Ignored and Required JSON properties were converted from string -> array in a backward-breaking way. [UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.Members)] - public class SonarrReleaseProfile + public class SonarrReleaseProfileV1 { public int Id { get; set; } public bool Enabled { get; set; } @@ -33,4 +37,18 @@ namespace TrashLib.Sonarr.Api.Objects public int IndexerId { get; set; } public IReadOnlyCollection Tags { get; set; } = new List(); } + + [UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.Members)] + public class SonarrReleaseProfile + { + public int Id { get; set; } + public bool Enabled { get; set; } + public string Name { get; set; } = ""; + public IReadOnlyCollection Required { get; set; } = new List(); + public IReadOnlyCollection Ignored { get; set; } = new List(); + public IReadOnlyCollection Preferred { get; set; } = new List(); + public bool IncludePreferredWhenRenaming { get; set; } + public int IndexerId { get; set; } + public IReadOnlyCollection Tags { get; set; } = new List(); + } } diff --git a/src/TrashLib/Sonarr/Api/SonarrApi.cs b/src/TrashLib/Sonarr/Api/SonarrApi.cs index d79d7c2e..8048dcdf 100644 --- a/src/TrashLib/Sonarr/Api/SonarrApi.cs +++ b/src/TrashLib/Sonarr/Api/SonarrApi.cs @@ -1,8 +1,9 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Flurl; using Flurl.Http; +using Newtonsoft.Json.Linq; using TrashLib.Config; using TrashLib.Sonarr.Api.Objects; @@ -10,19 +11,13 @@ namespace TrashLib.Sonarr.Api { public class SonarrApi : ISonarrApi { + private readonly ISonarrReleaseProfileCompatibilityHandler _profileHandler; private readonly IServerInfo _serverInfo; - public SonarrApi(IServerInfo serverInfo) + public SonarrApi(IServerInfo serverInfo, ISonarrReleaseProfileCompatibilityHandler profileHandler) { _serverInfo = serverInfo; - } - - public async Task GetVersion() - { - dynamic data = await BaseUrl() - .AppendPathSegment("system/status") - .GetJsonAsync(); - return new Version(data.version); + _profileHandler = profileHandler; } public async Task> GetTags() @@ -42,24 +37,30 @@ namespace TrashLib.Sonarr.Api public async Task> GetReleaseProfiles() { - return await BaseUrl() + var response = await BaseUrl() .AppendPathSegment("releaseprofile") - .GetJsonAsync>(); + .GetJsonAsync>(); + + return response + .Select(_profileHandler.CompatibleReleaseProfileForReceiving) + .ToList(); } public async Task UpdateReleaseProfile(SonarrReleaseProfile profileToUpdate) { await BaseUrl() .AppendPathSegment($"releaseprofile/{profileToUpdate.Id}") - .PutJsonAsync(profileToUpdate); + .PutJsonAsync(_profileHandler.CompatibleReleaseProfileForSending(profileToUpdate)); } public async Task CreateReleaseProfile(SonarrReleaseProfile newProfile) { - return await BaseUrl() + var response = await BaseUrl() .AppendPathSegment("releaseprofile") - .PostJsonAsync(newProfile) - .ReceiveJson(); + .PostJsonAsync(_profileHandler.CompatibleReleaseProfileForSending(newProfile)) + .ReceiveJson(); + + return _profileHandler.CompatibleReleaseProfileForReceiving(response); } public async Task> GetQualityDefinition() diff --git a/src/TrashLib/Sonarr/Api/SonarrReleaseProfileCompatibilityHandler.cs b/src/TrashLib/Sonarr/Api/SonarrReleaseProfileCompatibilityHandler.cs new file mode 100644 index 00000000..bcf3af31 --- /dev/null +++ b/src/TrashLib/Sonarr/Api/SonarrReleaseProfileCompatibilityHandler.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using System.IO; +using AutoMapper; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Schema; +using Newtonsoft.Json.Schema.Generation; +using Newtonsoft.Json.Serialization; +using Serilog; +using TrashLib.Sonarr.Api.Objects; + +namespace TrashLib.Sonarr.Api +{ + public class SonarrReleaseProfileCompatibilityHandler : ISonarrReleaseProfileCompatibilityHandler + { + private readonly ISonarrCompatibility _compatibility; + private readonly JSchemaGenerator _generator; + private readonly IMapper _mapper; + + public SonarrReleaseProfileCompatibilityHandler( + ISonarrCompatibility compatibility, + IMapper mapper) + { + _compatibility = compatibility; + _mapper = mapper; + _generator = new JSchemaGenerator + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + DefaultRequired = Required.Default + }; + } + + public object CompatibleReleaseProfileForSending(SonarrReleaseProfile profile) + { + return _compatibility.ArraysNeededForReleaseProfileRequiredAndIgnored + ? profile + : _mapper.Map(profile); + } + + public SonarrReleaseProfile CompatibleReleaseProfileForReceiving(JObject profile) + { + JSchema? schema; + IList? errorMessages; + + schema = _generator.Generate(typeof(SonarrReleaseProfile)); + if (profile.IsValid(schema, out errorMessages)) + { + return profile.ToObject() + ?? throw new InvalidDataException("SonarrReleaseProfile V2 parsing failed"); + } + + Log.Debug("SonarrReleaseProfile is not a match for V2, proceeding to V1: {Reasons}", errorMessages); + + schema = _generator.Generate(typeof(SonarrReleaseProfileV1)); + if (profile.IsValid(schema, out errorMessages)) + { + // This will throw if there's an issue during mapping. + return _mapper.Map(profile.ToObject()); + } + + throw new InvalidDataException( + $"SonarrReleaseProfile expected, but no supported schema detected: {errorMessages}"); + } + } +} diff --git a/src/TrashLib/Sonarr/ISonarrCompatibility.cs b/src/TrashLib/Sonarr/ISonarrCompatibility.cs new file mode 100644 index 00000000..7dd9b958 --- /dev/null +++ b/src/TrashLib/Sonarr/ISonarrCompatibility.cs @@ -0,0 +1,10 @@ +namespace TrashLib.Sonarr +{ + public interface ISonarrCompatibility + { + bool SupportsNamedReleaseProfiles { get; } + bool ArraysNeededForReleaseProfileRequiredAndIgnored { get; } + string InformationalVersion { get; } + string MinimumVersion { get; } + } +} diff --git a/src/TrashLib/Sonarr/ReleaseProfile/ReleaseProfileUpdater.cs b/src/TrashLib/Sonarr/ReleaseProfile/ReleaseProfileUpdater.cs index e2bece88..e6bd9bf5 100644 --- a/src/TrashLib/Sonarr/ReleaseProfile/ReleaseProfileUpdater.cs +++ b/src/TrashLib/Sonarr/ReleaseProfile/ReleaseProfileUpdater.cs @@ -13,13 +13,19 @@ namespace TrashLib.Sonarr.ReleaseProfile internal class ReleaseProfileUpdater : IReleaseProfileUpdater { private readonly ISonarrApi _api; + private readonly ISonarrCompatibility _compatibility; private readonly IReleaseProfileGuideParser _parser; - public ReleaseProfileUpdater(ILogger logger, IReleaseProfileGuideParser parser, ISonarrApi api) + public ReleaseProfileUpdater( + ILogger logger, + IReleaseProfileGuideParser parser, + ISonarrApi api, + ISonarrCompatibility compatibility) { Log = logger; _parser = parser; _api = api; + _compatibility = compatibility; } private ILogger Log { get; } @@ -47,18 +53,13 @@ namespace TrashLib.Sonarr.ReleaseProfile } } - private async Task DoVersionEnforcement() + private void DoVersionEnforcement() { - // Since this script requires a specific version of v3 Sonarr that implements name support for - // release profiles, we perform that version check here and bail out if it does not meet a minimum - // required version. - var minimumVersion = new Version("3.0.4.1098"); - var version = await _api.GetVersion(); - if (version < minimumVersion) + if (!_compatibility.SupportsNamedReleaseProfiles) { throw new VersionException( - $"Your Sonarr version {version} does not meet the minimum " + - $"required version of {minimumVersion} to use this program"); + $"Your Sonarr version {_compatibility.InformationalVersion} does not meet the minimum " + + $"required version of {_compatibility.MinimumVersion} to use this program"); } } @@ -92,8 +93,8 @@ namespace TrashLib.Sonarr.ReleaseProfile .SelectMany(kvp => kvp.Value.Select(term => new SonarrPreferredTerm(kvp.Key, term))) .ToList(); - profileToUpdate.Ignored = string.Join(',', profile.Ignored); - profileToUpdate.Required = string.Join(',', profile.Required); + profileToUpdate.Ignored = profile.Ignored.ToList(); //string.Join(',', profile.Ignored); + profileToUpdate.Required = profile.Required.ToList(); //string.Join(',', profile.Required); // Null means the guide didn't specify a value for this, so we leave the existing setting intact. if (profile.IncludePreferredWhenRenaming != null) @@ -127,7 +128,7 @@ namespace TrashLib.Sonarr.ReleaseProfile private async Task ProcessReleaseProfiles(IDictionary profiles, ReleaseProfileConfig config) { - await DoVersionEnforcement(); + DoVersionEnforcement(); List tagIds = new(); diff --git a/src/TrashLib/Sonarr/SonarrAutofacModule.cs b/src/TrashLib/Sonarr/SonarrAutofacModule.cs index 6126058f..cabc0725 100644 --- a/src/TrashLib/Sonarr/SonarrAutofacModule.cs +++ b/src/TrashLib/Sonarr/SonarrAutofacModule.cs @@ -13,9 +13,15 @@ namespace TrashLib.Sonarr builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterType() + .As() + .SingleInstance(); + // Release Profile Support builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterType() + .As(); // Quality Definition Support builder.RegisterType().As(); diff --git a/src/TrashLib/Sonarr/SonarrCompatibility.cs b/src/TrashLib/Sonarr/SonarrCompatibility.cs new file mode 100644 index 00000000..9c79fa9b --- /dev/null +++ b/src/TrashLib/Sonarr/SonarrCompatibility.cs @@ -0,0 +1,38 @@ +using System; +using System.Reactive.Linq; +using System.Reactive.Threading.Tasks; +using Flurl; +using Flurl.Http; +using TrashLib.Config; + +namespace TrashLib.Sonarr +{ + public class SonarrCompatibility : ISonarrCompatibility + { + private Version _version = new(); + + public SonarrCompatibility(IServerInfo serverInfo) + { + var task = serverInfo.BuildUrl() + .AppendPathSegment("system/status") + .GetJsonAsync(); + + task.ToObservable() + .Select(x => new Version(x.version)) + .Subscribe(x => _version = x); + } + + public bool SupportsNamedReleaseProfiles => + _version >= new Version(MinimumVersion); + + // Background: Issue #16 filed which points to a backward-breaking API + // change made in Sonarr at commit [deed85d2f]. + // + // [deed85d2f]: https://github.com/Sonarr/Sonarr/commit/deed85d2f9147e6180014507ef4f5af3695b0c61 + public bool ArraysNeededForReleaseProfileRequiredAndIgnored => + _version >= new Version("3.0.6.1355"); + + public string InformationalVersion => _version.ToString(); + public string MinimumVersion => "3.0.4.1098"; + } +} diff --git a/src/TrashLib/Startup/AutoMapperConfig.cs b/src/TrashLib/Startup/AutoMapperConfig.cs new file mode 100644 index 00000000..49c2dcd7 --- /dev/null +++ b/src/TrashLib/Startup/AutoMapperConfig.cs @@ -0,0 +1,22 @@ +using AutoMapper; + +namespace TrashLib.Startup +{ + public static class AutoMapperConfig + { + public static IMapper Setup() + { + // todo: consider using AutoMapper.Contrib.Autofac.DependencyInjection + var mapperConfig = new MapperConfiguration(cfg => + { + cfg.AddMaps(typeof(AutoMapperConfig)); + }); + +#if DEBUG + mapperConfig.AssertConfigurationIsValid(); +#endif + + return mapperConfig.CreateMapper(); + } + } +} diff --git a/src/TrashLib/TrashLib.csproj b/src/TrashLib/TrashLib.csproj index fdeecf29..3c249bd3 100644 --- a/src/TrashLib/TrashLib.csproj +++ b/src/TrashLib/TrashLib.csproj @@ -1,14 +1,17 @@ + + +