diff --git a/src/Lidarr.Http/ClientSchema/SchemaBuilder.cs b/src/Lidarr.Http/ClientSchema/SchemaBuilder.cs index 9aacabbda..1ffa7ff8c 100644 --- a/src/Lidarr.Http/ClientSchema/SchemaBuilder.cs +++ b/src/Lidarr.Http/ClientSchema/SchemaBuilder.cs @@ -3,11 +3,13 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text.Json; +using DryIoc; using NzbDrone.Common.EnsureThat; using NzbDrone.Common.Extensions; using NzbDrone.Common.Reflection; using NzbDrone.Common.Serializer; using NzbDrone.Core.Annotations; +using NzbDrone.Core.Localization; namespace Lidarr.Http.ClientSchema { @@ -15,6 +17,12 @@ namespace Lidarr.Http.ClientSchema { private const string PRIVATE_VALUE = "********"; private static Dictionary _mappings = new Dictionary(); + private static ILocalizationService _localizationService; + + public static void Initialize(IContainer container) + { + _localizationService = container.Resolve(); + } public static List ToSchema(object model) { @@ -106,13 +114,27 @@ namespace Lidarr.Http.ClientSchema if (propertyInfo.PropertyType.IsSimpleType()) { var fieldAttribute = property.Item2; + + var label = fieldAttribute.Label.IsNotNullOrWhiteSpace() + ? _localizationService.GetLocalizedString(fieldAttribute.Label, + GetTokens(type, fieldAttribute.Label, TokenField.Label)) + : fieldAttribute.Label; + var helpText = fieldAttribute.HelpText.IsNotNullOrWhiteSpace() + ? _localizationService.GetLocalizedString(fieldAttribute.HelpText, + GetTokens(type, fieldAttribute.Label, TokenField.HelpText)) + : fieldAttribute.HelpText; + var helpTextWarning = fieldAttribute.HelpTextWarning.IsNotNullOrWhiteSpace() + ? _localizationService.GetLocalizedString(fieldAttribute.HelpTextWarning, + GetTokens(type, fieldAttribute.Label, TokenField.HelpTextWarning)) + : fieldAttribute.HelpTextWarning; + var field = new Field { Name = prefix + GetCamelCaseName(propertyInfo.Name), - Label = fieldAttribute.Label, + Label = label, Unit = fieldAttribute.Unit, - HelpText = fieldAttribute.HelpText, - HelpTextWarning = fieldAttribute.HelpTextWarning, + HelpText = helpText, + HelpTextWarning = helpTextWarning, HelpLink = fieldAttribute.HelpLink, Order = fieldAttribute.Order, Advanced = fieldAttribute.Advanced, @@ -172,6 +194,24 @@ namespace Lidarr.Http.ClientSchema .ToArray(); } + private static Dictionary GetTokens(Type type, string label, TokenField field) + { + var tokens = new Dictionary(); + + foreach (var propertyInfo in type.GetProperties()) + { + foreach (var attribute in propertyInfo.GetCustomAttributes(true)) + { + if (attribute is FieldTokenAttribute fieldTokenAttribute && fieldTokenAttribute.Field == field && fieldTokenAttribute.Label == label) + { + tokens.Add(fieldTokenAttribute.Token, fieldTokenAttribute.Value); + } + } + } + + return tokens; + } + private static List GetSelectOptions(Type selectOptions) { if (selectOptions.IsEnum) diff --git a/src/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs b/src/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs index de47d96ae..cb12aff19 100644 --- a/src/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs +++ b/src/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs @@ -1,7 +1,10 @@ +using System.Collections.Generic; using FluentAssertions; using Lidarr.Http.ClientSchema; +using Moq; using NUnit.Framework; using NzbDrone.Core.Annotations; +using NzbDrone.Core.Localization; using NzbDrone.Test.Common; namespace NzbDrone.Api.Test.ClientSchemaTests @@ -9,6 +12,16 @@ namespace NzbDrone.Api.Test.ClientSchemaTests [TestFixture] public class SchemaBuilderFixture : TestBase { + [SetUp] + public void Setup() + { + Mocker.GetMock() + .Setup(s => s.GetLocalizedString(It.IsAny(), It.IsAny>())) + .Returns>((s, d) => s); + + SchemaBuilder.Initialize(Mocker.Container); + } + [Test] public void should_return_field_for_every_property() { diff --git a/src/NzbDrone.Core/Annotations/FieldDefinitionAttribute.cs b/src/NzbDrone.Core/Annotations/FieldDefinitionAttribute.cs index 8d098d7f6..4a56117c3 100644 --- a/src/NzbDrone.Core/Annotations/FieldDefinitionAttribute.cs +++ b/src/NzbDrone.Core/Annotations/FieldDefinitionAttribute.cs @@ -41,6 +41,23 @@ namespace NzbDrone.Core.Annotations public string Hint { get; set; } } + [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] + public class FieldTokenAttribute : Attribute + { + public FieldTokenAttribute(TokenField field, string label = "", string token = "", object value = null) + { + Label = label; + Field = field; + Token = token; + Value = value?.ToString(); + } + + public string Label { get; set; } + public TokenField Field { get; set; } + public string Token { get; set; } + public string Value { get; set; } + } + public class FieldSelectOption { public int Value { get; set; } @@ -86,4 +103,11 @@ namespace NzbDrone.Core.Annotations ApiKey, UserName } + + public enum TokenField + { + Label, + HelpText, + HelpTextWarning + } } diff --git a/src/NzbDrone.Host/Bootstrap.cs b/src/NzbDrone.Host/Bootstrap.cs index 38828dc72..30dd1e627 100644 --- a/src/NzbDrone.Host/Bootstrap.cs +++ b/src/NzbDrone.Host/Bootstrap.cs @@ -8,6 +8,7 @@ using System.Security.Cryptography.X509Certificates; using System.Text; using DryIoc; using DryIoc.Microsoft.DependencyInjection; +using Lidarr.Http.ClientSchema; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -150,6 +151,8 @@ namespace NzbDrone.Host .AddNzbDroneLogger() .AddDatabase() .AddStartupContext(context); + + SchemaBuilder.Initialize(c); }) .ConfigureServices(services => { diff --git a/src/NzbDrone.Integration.Test/IntegrationTest.cs b/src/NzbDrone.Integration.Test/IntegrationTest.cs index 778082a27..811f269b0 100644 --- a/src/NzbDrone.Integration.Test/IntegrationTest.cs +++ b/src/NzbDrone.Integration.Test/IntegrationTest.cs @@ -1,5 +1,6 @@ +using System; +using System.Linq; using System.Threading; -using Lidarr.Http.ClientSchema; using NLog; using NUnit.Framework; using NzbDrone.Common.Extensions; @@ -50,17 +51,20 @@ namespace NzbDrone.Integration.Test // Make sure tasks have been initialized so the config put below doesn't cause errors WaitForCompletion(() => Tasks.All().SelectList(x => x.TaskName).Contains("RssSync"), 30000); - Indexers.Post(new Lidarr.Api.V1.Indexers.IndexerResource + var indexer = Indexers.Schema().FirstOrDefault(i => i.Implementation == nameof(Newznab)); + + if (indexer == null) { - EnableRss = false, - EnableInteractiveSearch = false, - EnableAutomaticSearch = false, - ConfigContract = nameof(NewznabSettings), - Implementation = nameof(Newznab), - Name = "NewznabTest", - Protocol = Core.Indexers.DownloadProtocol.Usenet, - Fields = SchemaBuilder.ToSchema(new NewznabSettings()) - }); + throw new NullReferenceException("Expected valid indexer schema, found null"); + } + + indexer.EnableRss = false; + indexer.EnableInteractiveSearch = false; + indexer.EnableAutomaticSearch = false; + indexer.ConfigContract = nameof(NewznabSettings); + indexer.Implementation = nameof(Newznab); + indexer.Name = "NewznabTest"; + indexer.Protocol = Core.Indexers.DownloadProtocol.Usenet; // Change Console Log Level to Debug so we get more details. var config = HostConfig.Get(1); diff --git a/src/NzbDrone.Test.Common/AutoMoq/AutoMoqer.cs b/src/NzbDrone.Test.Common/AutoMoq/AutoMoqer.cs index a1eb60a46..e42355819 100644 --- a/src/NzbDrone.Test.Common/AutoMoq/AutoMoqer.cs +++ b/src/NzbDrone.Test.Common/AutoMoq/AutoMoqer.cs @@ -29,6 +29,8 @@ namespace NzbDrone.Test.Common.AutoMoq AssemblyLoader.RegisterSQLiteResolver(); } + public IContainer Container => _container; + public virtual T Resolve() { var result = _container.Resolve();