From 3c6386f3188d951385d5b34af7a0b06287e6a166 Mon Sep 17 00:00:00 2001 From: Stevie Robinson Date: Fri, 10 Nov 2023 17:44:04 +0100 Subject: [PATCH] Translate fields on the backend (cherry picked from commit 48b12f5b00429a7cd218d23f0544641b0da62a06) --- .../ClientSchemaTests/SchemaBuilderFixture.cs | 13 ++++++ .../Annotations/FieldDefinitionAttribute.cs | 24 ++++++++++ src/NzbDrone.Host/Bootstrap.cs | 3 ++ .../IntegrationTest.cs | 28 ++++++----- src/NzbDrone.Test.Common/AutoMoq/AutoMoqer.cs | 2 + src/Radarr.Http/ClientSchema/SchemaBuilder.cs | 46 +++++++++++++++++-- 6 files changed, 101 insertions(+), 15 deletions(-) diff --git a/src/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs b/src/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs index 755411a24..638fadd03 100644 --- a/src/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs +++ b/src/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs @@ -1,6 +1,9 @@ +using System.Collections.Generic; using FluentAssertions; +using Moq; using NUnit.Framework; using NzbDrone.Core.Annotations; +using NzbDrone.Core.Localization; using NzbDrone.Test.Common; using Radarr.Http.ClientSchema; @@ -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 b5329038c..b4ec24286 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; } @@ -84,4 +101,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 f2d5da996..f5f6f5a5f 100644 --- a/src/NzbDrone.Host/Bootstrap.cs +++ b/src/NzbDrone.Host/Bootstrap.cs @@ -23,6 +23,7 @@ using NzbDrone.Common.Instrumentation; using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.Datastore.Extensions; +using Radarr.Http.ClientSchema; using PostgresOptions = NzbDrone.Core.Datastore.PostgresOptions; namespace NzbDrone.Host @@ -146,6 +147,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 bfc0a4ab4..d55d1dab0 100644 --- a/src/NzbDrone.Integration.Test/IntegrationTest.cs +++ b/src/NzbDrone.Integration.Test/IntegrationTest.cs @@ -1,3 +1,5 @@ +using System; +using System.Linq; using System.Threading; using NLog; using NUnit.Framework; @@ -7,7 +9,6 @@ using NzbDrone.Core.Datastore.Migration.Framework; using NzbDrone.Core.Indexers.Newznab; using NzbDrone.Test.Common; using NzbDrone.Test.Common.Datastore; -using Radarr.Http.ClientSchema; namespace NzbDrone.Integration.Test { @@ -48,19 +49,22 @@ namespace NzbDrone.Integration.Test protected override void InitializeTestTarget() { // Make sure tasks have been initialized so the config put below doesn't cause errors - WaitForCompletion(() => Tasks.All().SelectList(x => x.TaskName).Contains("RssSync"), 20000); + WaitForCompletion(() => Tasks.All().SelectList(x => x.TaskName).Contains("RssSync"), 30000); - Indexers.Post(new Radarr.Api.V3.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 2459cb092..288b4c096 100644 --- a/src/NzbDrone.Test.Common/AutoMoq/AutoMoqer.cs +++ b/src/NzbDrone.Test.Common/AutoMoq/AutoMoqer.cs @@ -23,6 +23,8 @@ namespace NzbDrone.Test.Common.AutoMoq SetupAutoMoqer(CreateTestContainer(new Container())); } + public IContainer Container => _container; + public virtual T Resolve() { var result = _container.Resolve(); diff --git a/src/Radarr.Http/ClientSchema/SchemaBuilder.cs b/src/Radarr.Http/ClientSchema/SchemaBuilder.cs index 7fe9e8b57..5fa28904a 100644 --- a/src/Radarr.Http/ClientSchema/SchemaBuilder.cs +++ b/src/Radarr.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 Radarr.Http.ClientSchema { @@ -15,6 +17,12 @@ namespace Radarr.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) { @@ -107,13 +115,27 @@ namespace Radarr.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, @@ -173,6 +195,24 @@ namespace Radarr.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)