Skip to content

Commit 866ddb9

Browse files
committed
Merge remote-tracking branch 'refs/remotes/origin/dev'
2 parents 3dc3a5b + 4073495 commit 866ddb9

9 files changed

Lines changed: 259 additions & 7 deletions

src/CommonAssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
[assembly: ComVisible(false)]
1515

16-
[assembly: AssemblyVersion("2.10.0.0")]
17-
[assembly: AssemblyFileVersion("2.10.0.0")]
16+
[assembly: AssemblyVersion("2.10.3.0")]
17+
[assembly: AssemblyFileVersion("2.10.3.0")]
1818

1919
[assembly: NeutralResourcesLanguage("en-US")]
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Linq.Expressions;
6+
using System.Reflection;
7+
8+
namespace NuGet.Server.DataServices
9+
{
10+
public class IgnoreCaseForPackageIdInterceptor : ExpressionVisitor
11+
{
12+
private static readonly MemberInfo _idMember = typeof(ODataPackage).GetProperty("Id");
13+
private static readonly Expression<Func<string, string, int>> _ordinalIgnoreCaseComparer = (a, b) => StringComparer.OrdinalIgnoreCase.Compare(a, b);
14+
15+
protected override Expression VisitBinary(BinaryExpression node)
16+
{
17+
// Change equality comparisons on Version to normalized comparisons on NormalizedVersion
18+
if (node.NodeType == ExpressionType.Equal)
19+
{
20+
// Figure out which side is the target
21+
ConstantExpression constSide = (node.Left as ConstantExpression) ?? (node.Right as ConstantExpression);
22+
if (constSide != null && constSide.Type == typeof(string))
23+
{
24+
MemberExpression memberSide = (node.Right as MemberExpression) ?? (node.Left as MemberExpression);
25+
if (memberSide != null && memberSide.Member == _idMember)
26+
{
27+
// We have a "Package.Id == <constant>" expression!
28+
29+
// Rewrite it to StringComparer.OrdinalIgnoreCase.Compare(a, b) == 0
30+
return Expression.MakeBinary(
31+
ExpressionType.Equal,
32+
Expression.Invoke(_ordinalIgnoreCaseComparer,
33+
Expression.MakeMemberAccess(memberSide.Expression, _idMember),
34+
constSide
35+
),
36+
Expression.Constant(0));
37+
}
38+
}
39+
}
40+
return node;
41+
}
42+
}
43+
}

src/NuGet.Server/DataServices/PackageContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public IQueryable<ODataPackage> Packages
2222
.GetPackages()
2323
.Select(package => package.AsODataPackage())
2424
.AsQueryable()
25-
.InterceptWith(new NormalizeVersionInterceptor());
25+
.InterceptWith(new IgnoreCaseForPackageIdInterceptor(), new NormalizeVersionInterceptor());
2626
}
2727
}
2828
}

src/NuGet.Server/Infrastructure/ServerPackageRepository.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
using System.Threading;
1111
using System.Threading.Tasks;
1212
using System.Web.Configuration;
13-
using NuGet.Resources;
1413
using NuGet.Server.Logging;
1514

1615
namespace NuGet.Server.Infrastructure

src/NuGet.Server/Infrastructure/ServerPackageStore.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Runtime.Serialization;
99
using System.Threading;
1010
using System.Threading.Tasks;
11+
using Newtonsoft.Json;
1112

1213
namespace NuGet.Server.Infrastructure
1314
{
@@ -53,10 +54,17 @@ private void Load()
5354
}
5455
}
5556
}
56-
catch (SerializationException)
57+
catch (Exception ex)
5758
{
58-
// In case this happens, remove the file
59-
_fileSystem.DeleteFile(_fileName);
59+
if (ex is JsonReaderException || ex is SerializationException)
60+
{
61+
// In case this happens, remove the file
62+
_fileSystem.DeleteFile(_fileName);
63+
}
64+
else
65+
{
66+
throw;
67+
}
6068
}
6169
}
6270
}

