Skip to content

Commit 98d7c35

Browse files
authored
Feature flag to show reduced version list on package details page (#10506)
* Feature flag
1 parent 515edc5 commit 98d7c35

9 files changed

Lines changed: 148 additions & 6 deletions

File tree

src/AccountDeleter/EmptyFeatureFlagService.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,5 +358,10 @@ public bool IsMcpServerPackageDisplayEnabled()
358358
{
359359
throw new NotImplementedException();
360360
}
361+
362+
public bool IsReducedVersionListsEnabled()
363+
{
364+
throw new NotImplementedException();
365+
}
361366
}
362367
}

src/GitHubVulnerabilities2Db/Fakes/FakeFeatureFlagService.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,5 +359,10 @@ public bool IsMcpServerPackageDisplayEnabled()
359359
{
360360
throw new NotImplementedException();
361361
}
362+
363+
public bool IsReducedVersionListsEnabled()
364+
{
365+
throw new NotImplementedException();
366+
}
362367
}
363368
}

src/NuGetGallery.Services/Configuration/FeatureFlagService.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ public class FeatureFlagService : IFeatureFlagService
7171
private const string McpServerPackageFilteringFeatureName = GalleryPrefix + "McpServerPackageFiltering";
7272
private const string McpServerPackageDisplayFeatureName = GalleryPrefix + "McpServerPackageDisplay";
7373
private const string EnableApiKeyV5ForOIDCFeatureName = GalleryPrefix + "EnableApiKeyV5ForOIDC";
74+
private const string ReducedVersionLists = GalleryPrefix + "ReducedVersionLists";
7475

7576
private const string ODataV1GetAllNonHijackedFeatureName = GalleryPrefix + "ODataV1GetAllNonHijacked";
7677
private const string ODataV1GetAllCountNonHijackedFeatureName = GalleryPrefix + "ODataV1GetAllCountNonHijacked";
@@ -421,7 +422,8 @@ public bool IsNuGetAccountPasswordLoginEnabled()
421422
return _client.IsEnabled(NuGetAccountPasswordLoginFeatureName, defaultValue: true);
422423
}
423424

424-
public bool IsFrameworkFilteringEnabled(User user) {
425+
public bool IsFrameworkFilteringEnabled(User user)
426+
{
425427
return _client.IsEnabled(FrameworkFilteringFeatureName, user, defaultValue: false);
426428
}
427429

@@ -464,5 +466,10 @@ public bool IsMcpServerPackageDisplayEnabled()
464466
{
465467
return _client.IsEnabled(McpServerPackageDisplayFeatureName, defaultValue: false);
466468
}
469+
470+
public bool IsReducedVersionListsEnabled()
471+
{
472+
return _client.IsEnabled(ReducedVersionLists, defaultValue: false);
473+
}
467474
}
468475
}

src/NuGetGallery.Services/Configuration/IFeatureFlagService.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,5 +376,10 @@ public interface IFeatureFlagService
376376
bool IsMcpServerPackageFilteringEnabled();
377377

378378
bool IsMcpServerPackageDisplayEnabled();
379+
380+
/// <summary>
381+
/// when enabled, the package details page shows short version lists.
382+
/// </summary>
383+
bool IsReducedVersionListsEnabled();
379384
}
380385
}

src/NuGetGallery.Services/PackageManagement/IPackageService.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,25 @@ IReadOnlyCollection<Package> FindPackagesById(
5555
bool includeDeprecations,
5656
bool includeSupportedFrameworks);
5757

58+
/// <summary>
59+
/// Returns <paramref name="maxCount"/> latest (by Created) versions of packages with an <see cref="Package.Id"/> of <paramref name="id"/>.
60+
/// The list always includes the <paramref name="includeVersion"/> version if specified.
61+
/// </summary>
62+
/// <param name="id"></param>
63+
/// <param name="includeVersion"></param>
64+
/// <param name="includePackageRegistration"></param>
65+
/// <param name="includeDeprecations"></param>
66+
/// <param name="includeSupportedFrameworks"></param>
67+
/// <param name="maxCount"></param>
68+
/// <returns></returns>
69+
IReadOnlyCollection<Package> FindLatestVersionsById(
70+
string id,
71+
string includeVersion,
72+
bool includePackageRegistration,
73+
bool includeDeprecations,
74+
bool includeSupportedFrameworks,
75+
int maxCount);
76+
5877
/// <summary>
5978
/// Gets the package with the given ID and version when exists;
6079
/// otherwise gets the latest package version for the given package ID matching the provided constraints.

src/NuGetGallery.Services/PackageManagement/PackageService.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,64 @@ public IReadOnlyCollection<Package> FindPackagesById(
275275
return packages.ToList();
276276
}
277277

