diff --git a/src/NuGet.Services.Search.Client/Client/SearchClient.cs b/src/NuGet.Services.Search.Client/Client/SearchClient.cs index 178fba219a..1f5f9ffaf0 100644 --- a/src/NuGet.Services.Search.Client/Client/SearchClient.cs +++ b/src/NuGet.Services.Search.Client/Client/SearchClient.cs @@ -83,7 +83,8 @@ public async Task> Search( bool countOnly = false, bool explain = false, bool getAllVersions = false, - string supportedFramework = null) + string supportedFramework = null, + string semVerLevel = null) { IDictionary nameValue = new Dictionary(); nameValue.Add("q", query); @@ -91,6 +92,11 @@ public async Task> Search( nameValue.Add("take", take.ToString()); nameValue.Add("sortBy", SortNames[sortBy]); + if (!String.IsNullOrEmpty(semVerLevel)) + { + nameValue.Add("semVerLevel", semVerLevel); + } + if (!String.IsNullOrEmpty(supportedFramework)) { nameValue.Add("supportedFramework", supportedFramework); diff --git a/src/NuGetGallery.Core/Entities/Package.cs b/src/NuGetGallery.Core/Entities/Package.cs index e5ca80387b..1e11f720e4 100644 --- a/src/NuGetGallery.Core/Entities/Package.cs +++ b/src/NuGetGallery.Core/Entities/Package.cs @@ -80,6 +80,9 @@ public Package() public bool IsLatest { get; set; } public bool IsLatestStable { get; set; } + public bool IsLatestSemVer2 { get; set; } + public bool IsLatestStableSemVer2 { get; set; } + /// /// This is when the Package Entity was last touched (so caches can notice changes). In UTC. /// diff --git a/src/NuGetGallery.Core/NuGetGallery.Core.csproj b/src/NuGetGallery.Core/NuGetGallery.Core.csproj index dcb31cd7b1..bdfcc35ec1 100644 --- a/src/NuGetGallery.Core/NuGetGallery.Core.csproj +++ b/src/NuGetGallery.Core/NuGetGallery.Core.csproj @@ -85,19 +85,19 @@ ..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll - ..\..\packages\NuGet.Common.4.3.0-preview1-2507\lib\net45\NuGet.Common.dll + ..\..\packages\NuGet.Common.4.3.0-preview1-2524\lib\net45\NuGet.Common.dll - ..\..\packages\NuGet.Frameworks.4.3.0-preview1-2507\lib\net45\NuGet.Frameworks.dll + ..\..\packages\NuGet.Frameworks.4.3.0-preview1-2524\lib\net45\NuGet.Frameworks.dll - ..\..\packages\NuGet.Packaging.4.3.0-preview1-2507\lib\net45\NuGet.Packaging.dll + ..\..\packages\NuGet.Packaging.4.3.0-preview1-2524\lib\net45\NuGet.Packaging.dll - ..\..\packages\NuGet.Packaging.Core.4.3.0-preview1-2507\lib\net45\NuGet.Packaging.Core.dll + ..\..\packages\NuGet.Packaging.Core.4.3.0-preview1-2524\lib\net45\NuGet.Packaging.Core.dll - ..\..\packages\NuGet.Versioning.4.3.0-preview1-2507\lib\net45\NuGet.Versioning.dll + ..\..\packages\NuGet.Versioning.4.3.0-preview1-2524\lib\net45\NuGet.Versioning.dll diff --git a/src/NuGetGallery.Core/NuGetVersionExtensions.cs b/src/NuGetGallery.Core/NuGetVersionExtensions.cs index 5818f1d56c..a32f3b18d2 100644 --- a/src/NuGetGallery.Core/NuGetVersionExtensions.cs +++ b/src/NuGetGallery.Core/NuGetVersionExtensions.cs @@ -28,7 +28,12 @@ public static class NuGetVersionExtensions public static string ToNormalizedStringSafe(this NuGetVersion self) { - return self != null ? self.ToNormalizedString() : String.Empty; + return self != null ? self.ToNormalizedString() : string.Empty; + } + + public static string ToFullStringSafe(this NuGetVersion self) + { + return self != null ? self.ToFullString() : string.Empty; } public static bool IsValidVersionForLegacyClients(this NuGetVersion self) diff --git a/src/NuGetGallery.Core/SemVerLevelKey.cs b/src/NuGetGallery.Core/SemVerLevelKey.cs index c57f7b2707..8ba43aadf2 100644 --- a/src/NuGetGallery.Core/SemVerLevelKey.cs +++ b/src/NuGetGallery.Core/SemVerLevelKey.cs @@ -16,7 +16,8 @@ namespace NuGetGallery /// public static class SemVerLevelKey { - private static readonly NuGetVersion _semVer2Version = NuGetVersion.Parse("2.0.0"); + public static readonly string SemVerLevel2 = "2.0.0"; + private static readonly NuGetVersion _semVer2Version = NuGetVersion.Parse(SemVerLevel2); /// /// This could either indicate being SemVer1-compliant, or non-SemVer-compliant at all (e.g. System.Versioning pattern). diff --git a/src/NuGetGallery.Core/packages.config b/src/NuGetGallery.Core/packages.config index 560cd1ddf8..6a778ed237 100644 --- a/src/NuGetGallery.Core/packages.config +++ b/src/NuGetGallery.Core/packages.config @@ -9,11 +9,11 @@ - - - - - + + + + + \ No newline at end of file diff --git a/src/NuGetGallery/Areas/Admin/Controllers/DeleteController.cs b/src/NuGetGallery/Areas/Admin/Controllers/DeleteController.cs index b98594ba9b..50c7fa07c3 100644 --- a/src/NuGetGallery/Areas/Admin/Controllers/DeleteController.cs +++ b/src/NuGetGallery/Areas/Admin/Controllers/DeleteController.cs @@ -63,7 +63,7 @@ public virtual ActionResult Search(string query) } else if (splitQueryPart.Length == 2) { - var resultingPackage = _packageService.FindPackageByIdAndVersion(splitQueryPart[0].Trim(), splitQueryPart[1].Trim(), true); + var resultingPackage = _packageService.FindPackageByIdAndVersionStrict(splitQueryPart[0].Trim(), splitQueryPart[1].Trim()); if (resultingPackage != null) { results.Add(CreateDeleteSearchResult(resultingPackage)); diff --git a/src/NuGetGallery/Controllers/ApiController.cs b/src/NuGetGallery/Controllers/ApiController.cs index e510cb297b..21cbfff93e 100644 --- a/src/NuGetGallery/Controllers/ApiController.cs +++ b/src/NuGetGallery/Controllers/ApiController.cs @@ -140,11 +140,16 @@ public virtual async Task GetPackage(string id, string version) } else { - // if version is null, get the latest version from the database. + // If version is null, get the latest version from the database. // This ensures that on package restore scenario where version will be non null, we don't hit the database. try { - var package = PackageService.FindPackageByIdAndVersion(id, version, allowPrerelease: false); + var package = PackageService.FindPackageByIdAndVersion( + id, + version, + SemVerLevelKey.SemVer2, + allowPrerelease: false); + if (package == null) { return new HttpStatusCodeWithBodyResult(HttpStatusCode.NotFound, String.Format(CultureInfo.CurrentCulture, Strings.PackageWithIdAndVersionNotFound, id, version)); @@ -257,7 +262,7 @@ public async virtual Task VerifyPackageKeyAsync(string id, string private HttpStatusCodeWithBodyResult VerifyPackageKeyInternal(User user, Credential credential, string id, string version) { // Verify that the user has permission to push for the specific Id \ version combination. - var package = PackageService.FindPackageByIdAndVersion(id, version); + var package = PackageService.FindPackageByIdAndVersionStrict(id, version); if (package == null) { return new HttpStatusCodeWithBodyResult( @@ -530,7 +535,7 @@ private static ActionResult BadRequestForExceptionMessage(Exception ex) [ActionName("DeletePackageApi")] public virtual async Task DeletePackage(string id, string version) { - var package = PackageService.FindPackageByIdAndVersion(id, version); + var package = PackageService.FindPackageByIdAndVersionStrict(id, version); if (package == null) { return new HttpStatusCodeWithBodyResult( @@ -563,7 +568,7 @@ public virtual async Task DeletePackage(string id, string version) [ActionName("PublishPackageApi")] public virtual async Task PublishPackage(string id, string version) { - var package = PackageService.FindPackageByIdAndVersion(id, version); + var package = PackageService.FindPackageByIdAndVersionStrict(id, version); if (package == null) { return new HttpStatusCodeWithBodyResult( diff --git a/src/NuGetGallery/Controllers/CuratedFeedsController.cs b/src/NuGetGallery/Controllers/CuratedFeedsController.cs index 07d469df51..d7654caccf 100644 --- a/src/NuGetGallery/Controllers/CuratedFeedsController.cs +++ b/src/NuGetGallery/Controllers/CuratedFeedsController.cs @@ -62,9 +62,15 @@ public virtual async Task ListPackages(string curatedFeedName, str page = 1; } - q = (q ?? "").Trim(); + q = (q ?? string.Empty).Trim(); + + var searchFilter = SearchAdaptor.GetSearchFilter( + q, + page, + sortOrder: null, + context: SearchFilter.UISearchContext, + semVerLevel: SemVerLevelKey.SemVerLevel2); - var searchFilter = SearchAdaptor.GetSearchFilter(q, page, sortOrder: null, context: SearchFilter.UISearchContext); searchFilter.CuratedFeed = CuratedFeedService.GetFeedByName(curatedFeedName, includePackages: false); if (searchFilter.CuratedFeed == null) { @@ -91,7 +97,7 @@ public virtual async Task ListPackages(string curatedFeedName, str ViewBag.SearchTerm = q; - return View("ListPackages", viewModel); + return View("ListPackages", viewModel); } } } diff --git a/src/NuGetGallery/Controllers/ODataV1FeedController.cs b/src/NuGetGallery/Controllers/ODataV1FeedController.cs index 0fe79dd513..8fba345d97 100644 --- a/src/NuGetGallery/Controllers/ODataV1FeedController.cs +++ b/src/NuGetGallery/Controllers/ODataV1FeedController.cs @@ -101,7 +101,13 @@ private async Task GetCore(ODataQueryOptions o try { var searchAdaptorResult = await SearchAdaptor.FindByIdAndVersionCore( - _searchService, GetTraditionalHttpContext().Request, packages, id, version, curatedFeed: null); + _searchService, + GetTraditionalHttpContext().Request, + packages, + id, + version, + curatedFeed: null, + semVerLevel: null); // If intercepted, create a paged queryresult if (searchAdaptorResult.ResultsAreProvidedBySearchService) @@ -190,7 +196,14 @@ public async Task Search( // todo: search hijack should take queryOptions instead of manually parsing query options var searchAdaptorResult = await SearchAdaptor.SearchCore( - _searchService, GetTraditionalHttpContext().Request, packages, searchTerm, targetFramework, false, curatedFeed: null); + _searchService, + GetTraditionalHttpContext().Request, + packages, + searchTerm, + targetFramework, + false, + curatedFeed: null, + semVerLevel: null); // Packages provided by search service (even when not hijacked) var query = searchAdaptorResult.Packages; diff --git a/src/NuGetGallery/Controllers/ODataV2CuratedFeedController.cs b/src/NuGetGallery/Controllers/ODataV2CuratedFeedController.cs index 606f6b8bba..aa26664fd0 100644 --- a/src/NuGetGallery/Controllers/ODataV2CuratedFeedController.cs +++ b/src/NuGetGallery/Controllers/ODataV2CuratedFeedController.cs @@ -54,9 +54,14 @@ public IHttpActionResult Get( return NotFound(); } + var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel); + var queryable = _curatedFeedService.GetPackages(curatedFeedName) .Where(SemVerLevelKey.IsPackageCompliantWithSemVerLevel(semVerLevel)) - .ToV2FeedPackageQuery(_configurationService.GetSiteRoot(UseHttps()), _configurationService.Features.FriendlyLicenses) + .ToV2FeedPackageQuery( + _configurationService.GetSiteRoot(UseHttps()), + _configurationService.Features.FriendlyLicenses, + semVerLevelKey) .InterceptWith(new NormalizeVersionInterceptor()); return QueryResult(options, queryable, MaxPageSize); @@ -94,8 +99,10 @@ public async Task FindPackagesById( { if (string.IsNullOrEmpty(curatedFeedName) || string.IsNullOrEmpty(id)) { + var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel); + var emptyResult = Enumerable.Empty().AsQueryable() - .ToV2FeedPackageQuery(GetSiteRoot(), _configurationService.Features.FriendlyLicenses); + .ToV2FeedPackageQuery(GetSiteRoot(), _configurationService.Features.FriendlyLicenses, semVerLevelKey); return QueryResult(options, emptyResult, MaxPageSize); } @@ -126,11 +133,19 @@ private async Task GetCore( packages = packages.Where(p => p.Version == version); } + var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel); + // try the search service try { var searchAdaptorResult = await SearchAdaptor.FindByIdAndVersionCore( - _searchService, GetTraditionalHttpContext().Request, packages, id, version, curatedFeed: curatedFeed); + _searchService, + GetTraditionalHttpContext().Request, + packages, + id, + version, + curatedFeed: curatedFeed, + semVerLevel: semVerLevel); // If intercepted, create a paged queryresult if (searchAdaptorResult.ResultsAreProvidedBySearchService) @@ -148,7 +163,7 @@ private async Task GetCore( var pagedQueryable = packages .Take(options.Top != null ? Math.Min(options.Top.Value, MaxPageSize) : MaxPageSize) - .ToV2FeedPackageQuery(GetSiteRoot(), _configurationService.Features.FriendlyLicenses); + .ToV2FeedPackageQuery(GetSiteRoot(), _configurationService.Features.FriendlyLicenses, semVerLevelKey); return QueryResult(options, pagedQueryable, MaxPageSize, totalHits, (o, s, resultCount) => SearchAdaptor.GetNextLink(Request.RequestUri, resultCount, new { id }, o, s)); @@ -166,7 +181,11 @@ private async Task GetCore( return NotFound(); } - var queryable = packages.ToV2FeedPackageQuery(GetSiteRoot(), _configurationService.Features.FriendlyLicenses); + var queryable = packages.ToV2FeedPackageQuery( + GetSiteRoot(), + _configurationService.Features.FriendlyLicenses, + semVerLevelKey); + return QueryResult(options, queryable, MaxPageSize); } @@ -225,11 +244,20 @@ public async Task Search( // todo: search hijack should take queryOptions instead of manually parsing query options var searchAdaptorResult = await SearchAdaptor.SearchCore( - _searchService, GetTraditionalHttpContext().Request, packages, searchTerm, targetFramework, includePrerelease, curatedFeed: curatedFeed); + _searchService, + GetTraditionalHttpContext().Request, + packages, + searchTerm, + targetFramework, + includePrerelease, + curatedFeed: curatedFeed, + semVerLevel: semVerLevel); // Packages provided by search service (even when not hijacked) var query = searchAdaptorResult.Packages; + var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel); + // If intercepted, create a paged queryresult if (searchAdaptorResult.ResultsAreProvidedBySearchService) { @@ -237,7 +265,10 @@ public async Task Search( var totalHits = query.LongCount(); var pagedQueryable = query .Take(options.Top != null ? Math.Min(options.Top.Value, MaxPageSize) : MaxPageSize) - .ToV2FeedPackageQuery(GetSiteRoot(), _configurationService.Features.FriendlyLicenses); + .ToV2FeedPackageQuery( + GetSiteRoot(), + _configurationService.Features.FriendlyLicenses, + semVerLevelKey); return QueryResult(options, pagedQueryable, MaxPageSize, totalHits, (o, s, resultCount) => { @@ -245,14 +276,23 @@ public async Task Search( // Strip it of for backward compatibility. if (o.Top == null || (resultCount.HasValue && o.Top.Value >= resultCount.Value)) { - return SearchAdaptor.GetNextLink(Request.RequestUri, resultCount, new { searchTerm, targetFramework, includePrerelease }, o, s); + return SearchAdaptor.GetNextLink( + Request.RequestUri, + resultCount, + new { searchTerm, targetFramework, includePrerelease }, + o, + s); } return null; }); } // If not, just let OData handle things - var queryable = query.ToV2FeedPackageQuery(GetSiteRoot(), _configurationService.Features.FriendlyLicenses); + var queryable = query.ToV2FeedPackageQuery( + GetSiteRoot(), + _configurationService.Features.FriendlyLicenses, + semVerLevelKey); + return QueryResult(options, queryable, MaxPageSize); } diff --git a/src/NuGetGallery/Controllers/ODataV2FeedController.cs b/src/NuGetGallery/Controllers/ODataV2FeedController.cs index b6fefd5523..39cdc679cc 100644 --- a/src/NuGetGallery/Controllers/ODataV2FeedController.cs +++ b/src/NuGetGallery/Controllers/ODataV2FeedController.cs @@ -59,6 +59,8 @@ public async Task Get( .WithoutSortOnColumn(Id, ShouldIgnoreOrderById(options)) .InterceptWith(new NormalizeVersionInterceptor()); + var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel); + // Try the search service try { @@ -66,8 +68,13 @@ public async Task Get( if (_searchService is ExternalSearchService && SearchHijacker.IsHijackable(options, out hijackableQueryParameters)) { var searchAdaptorResult = await SearchAdaptor.FindByIdAndVersionCore( - _searchService, GetTraditionalHttpContext().Request, packages, - hijackableQueryParameters.Id, hijackableQueryParameters.Version, curatedFeed: null); + _searchService, + GetTraditionalHttpContext().Request, + packages, + hijackableQueryParameters.Id, + hijackableQueryParameters.Version, + curatedFeed: null, + semVerLevel: semVerLevel); // If intercepted, create a paged queryresult if (searchAdaptorResult.ResultsAreProvidedBySearchService) @@ -79,7 +86,10 @@ public async Task Get( var totalHits = packages.LongCount(); var pagedQueryable = packages .Take(options.Top != null ? Math.Min(options.Top.Value, MaxPageSize) : MaxPageSize) - .ToV2FeedPackageQuery(GetSiteRoot(), _configurationService.Features.FriendlyLicenses); + .ToV2FeedPackageQuery( + GetSiteRoot(), + _configurationService.Features.FriendlyLicenses, + semVerLevelKey); return QueryResult(options, pagedQueryable, MaxPageSize, totalHits, (o, s, resultCount) => SearchAdaptor.GetNextLink(Request.RequestUri, resultCount, null, o, s)); @@ -100,7 +110,11 @@ public async Task Get( return BadRequest(ODataQueryVerifier.GetValidationFailedMessage(options)); } - var queryable = packages.ToV2FeedPackageQuery(GetSiteRoot(), _configurationService.Features.FriendlyLicenses); + var queryable = packages.ToV2FeedPackageQuery( + GetSiteRoot(), + _configurationService.Features.FriendlyLicenses, + semVerLevelKey); + return QueryResult(options, queryable, MaxPageSize); } @@ -125,7 +139,13 @@ public async Task Get( // We are defaulting to semVerLevel = "2.0.0" by design. // The client is requesting a specific package version and should support what it requests. // If not, too bad :) - var result = await GetCore(options, id, version, semVerLevel: "2.0.0", return404NotFoundWhenNoResults: true); + var result = await GetCore( + options, + id, + version, + semVerLevel: SemVerLevelKey.SemVerLevel2, + return404NotFoundWhenNoResults: true); + return result.FormattedAsSingleResult(); } @@ -140,13 +160,23 @@ public async Task FindPackagesById( { if (string.IsNullOrEmpty(id)) { + var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel); + var emptyResult = Enumerable.Empty().AsQueryable() - .ToV2FeedPackageQuery(GetSiteRoot(), _configurationService.Features.FriendlyLicenses); + .ToV2FeedPackageQuery( + GetSiteRoot(), + _configurationService.Features.FriendlyLicenses, + semVerLevelKey); return QueryResult(options, emptyResult, MaxPageSize); } - return await GetCore(options, id, version: null, semVerLevel: semVerLevel, return404NotFoundWhenNoResults: false); + return await GetCore( + options, + id, + version: null, + semVerLevel: semVerLevel, + return404NotFoundWhenNoResults: false); } private async Task GetCore( @@ -174,11 +204,19 @@ private async Task GetCore( } } + var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel); + // try the search service try { var searchAdaptorResult = await SearchAdaptor.FindByIdAndVersionCore( - _searchService, GetTraditionalHttpContext().Request, packages, id, version, curatedFeed: null); + _searchService, + GetTraditionalHttpContext().Request, + packages, + id, + version, + curatedFeed: null, + semVerLevel: semVerLevel); // If intercepted, create a paged queryresult if (searchAdaptorResult.ResultsAreProvidedBySearchService) @@ -196,7 +234,10 @@ private async Task GetCore( var pagedQueryable = packages .Take(options.Top != null ? Math.Min(options.Top.Value, MaxPageSize) : MaxPageSize) - .ToV2FeedPackageQuery(GetSiteRoot(), _configurationService.Features.FriendlyLicenses); + .ToV2FeedPackageQuery( + GetSiteRoot(), + _configurationService.Features.FriendlyLicenses, + semVerLevelKey); return QueryResult(options, pagedQueryable, MaxPageSize, totalHits, (o, s, resultCount) => SearchAdaptor.GetNextLink(Request.RequestUri, resultCount, new { id }, o, s)); @@ -214,7 +255,11 @@ private async Task GetCore( return NotFound(); } - var queryable = packages.ToV2FeedPackageQuery(GetSiteRoot(), _configurationService.Features.FriendlyLicenses); + var queryable = packages.ToV2FeedPackageQuery( + GetSiteRoot(), + _configurationService.Features.FriendlyLicenses, + semVerLevelKey); + return QueryResult(options, queryable, MaxPageSize); } @@ -270,11 +315,20 @@ public async Task Search( // todo: search hijack should take options instead of manually parsing query options var searchAdaptorResult = await SearchAdaptor.SearchCore( - _searchService, GetTraditionalHttpContext().Request, packages, searchTerm, targetFramework, includePrerelease, curatedFeed: null); + _searchService, + GetTraditionalHttpContext().Request, + packages, + searchTerm, + targetFramework, + includePrerelease, + curatedFeed: null, + semVerLevel: semVerLevel); // Packages provided by search service (even when not hijacked) var query = searchAdaptorResult.Packages; + var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel); + // If intercepted, create a paged queryresult if (searchAdaptorResult.ResultsAreProvidedBySearchService) { @@ -282,7 +336,10 @@ public async Task Search( var totalHits = query.LongCount(); var pagedQueryable = query .Take(options.Top != null ? Math.Min(options.Top.Value, MaxPageSize) : MaxPageSize) - .ToV2FeedPackageQuery(GetSiteRoot(), _configurationService.Features.FriendlyLicenses); + .ToV2FeedPackageQuery( + GetSiteRoot(), + _configurationService.Features.FriendlyLicenses, + semVerLevelKey); return QueryResult(options, pagedQueryable, MaxPageSize, totalHits, (o, s, resultCount) => { @@ -303,7 +360,11 @@ public async Task Search( } // If not, just let OData handle things - var queryable = query.ToV2FeedPackageQuery(GetSiteRoot(), _configurationService.Features.FriendlyLicenses); + var queryable = query.ToV2FeedPackageQuery( + GetSiteRoot(), + _configurationService.Features.FriendlyLicenses, + semVerLevelKey); + return QueryResult(options, queryable, MaxPageSize); } @@ -317,7 +378,12 @@ public async Task SearchCount( [FromODataUri]bool includePrerelease = false, [FromUri]string semVerLevel = null) { - var searchResults = await Search(options, searchTerm, targetFramework, includePrerelease, semVerLevel); + var searchResults = await Search( + options, + searchTerm, + targetFramework, + includePrerelease, + semVerLevel); return searchResults.FormattedAsCountResult(); } @@ -397,9 +463,13 @@ public IHttpActionResult GetUpdates( idValues.Contains(p.PackageRegistration.Id.ToLower())) .OrderBy(p => p.PackageRegistration.Id); + var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel); var queryable = GetUpdates(packages, versionLookup, targetFrameworkValues, includeAllVersions, semVerLevel) .AsQueryable() - .ToV2FeedPackageQuery(GetSiteRoot(), _configurationService.Features.FriendlyLicenses); + .ToV2FeedPackageQuery( + GetSiteRoot(), + _configurationService.Features.FriendlyLicenses, + semVerLevelKey); return QueryResult(options, queryable, MaxPageSize); } @@ -417,7 +487,15 @@ public IHttpActionResult GetUpdatesCount( [FromODataUri]string versionConstraints = "", [FromUri]string semVerLevel = null) { - return GetUpdates(options, packageIds, versions, includePrerelease, includeAllVersions, targetFrameworks, versionConstraints, semVerLevel) + return GetUpdates( + options, + packageIds, + versions, + includePrerelease, + includeAllVersions, + targetFrameworks, + versionConstraints, + semVerLevel) .FormattedAsCountResult(); } diff --git a/src/NuGetGallery/Controllers/PackagesController.cs b/src/NuGetGallery/Controllers/PackagesController.cs index e106dfba6e..1fee906fa1 100644 --- a/src/NuGetGallery/Controllers/PackagesController.cs +++ b/src/NuGetGallery/Controllers/PackagesController.cs @@ -282,7 +282,7 @@ public virtual async Task UploadPackage(HttpPostedFileBase uploadF return View(); } - var package = _packageService.FindPackageByIdAndVersion(nuspec.GetId(), nuspec.GetVersion().ToStringSafe()); + var package = _packageService.FindPackageByIdAndVersionStrict(nuspec.GetId(), nuspec.GetVersion().ToStringSafe()); if (package != null) { ModelState.AddModelError( @@ -310,11 +310,11 @@ public virtual async Task DisplayPackage(string id, string version Package package; if (version != null && version.Equals(Constants.AbsoluteLatestUrlString, StringComparison.InvariantCultureIgnoreCase)) { - package = _packageService.FindAbsoluteLatestPackageById(id); + package = _packageService.FindAbsoluteLatestPackageById(id, SemVerLevelKey.SemVer2); } else { - package = _packageService.FindPackageByIdAndVersion(id, version); + package = _packageService.FindPackageByIdAndVersion(id, version, SemVerLevelKey.SemVer2); } if (package == null) @@ -354,8 +354,11 @@ public virtual async Task DisplayPackage(string id, string version .Normalize(NormalizationForm.FormC); var searchFilter = SearchAdaptor.GetSearchFilter( - "id:\"" + normalizedRegistrationId + "\" AND version:\"" + package.Version + "\"", - 1, null, SearchFilter.ODataSearchContext); + q: "id:\"" + normalizedRegistrationId + "\" AND version:\"" + package.Version + "\"", + page: 1, + sortOrder: null, + context: SearchFilter.ODataSearchContext, + semVerLevel: SemVerLevelKey.SemVerLevel2); searchFilter.IncludePrerelease = true; searchFilter.IncludeAllVersions = true; @@ -415,7 +418,13 @@ public virtual async Task ListPackages(PackageListSearchViewModel var cachedResults = HttpContext.Cache.Get("DefaultSearchResults"); if (cachedResults == null) { - var searchFilter = SearchAdaptor.GetSearchFilter(q, page, null, SearchFilter.UISearchContext); + var searchFilter = SearchAdaptor.GetSearchFilter( + q, + page, + sortOrder: null, + context: SearchFilter.UISearchContext, + semVerLevel: SemVerLevelKey.SemVerLevel2); + results = await _searchService.Search(searchFilter); // note: this is a per instance cache @@ -435,7 +444,13 @@ public virtual async Task ListPackages(PackageListSearchViewModel } else { - var searchFilter = SearchAdaptor.GetSearchFilter(q, page, null, SearchFilter.UISearchContext); + var searchFilter = SearchAdaptor.GetSearchFilter( + q, + page, + sortOrder: null, + context: SearchFilter.UISearchContext, + semVerLevel: SemVerLevelKey.SemVerLevel2); + results = await _searchService.Search(searchFilter); } @@ -472,7 +487,7 @@ public virtual async Task ListPackages(PackageListSearchViewModel [HttpGet] public virtual ActionResult ReportAbuse(string id, string version) { - var package = _packageService.FindPackageByIdAndVersion(id, version); + var package = _packageService.FindPackageByIdAndVersionStrict(id, version); if (package == null) { @@ -521,7 +536,7 @@ public virtual ActionResult ReportMyPackage(string id, string version) { var user = GetCurrentUser(); - var package = _packageService.FindPackageByIdAndVersion(id, version); + var package = _packageService.FindPackageByIdAndVersionStrict(id, version); if (package == null) { @@ -560,7 +575,7 @@ public virtual async Task ReportAbuse(string id, string version, R return ReportAbuse(id, version); } - var package = _packageService.FindPackageByIdAndVersion(id, version); + var package = _packageService.FindPackageByIdAndVersionStrict(id, version); if (package == null) { return HttpNotFound(); @@ -618,7 +633,7 @@ public virtual async Task ReportMyPackage(string id, string versio return ReportMyPackage(id, version); } - var package = _packageService.FindPackageByIdAndVersion(id, version); + var package = _packageService.FindPackageByIdAndVersionStrict(id, version); if (package == null) { return HttpNotFound(); @@ -812,7 +827,7 @@ public virtual async Task Delete(DeletePackagesRequest deletePacka var split = package.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); if (split.Length == 2) { - var packageToDelete = _packageService.FindPackageByIdAndVersion(split[0], split[1], allowPrerelease: true); + var packageToDelete = _packageService.FindPackageByIdAndVersionStrict(split[0], split[1]); if (packageToDelete != null) { packagesToDelete.Add(packageToDelete); @@ -992,7 +1007,7 @@ public virtual async Task ConfirmOwner(string id, string username, internal virtual async Task Edit(string id, string version, bool? listed, Func urlFactory) { - var package = _packageService.FindPackageByIdAndVersion(id, version); + var package = _packageService.FindPackageByIdAndVersionStrict(id, version); if (package == null) { return HttpNotFound(); @@ -1058,7 +1073,8 @@ public virtual async Task VerifyPackage() var model = new VerifyPackageRequest { Id = packageMetadata.Id, - Version = packageMetadata.Version.ToNormalizedStringSafe(), + Version = packageMetadata.Version.ToFullStringSafe(), + OriginalVersion = packageMetadata.Version.OriginalVersion, LicenseUrl = packageMetadata.LicenseUrl.ToEncodedUrlStringOrNull(), Listed = true, Language = packageMetadata.Language, @@ -1117,10 +1133,11 @@ public virtual async Task VerifyPackage(VerifyPackageRequest formD // Rule out problem scenario with multiple tabs - verification request (possibly with edits) was submitted by user // viewing a different package to what was actually most recently uploaded - if (!(String.IsNullOrEmpty(formData.Id) || String.IsNullOrEmpty(formData.Version))) + if (!(String.IsNullOrEmpty(formData.Id) || String.IsNullOrEmpty(formData.OriginalVersion))) { if (!(String.Equals(packageMetadata.Id, formData.Id, StringComparison.OrdinalIgnoreCase) - && String.Equals(packageMetadata.Version.ToNormalizedString(), formData.Version, StringComparison.OrdinalIgnoreCase))) + && String.Equals(packageMetadata.Version.ToNormalizedString(), formData.Version, StringComparison.OrdinalIgnoreCase) + && String.Equals(packageMetadata.Version.OriginalVersion, formData.OriginalVersion, StringComparison.OrdinalIgnoreCase))) { TempData["Message"] = "Your attempt to verify the package submission failed, because the package file appears to have changed. Please try again."; return new RedirectResult(Url.VerifyPackage()); @@ -1289,7 +1306,7 @@ public virtual async Task SetLicenseReportVisibility(string id, st internal virtual async Task SetLicenseReportVisibility(string id, string version, bool visible, Func urlFactory) { - var package = _packageService.FindPackageByIdAndVersion(id, version); + var package = _packageService.FindPackageByIdAndVersionStrict(id, version); if (package == null) { return HttpNotFound(); diff --git a/src/NuGetGallery/Infrastructure/Lucene/ExternalSearchService.cs b/src/NuGetGallery/Infrastructure/Lucene/ExternalSearchService.cs index 0e3038713e..70daf45f7a 100644 --- a/src/NuGetGallery/Infrastructure/Lucene/ExternalSearchService.cs +++ b/src/NuGetGallery/Infrastructure/Lucene/ExternalSearchService.cs @@ -128,7 +128,8 @@ private async Task SearchCore(SearchFilter filter, bool raw) countOnly: filter.CountOnly, explain: false, getAllVersions: filter.IncludeAllVersions, - supportedFramework: filter.SupportedFramework); + supportedFramework: filter.SupportedFramework, + semVerLevel: filter.SemVerLevel); sw.Stop(); SearchResults results = null; diff --git a/src/NuGetGallery/Infrastructure/Lucene/LuceneSearchService.cs b/src/NuGetGallery/Infrastructure/Lucene/LuceneSearchService.cs index 622babd348..5cf96200d2 100644 --- a/src/NuGetGallery/Infrastructure/Lucene/LuceneSearchService.cs +++ b/src/NuGetGallery/Infrastructure/Lucene/LuceneSearchService.cs @@ -58,7 +58,7 @@ private SearchResults SearchCore(SearchFilter searchFilter) int numRecords = searchFilter.Skip + searchFilter.Take; var searcher = new IndexSearcher(_directory, readOnly: true); - var query = ParseQuery(searchFilter); + var query = ParseQuery(searchFilter.SearchTerm); // IF searching by relevance, boost scores by download count. if (searchFilter.SortOrder == SortOrder.Relevance) @@ -67,7 +67,16 @@ private SearchResults SearchCore(SearchFilter searchFilter) query = new CustomScoreQuery(query, downloadCountBooster); } - var filterTerm = searchFilter.IncludePrerelease ? "IsLatest" : "IsLatestStable"; + string filterTerm; + if (SemVerLevelKey.ForSemVerLevel(searchFilter.SemVerLevel) == SemVerLevelKey.SemVer2) + { + filterTerm = searchFilter.IncludePrerelease ? "IsLatestSemVer2" : "IsLatestStableSemVer2"; + } + else + { + filterTerm = searchFilter.IncludePrerelease ? "IsLatest" : "IsLatestStable"; + } + Query filterQuery = new TermQuery(new Term(filterTerm, Boolean.TrueString)); if (searchFilter.CuratedFeed != null) { @@ -105,6 +114,8 @@ private static Package PackageFromDoc(Document doc) int packageSize = Int32.Parse(doc.Get("PackageFileSize"), CultureInfo.InvariantCulture); bool isLatest = Boolean.Parse(doc.Get("IsLatest")); bool isLatestStable = Boolean.Parse(doc.Get("IsLatestStable")); + bool isLatestSemVer2 = Boolean.Parse(doc.Get("IsLatestSemVer2")); + bool isLatestStableSemVer2 = Boolean.Parse(doc.Get("IsLatestStableSemVer2")); bool requiresLicenseAcceptance = Boolean.Parse(doc.Get("RequiresLicenseAcceptance")); DateTime created = DateTime.Parse(doc.Get("Created"), CultureInfo.InvariantCulture); DateTime published = DateTime.Parse(doc.Get("Published"), CultureInfo.InvariantCulture); @@ -150,6 +161,8 @@ private static Package PackageFromDoc(Document doc) IconUrl = doc.Get("IconUrl"), IsLatest = isLatest, IsLatestStable = isLatestStable, + IsLatestSemVer2 = isLatestSemVer2, + IsLatestStableSemVer2 = isLatestStableSemVer2, Key = key, Language = doc.Get("Language"), LastUpdated = lastUpdated, @@ -192,13 +205,13 @@ private static PackageDependency CreateDependency(string s) }; } - private static Query ParseQuery(SearchFilter searchFilter) + private static Query ParseQuery(string searchTerm) { // 1. parse the query into field clauses and general terms // We imagine that mostly, field clauses are meant to 'filter' results found searching for general terms. // The resulting clause collections may be empty. var queryParser = new NuGetQueryParser(); - var clauses = queryParser.Parse(searchFilter.SearchTerm).Select(StandardizeSearchTerms).ToList(); + var clauses = queryParser.Parse(searchTerm).Select(StandardizeSearchTerms).ToList(); var fieldSpecificTerms = clauses.Where(a => a.Field != null); var generalTerms = clauses.Where(a => a.Field == null); @@ -225,7 +238,7 @@ private static Query ParseQuery(SearchFilter searchFilter) // b) Id-targeted search? [id:Foo bar] // c) Other Field-targeted search? [author:Foo bar] bool doExactId = !fieldSpecificQueries.Any(); - Query generalQuery = BuildGeneralQuery(doExactId, searchFilter.SearchTerm, analyzer, generalTerms, generalQueries); + Query generalQuery = BuildGeneralQuery(doExactId, searchTerm, analyzer, generalTerms, generalQueries); // IF field targeting is done, we should basically want to AND their field specific queries with all other query terms if (fieldSpecificQueries.Any()) diff --git a/src/NuGetGallery/Infrastructure/PackageIndexEntity.cs b/src/NuGetGallery/Infrastructure/PackageIndexEntity.cs index 38c54feda7..8d922ad35d 100644 --- a/src/NuGetGallery/Infrastructure/PackageIndexEntity.cs +++ b/src/NuGetGallery/Infrastructure/PackageIndexEntity.cs @@ -161,6 +161,8 @@ public Document ToDocument() // Fields meant for filtering, also storing data to avoid hitting SQL while doing searches document.Add(new Field("IsLatest", Package.IsLatest.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED)); document.Add(new Field("IsLatestStable", Package.IsLatestStable.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED)); + document.Add(new Field("IsLatestSemVer2", Package.IsLatestSemVer2.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED)); + document.Add(new Field("IsLatestStableSemVer2", Package.IsLatestStableSemVer2.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED)); // Fields meant for filtering, sorting document.Add(new Field("PublishedDate", Package.Published.Ticks.ToString(CultureInfo.InvariantCulture), Field.Store.NO, Field.Index.NOT_ANALYZED)); diff --git a/src/NuGetGallery/Migrations/201704242001472_AddSemVer2LatestVersionColumns.Designer.cs b/src/NuGetGallery/Migrations/201704242001472_AddSemVer2LatestVersionColumns.Designer.cs new file mode 100644 index 0000000000..e2f639afc2 --- /dev/null +++ b/src/NuGetGallery/Migrations/201704242001472_AddSemVer2LatestVersionColumns.Designer.cs @@ -0,0 +1,29 @@ +// +namespace NuGetGallery.Migrations +{ + using System.CodeDom.Compiler; + using System.Data.Entity.Migrations; + using System.Data.Entity.Migrations.Infrastructure; + using System.Resources; + + [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] + public sealed partial class AddSemVer2LatestVersionColumns : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(AddSemVer2LatestVersionColumns)); + + string IMigrationMetadata.Id + { + get { return "201704242001472_AddSemVer2LatestVersionColumns"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/NuGetGallery/Migrations/201704242001472_AddSemVer2LatestVersionColumns.cs b/src/NuGetGallery/Migrations/201704242001472_AddSemVer2LatestVersionColumns.cs new file mode 100644 index 0000000000..3f64a552f8 --- /dev/null +++ b/src/NuGetGallery/Migrations/201704242001472_AddSemVer2LatestVersionColumns.cs @@ -0,0 +1,20 @@ +namespace NuGetGallery.Migrations +{ + using System; + using System.Data.Entity.Migrations; + + public partial class AddSemVer2LatestVersionColumns : DbMigration + { + public override void Up() + { + AddColumn("dbo.Packages", "IsLatestSemVer2", c => c.Boolean(nullable: false)); + AddColumn("dbo.Packages", "IsLatestStableSemVer2", c => c.Boolean(nullable: false)); + } + + public override void Down() + { + DropColumn("dbo.Packages", "IsLatestStableSemVer2"); + DropColumn("dbo.Packages", "IsLatestSemVer2"); + } + } +} diff --git a/src/NuGetGallery/Migrations/201704242001472_AddSemVer2LatestVersionColumns.resx b/src/NuGetGallery/Migrations/201704242001472_AddSemVer2LatestVersionColumns.resx new file mode 100644 index 0000000000..4c00f2aadc --- /dev/null +++ b/src/NuGetGallery/Migrations/201704242001472_AddSemVer2LatestVersionColumns.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 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 + + + dbo + + \ No newline at end of file diff --git a/src/NuGetGallery/NuGetGallery.csproj b/src/NuGetGallery/NuGetGallery.csproj index 217071dd1d..3ca4b11077 100644 --- a/src/NuGetGallery/NuGetGallery.csproj +++ b/src/NuGetGallery/NuGetGallery.csproj @@ -424,10 +424,10 @@ ..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll - ..\..\packages\NuGet.Common.4.3.0-preview1-2507\lib\net45\NuGet.Common.dll + ..\..\packages\NuGet.Common.4.3.0-preview1-2524\lib\net45\NuGet.Common.dll - ..\..\packages\NuGet.Frameworks.4.3.0-preview1-2507\lib\net45\NuGet.Frameworks.dll + ..\..\packages\NuGet.Frameworks.4.3.0-preview1-2524\lib\net45\NuGet.Frameworks.dll False @@ -435,10 +435,13 @@ True - ..\..\packages\NuGet.Packaging.4.3.0-preview1-2507\lib\net45\NuGet.Packaging.dll + ..\..\packages\NuGet.Packaging.4.3.0-preview1-2524\lib\net45\NuGet.Packaging.dll - ..\..\packages\NuGet.Packaging.Core.4.3.0-preview1-2507\lib\net45\NuGet.Packaging.Core.dll + ..\..\packages\NuGet.Packaging.Core.4.3.0-preview1-2524\lib\net45\NuGet.Packaging.Core.dll + + + ..\..\packages\NuGet.Protocol.4.3.0-preview1-2524\lib\net45\NuGet.Protocol.dll ..\..\packages\NuGet.Services.KeyVault.1.0.0.0\lib\net45\NuGet.Services.KeyVault.dll @@ -449,11 +452,8 @@ ..\..\packages\NuGet.Services.Platform.Client.3.0.29-r-master\lib\portable-net45+wp80+win\NuGet.Services.Platform.Client.dll True - - ..\..\packages\NuGet.Protocol.4.3.0-preview1-2507\lib\net45\NuGet.Protocol.dll - - ..\..\packages\NuGet.Versioning.4.3.0-preview1-2507\lib\net45\NuGet.Versioning.dll + ..\..\packages\NuGet.Versioning.4.3.0-preview1-2524\lib\net45\NuGet.Versioning.dll False @@ -761,6 +761,10 @@ + + + 201704242001472_AddSemVer2LatestVersionColumns.cs + @@ -1716,6 +1720,9 @@ 201705031714183_AddIndexSemVerLevelKey.cs + + 201704242001472_AddSemVer2LatestVersionColumns.cs + diff --git a/src/NuGetGallery/OData/PackageExtensions.cs b/src/NuGetGallery/OData/PackageExtensions.cs index 556df7484c..1b4bacfe28 100644 --- a/src/NuGetGallery/OData/PackageExtensions.cs +++ b/src/NuGetGallery/OData/PackageExtensions.cs @@ -32,6 +32,8 @@ public static IQueryable ToV1FeedPackageQuery(this IQueryable ToV1FeedPackageQuery(this IQueryable ToV2FeedPackageQuery(this IQueryable packages, string siteRoot, bool includeLicenseReport) + public static IQueryable ToV2FeedPackageQuery( + this IQueryable packages, + string siteRoot, + bool includeLicenseReport, + int? semVerLevelKey) { return ProjectV2FeedPackage( - packages - .Include(p => p.PackageRegistration), - siteRoot, includeLicenseReport); + packages.Include(p => p.PackageRegistration), + siteRoot, + includeLicenseReport, + semVerLevelKey); } // Does the actual projection of a Package object to a V2FeedPackage. // This is in a separate method for testability - internal static IQueryable ProjectV2FeedPackage(this IQueryable packages, string siteRoot, bool includeLicenseReport) + internal static IQueryable ProjectV2FeedPackage( + this IQueryable packages, + string siteRoot, + bool includeLicenseReport, + int? semVerLevelKey) { siteRoot = EnsureTrailingSlash(siteRoot); return packages.Select(p => new V2FeedPackage @@ -79,9 +90,12 @@ internal static IQueryable ProjectV2FeedPackage(this IQueryable

ProjectV2FeedPackage(this IQueryable

WithoutSortOnColumn(this IQueryable feedQuery, string columnName, bool confirmToIgnoreSort=true) + internal static IQueryable WithoutSortOnColumn( + this IQueryable feedQuery, + string columnName, + bool confirmToIgnoreSort = true) { return confirmToIgnoreSort ? feedQuery.InterceptWith(new ODataRemoveSorter(columnName)) : feedQuery; } diff --git a/src/NuGetGallery/OData/SearchService/SearchAdaptor.cs b/src/NuGetGallery/OData/SearchService/SearchAdaptor.cs index 72a411a367..b0e4bc99d2 100644 --- a/src/NuGetGallery/OData/SearchService/SearchAdaptor.cs +++ b/src/NuGetGallery/OData/SearchService/SearchAdaptor.cs @@ -24,14 +24,15 @@ public static class SearchAdaptor ///

internal const int MaxPageSize = 100; - public static SearchFilter GetSearchFilter(string q, int page, string sortOrder, string context) + public static SearchFilter GetSearchFilter(string q, int page, string sortOrder, string context, string semVerLevel) { var searchFilter = new SearchFilter(context) { SearchTerm = q, Skip = (page - 1) * Constants.DefaultPackageListPageSize, // pages are 1-based. Take = Constants.DefaultPackageListPageSize, - IncludePrerelease = true + IncludePrerelease = true, + SemVerLevel = semVerLevel }; switch (sortOrder) @@ -93,7 +94,8 @@ public static async Task FindByIdAndVersionCore( IQueryable packages, string id, string version, - CuratedFeed curatedFeed) + CuratedFeed curatedFeed, + string semVerLevel) { SearchFilter searchFilter; // We can only use Lucene if: @@ -112,6 +114,7 @@ public static async Task FindByIdAndVersionCore( } searchFilter.SearchTerm = searchTerm; + searchFilter.SemVerLevel = semVerLevel; searchFilter.IncludePrerelease = true; searchFilter.CuratedFeed = curatedFeed; searchFilter.SupportedFramework = null; @@ -132,7 +135,8 @@ public static async Task SearchCore( string searchTerm, string targetFramework, bool includePrerelease, - CuratedFeed curatedFeed) + CuratedFeed curatedFeed, + string semVerLevel) { SearchFilter searchFilter; // We can only use Lucene if: @@ -144,6 +148,7 @@ public static async Task SearchCore( searchFilter.IncludePrerelease = includePrerelease; searchFilter.CuratedFeed = curatedFeed; searchFilter.SupportedFramework = targetFramework; + searchFilter.SemVerLevel = semVerLevel; var results = await GetResultsFromSearchService(searchService, searchFilter); @@ -154,6 +159,8 @@ public static async Task SearchCore( { packages = packages.Where(p => !p.IsPrerelease); } + + packages = packages.Where(SemVerLevelKey.IsPackageCompliantWithSemVerLevel(semVerLevel)); return new SearchAdaptorResult(false, packages.Search(searchTerm)); } diff --git a/src/NuGetGallery/RequestModels/VerifyPackageRequest.cs b/src/NuGetGallery/RequestModels/VerifyPackageRequest.cs index 056c912568..c620937409 100644 --- a/src/NuGetGallery/RequestModels/VerifyPackageRequest.cs +++ b/src/NuGetGallery/RequestModels/VerifyPackageRequest.cs @@ -9,7 +9,17 @@ namespace NuGetGallery public class VerifyPackageRequest { public string Id { get; set; } + + /// + /// The normalized, full version string (for display purposes). + /// public string Version { get; set; } + + /// + /// The non-normalized, unmodified, original version as defined in the nuspec. + /// + public string OriginalVersion { get; set; } + public string LicenseUrl { get; set; } public bool Listed { get; set; } public EditPackageVersionRequest Edit { get; set; } diff --git a/src/NuGetGallery/Services/IPackageService.cs b/src/NuGetGallery/Services/IPackageService.cs index 3706b5a76d..d89af6026f 100644 --- a/src/NuGetGallery/Services/IPackageService.cs +++ b/src/NuGetGallery/Services/IPackageService.cs @@ -11,8 +11,26 @@ namespace NuGetGallery public interface IPackageService { PackageRegistration FindPackageRegistrationById(string id); - Package FindPackageByIdAndVersion(string id, string version, bool allowPrerelease = true); - Package FindAbsoluteLatestPackageById(string id); + + /// + /// Gets the package with the given ID and version when exists; + /// otherwise gets the latest package version for the given package ID matching the provided constraints. + /// + /// The package ID. + /// The package version if known; otherwise null to fallback to retrieve the latest version matching filter criteria. + /// The SemVer-level key that determines the SemVer filter to be applied. + /// True indicating pre-release packages are allowed, otherwise false. + /// + Package FindPackageByIdAndVersion(string id, string version, int? semVerLevelKey = null, bool allowPrerelease = true); + + /// + /// Gets the package with the given ID and version when exists; otherwise null. + /// + /// The package ID. + /// The package version. + Package FindPackageByIdAndVersionStrict(string id, string version); + + Package FindAbsoluteLatestPackageById(string id, int? semVerLevelKey); IEnumerable FindPackagesByOwner(User user, bool includeUnlisted); IEnumerable FindPackageRegistrationsByOwner(User user); IEnumerable FindDependentPackages(Package package); diff --git a/src/NuGetGallery/Services/PackageService.cs b/src/NuGetGallery/Services/PackageService.cs index 7327ef214f..89eef7aba6 100644 --- a/src/NuGetGallery/Services/PackageService.cs +++ b/src/NuGetGallery/Services/PackageService.cs @@ -133,7 +133,7 @@ public async Task CreatePackageAsync(PackageArchiveReader nugetPackage, // Wrap the exception for consistency of this API. throw new InvalidPackageException(exception.Message, exception); } - + var package = CreatePackageFromNuGetPackage(packageRegistration, nugetPackage, packageMetadata, packageStreamMetadata, user); packageRegistration.Packages.Add(package); await UpdateIsLatestAsync(packageRegistration, false); @@ -159,38 +159,66 @@ public virtual PackageRegistration FindPackageRegistrationById(string id) .SingleOrDefault(pr => pr.Id == id); } - public virtual Package FindPackageByIdAndVersion(string id, string version, bool allowPrerelease = true) + public virtual Package FindPackageByIdAndVersion( + string id, + string version, + int? semVerLevelKey = null, + bool allowPrerelease = true) { - if (String.IsNullOrWhiteSpace(id)) + if (string.IsNullOrWhiteSpace(id)) { throw new ArgumentNullException(nameof(id)); } - // Optimization: Every time we look at a package we almost always want to see - // all the other packages with the same ID via the PackageRegistration property. - // This resulted in a gnarly query. - // Instead, we can always query for all packages with the ID. - IEnumerable packagesQuery = _packageRepository.GetAll() - .Include(p => p.LicenseReports) - .Include(p => p.PackageRegistration) - .Where(p => (p.PackageRegistration.Id == id)); - - if (String.IsNullOrEmpty(version) && !allowPrerelease) + Package package = null; + if (!string.IsNullOrEmpty(version)) { - // If there's a specific version given, don't bother filtering by prerelease. You could be asking for a prerelease package. - packagesQuery = packagesQuery.Where(p => !p.IsPrerelease); + package = FindPackageByIdAndVersionStrict(id, version); } - var packageVersions = packagesQuery.ToList(); - - Package package; - if (String.IsNullOrEmpty(version)) + // Package version not found: fallback to latest version. + if (package == null) { - package = packageVersions.FirstOrDefault(p => p.IsLatestStable); + // Optimization: Every time we look at a package we almost always want to see + // all the other packages with the same ID via the PackageRegistration property. + // This resulted in a gnarly query. + // Instead, we can always query for all packages with the ID. + IEnumerable packagesQuery = GetPackagesByIdQueryable(id); - if (package == null && allowPrerelease) + if (string.IsNullOrEmpty(version) && !allowPrerelease) { - package = packageVersions.FirstOrDefault(p => p.IsLatest); + // If there's a specific version given, don't bother filtering by prerelease. + // You could be asking for a prerelease package. + packagesQuery = packagesQuery.Where(p => !p.IsPrerelease); + } + + var packageVersions = packagesQuery.ToList(); + + // Fallback behavior: collect the latest version. + // Check SemVer-level and allow-prerelease constraints. + if (semVerLevelKey == SemVerLevelKey.SemVer2) + { + package = packageVersions.FirstOrDefault(p => p.IsLatestStableSemVer2); + + if (package == null && allowPrerelease) + { + package = packageVersions.FirstOrDefault(p => p.IsLatestSemVer2); + } + } + + // Fallback behavior: collect the latest version. + // If SemVer-level is not defined, + // or SemVer-level = 2.0.0 and no package was marked as SemVer2-latest, + // then check for packages marked as non-SemVer2 latest. + if (semVerLevelKey == SemVerLevelKey.Unknown + || (semVerLevelKey == SemVerLevelKey.SemVer2 && package == null)) + { + package = packageVersions.FirstOrDefault(p => p.IsLatestStable); + + if (package == null && allowPrerelease) + { + package = packageVersions.FirstOrDefault(p => p.IsLatest); + } } // If we couldn't find a package marked as latest, then @@ -200,26 +228,45 @@ public virtual Package FindPackageByIdAndVersion(string id, string version, bool package = packageVersions.OrderByDescending(p => p.Version).FirstOrDefault(); } } - else + + return package; + } + + public virtual Package FindPackageByIdAndVersionStrict(string id, string version) + { + if (string.IsNullOrWhiteSpace(id)) { - package = packageVersions.SingleOrDefault( - p => p.PackageRegistration.Id.Equals(id, StringComparison.OrdinalIgnoreCase) && - ( - String.Equals(p.NormalizedVersion, NuGetVersionNormalizer.Normalize(version), StringComparison.OrdinalIgnoreCase) - )); + throw new ArgumentNullException(nameof(id)); } + + if (string.IsNullOrEmpty(version)) + { + throw new ArgumentException(nameof(version)); + } + + var normalizedVersion = NuGetVersionNormalizer.Normalize(version); + + // These string comparisons are case-(in)sensitive depending on SQLServer collation. + // Case-insensitive collation is recommended, e.g. SQL_Latin1_General_CP1_CI_AS. + var package = GetPackagesByIdQueryable(id) + .SingleOrDefault(p => p.NormalizedVersion == normalizedVersion); + return package; } - public virtual Package FindAbsoluteLatestPackageById(string id) + public virtual Package FindAbsoluteLatestPackageById(string id, int? semVerLevelKey) { - var packageVersions = _packageRepository.GetAll() - .Include(p => p.LicenseReports) - .Include(p => p.PackageRegistration) - .Where(p => p.PackageRegistration.Id == id) - .ToList(); + var packageVersions = GetPackagesByIdQueryable(id); - Package package = packageVersions.FirstOrDefault(p => p.IsLatest); + Package package; + if (semVerLevelKey == SemVerLevelKey.SemVer2) + { + package = packageVersions.FirstOrDefault(p => p.IsLatestSemVer2); + } + else + { + package = packageVersions.FirstOrDefault(p => p.IsLatest); + } // If we couldn't find a package marked as latest, then return the most recent one if (package == null) @@ -303,7 +350,7 @@ where VersionRange.Parse(d.VersionSpec).Satisfies(packageVersion) public async Task PublishPackageAsync(string id, string version, bool commitChanges = true) { - var package = FindPackageByIdAndVersion(id, version); + var package = FindPackageByIdAndVersionStrict(id, version); if (package == null) { @@ -342,7 +389,7 @@ public async Task AddPackageOwnerAsync(PackageRegistration package, User user) _packageOwnerRequestRepository.DeleteOnCommit(request); await _packageOwnerRequestRepository.CommitChangesAsync(); } - + await _auditingService.SaveAuditRecordAsync( new PackageRegistrationAuditRecord(package, AuditedPackageRegistrationAction.AddOwner, user.Username)); } @@ -396,7 +443,7 @@ public async Task MarkPackageListedAsync(Package package, bool commitChanges = t package.LastEdited = DateTime.UtcNow; await UpdateIsLatestAsync(package.PackageRegistration, false); - + await _auditingService.SaveAuditRecordAsync(new PackageAuditRecord(package, AuditedPackageAction.List)); if (commitChanges) @@ -488,6 +535,14 @@ public async Task ConfirmPackageOwnerAsync(PackageRegist return ConfirmOwnershipResult.Failure; } + private IQueryable GetPackagesByIdQueryable(string id) + { + return _packageRepository.GetAll() + .Include(p => p.LicenseReports) + .Include(p => p.PackageRegistration) + .Where(p => p.PackageRegistration.Id == id); + } + private PackageRegistration CreateOrGetPackageRegistration(User currentUser, PackageMetadata packageMetadata) { var packageRegistration = FindPackageRegistrationById(packageMetadata.Id); @@ -536,8 +591,8 @@ private Package CreatePackageFromNuGetPackage(PackageRegistration packageRegistr } public virtual Package EnrichPackageFromNuGetPackage( - Package package, - PackageArchiveReader packageArchive, + Package package, + PackageArchiveReader packageArchive, PackageMetadata packageMetadata, PackageStreamMetadata packageStreamMetadata, User user) @@ -546,7 +601,7 @@ public virtual Package EnrichPackageFromNuGetPackage( // However, we do also store a normalized copy for looking up later. package.Version = packageMetadata.Version.OriginalVersion; package.NormalizedVersion = packageMetadata.Version.ToNormalizedString(); - + package.Description = packageMetadata.Description; package.ReleaseNotes = packageMetadata.ReleaseNotes; package.HashAlgorithm = packageStreamMetadata.HashAlgorithm; @@ -592,7 +647,7 @@ public virtual Package EnrichPackageFromNuGetPackage( package.SupportedFrameworks.Add(new PackageFramework { TargetFramework = supportedFramework }); } } - + package.Dependencies = packageMetadata .GetDependencyGroups() .AsPackageDependencyEnumerable() @@ -606,7 +661,7 @@ public virtual Package EnrichPackageFromNuGetPackage( package.FlattenedDependencies = package.Dependencies.Flatten(); package.FlattenedPackageTypes = package.PackageTypes.Flatten(); - + // Identify the SemVerLevelKey using the original package version string and package dependencies package.SemVerLevelKey = SemVerLevelKey.ForPackage(packageMetadata.Version, package.Dependencies); @@ -737,31 +792,42 @@ public async Task UpdateIsLatestAsync(PackageRegistration packageRegistration, b } // TODO: improve setting the latest bit; this is horrible. Trigger maybe? + var currentUtcTime = DateTime.UtcNow; foreach (var pv in packageRegistration.Packages.Where(p => p.IsLatest || p.IsLatestStable)) { pv.IsLatest = false; pv.IsLatestStable = false; - pv.LastUpdated = DateTime.UtcNow; + pv.IsLatestSemVer2 = false; + pv.IsLatestStableSemVer2 = false; + pv.LastUpdated = currentUtcTime; } // If the last listed package was just unlisted, then we won't find another one - var latestPackage = FindPackage(packageRegistration.Packages, p => !p.Deleted && p.Listed); + var latestPackage = FindPackage( + packageRegistration.Packages, + p => !p.Deleted && p.Listed && p.SemVerLevelKey == SemVerLevelKey.Unknown); + + var latestSemVer2Package = FindPackage( + packageRegistration.Packages, + p => !p.Deleted && p.Listed && (p.SemVerLevelKey == SemVerLevelKey.SemVer2 || p.SemVerLevelKey == SemVerLevelKey.Unknown)); if (latestPackage != null) { latestPackage.IsLatest = true; - latestPackage.LastUpdated = DateTime.UtcNow; + latestPackage.LastUpdated = currentUtcTime; if (latestPackage.IsPrerelease) { // If the newest uploaded package is a prerelease package, we need to find an older package that is // a release version and set it to IsLatest. - var latestReleasePackage = FindPackage(packageRegistration.Packages.Where(p => !p.IsPrerelease && !p.Deleted && p.Listed)); + var latestReleasePackage = FindPackage( + packageRegistration.Packages.Where(p => !p.IsPrerelease && !p.Deleted && p.Listed && p.SemVerLevelKey == SemVerLevelKey.Unknown)); + if (latestReleasePackage != null) { // We could have no release packages latestReleasePackage.IsLatestStable = true; - latestReleasePackage.LastUpdated = DateTime.UtcNow; + latestReleasePackage.LastUpdated = currentUtcTime; } } else @@ -771,6 +837,32 @@ public async Task UpdateIsLatestAsync(PackageRegistration packageRegistration, b } } + if (latestSemVer2Package != null) + { + latestSemVer2Package.IsLatestSemVer2 = true; + latestSemVer2Package.LastUpdated = currentUtcTime; + + if (latestSemVer2Package.IsPrerelease) + { + // If the newest uploaded package is a prerelease package, we need to find an older package that is + // a release version and set it to IsLatest. + var latestSemVer2ReleasePackage = FindPackage( + packageRegistration.Packages.Where(p => !p.IsPrerelease && !p.Deleted && p.Listed && p.SemVerLevelKey == SemVerLevelKey.SemVer2)); + + if (latestSemVer2ReleasePackage != null) + { + // We could have no release packages + latestSemVer2ReleasePackage.IsLatestStableSemVer2 = true; + latestSemVer2ReleasePackage.LastUpdated = currentUtcTime; + } + } + else + { + // Only release versions are marked as IsLatestStable. + latestSemVer2Package.IsLatestStableSemVer2 = true; + } + } + if (commitChanges) { await _packageRepository.CommitChangesAsync(); @@ -783,12 +875,13 @@ private static Package FindPackage(IEnumerable packages, Func new NuGetVersion(p.Version)); + NuGetVersion version = packages.Max(p => new NuGetVersion(p.Version)); if (version == null) { return null; } + return packages.First(pv => pv.Version.Equals(version.OriginalVersion, StringComparison.OrdinalIgnoreCase)); } @@ -819,7 +912,7 @@ public async Task SetLicenseReportVisibilityAsync(Package package, bool visible, public async Task IncrementDownloadCountAsync(string id, string version, bool commitChanges = true) { - var package = FindPackageByIdAndVersion(id, version); + var package = FindPackageByIdAndVersionStrict(id, version); if (package != null) { package.DownloadCount++; diff --git a/src/NuGetGallery/Services/ReflowPackageService.cs b/src/NuGetGallery/Services/ReflowPackageService.cs index 6787353359..24df02b23c 100644 --- a/src/NuGetGallery/Services/ReflowPackageService.cs +++ b/src/NuGetGallery/Services/ReflowPackageService.cs @@ -27,7 +27,7 @@ public ReflowPackageService( public async Task ReflowAsync(string id, string version) { - var package = _packageService.FindPackageByIdAndVersion(id, version); + var package = _packageService.FindPackageByIdAndVersionStrict(id, version); if (package == null) { diff --git a/src/NuGetGallery/Services/SearchFilter.cs b/src/NuGetGallery/Services/SearchFilter.cs index d5af032130..ca7afe573a 100644 --- a/src/NuGetGallery/Services/SearchFilter.cs +++ b/src/NuGetGallery/Services/SearchFilter.cs @@ -22,6 +22,8 @@ public class SearchFilter public bool IncludePrerelease { get; set; } + public string SemVerLevel { get; set; } + public CuratedFeed CuratedFeed { get; set; } public SortOrder SortOrder { get; set; } diff --git a/src/NuGetGallery/ViewModels/PackageViewModel.cs b/src/NuGetGallery/ViewModels/PackageViewModel.cs index a40ba9f691..74c877025e 100644 --- a/src/NuGetGallery/ViewModels/PackageViewModel.cs +++ b/src/NuGetGallery/ViewModels/PackageViewModel.cs @@ -24,7 +24,9 @@ public PackageViewModel(Package package) LicenseUrl = package.LicenseUrl; HideLicenseReport = package.HideLicenseReport; LatestVersion = package.IsLatest; + LatestVersionSemVer2 = package.IsLatestSemVer2; LatestStableVersion = package.IsLatestStable; + LatestStableVersionSemVer2 = package.IsLatestStableSemVer2; LastUpdated = package.Published; Listed = package.Listed; Deleted = package.Deleted; @@ -49,6 +51,8 @@ public PackageViewModel(Package package) public DateTime LastUpdated { get; set; } public bool LatestVersion { get; set; } public bool LatestStableVersion { get; set; } + public bool LatestVersionSemVer2 { get; set; } + public bool LatestStableVersionSemVer2 { get; set; } public bool Prerelease { get; set; } public int DownloadCount { get; set; } public bool Listed { get; set; } diff --git a/src/NuGetGallery/Views/Packages/VerifyPackage.cshtml b/src/NuGetGallery/Views/Packages/VerifyPackage.cshtml index ee001c04f7..cf0ac66384 100644 --- a/src/NuGetGallery/Views/Packages/VerifyPackage.cshtml +++ b/src/NuGetGallery/Views/Packages/VerifyPackage.cshtml @@ -36,6 +36,11 @@ } +@helper HiddenField(string formId, string value) +{ + +} + @helper EditableField(string name, Expression> func, bool link = false, bool pre = false) { var formid = ExpressionHelper.GetExpressionText(func).Replace(".", "_"); @@ -79,7 +84,7 @@ Verify Package Details
  • @ReadOnlyField("Package ID", "Id", Model.Id)
  • -
  • @ReadOnlyField("Version", "Version", Model.Version)
  • +
  • @ReadOnlyField("Version", "Version", Model.Version)@HiddenField("OriginalVersion", Model.OriginalVersion)
  • @ReadOnlyField("Minimum NuGet Client Version", "MinClientVersion", Model.MinClientVersion.ToStringSafe())
  • @ReadOnlyField("License URL", "LicenseUrl", Model.LicenseUrl, link: true)
  • @ReadOnlyField("Language", "Language", Model.Language)
  • diff --git a/src/NuGetGallery/packages.config b/src/NuGetGallery/packages.config index 994516b946..0619042e19 100644 --- a/src/NuGetGallery/packages.config +++ b/src/NuGetGallery/packages.config @@ -77,15 +77,15 @@ - - + + - - - + + + - + diff --git a/tests/NuGetGallery.Core.Facts/NuGetGallery.Core.Facts.csproj b/tests/NuGetGallery.Core.Facts/NuGetGallery.Core.Facts.csproj index 50b7f7f796..67ac9c73f1 100644 --- a/tests/NuGetGallery.Core.Facts/NuGetGallery.Core.Facts.csproj +++ b/tests/NuGetGallery.Core.Facts/NuGetGallery.Core.Facts.csproj @@ -71,22 +71,22 @@ ..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll - ..\..\packages\NuGet.Common.4.3.0-preview1-2507\lib\net45\NuGet.Common.dll + ..\..\packages\NuGet.Common.4.3.0-preview1-2524\lib\net45\NuGet.Common.dll - ..\..\packages\NuGet.Frameworks.4.3.0-preview1-2507\lib\net45\NuGet.Frameworks.dll + ..\..\packages\NuGet.Frameworks.4.3.0-preview1-2524\lib\net45\NuGet.Frameworks.dll ..\..\packages\NuGet.Logging.3.5.0-beta-1160\lib\net45\NuGet.Logging.dll - ..\..\packages\NuGet.Packaging.4.3.0-preview1-2507\lib\net45\NuGet.Packaging.dll + ..\..\packages\NuGet.Packaging.4.3.0-preview1-2524\lib\net45\NuGet.Packaging.dll - ..\..\packages\NuGet.Packaging.Core.4.3.0-preview1-2507\lib\net45\NuGet.Packaging.Core.dll + ..\..\packages\NuGet.Packaging.Core.4.3.0-preview1-2524\lib\net45\NuGet.Packaging.Core.dll - ..\..\packages\NuGet.Versioning.4.3.0-preview1-2507\lib\net45\NuGet.Versioning.dll + ..\..\packages\NuGet.Versioning.4.3.0-preview1-2524\lib\net45\NuGet.Versioning.dll diff --git a/tests/NuGetGallery.Core.Facts/packages.config b/tests/NuGetGallery.Core.Facts/packages.config index 0a79845190..0e5b572617 100644 --- a/tests/NuGetGallery.Core.Facts/packages.config +++ b/tests/NuGetGallery.Core.Facts/packages.config @@ -9,12 +9,12 @@ - - + + - - - + + + diff --git a/tests/NuGetGallery.Facts/Controllers/ApiControllerFacts.cs b/tests/NuGetGallery.Facts/Controllers/ApiControllerFacts.cs index f473988867..3b3b545147 100644 --- a/tests/NuGetGallery.Facts/Controllers/ApiControllerFacts.cs +++ b/tests/NuGetGallery.Facts/Controllers/ApiControllerFacts.cs @@ -656,7 +656,7 @@ public class TheDeletePackageAction public async Task WillThrowIfAPackageWithTheIdAndNuGetVersionDoesNotExist() { var controller = new TestableApiController(); - controller.MockPackageService.Setup(x => x.FindPackageByIdAndVersion("theId", "1.0.42", true)).Returns((Package)null); + controller.MockPackageService.Setup(x => x.FindPackageByIdAndVersionStrict("theId", "1.0.42")).Returns((Package)null); controller.SetCurrentUser(new User()); var result = await controller.DeletePackage("theId", "1.0.42"); @@ -679,7 +679,7 @@ public async Task WillNotDeleteThePackageIfApiKeyDoesNotBelongToAnOwner() var controller = new TestableApiController(); controller.SetCurrentUser(notOwner); - controller.MockPackageService.Setup(x => x.FindPackageByIdAndVersion("theId", "1.0.42", true)).Returns(package); + controller.MockPackageService.Setup(x => x.FindPackageByIdAndVersionStrict("theId", "1.0.42")).Returns(package); var result = await controller.DeletePackage("theId", "1.0.42"); @@ -704,7 +704,7 @@ public async Task WillVerifyApiKeyScopeBeforeDelete(string apiKeyScope, bool isD var controller = new TestableApiController(); controller.SetCurrentUser(owner, apiKeyScope); - controller.MockPackageService.Setup(x => x.FindPackageByIdAndVersion("theId", "1.0.42", true)) + controller.MockPackageService.Setup(x => x.FindPackageByIdAndVersionStrict("theId", "1.0.42")) .Returns(package); var result = await controller.DeletePackage("theId", "1.0.42"); @@ -734,7 +734,7 @@ public async Task WillUnlistThePackageIfApiKeyBelongsToAnOwner() PackageRegistration = new PackageRegistration { Owners = new[] { new User(), owner } } }; var controller = new TestableApiController(); - controller.MockPackageService.Setup(x => x.FindPackageByIdAndVersion(It.IsAny(), It.IsAny(), true)).Returns(package); + controller.MockPackageService.Setup(x => x.FindPackageByIdAndVersionStrict(It.IsAny(), It.IsAny())).Returns(package); controller.SetCurrentUser(owner); ResultAssert.IsEmpty(await controller.DeletePackage("theId", "1.0.42")); @@ -773,7 +773,9 @@ public async Task GetPackageReturns404IfPackageIsNotFound() var actionResult = new RedirectResult("http://foo"); var controller = new TestableApiController(MockBehavior.Strict); - controller.MockPackageService.Setup(x => x.FindPackageByIdAndVersion(packageId, packageVersion, false)).Returns((Package)null).Verifiable(); + controller.MockPackageService + .Setup(x => x.FindPackageByIdAndVersion(packageId, packageVersion, SemVerLevelKey.SemVer2, false)) + .Returns((Package)null).Verifiable(); controller.MockPackageFileService.Setup(s => s.CreateDownloadPackageActionResultAsync(It.IsAny(), packageId, packageVersion)) .Returns(Task.FromResult(actionResult)) .Verifiable(); @@ -869,7 +871,9 @@ public async Task GetPackageReturnsLatestPackageIfNoVersionIsProvided() var package = new Package() { Version = "1.2.0408", NormalizedVersion = "1.2.408" }; var actionResult = new EmptyResult(); var controller = new TestableApiController(MockBehavior.Strict); - controller.MockPackageService.Setup(x => x.FindPackageByIdAndVersion(packageId, "", false)).Returns(package); + controller.MockPackageService + .Setup(x => x.FindPackageByIdAndVersion(packageId, string.Empty, SemVerLevelKey.SemVer2, false)) + .Returns(package); //controller.MockPackageService.Setup(x => x.AddDownloadStatistics(It.IsAny())).Verifiable(); controller.MockPackageFileService.Setup(s => s.CreateDownloadPackageActionResultAsync(HttpRequestUrl, packageId, package.NormalizedVersion)) @@ -908,7 +912,9 @@ public async Task GetPackageReturns503IfNoVersionIsProvidedAndDatabaseUnavailabl var package = new Package(); var actionResult = new EmptyResult(); var controller = new TestableApiController(MockBehavior.Strict); - controller.MockPackageService.Setup(x => x.FindPackageByIdAndVersion("Baz", "", false)).Throws(new DataException("Oh noes, database broken!")); + controller.MockPackageService + .Setup(x => x.FindPackageByIdAndVersion("Baz", string.Empty, SemVerLevelKey.SemVer2, false)) + .Throws(new DataException("Oh noes, database broken!")); controller.MockPackageFileService.Setup(s => s.CreateDownloadPackageActionResultAsync(HttpRequestUrl, packageId, package.NormalizedVersion)) .Returns(Task.FromResult(actionResult)) .Verifiable(); @@ -945,7 +951,7 @@ public async Task WillThrowIfAPackageWithTheIdAndNuGetVersionDoesNotExist() { // Arrange var controller = new TestableApiController(); - controller.MockPackageService.Setup(x => x.FindPackageByIdAndVersion("theId", "1.0.42", true)).Returns((Package)null); + controller.MockPackageService.Setup(x => x.FindPackageByIdAndVersionStrict("theId", "1.0.42")).Returns((Package)null); controller.SetCurrentUser(new User()); // Act @@ -970,7 +976,7 @@ public async Task WillNotListThePackageIfApiKeyDoesNotBelongToAnOwner() }; var controller = new TestableApiController(); - controller.MockPackageService.Setup(x => x.FindPackageByIdAndVersion("theId", "1.0.42", true)).Returns(package); + controller.MockPackageService.Setup(x => x.FindPackageByIdAndVersionStrict("theId", "1.0.42")).Returns(package); controller.SetCurrentUser(owner); // Act @@ -996,7 +1002,7 @@ public async Task WillListThePackageIfUserIsAnOwner() }; var controller = new TestableApiController(); - controller.MockPackageService.Setup(x => x.FindPackageByIdAndVersion(It.IsAny(), It.IsAny(), true)).Returns(package); + controller.MockPackageService.Setup(x => x.FindPackageByIdAndVersionStrict(It.IsAny(), It.IsAny())).Returns(package); controller.SetCurrentUser(owner); // Act @@ -1041,7 +1047,7 @@ internal TestableApiController SetupController(string keyType, string scopes, Pa var id = package?.PackageRegistration?.Id ?? "foo"; var version = package?.Version ?? "1.0.0"; - controller.MockPackageService.Setup(s => s.FindPackageByIdAndVersion(id, version, true)).Returns(package); + controller.MockPackageService.Setup(s => s.FindPackageByIdAndVersionStrict(id, version)).Returns(package); controller.SetCurrentUser(user, scopes); diff --git a/tests/NuGetGallery.Facts/Controllers/PackagesControllerFacts.cs b/tests/NuGetGallery.Facts/Controllers/PackagesControllerFacts.cs index 63b64aae64..600975e1a9 100644 --- a/tests/NuGetGallery.Facts/Controllers/PackagesControllerFacts.cs +++ b/tests/NuGetGallery.Facts/Controllers/PackagesControllerFacts.cs @@ -190,7 +190,7 @@ public async Task GivenANonExistantPackageIt404s() var packageService = new Mock(); var controller = CreateController(packageService: packageService); - packageService.Setup(p => p.FindPackageByIdAndVersion("Foo", "1.1.1", true)) + packageService.Setup(p => p.FindPackageByIdAndVersion("Foo", "1.1.1", SemVerLevelKey.SemVer2, true)) .ReturnsNull(); // Act @@ -210,7 +210,7 @@ public async Task GivenAValidPackageThatTheCurrentUserDoesNotOwnItDisplaysCurren packageService: packageService, indexingService: indexingService); controller.SetCurrentUser(TestUtility.FakeUser); - packageService.Setup(p => p.FindPackageByIdAndVersion("Foo", "1.1.1", true)) + packageService.Setup(p => p.FindPackageByIdAndVersion("Foo", "1.1.1", SemVerLevelKey.SemVer2, true)) .Returns(new Package() { PackageRegistration = new PackageRegistration() @@ -268,7 +268,7 @@ public async Task GivenAValidPackageThatTheCurrentUserOwnsItDisablesResponseCach }; packageService - .Setup(p => p.FindPackageByIdAndVersion("Foo", "1.1.1", true)) + .Setup(p => p.FindPackageByIdAndVersion("Foo", "1.1.1", SemVerLevelKey.SemVer2, true)) .Returns(package); // Act @@ -308,7 +308,7 @@ public async Task GivenAValidPackageThatTheCurrentUserOwnsWithNoEditsItDisplaysC }; packageService - .Setup(p => p.FindPackageByIdAndVersion("Foo", "1.1.1", true)) + .Setup(p => p.FindPackageByIdAndVersion("Foo", "1.1.1", SemVerLevelKey.SemVer2, true)) .Returns(package); editPackageService .Setup(e => e.GetPendingMetadata(package)) @@ -355,7 +355,7 @@ public async Task GivenAValidPackageThatTheCurrentUserOwnsWithEditsItDisplaysEdi }; packageService - .Setup(p => p.FindPackageByIdAndVersion("Foo", "1.1.1", true)) + .Setup(p => p.FindPackageByIdAndVersion("Foo", "1.1.1", SemVerLevelKey.SemVer2, true)) .Returns(package); editPackageService .Setup(e => e.GetPendingMetadata(package)) @@ -387,7 +387,7 @@ public async Task GivenAnAbsoluteLatestVersionItQueriesTheCorrectVersion() controller.SetCurrentUser(TestUtility.FakeUser); packageService - .Setup(p => p.FindAbsoluteLatestPackageById("Foo")) + .Setup(p => p.FindAbsoluteLatestPackageById("Foo", SemVerLevelKey.SemVer2)) .Returns(new Package() { PackageRegistration = new PackageRegistration() @@ -425,7 +425,7 @@ public async Task GivenAValidPackageWithNoVersionThatTheCurrentUserDoesNotOwnItD packageService: packageService, indexingService: indexingService); controller.SetCurrentUser(TestUtility.FakeUser); - packageService.Setup(p => p.FindPackageByIdAndVersion("Foo", null, true)) + packageService.Setup(p => p.FindPackageByIdAndVersion("Foo", null, SemVerLevelKey.SemVer2, true)) .Returns(new Package() { PackageRegistration = new PackageRegistration() @@ -620,7 +620,7 @@ public async Task UpdatesUnlistedIfSelected() .Throws(new Exception("Shouldn't be called")); packageService.Setup(svc => svc.MarkPackageUnlistedAsync(It.IsAny(), It.IsAny())) .Returns(Task.FromResult(0)).Verifiable(); - packageService.Setup(svc => svc.FindPackageByIdAndVersion("Foo", "1.0", true)) + packageService.Setup(svc => svc.FindPackageByIdAndVersionStrict("Foo", "1.0")) .Returns(package).Verifiable(); var indexingService = new Mock(); @@ -656,7 +656,7 @@ public async Task UpdatesUnlistedIfNotSelected() .Returns(Task.FromResult(0)).Verifiable(); packageService.Setup(svc => svc.MarkPackageUnlistedAsync(It.IsAny(), It.IsAny())) .Throws(new Exception("Shouldn't be called")); - packageService.Setup(svc => svc.FindPackageByIdAndVersion("Foo", "1.0", true)) + packageService.Setup(svc => svc.FindPackageByIdAndVersionStrict("Foo", "1.0")) .Returns(package).Verifiable(); var indexingService = new Mock(); @@ -708,7 +708,7 @@ public async Task SendsMessageToGalleryOwnerWithEmailOnlyWhenUnauthenticated() Version = "2.0.1" }; var packageService = new Mock(); - packageService.Setup(p => p.FindPackageByIdAndVersion("mordor", "2.0.1", true)).Returns(package); + packageService.Setup(p => p.FindPackageByIdAndVersionStrict("mordor", "2.0.1")).Returns(package); var httpContext = new Mock(); var controller = CreateController( packageService: packageService, @@ -749,7 +749,7 @@ public async Task SendsMessageToGalleryOwnerWithUserInfoWhenAuthenticated() Version = "2.0.1" }; var packageService = new Mock(); - packageService.Setup(p => p.FindPackageByIdAndVersion("mordor", It.IsAny(), true)).Returns(package); + packageService.Setup(p => p.FindPackageByIdAndVersionStrict("mordor", It.IsAny())).Returns(package); var httpContext = new Mock(); var controller = CreateController( packageService: packageService, @@ -785,7 +785,7 @@ public void FormRedirectsPackageOwnerToReportMyPackage() Version = "2.0.1" }; var packageService = new Mock(); - packageService.Setup(p => p.FindPackageByIdAndVersion("Mordor", It.IsAny(), true)).Returns(package); + packageService.Setup(p => p.FindPackageByIdAndVersionStrict("Mordor", It.IsAny())).Returns(package); var httpContext = new Mock(); var controller = CreateController( packageService: packageService, @@ -810,7 +810,7 @@ public async Task HtmlEncodesMessageContent() Version = "2.0.1" }; var packageService = new Mock(); - packageService.Setup(p => p.FindPackageByIdAndVersion("mordor", "2.0.1", true)).Returns(package); + packageService.Setup(p => p.FindPackageByIdAndVersionStrict("mordor", "2.0.1")).Returns(package); var httpContext = new Mock(); httpContext.Setup(h => h.Request.IsAuthenticated).Returns(false); var controller = CreateController( @@ -851,7 +851,7 @@ public void FormRedirectsNonOwnersToReportAbuse() }; var user = new User { EmailAddress = "frodo@hobbiton.example.com", Username = "Frodo", Key = 2 }; var packageService = new Mock(); - packageService.Setup(p => p.FindPackageByIdAndVersion("Mordor", It.IsAny(), true)).Returns(package); + packageService.Setup(p => p.FindPackageByIdAndVersionStrict("Mordor", It.IsAny())).Returns(package); var httpContext = new Mock(); var controller = CreateController( packageService: packageService, @@ -874,7 +874,7 @@ public async void HtmlEncodesMessageContent() Version = "2.0.1" }; var packageService = new Mock(); - packageService.Setup(p => p.FindPackageByIdAndVersion("mordor", "2.0.1", true)).Returns(package); + packageService.Setup(p => p.FindPackageByIdAndVersionStrict("mordor", "2.0.1")).Returns(package); ReportPackageRequest reportRequest = null; var messageService = new Mock(); @@ -1093,7 +1093,7 @@ public async Task WillShowTheViewWithErrorsWhenThePackageAlreadyExists() var fakeFileStream = TestPackage.CreateTestPackageStream("theId", "1.0.0"); fakeUploadedFile.Setup(x => x.InputStream).Returns(fakeFileStream); var fakePackageService = new Mock(); - fakePackageService.Setup(x => x.FindPackageByIdAndVersion(It.IsAny(), It.IsAny(), It.IsAny())).Returns( + fakePackageService.Setup(x => x.FindPackageByIdAndVersionStrict(It.IsAny(), It.IsAny())).Returns( new Package { PackageRegistration = new PackageRegistration { Id = "theId" }, Version = "theVersion" }); var controller = CreateController( packageService: fakePackageService); @@ -2016,7 +2016,7 @@ public async Task IndexingAndPackageServicesAreUpdated() .Throws(new Exception("Shouldn't be called")); packageService.Setup(svc => svc.SetLicenseReportVisibilityAsync(It.IsAny(), It.Is(t => t == false), It.IsAny())) .Returns(Task.CompletedTask).Verifiable(); - packageService.Setup(svc => svc.FindPackageByIdAndVersion("Foo", "1.0", true)) + packageService.Setup(svc => svc.FindPackageByIdAndVersionStrict("Foo", "1.0")) .Returns(package).Verifiable(); var httpContext = new Mock(); diff --git a/tests/NuGetGallery.Facts/Infrastructure/LuceneSearchServiceFacts.cs b/tests/NuGetGallery.Facts/Infrastructure/LuceneSearchServiceFacts.cs index 8db43c6de0..48dae28d49 100644 --- a/tests/NuGetGallery.Facts/Infrastructure/LuceneSearchServiceFacts.cs +++ b/tests/NuGetGallery.Facts/Infrastructure/LuceneSearchServiceFacts.cs @@ -14,8 +14,10 @@ namespace NuGetGallery.Infrastructure public class LuceneSearchServiceFacts { // This works because we index the description - [Fact] - public void IndexAndSearchAPackageByDescription() + [Theory] + [InlineData(null)] + [InlineData("2.0.0")] + public void IndexAndSearchAPackageByDescription(string semVerLevel) { var packages = new List { @@ -31,7 +33,9 @@ public void IndexAndSearchAPackageByDescription() Description = "Package #1 is an awesome package", Listed = true, IsLatestStable = true, + IsLatestStableSemVer2 = true, IsLatest = true, + IsLatestSemVer2 = true, IsPrerelease = true, DownloadCount = 100, FlattenedAuthors = "", @@ -42,15 +46,17 @@ public void IndexAndSearchAPackageByDescription() } }; - var results = IndexAndSearch(packages, "awesome"); + var results = IndexAndSearch(packages, "awesome", semVerLevel); Assert.Single(results); Assert.Equal(3, results[0].Key); Assert.Equal(1, results[0].PackageRegistrationKey); } - [Fact] - public void ResultsIncludeVersionAndNormalizedVersion() + [Theory] + [InlineData(null)] + [InlineData("2.0.0")] + public void ResultsIncludeVersionAndNormalizedVersion(string semVerLevel) { var packages = new List { @@ -67,8 +73,10 @@ public void ResultsIncludeVersionAndNormalizedVersion() Title = "Package #1 4.2.0", Description = "Package #1 is an awesome package", Listed = true, - IsLatestStable = true, IsLatest = true, + IsLatestSemVer2 = true, + IsLatestStable = true, + IsLatestStableSemVer2 = true, IsPrerelease = true, DownloadCount = 100, FlattenedAuthors = "", @@ -79,15 +87,17 @@ public void ResultsIncludeVersionAndNormalizedVersion() } }; - var results = IndexAndSearch(packages, "awesome"); + var results = IndexAndSearch(packages, "awesome", semVerLevel); Assert.Single(results); Assert.Equal("01.02.03", results[0].Version); Assert.Equal("1.2.3", results[0].NormalizedVersion); } - [Fact] - public void ResultsIncludeVersionAndNormalizedVersionEvenIfNormalizedVersionColumnNull() + [Theory] + [InlineData(null)] + [InlineData("2.0.0")] + public void ResultsIncludeVersionAndNormalizedVersionEvenIfNormalizedVersionColumnNull(string semVerLevel) { var packages = new List { @@ -103,8 +113,10 @@ public void ResultsIncludeVersionAndNormalizedVersionEvenIfNormalizedVersionColu Title = "Package #1 4.2.0", Description = "Package #1 is an awesome package", Listed = true, - IsLatestStable = true, IsLatest = true, + IsLatestSemVer2 = true, + IsLatestStable = true, + IsLatestStableSemVer2 = true, IsPrerelease = true, DownloadCount = 100, FlattenedAuthors = "", @@ -115,7 +127,7 @@ public void ResultsIncludeVersionAndNormalizedVersionEvenIfNormalizedVersionColu } }; - var results = IndexAndSearch(packages, "awesome"); + var results = IndexAndSearch(packages, "awesome", semVerLevel); Assert.Single(results); Assert.Equal("01.02.03", results[0].Version); @@ -123,8 +135,10 @@ public void ResultsIncludeVersionAndNormalizedVersionEvenIfNormalizedVersionColu } // This works because we do some wildcard magic in our searches. - [Fact] - public void IndexAndSearchDavid123For12() + [Theory] + [InlineData(null)] + [InlineData("2.0.0")] + public void IndexAndSearchDavid123For12(string semVerLevel) { var packages = new List { @@ -141,21 +155,25 @@ public void IndexAndSearchDavid123For12() Description = "Description", Listed = true, IsLatest = true, + IsLatestSemVer2 = true, IsLatestStable = true, + IsLatestStableSemVer2 = true, FlattenedAuthors = "DavidX", Title = "DavidTest123", Version = "1.1", } }; - var results = IndexAndSearch(packages, "12"); + var results = IndexAndSearch(packages, "12", semVerLevel); Assert.Single(results); Assert.Equal("DavidTest123", results[0].Title); } - [Fact] - public void IndexAndSearchWithWordStemming() + [Theory] + [InlineData(null)] + [InlineData("2.0.0")] + public void IndexAndSearchWithWordStemming(string semVerLevel) { var packages = new List { @@ -179,14 +197,16 @@ public void IndexAndSearchWithWordStemming() } }; - var results = IndexAndSearch(packages, "compressed"); + var results = IndexAndSearch(packages, "compressed", semVerLevel); Assert.Empty(results); // currently stemming is not working //Assert.Equal("SuperzipLib", results[0].Title); } - [Fact] - public void SearchUsingCombinedIdAndGeneralTerms() + [Theory] + [InlineData(null)] + [InlineData("2.0.0")] + public void SearchUsingCombinedIdAndGeneralTerms(string semVerLevel) { var packages = new List { @@ -203,7 +223,9 @@ public void SearchUsingCombinedIdAndGeneralTerms() Description = "Yeah", Listed = true, IsLatest = true, + IsLatestSemVer2 = true, IsLatestStable = true, + IsLatestStableSemVer2 = true, FlattenedAuthors = "Eric I", Title = "Red Death", Version = "1.1.2", @@ -221,21 +243,25 @@ public void SearchUsingCombinedIdAndGeneralTerms() Description = "Library for compressing your filez", Listed = true, IsLatest = true, + IsLatestSemVer2 = true, IsLatestStable = true, + IsLatestStableSemVer2 = true, FlattenedAuthors = "Eric II", Title = "Red Herring", Version = "1.1.2", }, }; - var results = IndexAndSearch(packages, "Id:Red Death"); + var results = IndexAndSearch(packages, "Id:Red Death", semVerLevel); Assert.Equal(1, results.Count); Assert.Equal("Red Death", results[0].Title); } - [Fact] - public void SearchUsingExactPackageId() + [Theory] + [InlineData(null)] + [InlineData("2.0.0")] + public void SearchUsingExactPackageId(string semVerLevel) { var packages = new List { @@ -253,7 +279,9 @@ public void SearchUsingExactPackageId() DownloadCount = 3, Listed = true, IsLatest = true, + IsLatestSemVer2 = true, IsLatestStable = true, + IsLatestStableSemVer2 = true, FlattenedAuthors = "M S C", Tags = "NuGetTag", Title = "NuGet.Core", @@ -274,7 +302,9 @@ public void SearchUsingExactPackageId() DownloadCount = 3, Listed = true, IsLatest = true, + IsLatestSemVer2 = true, IsLatestStable = true, + IsLatestStableSemVer2 = true, FlattenedAuthors = "Laugh", Title = "SomeotherNuGet.Core.SimilarlyNamedPackage", Version = "1.5.20902.9026", @@ -282,7 +312,7 @@ public void SearchUsingExactPackageId() }; // simple query - var results = IndexAndSearch(packages, "NuGet.Core"); + var results = IndexAndSearch(packages, "NuGet.Core", semVerLevel); Assert.Equal(2, results.Count); Assert.Equal("NuGet.Core", results[0].Title); Assert.Equal(144, results[0].Key); @@ -326,7 +356,9 @@ public void SearchForNuGetCoreWithExactField(string field, string term) Description = "NuGet.Core is the core framework assembly for NuGet that the rest of NuGet builds upon.", Listed = true, IsLatest = true, + IsLatestSemVer2 = true, IsLatestStable = true, + IsLatestStableSemVer2 = true, FlattenedAuthors = "Alpha Beta Gamma", Title = "NuGet.Core", Version = "1.5.20902.9026", @@ -355,14 +387,21 @@ public void SearchForNuGetCoreWithExactField(string field, string term) }; // query targeted specifically against id field should work equally well - var results = IndexAndSearch(packages, field + ":" + term); + var results = IndexAndSearch(packages, field + ":" + term, semVerLevel: null); + Assert.NotEmpty(results); + Assert.Equal("NuGet.Core", results[0].Title); + Assert.Equal("NuGet.Core", results[0].PackageRegistration.Id); + + results = IndexAndSearch(packages, field + ":" + term, semVerLevel: "2.0.0"); Assert.NotEmpty(results); Assert.Equal("NuGet.Core", results[0].Title); Assert.Equal("NuGet.Core", results[0].PackageRegistration.Id); } - [Fact] - public void SearchForJQueryUICombinedWithPartialId() + [Theory] + [InlineData(null)] + [InlineData("2.0.0")] + public void SearchForJQueryUICombinedWithPartialId(string semVerLevel) { var packages = new List { @@ -379,20 +418,24 @@ public void SearchForJQueryUICombinedWithPartialId() Description = "jQuery UI is etc etc and many more important things", Listed = true, IsLatest = true, + IsLatestSemVer2 = true, IsLatestStable = true, + IsLatestStableSemVer2 = true, FlattenedAuthors = "Alpha Beta Gamma", Title = "JQuery UI (Combined Blobbary)", Tags = "web javascript", }, }; - var results = IndexAndSearch(packages, "id:JQuery.ui"); + var results = IndexAndSearch(packages, "id:JQuery.ui", semVerLevel); Assert.NotEmpty(results); Assert.Equal("JQuery.UI.Combined", results[0].PackageRegistration.Id); } - [Fact] - public void SearchForDegenerateSingleQuoteQuery() + [Theory] + [InlineData(null)] + [InlineData("2.0.0")] + public void SearchForDegenerateSingleQuoteQuery(string semVerLevel) { var packages = new List { @@ -409,20 +452,24 @@ public void SearchForDegenerateSingleQuoteQuery() Description = "jQuery UI is etc etc and many more important things", Listed = true, IsLatest = true, + IsLatestSemVer2 = true, IsLatestStable = true, + IsLatestStableSemVer2 = true, FlattenedAuthors = "Alpha Beta Gamma", Title = "JQuery UI (Combined Blobbary)", Tags = "web javascript", }, }; - var results = IndexAndSearch(packages, "\""); + var results = IndexAndSearch(packages, "\"", semVerLevel); Assert.NotEmpty(results); Assert.Equal("JQuery.UI.Combined", results[0].PackageRegistration.Id); } - [Fact] - public void SearchUsesPackageRegistrationDownloadCountsToPrioritize() + [Theory] + [InlineData(null)] + [InlineData("2.0.0")] + public void SearchUsesPackageRegistrationDownloadCountsToPrioritize(string semVerLevel) { var packages = new List { @@ -439,8 +486,10 @@ public void SearchUsesPackageRegistrationDownloadCountsToPrioritize() Description = "FooQuery is overall much less popular than JQuery UI", DownloadCount = 5, Listed = true, - IsLatest = true, - IsLatestStable = true, + IsLatest = true, + IsLatestSemVer2 = true, + IsLatestStable = true, + IsLatestStableSemVer2 = true, FlattenedAuthors = "Alpha Beta Gamma", Title = "FooQuery", Tags = "web javascript", @@ -458,22 +507,26 @@ public void SearchUsesPackageRegistrationDownloadCountsToPrioritize() DownloadCount = 3, Description = "jQuery UI has only a few downloads of its latest and greatest version, but many total downloads", Listed = true, - IsLatest = true, - IsLatestStable = true, + IsLatest = true, + IsLatestSemVer2 = true, + IsLatestStable = true, + IsLatestStableSemVer2 = true, FlattenedAuthors = "Alpha Beta Gamma", Title = "JQuery UI (Combined Blobbary)", Tags = "web javascript", }, }; - var results = IndexAndSearch(packages, ""); + var results = IndexAndSearch(packages, string.Empty, semVerLevel); Assert.NotEmpty(results); Assert.Equal("JQuery.UI.Combined", results[0].PackageRegistration.Id); Assert.Equal("FooQuery", results[1].PackageRegistration.Id); } - [Fact] - public void IndexAndSearchRetrievesCanDriveV2Feed() + [Theory] + [InlineData(null)] + [InlineData("2.0.0")] + public void IndexAndSearchRetrievesCanDriveV2Feed(string semVerLevel) { Package p = new Package { @@ -495,7 +548,9 @@ public void IndexAndSearchRetrievesCanDriveV2Feed() // This is a test hash Hash = "Ii4+Gr44RAClAno38k5MYAkcBE6yn2LE2xO+/ViKco45+hoxtwKAytmPWEMCJWhH8FyitjebvS5Fsf+ixI5xIg==", IsLatest = true, + IsLatestSemVer2 = true, IsLatestStable = true, + IsLatestStableSemVer2 = true, IsPrerelease = false, Language = "en", LastUpdated = DateTime.UtcNow, @@ -522,8 +577,11 @@ public void IndexAndSearchRetrievesCanDriveV2Feed() }; var packages = new[] { p }; - var results = IndexAndSearch(packages, ""); - var r = results.AsQueryable().ToV2FeedPackageQuery("http://www.nuget.org/", true).First(); + var results = IndexAndSearch(packages, string.Empty, semVerLevel); + var r = results.AsQueryable().ToV2FeedPackageQuery( + "http://www.nuget.org/", + includeLicenseReport: true, + semVerLevelKey: SemVerLevelKey.Unknown).First(); Assert.Equal("Pride", r.Id); Assert.Equal("3.4 RC", r.Version); @@ -556,8 +614,10 @@ public void IndexAndSearchRetrievesCanDriveV2Feed() } // See issue https://github.com/NuGet/NuGetGallery/issues/406 - [Fact] - public void SearchWorksAroundLuceneQuerySyntaxExceptions() + [Theory] + [InlineData(null)] + [InlineData("2.0.0")] + public void SearchWorksAroundLuceneQuerySyntaxExceptions(string semVerLevel) { var packages = new List { @@ -574,7 +634,9 @@ public void SearchWorksAroundLuceneQuerySyntaxExceptions() Description = "NuGet.Core is the core framework assembly for NuGet that the rest of NuGet builds upon.", Listed = true, IsLatest = true, + IsLatestSemVer2 = true, IsLatestStable = true, + IsLatestStableSemVer2 = true, FlattenedAuthors = "Alpha Beta Gamma", LicenseUrl = "http://nuget.codeplex.com/license", Title = "NuGet.Core", @@ -582,11 +644,11 @@ public void SearchWorksAroundLuceneQuerySyntaxExceptions() }, }; - var results = IndexAndSearch(packages, "*Core"); // Lucene parser throws for leading asterisk in searches + var results = IndexAndSearch(packages, "*Core", semVerLevel); // Lucene parser throws for leading asterisk in searches Assert.NotEmpty(results); } - private IList IndexAndSearch(IEnumerable packages, string searchTerm) + private IList IndexAndSearch(IEnumerable packages, string searchTerm, string semVerLevel) { Directory d = new RAMDirectory(); @@ -614,6 +676,7 @@ private IList IndexAndSearch(IEnumerable packages, string sear Skip = 0, Take = 10, SearchTerm = searchTerm, + SemVerLevel = semVerLevel }; var results = luceneSearchService.Search(searchFilter).Result.Data.ToList(); diff --git a/tests/NuGetGallery.Facts/NuGetGallery.Facts.csproj b/tests/NuGetGallery.Facts/NuGetGallery.Facts.csproj index 100854b412..abe6107208 100644 --- a/tests/NuGetGallery.Facts/NuGetGallery.Facts.csproj +++ b/tests/NuGetGallery.Facts/NuGetGallery.Facts.csproj @@ -197,10 +197,10 @@ ..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll - ..\..\packages\NuGet.Common.4.3.0-preview1-2507\lib\net45\NuGet.Common.dll + ..\..\packages\NuGet.Common.4.3.0-preview1-2524\lib\net45\NuGet.Common.dll - ..\..\packages\NuGet.Frameworks.4.3.0-preview1-2507\lib\net45\NuGet.Frameworks.dll + ..\..\packages\NuGet.Frameworks.4.3.0-preview1-2524\lib\net45\NuGet.Frameworks.dll False @@ -208,17 +208,17 @@ True - ..\..\packages\NuGet.Packaging.4.3.0-preview1-2507\lib\net45\NuGet.Packaging.dll + ..\..\packages\NuGet.Packaging.4.3.0-preview1-2524\lib\net45\NuGet.Packaging.dll - ..\..\packages\NuGet.Packaging.Core.4.3.0-preview1-2507\lib\net45\NuGet.Packaging.Core.dll + ..\..\packages\NuGet.Packaging.Core.4.3.0-preview1-2524\lib\net45\NuGet.Packaging.Core.dll ..\..\packages\NuGet.Services.KeyVault.1.0.0.0\lib\net45\NuGet.Services.KeyVault.dll True - ..\..\packages\NuGet.Versioning.4.3.0-preview1-2507\lib\net45\NuGet.Versioning.dll + ..\..\packages\NuGet.Versioning.4.3.0-preview1-2524\lib\net45\NuGet.Versioning.dll False diff --git a/tests/NuGetGallery.Facts/OData/Interceptors/PackageExtensionsFacts.cs b/tests/NuGetGallery.Facts/OData/Interceptors/PackageExtensionsFacts.cs index 3bcc61bc79..7a01aa3840 100644 --- a/tests/NuGetGallery.Facts/OData/Interceptors/PackageExtensionsFacts.cs +++ b/tests/NuGetGallery.Facts/OData/Interceptors/PackageExtensionsFacts.cs @@ -12,8 +12,10 @@ public class PackageExtensionsFacts { public class TheProjectV2FeedPackageMethod { - [Fact] - public void MapsBasicPackagePropertiesCorrectly() + [Theory] + [InlineData(null)] // SemVerLevelKey.Unknown + [InlineData(2)] // SemVerLevelKey.SemVer2 + public void MapsBasicPackagePropertiesCorrectly(int? semVerLevelKey) { // Arrange var packages = new List @@ -25,7 +27,8 @@ public void MapsBasicPackagePropertiesCorrectly() var projected = PackageExtensions.ProjectV2FeedPackage( packages, siteRoot: "http://nuget.org", - includeLicenseReport: true).ToList(); + includeLicenseReport: true, + semVerLevelKey: semVerLevelKey).ToList(); // Assert var actual = projected.Single(); @@ -74,7 +77,8 @@ public void InjectsGalleryUrlsCorrectly() var projected = PackageExtensions.ProjectV2FeedPackage( packages, siteRoot: "http://nuget.org", - includeLicenseReport: true).ToList(); + includeLicenseReport: true, + semVerLevelKey: SemVerLevelKey.Unknown).ToList(); // Assert var actual = projected.Single(); @@ -97,7 +101,8 @@ public void InjectsDummyDateIfNotListed() var projected = PackageExtensions.ProjectV2FeedPackage( packages, siteRoot: "http://nuget.org", - includeLicenseReport: true).ToList(); + includeLicenseReport: true, + semVerLevelKey: SemVerLevelKey.Unknown).ToList(); // Assert var actual = projected.Single(); @@ -117,7 +122,8 @@ public void ReturnsNullLicenseReportInfoIfFeatureDisabled() var projected = PackageExtensions.ProjectV2FeedPackage( packages, siteRoot: "http://nuget.org", - includeLicenseReport: false).ToList(); + includeLicenseReport: false, + semVerLevelKey: SemVerLevelKey.Unknown).ToList(); // Assert var actual = projected.Single(); @@ -147,7 +153,9 @@ public static Package CreateFakeBasePackage() Description = "The standard repository for all knowledge and wisdom", IconUrl = "http://notreal.example/foo.ico", IsLatestStable = false, + IsLatestStableSemVer2 = false, IsLatest = true, + IsLatestSemVer2 = true, IsPrerelease = true, LastUpdated = new DateTime(2002, 4, 30), Language = "en-GB", diff --git a/tests/NuGetGallery.Facts/Services/PackageServiceFacts.cs b/tests/NuGetGallery.Facts/Services/PackageServiceFacts.cs index 3171ba6826..89d4782226 100644 --- a/tests/NuGetGallery.Facts/Services/PackageServiceFacts.cs +++ b/tests/NuGetGallery.Facts/Services/PackageServiceFacts.cs @@ -8,7 +8,6 @@ using Moq; using NuGet.Frameworks; using NuGet.Packaging; -using NuGet.Packaging.Core; using NuGet.Versioning; using NuGetGallery.Auditing; using NuGetGallery.Framework; @@ -1026,8 +1025,7 @@ public async Task DoNotCommitIfCommitChangesIsFalse() packageRegistration.Packages.Add(package); var packageRepository = new Mock>(); - var service = CreateService(packageRepository: packageRepository, setup: - mockService => { mockService.Setup(x => x.FindPackageByIdAndVersion(It.IsAny(), It.IsAny(), true)).Returns(package); }); + var service = CreateService(packageRepository: packageRepository); // Act await service.UpdateIsLatestAsync(packageRegistration, commitChanges: false); @@ -1045,8 +1043,7 @@ public async Task CommitIfCommitChangesIsTrue() packageRegistration.Packages.Add(package); var packageRepository = new Mock>(); - var service = CreateService(packageRepository: packageRepository, setup: - mockService => { mockService.Setup(x => x.FindPackageByIdAndVersion(It.IsAny(), It.IsAny(), true)).Returns(package); }); + var service = CreateService(packageRepository: packageRepository); // Act await service.UpdateIsLatestAsync(packageRegistration, true); @@ -1068,8 +1065,7 @@ public async Task WillUpdateIsLatest1() var packageRepository = new Mock>(MockBehavior.Strict); packageRepository.Setup(r => r.CommitChangesAsync()) .Returns(Task.CompletedTask).Verifiable(); - var service = CreateService(packageRepository: packageRepository, setup: - mockService => { mockService.Setup(x => x.FindPackageByIdAndVersion(It.IsAny(), It.IsAny(), true)).Returns(package10A); }); + var service = CreateService(packageRepository: packageRepository); // Act await service.UpdateIsLatestAsync(packageRegistration, true); @@ -1097,8 +1093,7 @@ public async Task WillUpdateIsLatest2() var packageRepository = new Mock>(MockBehavior.Strict); packageRepository.Setup(r => r.CommitChangesAsync()) .Returns(Task.CompletedTask).Verifiable(); - var service = CreateService(packageRepository: packageRepository, setup: - mockService => { mockService.Setup(x => x.FindPackageByIdAndVersion(It.IsAny(), It.IsAny(), true)).Returns(package100); }); + var service = CreateService(packageRepository: packageRepository); // Act await service.UpdateIsLatestAsync(packageRegistration, true); @@ -1142,13 +1137,15 @@ public void WillThrowIfIdIsNullOrEmpty(string id) Assert.Equal("id", ex.ParamName); } - [Fact] - public void ReturnsTheLatestStableVersionIfAvailable() + [Theory] + [InlineData(null)] + [InlineData("2.0.0")] + public void ReturnsTheLatestStableVersionIfAvailable(string semVerLevel) { // Arrange var repository = new Mock>(MockBehavior.Strict); var packageRegistration = new PackageRegistration { Id = "theId" }; - var package1 = new Package { Version = "1.0", PackageRegistration = packageRegistration, Listed = true, IsLatestStable = true }; + var package1 = new Package { Version = "1.0", PackageRegistration = packageRegistration, Listed = true, IsLatestStable = true, IsLatestStableSemVer2 = true }; var package2 = new Package { Version = "1.0.0a", PackageRegistration = packageRegistration, IsPrerelease = true, Listed = true, IsLatest = true }; repository @@ -1157,12 +1154,35 @@ public void ReturnsTheLatestStableVersionIfAvailable() var service = CreateService(packageRepository: repository); // Act - var result = service.FindPackageByIdAndVersion("theId", version: null); + var result = service.FindPackageByIdAndVersion("theId", version: null, semVerLevelKey: SemVerLevelKey.ForSemVerLevel(semVerLevel)); // Assert Assert.NotNull(result); Assert.Equal("1.0", result.Version); } + + [Fact] + public void ReturnsTheLatestStableSemVer2VersionIfAvailable() + { + // Arrange + var repository = new Mock>(MockBehavior.Strict); + var packageRegistration = new PackageRegistration { Id = "theId" }; + var package0 = new Package { Version = "1.0.0+metadata", PackageRegistration = packageRegistration, Listed = true, IsLatestStableSemVer2 = true }; + var package1 = new Package { Version = "1.0", PackageRegistration = packageRegistration, Listed = true, IsLatestStable = true }; + var package2 = new Package { Version = "1.0.0a", PackageRegistration = packageRegistration, IsPrerelease = true, Listed = true, IsLatest = true }; + + repository + .Setup(repo => repo.GetAll()) + .Returns(new[] { package0, package1, package2 }.AsQueryable()); + var service = CreateService(packageRepository: repository); + + // Act + var result = service.FindPackageByIdAndVersion("theId", version: null, semVerLevelKey: SemVerLevelKey.SemVer2); + + // Assert + Assert.NotNull(result); + Assert.Equal("1.0.0+metadata", result.Version); + } [Fact] public void ReturnsTheLatestVersionIfNoLatestStableVersionIsAvailable() @@ -1233,7 +1253,7 @@ public void ReturnsTheMostRecentVersionIfNoLatestVersionIsAvailable() public class TheFindAbsoluteLatestPackageByIdMethod { [Fact] - public void ReturnsTheLatestVersion() + public void ReturnsTheLatestVersionWhenSemVerLevelUnknown() { // Arrange var repository = new Mock>(MockBehavior.Strict); @@ -1247,7 +1267,7 @@ public void ReturnsTheLatestVersion() var service = CreateService(packageRepository: repository); // Act - var result = service.FindAbsoluteLatestPackageById("theId"); + var result = service.FindAbsoluteLatestPackageById("theId", SemVerLevelKey.Unknown); // Assert Assert.NotNull(result); @@ -1255,14 +1275,36 @@ public void ReturnsTheLatestVersion() } [Fact] - public void ReturnsTheMostRecentVersion() + public void ReturnsTheLatestVersionWhenSemVerLevel2() + { + // Arrange + var repository = new Mock>(MockBehavior.Strict); + var packageRegistration = new PackageRegistration { Id = "theId" }; + var package1 = new Package { Version = "1.0", PackageRegistration = packageRegistration, Listed = true, IsLatestStable = true }; + var package2 = new Package { Version = "2.0.0-alpha.1", PackageRegistration = packageRegistration, IsPrerelease = true, Listed = true, IsLatest = true, SemVerLevelKey = SemVerLevelKey.SemVer2 }; + + repository + .Setup(repo => repo.GetAll()) + .Returns(new[] { package1, package2 }.AsQueryable()); + var service = CreateService(packageRepository: repository); + + // Act + var result = service.FindAbsoluteLatestPackageById("theId", SemVerLevelKey.SemVer2); + + // Assert + Assert.NotNull(result); + Assert.Equal("2.0.0-alpha.1", result.Version); + } + + [Fact] + public void ReturnsTheMostRecentVersionWhenSemVerLevelUnknown() { // Arrange var repository = new Mock>(MockBehavior.Strict); var packageRegistration = new PackageRegistration { Id = "theId" }; var package1 = new Package { Version = "1.0", PackageRegistration = packageRegistration, Listed = true }; - var package2 = new Package { Version = "2.0.0a", PackageRegistration = packageRegistration, IsPrerelease = true, Listed = true }; - var package3 = new Package { Version = "2.0.0", PackageRegistration = packageRegistration, Listed = true }; + var package2 = new Package { Version = "2.0.0-alpha", PackageRegistration = packageRegistration, IsPrerelease = true, Listed = true }; + var package3 = new Package { Version = "2.0.0", PackageRegistration = packageRegistration, Listed = true, IsLatest = true }; repository .Setup(repo => repo.GetAll()) @@ -1270,11 +1312,34 @@ public void ReturnsTheMostRecentVersion() var service = CreateService(packageRepository: repository); // Act - var result = service.FindAbsoluteLatestPackageById("theId"); + var result = service.FindAbsoluteLatestPackageById("theId", SemVerLevelKey.Unknown); // Assert Assert.NotNull(result); - Assert.Equal("2.0.0a", result.Version); + Assert.Equal("2.0.0", result.Version); + } + + [Fact] + public void ReturnsTheMostRecentVersionWhenSemVerLevel2() + { + // Arrange + var repository = new Mock>(MockBehavior.Strict); + var packageRegistration = new PackageRegistration { Id = "theId" }; + var package1 = new Package { Version = "1.0", PackageRegistration = packageRegistration, Listed = true }; + var package2 = new Package { Version = "2.0.0-alpha.1", PackageRegistration = packageRegistration, IsPrerelease = true, Listed = true, SemVerLevelKey = SemVerLevelKey.SemVer2 }; + var package3 = new Package { Version = "2.0.0+metadata", PackageRegistration = packageRegistration, Listed = true, SemVerLevelKey = SemVerLevelKey.SemVer2, IsLatestSemVer2 = true }; + + repository + .Setup(repo => repo.GetAll()) + .Returns(new[] { package1, package2, package3 }.AsQueryable()); + var service = CreateService(packageRepository: repository); + + // Act + var result = service.FindAbsoluteLatestPackageById("theId", SemVerLevelKey.SemVer2); + + // Assert + Assert.NotNull(result); + Assert.Equal("2.0.0+metadata", result.Version); } } @@ -1624,7 +1689,7 @@ public async Task WillSetThePublishedDateOnThePackageBeingPublished() package.PackageRegistration.Packages.Add(package); var packageRepository = new Mock>(); var service = CreateService(packageRepository: packageRepository, setup: - mockPackageService => { mockPackageService.Setup(x => x.FindPackageByIdAndVersion(It.IsAny(), It.IsAny(), true)).Returns(package); }); + mockPackageService => { mockPackageService.Setup(x => x.FindPackageByIdAndVersionStrict(It.IsAny(), It.IsAny())).Returns(package); }); await service.PublishPackageAsync("theId", "1.0.42"); @@ -1647,7 +1712,7 @@ public async Task WillSetThePublishedDateOnThePackageBeingPublishedWithOverload( package.PackageRegistration.Packages.Add(package); var packageRepository = new Mock>(); var service = CreateService(packageRepository: packageRepository, setup: - mockPackageService => { mockPackageService.Setup(x => x.FindPackageByIdAndVersion(It.IsAny(), It.IsAny(), true)).Returns(package); }); + mockPackageService => { mockPackageService.Setup(x => x.FindPackageByIdAndVersionStrict(It.IsAny(), It.IsAny())).Returns(package); }); await service.PublishPackageAsync(package, commitChanges: false); @@ -1671,7 +1736,7 @@ public async Task WillSetUpdateIsLatestStableOnThePackageWhenItIsTheLatestVersio package.PackageRegistration.Packages.Add(package); var packageRepository = new Mock>(); var service = CreateService(packageRepository: packageRepository, setup: - mockPackageService => { mockPackageService.Setup(x => x.FindPackageByIdAndVersion(It.IsAny(), It.IsAny(), true)).Returns(package); }); + mockPackageService => { mockPackageService.Setup(x => x.FindPackageByIdAndVersionStrict(It.IsAny(), It.IsAny())).Returns(package); }); await service.PublishPackageAsync(package); @@ -1694,7 +1759,7 @@ public async Task WillSetUpdateIsLatestStableOnThePackageWhenItIsTheLatestVersio package.PackageRegistration.Packages.Add(new Package { Version = "1.0", PackageRegistration = package.PackageRegistration }); var packageRepository = new Mock>(); var service = CreateService(packageRepository: packageRepository, setup: - mockPackageService => { mockPackageService.Setup(x => x.FindPackageByIdAndVersion(It.IsAny(), It.IsAny(), true)).Returns(package); }); + mockPackageService => { mockPackageService.Setup(x => x.FindPackageByIdAndVersionStrict(It.IsAny(), It.IsAny())).Returns(package); }); await service.PublishPackageAsync("theId", "1.0.42"); @@ -1723,7 +1788,7 @@ public async Task WillNotSetUpdateIsLatestStableOnThePackageWhenItIsNotTheLatest }); var packageRepository = new Mock>(); var service = CreateService(packageRepository: packageRepository, setup: - mockPackageService => { mockPackageService.Setup(x => x.FindPackageByIdAndVersion(It.IsAny(), It.IsAny(), true)).Returns(package); }); + mockPackageService => { mockPackageService.Setup(x => x.FindPackageByIdAndVersionStrict(It.IsAny(), It.IsAny())).Returns(package); }); await service.PublishPackageAsync(package); @@ -1754,7 +1819,7 @@ public async Task PublishPackageUpdatesIsAbsoluteLatestForPrereleasePackage() package.PackageRegistration.Packages.Add(package39); var packageRepository = new Mock>(); var service = CreateService(packageRepository: packageRepository, setup: - mockPackageService => { mockPackageService.Setup(x => x.FindPackageByIdAndVersion(It.IsAny(), It.IsAny(), true)).Returns(package); }); + mockPackageService => { mockPackageService.Setup(x => x.FindPackageByIdAndVersionStrict(It.IsAny(), It.IsAny())).Returns(package); }); await service.PublishPackageAsync("theId", "1.0.42-alpha"); Assert.True(package39.IsLatestStable); @@ -1787,7 +1852,7 @@ public async Task PublishPackageUpdatesIsAbsoluteLatestForPrereleasePackageWithO package.PackageRegistration.Packages.Add(package39); var packageRepository = new Mock>(); var service = CreateService(packageRepository: packageRepository, setup: - mockPackageService => { mockPackageService.Setup(x => x.FindPackageByIdAndVersion(It.IsAny(), It.IsAny(), true)).Returns(package); }); + mockPackageService => { mockPackageService.Setup(x => x.FindPackageByIdAndVersionStrict(It.IsAny(), It.IsAny())).Returns(package); }); await service.PublishPackageAsync(package); @@ -1822,7 +1887,7 @@ public async Task SetUpdateDoesNotSetIsLatestStableForAnyIfAllPackagesArePrerele package.PackageRegistration.Packages.Add(package39); var packageRepository = new Mock>(); var service = CreateService(packageRepository: packageRepository, setup: - mockPackageService => { mockPackageService.Setup(x => x.FindPackageByIdAndVersion(It.IsAny(), It.IsAny(), true)).Returns(package); }); + mockPackageService => { mockPackageService.Setup(x => x.FindPackageByIdAndVersionStrict(It.IsAny(), It.IsAny())).Returns(package); }); await service.PublishPackageAsync("theId", "1.0.42-alpha"); Assert.False(package39.IsLatestStable); @@ -1856,7 +1921,7 @@ public async Task SetUpdateDoesNotSetIsLatestStableForAnyIfAllPackagesArePrerele package.PackageRegistration.Packages.Add(package39); var packageRepository = new Mock>(); var service = CreateService(packageRepository: packageRepository, setup: - mockPackageService => { mockPackageService.Setup(x => x.FindPackageByIdAndVersion(It.IsAny(), It.IsAny(), true)).Returns(package); }); + mockPackageService => { mockPackageService.Setup(x => x.FindPackageByIdAndVersionStrict(It.IsAny(), It.IsAny())).Returns(package); }); await service.PublishPackageAsync(package); Assert.False(package39.IsLatestStable); @@ -1871,7 +1936,7 @@ public async Task WillThrowIfThePackageDoesNotExist() var service = CreateService(setup: mockPackageService => { - mockPackageService.Setup(x => x.FindPackageByIdAndVersion(It.IsAny(), It.IsAny(), true)).Returns( + mockPackageService.Setup(x => x.FindPackageByIdAndVersionStrict(It.IsAny(), It.IsAny())).Returns( (Package)null); }); diff --git a/tests/NuGetGallery.Facts/Services/ReflowPackageServiceFacts.cs b/tests/NuGetGallery.Facts/Services/ReflowPackageServiceFacts.cs index f3c8128f87..74c6ba244d 100644 --- a/tests/NuGetGallery.Facts/Services/ReflowPackageServiceFacts.cs +++ b/tests/NuGetGallery.Facts/Services/ReflowPackageServiceFacts.cs @@ -299,7 +299,7 @@ private static Mock SetupPackageService(Package package) packageService.CallBase = true; packageService - .Setup(s => s.FindPackageByIdAndVersion("test", "1.0.0", true)) + .Setup(s => s.FindPackageByIdAndVersionStrict("test", "1.0.0")) .Returns(package) .Verifiable(); diff --git a/tests/NuGetGallery.Facts/packages.config b/tests/NuGetGallery.Facts/packages.config index 43e1c54671..b628017d6e 100644 --- a/tests/NuGetGallery.Facts/packages.config +++ b/tests/NuGetGallery.Facts/packages.config @@ -38,13 +38,13 @@ - - + + - - + + - +