From dd23c6922b3e8c6e3083b3d94164e547de8c67dc Mon Sep 17 00:00:00 2001 From: Maarten Balliauw Date: Tue, 15 Mar 2016 18:58:50 +0100 Subject: [PATCH] NuGet.Server - Support normalized version (and use it in queries) #2944 https://github.com/NuGet/NuGetGallery/issues/2944 --- .../NormalizeVersionInterceptor.cs | 44 +++++++++++++++++++ src/NuGet.Server/DataServices/ODataPackage.cs | 2 + .../DataServices/PackageContext.cs | 2 +- .../DataServices/PackageExtensions.cs | 2 + .../PackageIdComparisonVisitor.cs | 44 ------------------- src/NuGet.Server/DataServices/Packages.svc.cs | 9 ++-- src/NuGet.Server/NuGet.Server.csproj | 2 +- 7 files changed, 56 insertions(+), 49 deletions(-) create mode 100644 src/NuGet.Server/DataServices/NormalizeVersionInterceptor.cs delete mode 100644 src/NuGet.Server/DataServices/PackageIdComparisonVisitor.cs diff --git a/src/NuGet.Server/DataServices/NormalizeVersionInterceptor.cs b/src/NuGet.Server/DataServices/NormalizeVersionInterceptor.cs new file mode 100644 index 00000000..b54f57c7 --- /dev/null +++ b/src/NuGet.Server/DataServices/NormalizeVersionInterceptor.cs @@ -0,0 +1,44 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq.Expressions; +using System.Reflection; + +namespace NuGet.Server.DataServices +{ + public class NormalizeVersionInterceptor : ExpressionVisitor + { + private static readonly MemberInfo _versionMember = typeof(ODataPackage).GetProperty("Version"); + private static readonly MemberInfo _normalizedVersionMember = typeof(ODataPackage).GetProperty("NormalizedVersion"); + + protected override Expression VisitBinary(BinaryExpression node) + { + // Change equality comparisons on Version to normalized comparisons on NormalizedVersion + if (node.NodeType == ExpressionType.Equal) + { + // Figure out which side is the target + ConstantExpression constSide = (node.Left as ConstantExpression) ?? (node.Right as ConstantExpression); + if (constSide != null && constSide.Type == typeof(string)) + { + MemberExpression memberSide = (node.Right as MemberExpression) ?? (node.Left as MemberExpression); + if (memberSide != null && memberSide.Member == _versionMember) + { + // We have a "Package.Version == " expression! + + // Transform the constant version into a normalized version + SemanticVersion semanticVersion; + if (SemanticVersion.TryParse((string) constSide.Value, out semanticVersion)) + { + // Create a new expression that checks the new constant against NormalizedVersion instead + return Expression.MakeBinary( + ExpressionType.Equal, + left: Expression.Constant(semanticVersion.ToNormalizedString()), + right: Expression.MakeMemberAccess(memberSide.Expression, _normalizedVersionMember)); + } + } + } + } + return node; + } + } +} \ No newline at end of file diff --git a/src/NuGet.Server/DataServices/ODataPackage.cs b/src/NuGet.Server/DataServices/ODataPackage.cs index 8b8491f5..9800254f 100644 --- a/src/NuGet.Server/DataServices/ODataPackage.cs +++ b/src/NuGet.Server/DataServices/ODataPackage.cs @@ -18,6 +18,8 @@ public class ODataPackage public string Version { get; set; } + public string NormalizedVersion { get; set; } + public bool IsPrerelease { get; set; } public string Title { get; set; } diff --git a/src/NuGet.Server/DataServices/PackageContext.cs b/src/NuGet.Server/DataServices/PackageContext.cs index 3f4ee7d6..49df802e 100644 --- a/src/NuGet.Server/DataServices/PackageContext.cs +++ b/src/NuGet.Server/DataServices/PackageContext.cs @@ -22,7 +22,7 @@ public IQueryable Packages .GetPackages() .Select(package => package.AsODataPackage()) .AsQueryable() - .InterceptWith(new PackageIdComparisonVisitor()); + .InterceptWith(new NormalizeVersionInterceptor()); } } } diff --git a/src/NuGet.Server/DataServices/PackageExtensions.cs b/src/NuGet.Server/DataServices/PackageExtensions.cs index 72b8f024..29fb7951 100644 --- a/src/NuGet.Server/DataServices/PackageExtensions.cs +++ b/src/NuGet.Server/DataServices/PackageExtensions.cs @@ -23,6 +23,7 @@ public static ODataPackage AsODataPackage(this IPackage package) { Id = package.Id, Version = package.Version.ToString(), + NormalizedVersion = package.Version.ToNormalizedString(), IsPrerelease = !package.IsReleaseVersion(), Title = package.Title, Authors = string.Join(",", package.Authors), @@ -59,6 +60,7 @@ public static ODataPackage AsODataPackage(this ServerPackage package) { Id = package.Id, Version = package.Version.ToString(), + NormalizedVersion = package.Version.ToNormalizedString(), IsPrerelease = !package.IsReleaseVersion(), Title = package.Title, Authors = string.Join(",", package.Authors), diff --git a/src/NuGet.Server/DataServices/PackageIdComparisonVisitor.cs b/src/NuGet.Server/DataServices/PackageIdComparisonVisitor.cs deleted file mode 100644 index 5789d13c..00000000 --- a/src/NuGet.Server/DataServices/PackageIdComparisonVisitor.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq.Expressions; - -namespace NuGet.Server.DataServices -{ - /// - /// This class is used to replace expression - /// element.Id == "packageId" - /// with - /// string.Equals(element.Id, "packageId", StringComparison.OrdinalIgnoreCase) - /// so that package id comparison is case insensitive. - /// - public class PackageIdComparisonVisitor : ExpressionVisitor - { - protected override Expression VisitBinary(BinaryExpression node) - { - if (node.NodeType == ExpressionType.Equal && - node.Left.ToString() == "element.Id") - { - try - { - var stringEqualsMethod = typeof(string).GetMethod( - "Equals", - new[] { typeof(string), typeof(string), typeof(StringComparison) }); - var newExpression = Expression.Call( - stringEqualsMethod, - node.Left, - node.Right, - Expression.Constant(StringComparison.OrdinalIgnoreCase)); - return newExpression; - } - catch - { - return base.VisitBinary(node); - } - } - - return base.VisitBinary(node); - } - } -} \ No newline at end of file diff --git a/src/NuGet.Server/DataServices/Packages.svc.cs b/src/NuGet.Server/DataServices/Packages.svc.cs index f1dfa563..4477ef5f 100644 --- a/src/NuGet.Server/DataServices/Packages.svc.cs +++ b/src/NuGet.Server/DataServices/Packages.svc.cs @@ -119,7 +119,8 @@ public IQueryable Search(string searchTerm, string targetFramework return Repository .Search(searchTerm, targetFrameworks, includePrerelease) .Select(package => package.AsODataPackage()) - .AsQueryable(); + .AsQueryable() + .InterceptWith(new NormalizeVersionInterceptor()); } [WebGet] @@ -129,7 +130,8 @@ public IQueryable FindPackagesById(string id) .FindPackagesById(id) .Where(package => package.Listed) .Select(package => package.AsODataPackage()) - .AsQueryable(); + .AsQueryable() + .InterceptWith(new NormalizeVersionInterceptor()); } [WebGet] @@ -173,7 +175,8 @@ public IQueryable GetUpdates( return Repository .GetUpdatesCore(packagesToUpdate, includePrerelease, includeAllVersions, targetFrameworkValues, versionConstraintsList) .Select(package => package.AsODataPackage()) - .AsQueryable(); + .AsQueryable() + .InterceptWith(new NormalizeVersionInterceptor()); } } } diff --git a/src/NuGet.Server/NuGet.Server.csproj b/src/NuGet.Server/NuGet.Server.csproj index 3dfa3e59..fc8f915d 100644 --- a/src/NuGet.Server/NuGet.Server.csproj +++ b/src/NuGet.Server/NuGet.Server.csproj @@ -80,7 +80,7 @@ - +