From f101507994e8514b6b355794c89c4d2894adbe12 Mon Sep 17 00:00:00 2001 From: Gary Ewan Park Date: Mon, 13 Mar 2023 11:34:12 +0000 Subject: [PATCH] (#9) Alter filter construction for prereleases When the prerelease option is included in the outgoing query, a decision is made about including either IsLatestVersion is IsAbsoluteLatestVersion in the query. However, in the case where preelease is true and the feed being queried doesn't support IsAbsoluteLatestVersion, the IsLatestVersion is included in the query. This is deemed as wrong, since it is the exact opposite of what is being asked for. This commit addresses this issue specifically by only including IsLatestVersion when explicitly not asking for prerelease. We believe that this will prevent confusion when folks are using Chocolatey. In addition, when doing the PackageAsync call, when we can't use IsLatestVersion or IsAbsoluteLatestVersion, defer to using a new helper method FindPackageByIdAsync which was introduced in another scope of work, to selectively search for a package given the searchTerm then order by version, and return the highest. This is required, since the query can potentially return more than more result, which is not desired. In order to use this method, it was necessary to further extend the IV2FeedParser to include the method definition. --- .../LegacyFeed/IV2FeedParser.cs | 8 ++ .../LegacyFeed/V2FeedListResource.cs | 110 ++++++++++++++++-- .../PublicAPI/net472/PublicAPI.Unshipped.txt | 1 + .../netcoreapp5.0/PublicAPI.Unshipped.txt | 1 + .../netstandard2.0/PublicAPI.Unshipped.txt | 1 + 5 files changed, 109 insertions(+), 12 deletions(-) diff --git a/src/NuGet.Core/NuGet.Protocol/LegacyFeed/IV2FeedParser.cs b/src/NuGet.Core/NuGet.Protocol/LegacyFeed/IV2FeedParser.cs index e44e52021bf..ac9fdbc210d 100644 --- a/src/NuGet.Core/NuGet.Protocol/LegacyFeed/IV2FeedParser.cs +++ b/src/NuGet.Core/NuGet.Protocol/LegacyFeed/IV2FeedParser.cs @@ -39,6 +39,14 @@ Task GetSearchPageAsync( // Start - Chocolatey Specific Modification ////////////////////////////////////////////////////////// + Task> FindPackagesByIdAsync( + string id, + bool includeUnlisted, + bool includePrerelease, + SourceCacheContext sourceCacheContext, + ILogger log, + CancellationToken token); + Task> GetPackageVersionsAsync( string id, bool includeUnlisted, diff --git a/src/NuGet.Core/NuGet.Protocol/LegacyFeed/V2FeedListResource.cs b/src/NuGet.Core/NuGet.Protocol/LegacyFeed/V2FeedListResource.cs index 62ab56c37f3..b34d901f844 100644 --- a/src/NuGet.Core/NuGet.Protocol/LegacyFeed/V2FeedListResource.cs +++ b/src/NuGet.Core/NuGet.Protocol/LegacyFeed/V2FeedListResource.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using NuGet.Common; +using NuGet.Packaging; using NuGet.Protocol; using NuGet.Protocol.Core.Types; @@ -52,13 +53,33 @@ public async override Task> ListAsync( { var supportsIsAbsoluteLatestVersion = await _feedCapabilities.SupportsIsAbsoluteLatestVersionAsync(logger, token); - if (prerelease && supportsIsAbsoluteLatestVersion) + + ////////////////////////////////////////////////////////// + // Start - Chocolatey Specific Modification + ////////////////////////////////////////////////////////// + + if (prerelease) { - filter = new SearchFilter(includePrerelease: true, filter: SearchFilterType.IsAbsoluteLatestVersion) + if (supportsIsAbsoluteLatestVersion) { - OrderBy = SearchOrderBy.Id, - IncludeDelisted = includeDelisted - }; + filter = new SearchFilter(includePrerelease: true, filter: SearchFilterType.IsAbsoluteLatestVersion) + { + OrderBy = SearchOrderBy.Id, + IncludeDelisted = includeDelisted + }; + } + else + { + filter = new SearchFilter(includePrerelease: true, filter: null) + { + OrderBy = SearchOrderBy.Version, + IncludeDelisted = includeDelisted + }; + } + + ////////////////////////////////////////////////////////// + // End - Chocolatey Specific Modification + ////////////////////////////////////////////////////////// } else { @@ -85,14 +106,33 @@ public async override Task> ListAsync( { var supportsIsAbsoluteLatestVersion = await _feedCapabilities.SupportsIsAbsoluteLatestVersionAsync(logger, token); - if (prerelease && supportsIsAbsoluteLatestVersion) + + ////////////////////////////////////////////////////////// + // Start - Chocolatey Specific Modification + ////////////////////////////////////////////////////////// + + if (prerelease) { - filter = new SearchFilter(includePrerelease: true, - filter: SearchFilterType.IsAbsoluteLatestVersion) + if (supportsIsAbsoluteLatestVersion) { - IncludeDelisted = includeDelisted, - OrderBy = SearchOrderBy.Id - }; + filter = new SearchFilter(includePrerelease: true, filter: SearchFilterType.IsAbsoluteLatestVersion) + { + IncludeDelisted = includeDelisted, + OrderBy = SearchOrderBy.Id + }; + } + else + { + filter = new SearchFilter(includePrerelease: true, null) + { + IncludeDelisted = includeDelisted, + OrderBy = SearchOrderBy.Version + }; + } + + ////////////////////////////////////////////////////////// + // End - Chocolatey Specific Modification + ////////////////////////////////////////////////////////// } else { @@ -121,7 +161,41 @@ public async override Task PackageAsync( CancellationToken token) { var metadataCache = new MetadataReferenceCache(); - var searchFilter = new SearchFilter(prerelease); + + var supportsIsAbsoluteLatestVersion = await _feedCapabilities.SupportsIsAbsoluteLatestVersionAsync(logger, token); + SearchFilter searchFilter = null; + + if (prerelease) + { + if (supportsIsAbsoluteLatestVersion) + { + searchFilter = new SearchFilter(includePrerelease: true, filter: SearchFilterType.IsAbsoluteLatestVersion); + } + else + { + // In this scenario, we can't do a IsLatestVersion or IsAbsoluteVersion query, which means that any query we + // do will potentially result in more than one result, which is not desired. + // Instead, use the FindPackageByIdAsync method, and then order the results by version, and return the + // top result. + var packages = await FindPackageByIdAsync(searchTerm, includeUnlisted: false, prerelease, sourceCacheContext: null, logger, token); + + searchFilter = new SearchFilter(includePrerelease: true, filter: null); + searchFilter.OrderBy = SearchOrderBy.Version; + + if (packages.Count == 0) + { + return null; + } + + var highestVersionPackage = packages.OrderByDescending(p => p.Version).FirstOrDefault(); + return V2FeedUtilities.CreatePackageSearchResult(highestVersionPackage, metadataCache, searchFilter, (V2FeedParser)_feedParser, logger, token); + } + } + else + { + searchFilter = new SearchFilter(includePrerelease: false, filter: SearchFilterType.IsLatestVersion); + } + searchFilter.ExactPackageId = true; var pageOfResults = await _feedParser.GetPackagesPageAsync(searchTerm, searchFilter, 0, 1, logger, token); @@ -134,6 +208,18 @@ public async override Task PackageAsync( return V2FeedUtilities.CreatePackageSearchResult(pageOfResults.Items[0], metadataCache, searchFilter, (V2FeedParser)_feedParser, logger, token); } + private async Task> FindPackageByIdAsync(string packageId, bool includeUnlisted, bool includePrerelease, SourceCacheContext sourceCacheContext, Common.ILogger log, CancellationToken token) + { + if (await _feedCapabilities.SupportsFindPackagesByIdAsync(log, token)) + { + return await _feedParser.FindPackagesByIdAsync(packageId, includeUnlisted, includePrerelease, sourceCacheContext, log, token); + } + else + { + return await _feedParser.GetPackageVersionsAsync(packageId, includeUnlisted, includePrerelease, sourceCacheContext, log, token); + } + } + ////////////////////////////////////////////////////////// // End - Chocolatey Specific Modification ////////////////////////////////////////////////////////// diff --git a/src/NuGet.Core/NuGet.Protocol/PublicAPI/net472/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Protocol/PublicAPI/net472/PublicAPI.Unshipped.txt index 55268e199c0..541fb1e9099 100644 --- a/src/NuGet.Core/NuGet.Protocol/PublicAPI/net472/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Protocol/PublicAPI/net472/PublicAPI.Unshipped.txt @@ -146,6 +146,7 @@ NuGet.Protocol.IHttpSource.ProcessStreamAsync(NuGet.Protocol.HttpSourceReques NuGet.Protocol.IHttpSource.ProcessStreamAsync(NuGet.Protocol.HttpSourceRequest request, System.Func> processAsync, NuGet.Protocol.Core.Types.SourceCacheContext cacheContext, NuGet.Common.ILogger log, System.Threading.CancellationToken token) -> System.Threading.Tasks.Task NuGet.Protocol.IHttpSource.RetryHandler.get -> NuGet.Protocol.IHttpRetryHandler NuGet.Protocol.IHttpSource.RetryHandler.set -> void +NuGet.Protocol.IV2FeedParser.FindPackagesByIdAsync(string id, bool includeUnlisted, bool includePrerelease, NuGet.Protocol.Core.Types.SourceCacheContext sourceCacheContext, NuGet.Common.ILogger log, System.Threading.CancellationToken token) -> System.Threading.Tasks.Task> NuGet.Protocol.IV2FeedParser.GetPackageVersionsAsync(string id, bool includeUnlisted, bool includePreRelease, NuGet.Protocol.Core.Types.SourceCacheContext sourceCacheContext, NuGet.Common.ILogger log, System.Threading.CancellationToken token) -> System.Threading.Tasks.Task> NuGet.Protocol.LocalPackageSearchMetadata.BugTrackerUrl.get -> System.Uri NuGet.Protocol.LocalPackageSearchMetadata.DocsUrl.get -> System.Uri diff --git a/src/NuGet.Core/NuGet.Protocol/PublicAPI/netcoreapp5.0/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Protocol/PublicAPI/netcoreapp5.0/PublicAPI.Unshipped.txt index 55268e199c0..541fb1e9099 100644 --- a/src/NuGet.Core/NuGet.Protocol/PublicAPI/netcoreapp5.0/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Protocol/PublicAPI/netcoreapp5.0/PublicAPI.Unshipped.txt @@ -146,6 +146,7 @@ NuGet.Protocol.IHttpSource.ProcessStreamAsync(NuGet.Protocol.HttpSourceReques NuGet.Protocol.IHttpSource.ProcessStreamAsync(NuGet.Protocol.HttpSourceRequest request, System.Func> processAsync, NuGet.Protocol.Core.Types.SourceCacheContext cacheContext, NuGet.Common.ILogger log, System.Threading.CancellationToken token) -> System.Threading.Tasks.Task NuGet.Protocol.IHttpSource.RetryHandler.get -> NuGet.Protocol.IHttpRetryHandler NuGet.Protocol.IHttpSource.RetryHandler.set -> void +NuGet.Protocol.IV2FeedParser.FindPackagesByIdAsync(string id, bool includeUnlisted, bool includePrerelease, NuGet.Protocol.Core.Types.SourceCacheContext sourceCacheContext, NuGet.Common.ILogger log, System.Threading.CancellationToken token) -> System.Threading.Tasks.Task> NuGet.Protocol.IV2FeedParser.GetPackageVersionsAsync(string id, bool includeUnlisted, bool includePreRelease, NuGet.Protocol.Core.Types.SourceCacheContext sourceCacheContext, NuGet.Common.ILogger log, System.Threading.CancellationToken token) -> System.Threading.Tasks.Task> NuGet.Protocol.LocalPackageSearchMetadata.BugTrackerUrl.get -> System.Uri NuGet.Protocol.LocalPackageSearchMetadata.DocsUrl.get -> System.Uri diff --git a/src/NuGet.Core/NuGet.Protocol/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Protocol/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt index fa203e6dcac..9dd96c70cc8 100644 --- a/src/NuGet.Core/NuGet.Protocol/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Protocol/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt @@ -145,6 +145,7 @@ NuGet.Protocol.IHttpSource.ProcessStreamAsync(NuGet.Protocol.HttpSourceReques NuGet.Protocol.IHttpSource.ProcessStreamAsync(NuGet.Protocol.HttpSourceRequest request, System.Func> processAsync, NuGet.Protocol.Core.Types.SourceCacheContext cacheContext, NuGet.Common.ILogger log, System.Threading.CancellationToken token) -> System.Threading.Tasks.Task NuGet.Protocol.IHttpSource.RetryHandler.get -> NuGet.Protocol.IHttpRetryHandler NuGet.Protocol.IHttpSource.RetryHandler.set -> void +NuGet.Protocol.IV2FeedParser.FindPackagesByIdAsync(string id, bool includeUnlisted, bool includePrerelease, NuGet.Protocol.Core.Types.SourceCacheContext sourceCacheContext, NuGet.Common.ILogger log, System.Threading.CancellationToken token) -> System.Threading.Tasks.Task> NuGet.Protocol.IV2FeedParser.GetPackageVersionsAsync(string id, bool includeUnlisted, bool includePreRelease, NuGet.Protocol.Core.Types.SourceCacheContext sourceCacheContext, NuGet.Common.ILogger log, System.Threading.CancellationToken token) -> System.Threading.Tasks.Task> NuGet.Protocol.LocalPackageSearchMetadata.BugTrackerUrl.get -> System.Uri NuGet.Protocol.LocalPackageSearchMetadata.DocsUrl.get -> System.Uri