diff --git a/Octokit.Tests/Models/RequestParametersTests.cs b/Octokit.Tests/Models/RequestParametersTests.cs index 516e2369..187e8806 100644 --- a/Octokit.Tests/Models/RequestParametersTests.cs +++ b/Octokit.Tests/Models/RequestParametersTests.cs @@ -116,7 +116,7 @@ namespace Octokit.Tests.Models [Fact] public void UsesParameterAttributeForKey() { - var model = new WithPropertyNameDifferentFromKey() { LongPropertyName = "verbose" }; + var model = new WithPropertyNameDifferentFromKey() { LongPropertyName = "verbose" }; var result = model.ToParametersDictionary(); diff --git a/Octokit.Tests/OctokitRT.Tests.csproj b/Octokit.Tests/OctokitRT.Tests.csproj index 17ea6ba9..6112365f 100644 --- a/Octokit.Tests/OctokitRT.Tests.csproj +++ b/Octokit.Tests/OctokitRT.Tests.csproj @@ -89,7 +89,7 @@ - + diff --git a/Octokit/Helpers/ReflectionExtensions.cs b/Octokit/Helpers/ReflectionExtensions.cs index d7966b9b..0e35ce7e 100644 --- a/Octokit/Helpers/ReflectionExtensions.cs +++ b/Octokit/Helpers/ReflectionExtensions.cs @@ -1,4 +1,8 @@ using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; namespace Octokit { @@ -11,7 +15,48 @@ namespace Octokit public static bool IsNullable(this Type type) { - return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + return type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + } + +#if !NETFX_CORE + public static Type GetTypeInfo(this Type type) + { + return type; + } +#endif + +#if NETFX_CORE + public static IEnumerable GetMember(this Type type, string name) + { + return type.GetTypeInfo().DeclaredMembers.Where(m => m.Name == name); + } + + public static bool IsAssignableFrom(this Type type, Type otherType) + { + return type.GetTypeInfo().IsAssignableFrom(otherType.GetTypeInfo()); + } +#endif + public static IEnumerable GetAllProperties(this Type type) + { +#if NETFX_CORE + var typeInfo = type.GetTypeInfo(); + var properties = typeInfo.DeclaredProperties; + + var baseType = typeInfo.BaseType; + + return baseType == null ? properties : properties.Concat(baseType.GetAllProperties()); +#else + return type.GetProperties(BindingFlags.Instance | BindingFlags.Public); +#endif + } + + public static bool IsEnumeration(this Type type) + { +#if NETFX_CORE + return type.GetTypeInfo().IsEnum; +#else + return type.IsEnum; +#endif } } } diff --git a/Octokit/Models/Request/RequestParameters.cs b/Octokit/Models/Request/RequestParameters.cs index c96d79ee..d47d4312 100644 --- a/Octokit/Models/Request/RequestParameters.cs +++ b/Octokit/Models/Request/RequestParameters.cs @@ -14,74 +14,109 @@ namespace Octokit /// public abstract class RequestParameters { - static readonly ConcurrentDictionary> _propertiesMap = - new ConcurrentDictionary>(); + static readonly ConcurrentDictionary> _propertiesMap = + new ConcurrentDictionary>(); public virtual IDictionary ToParametersDictionary() { - var properties = _propertiesMap.GetOrAdd(GetType(), GetPropertiesForType); - - return (from property in properties - let value = GetValue(property) - let key = GetKey(property) - where value != null - select new { key, value }).ToDictionary(kvp => kvp.key, kvp => kvp.value); + var map = _propertiesMap.GetOrAdd(GetType(), GetPropertyParametersForType); + return (from property in map + let value = property.GetValue(this) + let key = property.Key + where value != null + select new { key, value }).ToDictionary(kvp => kvp.key, kvp => kvp.value); } - static List GetPropertiesForType(Type type) + static List GetPropertyParametersForType(Type type) { - return type.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList(); + return type.GetAllProperties() + .Select(p => new PropertyParameter(p)) + .ToList(); } [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "GitHub API depends on lower case strings")] - static string GetKey(PropertyInfo property) + static Func GetValueFunc(Type propertyType) { - var attribute = property.GetCustomAttributes(typeof(ParameterAttribute), false) - .Cast() - .FirstOrDefault(attr => attr.Key != null); - - return attribute == null - ? property.Name.ToLowerInvariant() - : attribute.Key; - } - - [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", - Justification = "GitHub API depends on lower case strings")] - string GetValue(PropertyInfo property) - { - var value = property.GetValue(this, null); - - if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType)) + if (typeof(IEnumerable).IsAssignableFrom(propertyType)) { - var list = (IEnumerable)value; - return !list.Any() ? null : String.Join(",", list); - } - - if (property.PropertyType.IsDateTimeOffset() && value != null) - { - return ((DateTimeOffset)value).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ", - CultureInfo.InvariantCulture); - } - - if (property.PropertyType.IsEnum && value != null) - { - var member = property.PropertyType.GetMember(value.ToString()).FirstOrDefault(); - if (member != null) + return (prop, value) => { - var attribute = member.GetCustomAttributes(typeof(ParameterAttribute), false) - .Cast() - .FirstOrDefault(); - if (attribute != null) - { - return attribute.Value; - } - } + var list = ((IEnumerable)value).ToArray(); + return !list.Any() ? null : String.Join(",", list); + }; } - return value != null + if (propertyType.IsDateTimeOffset()) + { + return (prop, value) => + { + if (value == null) return null; + return ((DateTimeOffset)value).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ", + CultureInfo.InvariantCulture); + }; + } + + if (propertyType.IsEnumeration()) + { + var enumToAttributeDictionary = Enum.GetNames(propertyType) + .ToDictionary(name => name, name => GetParameterAttributeValueForEnumName(propertyType, name)); + return (prop, value) => + { + if (value == null) return null; + string attributeValue; + + return enumToAttributeDictionary.TryGetValue(value.ToString(), out attributeValue) + ? attributeValue ?? value.ToString().ToLowerInvariant() + : value.ToString().ToLowerInvariant(); + }; + } + + return (prop, value) => value != null ? value.ToString().ToLowerInvariant() : null; } + + static string GetParameterAttributeValueForEnumName(Type enumType, string name) + { + var member = enumType.GetMember(name).FirstOrDefault(); + if (member == null) return null; + var attribute = member.GetCustomAttributes(typeof(ParameterAttribute), false) + .Cast() + .FirstOrDefault(); + return attribute != null ? attribute.Value : null; + } + + class PropertyParameter + { + readonly Func _valueFunc; + readonly PropertyInfo _property; + public PropertyParameter(PropertyInfo property) + { + _property = property; + Key = GetParameterKeyFromProperty(property); + _valueFunc = GetValueFunc(property.PropertyType); + } + + public string Key { get; private set; } + + public string GetValue(object instance) + { + return _valueFunc(_property, _property.GetValue(instance, null)); + } + + [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", + Justification = "GitHub API depends on lower case strings")] + static string GetParameterKeyFromProperty(PropertyInfo property) + { + var attribute = property.GetCustomAttributes(typeof(ParameterAttribute), false) + .Cast() + .FirstOrDefault(attr => attr.Key != null); + + return attribute == null + ? property.Name.ToLowerInvariant() + : attribute.Key; + } + } } } diff --git a/Octokit/OctokitRT.csproj b/Octokit/OctokitRT.csproj index 1ba1753c..45e0b9f6 100644 --- a/Octokit/OctokitRT.csproj +++ b/Octokit/OctokitRT.csproj @@ -149,6 +149,7 @@ + @@ -192,6 +193,7 @@ +