Skip to content

Commit ef8d650

Browse files
author
Bart Koelman
committed
Fixed: De-duplicate field names in sparse fieldset; do not ignore case
Inlined TypeHelper.ConvertCollection
1 parent 1439d4f commit ef8d650

File tree

8 files changed

+46
-62
lines changed

8 files changed

+46
-62
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
5+
namespace JsonApiDotNetCore.Extensions
6+
{
7+
internal static class SystemCollectionExtensions
8+
{
9+
public static void AddRange<T>(this ICollection<T> source, IEnumerable<T> items)
10+
{
11+
if (source == null) throw new ArgumentNullException(nameof(source));
12+
if (items == null) throw new ArgumentNullException(nameof(items));
13+
14+
foreach (var item in items)
15+
{
16+
source.Add(item);
17+
}
18+
}
19+
20+
public static void AddRange<T>(this IList source, IEnumerable<T> items)
21+
{
22+
if (source == null) throw new ArgumentNullException(nameof(source));
23+
if (items == null) throw new ArgumentNullException(nameof(items));
24+
25+
foreach (var item in items)
26+
{
27+
source.Add(item);
28+
}
29+
}
30+
}
31+
}

src/JsonApiDotNetCore/Extensions/TypeExtensions.cs

Lines changed: 4 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,6 @@ namespace JsonApiDotNetCore.Extensions
99
{
1010
internal static class TypeExtensions
1111
{
12-
13-
/// <summary>
14-
/// Extension to use the LINQ AddRange method on an IList
15-
/// </summary>
16-
public static void AddRange<T>(this IList list, IEnumerable<T> items)
17-
{
18-
if (list == null) throw new ArgumentNullException(nameof(list));
19-
if (items == null) throw new ArgumentNullException(nameof(items));
20-
21-
if (list is List<T> genericList)
22-
{
23-
genericList.AddRange(items);
24-
}
25-
else
26-
{
27-
foreach (var item in items)
28-
{
29-
list.Add(item);
30-
}
31-
}
32-
}
33-
3412
/// <summary>
3513
/// Extension to use the LINQ cast method in a non-generic way:
3614
/// <code>
@@ -42,32 +20,13 @@ public static IEnumerable Cast(this IEnumerable source, Type type)
4220
{
4321
if (source == null) throw new ArgumentNullException(nameof(source));
4422
if (type == null) throw new ArgumentNullException(nameof(type));
45-
return TypeHelper.ConvertCollection(source.Cast<object>(), type);
46-
}
47-
48-
public static Type GetElementType(this IEnumerable enumerable)
49-
{
50-
var enumerableTypes = enumerable.GetType()
51-
.GetInterfaces()
52-
.Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
53-
.ToList();
54-
55-
var numberOfEnumerableTypes = enumerableTypes.Count;
56-
57-
if (numberOfEnumerableTypes == 0)
58-
{
59-
throw new ArgumentException($"{nameof(enumerable)} of type {enumerable.GetType().FullName} does not implement a generic variant of {nameof(IEnumerable)}");
60-
}
6123

62-
if (numberOfEnumerableTypes > 1)
24+
var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(type));
25+
foreach (var item in source.Cast<object>())
6326
{
64-
throw new ArgumentException($"{nameof(enumerable)} of type {enumerable.GetType().FullName} implements more than one generic variant of {nameof(IEnumerable)}:\n" +
65-
$"{string.Join("\n", enumerableTypes.Select(t => t.FullName))}");
27+
list.Add(TypeHelper.ConvertType(item, type));
6628
}
67-
68-
var elementType = enumerableTypes[0].GenericTypeArguments[0];
69-
70-
return elementType;
29+
return list;
7130
}
7231

7332
/// <summary>

src/JsonApiDotNetCore/Internal/TypeHelper.cs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,6 @@ namespace JsonApiDotNetCore.Internal
1111
{
1212
internal static class TypeHelper
1313
{
14-
public static IList ConvertCollection(IEnumerable<object> collection, Type targetType)
15-
{
16-
var list = Activator.CreateInstance(typeof(List<>).MakeGenericType(targetType)) as IList;
17-
foreach (var item in collection)
18-
list.Add(ConvertType(item, targetType));
19-
return list;
20-
}
2114
private static bool IsNullable(Type type)
2215
{
2316
return (!type.IsValueType || Nullable.GetUnderlyingType(type) != null);

src/JsonApiDotNetCore/Models/Annotation/AttrAttribute.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ private PropertyInfo GetResourceProperty(object resource)
124124
/// <summary>
125125
/// Whether or not the provided exposed name is equivalent to the one defined in on the model
126126
/// </summary>
127-
public bool Is(string publicRelationshipName)
128-
=> string.Equals(publicRelationshipName, PublicAttributeName, StringComparison.OrdinalIgnoreCase);
127+
public bool Is(string publicRelationshipName) => publicRelationshipName == PublicAttributeName;
129128
}
130129
}

src/JsonApiDotNetCore/Models/Annotation/RelationshipAttribute.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,9 @@ public override int GetHashCode()
7777
}
7878

7979
/// <summary>
80-
/// Whether or not the provided exposed name is equivalent to the one defined in on the model
80+
/// Whether or not the provided exposed name is equivalent to the one defined in the model
8181
/// </summary>
82-
public virtual bool Is(string publicRelationshipName)
83-
=> string.Equals(publicRelationshipName, PublicRelationshipName, StringComparison.OrdinalIgnoreCase);
82+
public virtual bool Is(string publicRelationshipName) => publicRelationshipName == PublicRelationshipName;
8483

8584
/// <summary>
8685
/// The internal navigation property path to the related entity.

src/JsonApiDotNetCore/QueryParameterServices/SparseFieldsService.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Linq;
33
using JsonApiDotNetCore.Controllers;
44
using JsonApiDotNetCore.Exceptions;
5+
using JsonApiDotNetCore.Extensions;
56
using JsonApiDotNetCore.Internal.Contracts;
67
using JsonApiDotNetCore.Internal.Query;
78
using JsonApiDotNetCore.Managers.Contracts;
@@ -58,8 +59,10 @@ public virtual void Parse(string parameterName, StringValues parameterValue)
5859
// articles?fields[articles]=prop1,prop2 <-- this form in invalid UNLESS "articles" is actually a relationship on Article
5960
// articles?fields[relationship]=prop1,prop2
6061
EnsureNoNestedResourceRoute(parameterName);
61-
var fields = new List<string> { nameof(Identifiable.Id) };
62-
fields.AddRange(((string)parameterValue).Split(QueryConstants.COMMA));
62+
63+
HashSet<string> fields = new HashSet<string>();
64+
fields.Add(nameof(Identifiable.Id).ToLowerInvariant());
65+
fields.AddRange(((string) parameterValue).Split(QueryConstants.COMMA));
6366

6467
var keySplit = parameterName.Split(QueryConstants.OPEN_BRACKET, QueryConstants.CLOSE_BRACKET);
6568

src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ private void SetHasManyRelationship(
234234
relatedInstance.StringId = rio.Id;
235235
return relatedInstance;
236236
});
237-
var convertedCollection = TypeHelper.ConvertCollection(relatedResources, attr.RightType);
237+
var convertedCollection = relatedResources.Cast(attr.RightType);
238238
attr.SetValue(entity, convertedCollection);
239239
}
240240

test/UnitTests/QueryParameters/SparseFieldsServiceTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ public void Parse_ValidSelection_CanParse()
6868

6969
// Assert
7070
Assert.NotEmpty(result);
71-
Assert.Equal(idAttribute, result.First());
72-
Assert.Equal(attribute, result[1]);
71+
Assert.Contains(idAttribute, result);
72+
Assert.Contains(attribute, result);
7373
}
7474

7575
[Fact]

0 commit comments

Comments
 (0)