278+
public IReadOnlyCollection<Package> FindLatestVersionsById(
279+
string id,
280+
string includeVersion,
281+
bool includePackageRegistration,
282+
bool includeDeprecations,
283+
bool includeSupportedFrameworks,
284+
int maxCount)
285+
{
286+
if (id == null)
287+
{
288+
throw new ArgumentNullException(nameof(id));
289+
}
290+
291+
if (maxCount < 1)
292+
{
293+
throw new ArgumentOutOfRangeException(nameof(maxCount), "Max count must be greater than 0.");
294+
}
295+
296+
var packages = GetPackagesByIdQueryable(
297+
id,
298+
includeLicenseReports: false,
299+
includePackageRegistration: includePackageRegistration,
300+
includeUser: false,
301+
includeSymbolPackages: false,
302+
includeDeprecations: includeDeprecations,
303+
includeDeprecationRelationships: false,
304+
includeSupportedFrameworks: includeSupportedFrameworks)
305+
.OrderByDescending(p => p.Created)
306+
.Take(maxCount)
307+
.ToList();
308+
309+
if (!string.IsNullOrWhiteSpace(includeVersion) && !packages.Any(p => p.NormalizedVersion == includeVersion))
310+
{
311+
var requiredPackage = GetPackagesByIdQueryable(
312+
id,
313+
includeLicenseReports: false,
314+
includePackageRegistration: includePackageRegistration,
315+
includeUser: false,
316+
includeSymbolPackages: false,
317+
includeDeprecations: includeDeprecations,
318+
includeDeprecationRelationships: false,
319+
includeSupportedFrameworks: includeSupportedFrameworks)
320+
.Where(p => p.NormalizedVersion == includeVersion)
321+
.SingleOrDefault();
322+
323+
if (requiredPackage is not null)
324+
{
325+
if (packages.Count >= maxCount)
326+
{
327+
packages.RemoveAt(packages.Count - 1);
328+
}
329+
packages.Add(requiredPackage);
330+
}
331+
}
332+
333+
return packages;
334+
}
335+
278336
public virtual Package FindPackageByIdAndVersion(
279337
string id,
280338
string version,

src/NuGetGallery/Controllers/PackagesController.cs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -926,11 +926,25 @@ public virtual async Task<ActionResult> DisplayPackage(string id, string version
926926
return RedirectToActionPermanent("DisplayPackage", new { id = id, version = normalized });
927927
}
928928

929-
// Load all packages with the ID.
930-
var allVersions = _packageService.FindPackagesById(id,
931-
includePackageRegistration: true,
932-
includeDeprecations: true,
933-
includeSupportedFrameworks: true);
929+
IReadOnlyCollection<Package> allVersions;
930+
931+
if (_featureFlagService.IsReducedVersionListsEnabled())
932+
{
933+
allVersions = _packageService.FindLatestVersionsById(id,
934+
includeVersion: normalized,
935+
includePackageRegistration: true,
936+
includeDeprecations: true,
937+
includeSupportedFrameworks: true,
938+
maxCount: 20);
939+
}
940+
else
941+
{
942+
// Load all packages with the ID.
943+
allVersions = _packageService.FindPackagesById(id,
944+
includePackageRegistration: true,
945+
includeDeprecations: true,
946+
includeSupportedFrameworks: true);
947+
}
934948

935949
var filterContext = new PackageFilterContext(RouteData?.Route, version);
936950
var package = _packageFilter.GetFiltered(allVersions, filterContext);

src/VerifyMicrosoftPackage/Fakes/FakeFeatureFlagService.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,5 +149,7 @@ public class FakeFeatureFlagService : IFeatureFlagService
149149
public bool IsMcpServerPackageFilteringEnabled() => throw new NotImplementedException();
150150

151151
public bool IsMcpServerPackageDisplayEnabled() => throw new NotImplementedException();
152+
153+
public bool IsReducedVersionListsEnabled() => throw new NotImplementedException();
152154
}
153155
}

tests/NuGetGallery.Facts/Controllers/PackagesControllerFacts.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,9 @@ private static PackagesController CreateController(
241241
{
242242
featureFlagService = new Mock<IFeatureFlagService>();
243243
featureFlagService.SetReturnsDefault<bool>(true);
244+
featureFlagService
245+
.Setup(ff => ff.IsReducedVersionListsEnabled())
246+
.Returns(false);
244247
}
245248

246249
renameService = renameService ?? new Mock<IPackageRenameService>();
@@ -538,6 +541,30 @@ public async Task IsIndexCheckOnlyHappensForRecentlyChangedPackages(DateTime cre
538541
searchService.Verify(x => x.RawSearch(It.IsAny<SearchFilter>()), Times.Exactly(searchTimes));
539542
}
540543

544+
[Fact]
545+
public async Task UsesFindLatestVersionsWhenFeatureFlagIsEnabled()
546+
{
547+
var featureFlagServiceMock = new Mock<IFeatureFlagService>();
548+
featureFlagServiceMock
549+
.Setup(ff => ff.IsReducedVersionListsEnabled())
550+
.Returns(true);
551+
552+
var packageService = new Mock<IPackageService>();
553+
554+
var controller = CreateController(
555+
GetConfigurationService(),
556+
packageService: packageService,
557+
featureFlagService: featureFlagServiceMock);
558+
559+
await controller.DisplayPackage("TestPackage", "0.1.2");
560+
561+
packageService
562+
.Verify(ps => ps.FindLatestVersionsById("TestPackage", "0.1.2", It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<int>()), Times.Once);
563+
564+
packageService
565+
.Verify(ps => ps.FindPackagesById(It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<bool>()), Times.Never);
566+
}
567+
541568
[Fact]
542569
public async Task GivenANonNormalizedVersionIt302sToTheNormalizedVersion()
543570
{

0 commit comments

Comments
 (0)