diff --git a/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs b/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs new file mode 100644 index 000000000..d80aed440 --- /dev/null +++ b/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs @@ -0,0 +1,45 @@ +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Api.ClientSchema; +using NzbDrone.Test.Common; + +namespace NzbDrone.Api.Test.ClientSchemaTests +{ + [TestFixture] + public class SchemaBuilderFixture : TestBase + { + [Test] + public void should_return_field_for_every_property() + { + var schema = SchemaBuilder.GenerateSchema(new TestModel()); + schema.Should().HaveCount(2); + } + + + [Test] + public void schema_should_have_proper_fields() + { + var model = new TestModel + { + FirstName = "Bob", + LastName = "Poop" + }; + + var schema = SchemaBuilder.GenerateSchema(model); + + schema.Should().Contain(c => c.Order == 1 && c.Name == "LastName" && c.Label == "Last Name" && c.HelpText == "Your Last Name" && c.Value == "Poop"); + schema.Should().Contain(c => c.Order == 0 && c.Name == "FirstName" && c.Label == "First Name" && c.HelpText == "Your First Name" && c.Value == "Bob"); + } + + } + + + public class TestModel + { + [FieldDefinition(0, Label = "First Name", HelpText = "Your First Name")] + public string FirstName { get; set; } + + [FieldDefinition(1, Label = "Last Name", HelpText = "Your Last Name")] + public string LastName { get; set; } + } +} \ No newline at end of file diff --git a/NzbDrone.Api.Test/NzbDrone.Api.Test.csproj b/NzbDrone.Api.Test/NzbDrone.Api.Test.csproj index 0c884e4f9..476e0707d 100644 --- a/NzbDrone.Api.Test/NzbDrone.Api.Test.csproj +++ b/NzbDrone.Api.Test/NzbDrone.Api.Test.csproj @@ -72,6 +72,7 @@ + diff --git a/NzbDrone.Api/ClientSchema/Field.cs b/NzbDrone.Api/ClientSchema/Field.cs new file mode 100644 index 000000000..2fe6388fe --- /dev/null +++ b/NzbDrone.Api/ClientSchema/Field.cs @@ -0,0 +1,11 @@ +namespace NzbDrone.Api.ClientSchema +{ + public class Field + { + public int Order { get; set; } + public string Name { get; set; } + public string Label { get; set; } + public string HelpText { get; set; } + public string Value { get; set; } + } +} \ No newline at end of file diff --git a/NzbDrone.Api/ClientSchema/FieldDefinitionAttribute.cs b/NzbDrone.Api/ClientSchema/FieldDefinitionAttribute.cs new file mode 100644 index 000000000..1c1c06699 --- /dev/null +++ b/NzbDrone.Api/ClientSchema/FieldDefinitionAttribute.cs @@ -0,0 +1,18 @@ +using System; + +namespace NzbDrone.Api.ClientSchema +{ + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] + public class FieldDefinitionAttribute : Attribute + { + + public FieldDefinitionAttribute(int order) + { + Order = order; + } + + public int Order { get; private set; } + public string Label { get; set; } + public string HelpText { get; set; } + } +} \ No newline at end of file diff --git a/NzbDrone.Api/ClientSchema/SchemaBuilder.cs b/NzbDrone.Api/ClientSchema/SchemaBuilder.cs new file mode 100644 index 000000000..8be6507b6 --- /dev/null +++ b/NzbDrone.Api/ClientSchema/SchemaBuilder.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using NzbDrone.Common.Reflection; + +namespace NzbDrone.Api.ClientSchema +{ + public static class SchemaBuilder + { + public static List GenerateSchema(object model) + { + var properties = model.GetType().GetSimpleProperties(); + + var result = new List(properties.Count); + + foreach (var propertyInfo in properties) + { + var fieldAttribute = propertyInfo.GetAttribute(); + + var field = new Field() + { + Name = propertyInfo.Name, + Label = fieldAttribute.Label, + HelpText = fieldAttribute.HelpText, + Order = fieldAttribute.Order, + + }; + + var value = propertyInfo.GetValue(model, null); + if (value != null) + { + field.Value = value.ToString(); + } + + result.Add(field); + } + + return result; + + } + } +} \ No newline at end of file diff --git a/NzbDrone.Api/Indexers/IndexerModule.cs b/NzbDrone.Api/Indexers/IndexerModule.cs index 537771cb7..5931efbab 100644 --- a/NzbDrone.Api/Indexers/IndexerModule.cs +++ b/NzbDrone.Api/Indexers/IndexerModule.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; +using NzbDrone.Api.ClientSchema; using NzbDrone.Core.Indexers; +using Omu.ValueInjecter; namespace NzbDrone.Api.Indexers { @@ -15,7 +17,18 @@ namespace NzbDrone.Api.Indexers private List GetAll() { - return ApplyToList(_indexerService.All); + var indexers = _indexerService.All(); + + var result = new List(indexers.Count); + + foreach (var indexerDefinition in indexers) + { + var resource = new IndexerResource(); + resource.InjectFrom(indexerDefinition); + resource.Fields = SchemaBuilder.GenerateSchema(indexerDefinition.Settings); + } + + return result; } } } \ No newline at end of file diff --git a/NzbDrone.Api/Indexers/IndexerResource.cs b/NzbDrone.Api/Indexers/IndexerResource.cs index fadce1a5e..0274e0658 100644 --- a/NzbDrone.Api/Indexers/IndexerResource.cs +++ b/NzbDrone.Api/Indexers/IndexerResource.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using NzbDrone.Api.ClientSchema; using NzbDrone.Api.REST; namespace NzbDrone.Api.Indexers @@ -7,6 +9,7 @@ namespace NzbDrone.Api.Indexers { public Boolean Enable { get; set; } public String Name { get; set; } - public String Settings { get; set; } + + public List Fields { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Api/NzbDrone.Api.csproj b/NzbDrone.Api/NzbDrone.Api.csproj index 5af7f8900..631f29021 100644 --- a/NzbDrone.Api/NzbDrone.Api.csproj +++ b/NzbDrone.Api/NzbDrone.Api.csproj @@ -84,6 +84,9 @@ + + + diff --git a/NzbDrone.Common/Reflection/ReflectionExtensions.cs b/NzbDrone.Common/Reflection/ReflectionExtensions.cs index 5f8474e7b..d772f5765 100644 --- a/NzbDrone.Common/Reflection/ReflectionExtensions.cs +++ b/NzbDrone.Common/Reflection/ReflectionExtensions.cs @@ -45,5 +45,16 @@ namespace NzbDrone.Common.Reflection return propertyInfo.CanWrite && propertyInfo.GetSetMethod(false) != null; } + public static T GetAttribute(this MemberInfo member, bool isRequired = true) where T : Attribute + { + var attribute = member.GetCustomAttributes(typeof(T), false).SingleOrDefault(); + + if (attribute == null && isRequired) + { + throw new ArgumentException(String.Format("The {0} attribute must be defined on member {1}", typeof(T).Name, member.Name)); + } + + return (T)attribute; + } } } \ No newline at end of file diff --git a/UI/Settings/Indexers/CollectionView.js b/UI/Settings/Indexers/CollectionView.js index 51023fc28..76a9ca9ce 100644 --- a/UI/Settings/Indexers/CollectionView.js +++ b/UI/Settings/Indexers/CollectionView.js @@ -1,10 +1,8 @@ 'use strict'; - -define(['app', 'Settings/Indexers/ItemView'], function (app) { - +define(['app', 'Settings/Indexers/ItemView'], function () { NzbDrone.Settings.Indexers.CollectionView = Backbone.Marionette.CompositeView.extend({ itemView : NzbDrone.Settings.Indexers.ItemView, itemViewContainer : '#x-indexers', template : 'Settings/Indexers/CollectionTemplate' }); -}); \ No newline at end of file +}); diff --git a/UI/Settings/Indexers/Model.js b/UI/Settings/Indexers/Model.js index f1cf3792a..aa8592e0d 100644 --- a/UI/Settings/Indexers/Model.js +++ b/UI/Settings/Indexers/Model.js @@ -1,13 +1,5 @@ "use strict"; -define(['app'], function (app) { +define(['app'], function () { NzbDrone.Settings.Indexers.Model = Backbone.DeepModel.extend({ - mutators: { - fields: function () { - return [ - { key: 'username', title: 'Username', helpText: 'HALP!', value: 'mark', index: 0 }, - { key: 'apiKey', title: 'API Key', helpText: 'HALP!', value: '', index: 1 } - ]; - } - } }); });