src/NuGet.Server/NuGet.Server.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
<Compile Include="Core\FrameworkNameExtensions.cs" />
7878
<Compile Include="Core\IServiceResolver.cs" />
7979
<Compile Include="Core\PackageExtensions.cs" />
80+
<Compile Include="DataServices\IgnoreCaseForPackageIdInterceptor.cs" />
8081
<Compile Include="DataServices\ODataPackage.cs" />
8182
<Compile Include="DataServices\PackageContext.cs" />
8283
<Compile Include="DataServices\PackageExtensions.cs" />
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Linq.Expressions;
8+
using System.Reflection;
9+
using NuGet.Server.DataServices;
10+
using Xunit;
11+
12+
namespace NuGet.Server.Tests
13+
{
14+
public class IgnoreCaseForPackageIdInterceptorTest
15+
{
16+
private static readonly MemberInfo _idMember = typeof(ODataPackage).GetProperty("Id");
17+
private static readonly Expression<Func<string, string, int>> _ordinalIgnoreCaseComparer = (a, b) => StringComparer.OrdinalIgnoreCase.Compare(a, b);
18+
19+
public static IEnumerable<object[]> TheoryData
20+
{
21+
get
22+
{
23+
return new[]
24+
{
25+
new object[]
26+
{
27+
Expression.MakeBinary(
28+
ExpressionType.Equal,
29+
Expression.Constant("NEWTONSOFT.JSON"),
30+
Expression.MakeMemberAccess(Expression.Parameter(typeof(ODataPackage)), _idMember)),
31+
32+
Expression.MakeBinary(
33+
ExpressionType.Equal,
34+
Expression.Invoke(_ordinalIgnoreCaseComparer,
35+
Expression.MakeMemberAccess(Expression.Parameter(typeof(ODataPackage)), _idMember),
36+
Expression.Constant("NEWTONSOFT.JSON")
37+
),
38+
Expression.Constant(0))
39+
},
40+
41+
new object[]
42+
{
43+
Expression.MakeBinary(
44+
ExpressionType.Equal,
45+
Expression.MakeMemberAccess(Expression.Parameter(typeof(ODataPackage)), _idMember),
46+
Expression.Constant("NEWTONSOFT.JSON")),
47+
48+
Expression.MakeBinary(
49+
ExpressionType.Equal,
50+
Expression.Invoke(_ordinalIgnoreCaseComparer,
51+
Expression.MakeMemberAccess(Expression.Parameter(typeof(ODataPackage)), _idMember),
52+
Expression.Constant("NEWTONSOFT.JSON")
53+
),
54+
Expression.Constant(0))
55+
}
56+
};
57+
}
58+
}
59+
60+
[Theory]
61+
[MemberData("TheoryData")]
62+
public void RewritesIdComparisonToIgnoreCaseComparison(Expression originalExpression, Expression expectedExpression)
63+
{
64+
// Arrange
65+
var interceptor = new IgnoreCaseForPackageIdInterceptor();
66+
67+
// Act
68+
var rewrittenExpression = interceptor.Visit(originalExpression);
69+
70+
// Assert
71+
Assert.Equal(rewrittenExpression.ToString(), expectedExpression.ToString());
72+
}
73+
74+
[Fact]
75+
public void FindsPackagesIgnoringCase()
76+
{
77+
// Arrange
78+
var data = new List<ODataPackage>();
79+
data.Add(new ODataPackage { Id = "foo" });
80+
data.Add(new ODataPackage { Id = "BAR" });
81+
data.Add(new ODataPackage { Id = "bAz" });
82+
83+
var queryable = data.AsQueryable().InterceptWith(new IgnoreCaseForPackageIdInterceptor());
84+
85+
// Act
86+
var result1 = queryable.FirstOrDefault(p => p.Id == "foo");
87+
var result2 = queryable.FirstOrDefault(p => p.Id == "FOO");
88+
var result3 = queryable.FirstOrDefault(p => p.Id == "Foo");
89+
90+
var result4 = queryable.FirstOrDefault(p => p.Id == "bar");
91+
var result5 = queryable.FirstOrDefault(p => p.Id == "BAR");
92+
var result6 = queryable.FirstOrDefault(p => p.Id == "baR");
93+
94+
var result7 = queryable.FirstOrDefault(p => p.Id == "baz");
95+
var result8 = queryable.FirstOrDefault(p => p.Id == "BAZ");
96+
var result9 = queryable.FirstOrDefault(p => p.Id == "bAz");
97+
98+
// Assert
99+
Assert.Equal(result1.Id, data[0].Id);
100+
Assert.Equal(result2.Id, data[0].Id);
101+
Assert.Equal(result3.Id, data[0].Id);
102+
103+
Assert.Equal(result4.Id, data[1].Id);
104+
Assert.Equal(result5.Id, data[1].Id);
105+
Assert.Equal(result6.Id, data[1].Id);
106+
107+
Assert.Equal(result7.Id, data[2].Id);
108+
Assert.Equal(result8.Id, data[2].Id);
109+
Assert.Equal(result9.Id, data[2].Id);
110+
}
111+
}
112+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Linq.Expressions;
7+
using System.Reflection;
8+
using NuGet.Server.DataServices;
9+
using Xunit;
10+
11+
namespace NuGet.Server.Tests
12+
{
13+
public class NormalizeVersionInterceptorTest
14+
{
15+
private static readonly MemberInfo _versionMember = typeof(ODataPackage).GetProperty("Version");
16+
private static readonly MemberInfo _normalizedVersionMember = typeof(ODataPackage).GetProperty("NormalizedVersion");
17+
18+
public static IEnumerable<object[]> TheoryData
19+
{
20+
get
21+
{
22+
return new[]
23+
{
24+
new object[]
25+
{
26+
Expression.MakeBinary(
27+
ExpressionType.Equal,
28+
Expression.Constant("1.0.0.0"),
29+
Expression.MakeMemberAccess(Expression.Parameter(typeof(ODataPackage)), _versionMember)),
30+
31+
Expression.MakeBinary(
32+
ExpressionType.Equal,
33+
Expression.Constant("1.0.0"),
34+
Expression.MakeMemberAccess(Expression.Parameter(typeof(ODataPackage)), _normalizedVersionMember))
35+
},
36+
37+
new object[]
38+
{
39+
Expression.MakeBinary(
40+
ExpressionType.Equal,
41+
Expression.MakeMemberAccess(Expression.Parameter(typeof(ODataPackage)), _versionMember),
42+
Expression.Constant("1.0.0.0")),
43+
44+
Expression.MakeBinary(
45+
ExpressionType.Equal,
46+
Expression.Constant("1.0.0"),
47+
Expression.MakeMemberAccess(Expression.Parameter(typeof(ODataPackage)), _normalizedVersionMember))
48+
}
49+
};
50+
}
51+
}
52+
53+
[Theory]
54+
[MemberData("TheoryData")]
55+
public void RewritesVersionPropertyNameToNormalizedVersionPropertyName(Expression originalExpression, Expression expectedExpression)
56+
{
57+
// Arrange
58+
var interceptor = new NormalizeVersionInterceptor();
59+
60+
// Act
61+
var rewrittenExpression = interceptor.Visit(originalExpression);
62+
63+
// Assert
64+
Assert.Equal(rewrittenExpression.ToString(), expectedExpression.ToString());
65+
}
66+
67+
[Fact]
68+
public void FindsPackagesUsingNormalizedVersion()
69+
{
70+
// Arrange
71+
var data = new List<ODataPackage>();
72+
data.Add(new ODataPackage { Id = "foo", Version = "1.0.0.0.0.0", NormalizedVersion = "1.0.0"});
73+
74+
var queryable = data.AsQueryable().InterceptWith(new NormalizeVersionInterceptor());
75+
76+
// Act
77+
var result1 = queryable.FirstOrDefault(p => p.Version == "1.0");
78+
var result2 = queryable.FirstOrDefault(p => p.Version == "1.0.0");
79+
var result3 = queryable.FirstOrDefault(p => p.Version == "1.0.0.0");
80+
81+
// Assert
82+
Assert.Equal(result1, data[0]);
83+
Assert.Equal(result2, data[0]);
84+
Assert.Equal(result3, data[0]);
85+
}
86+
}
87+
}

test/NuGet.Server.Tests/NuGet.Server.Tests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,11 @@
6666
</CodeAnalysisDependentAssemblyPaths>
6767
</ItemGroup>
6868
<ItemGroup>
69+
<Compile Include="IgnoreCaseForPackageIdInterceptorTest.cs" />
6970
<Compile Include="JsonNetPackagesSerializerTests.cs" />
7071
<Compile Include="FeedPackageTest.cs" />
7172
<Compile Include="Infrastructure\TemporaryDirectory.cs" />
73+
<Compile Include="NormalizeVersionInterceptorTest.cs" />
7274
<Compile Include="PackageAuthenticationServiceTests.cs" />
7375
<Compile Include="PackageSvcFacts.cs" />
7476
<Compile Include="Properties\AssemblyInfo.cs" />

0 commit comments

Comments
 (0)