Skip to content
Draft
53 changes: 43 additions & 10 deletions src/NuGetGallery/ViewModels/DependencySetsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,45 @@ public DependencySetsViewModel(IEnumerable<PackageDependency> packageDependencie
{
try
{
DependencySets = new Dictionary<string, IEnumerable<DependencyViewModel>>();
// Create a list to hold TFM string, parsed framework, and dependencies for proper sorting
var frameworkGroups = new List<(string tfmString, NuGetFramework framework, string friendlyName, IEnumerable<DependencyViewModel> dependencies)>();

var dependencySets = packageDependencies.GroupBy(d => d.TargetFramework);

OnlyHasAllFrameworks = dependencySets.Count() == 1 && dependencySets.First().Key == null;

foreach (var dependencySet in dependencySets)
{
var targetFramework = dependencySet.Key == null
? "All Frameworks"
: NuGetFramework.Parse(dependencySet.Key).ToFriendlyName();
string tfmString = dependencySet.Key;
string friendlyName;
NuGetFramework framework;

if (!DependencySets.ContainsKey(targetFramework))
if (tfmString == null)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

string.IsNullOrEmpty(tfmString )

{
DependencySets.Add(targetFramework,
dependencySet.OrderBy(x => x.Id).Select(d => d.Id == null ? null : new DependencyViewModel(d.Id, d.VersionSpec)));
friendlyName = "All Frameworks";
framework = null;
}
else
{
framework = NuGetFramework.Parse(tfmString);
friendlyName = framework.ToFriendlyName();
}

var dependencies = dependencySet.OrderBy(x => x.Id, StringComparer.OrdinalIgnoreCase).Select(d => d.Id == null ? null : new DependencyViewModel(d.Id, d.VersionSpec));
frameworkGroups.Add((tfmString, framework, friendlyName, dependencies));
}

// Sort by framework using NuGetFrameworkSorter, with null frameworks (All Frameworks) first
var sortedGroups = frameworkGroups.OrderBy(g => g.framework, NullableFrameworkComparer.Instance);

// Build an ordered list to preserve the sorting
var orderedDependencySets = new List<KeyValuePair<string, IEnumerable<DependencyViewModel>>>();
foreach (var group in sortedGroups)
{
orderedDependencySets.Add(new KeyValuePair<string, IEnumerable<DependencyViewModel>>(group.friendlyName, group.dependencies));
}

// Order the top level frameworks by their resulting friendly name
DependencySets = DependencySets.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value);
DependencySets = orderedDependencySets;
}
catch (Exception e)
{
Expand All @@ -48,7 +66,7 @@ public DependencySetsViewModel(IEnumerable<PackageDependency> packageDependencie
}
}

public IDictionary<string, IEnumerable<DependencyViewModel>> DependencySets { get; private set; }
public IReadOnlyList<KeyValuePair<string, IEnumerable<DependencyViewModel>>> DependencySets { get; private set; }
public bool OnlyHasAllFrameworks { get; private set; }

public class DependencyViewModel
Expand All @@ -72,5 +90,20 @@ public DependencyViewModel(string id, string versionSpec)
public string VersionSpec { get; private set; }
public string PackageUrl { get; private set; }
}

private class NullableFrameworkComparer : IComparer<NuGetFramework>
{
Comment thread
joelverhagen marked this conversation as resolved.
public static readonly NullableFrameworkComparer Instance = new NullableFrameworkComparer();

public int Compare(NuGetFramework x, NuGetFramework y)
{
// Put "All Frameworks" (null) first
if (x == null && y == null) return 0;
if (x == null) return -1;
if (y == null) return 1;
// Use NuGetFrameworkSorter for actual frameworks
return NuGetFrameworkSorter.Instance.Compare(x, y);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Linq;
using NuGet.Services.Entities;
using Xunit;
Expand All @@ -26,10 +27,10 @@ public void GivenAListOfDependenciesItShouldGroupByTargetFrameworkName()

// Assert
Assert.Equal(3, vm.DependencySets.Count);
Assert.Null(vm.DependencySets["All Frameworks"].Single());
Assert.Null(vm.DependencySets["Portable Class Library (.NETFramework 4.5, Windows 8.0)"].Single());
Assert.Null(vm.DependencySets.Single(kvp => kvp.Key == "All Frameworks").Value.Single());
Assert.Null(vm.DependencySets.Single(kvp => kvp.Key == "Portable Class Library (.NETFramework 4.5, Windows 8.0)").Value.Single());

var actual = vm.DependencySets["Portable Class Library (.NETFramework 4.0, Silverlight 4.0, Windows 8.0, WindowsPhone 7.1)"].ToArray();
var actual = vm.DependencySets.Single(kvp => kvp.Key == "Portable Class Library (.NETFramework 4.0, Silverlight 4.0, Windows 8.0, WindowsPhone 7.1)").Value.ToArray();
Assert.Single(actual);
Assert.Equal("Microsoft.Net.Http", actual[0].Id);
Assert.Equal("(>= 2.1.0 && < 3.0.0)", actual[0].VersionSpec);
Expand All @@ -55,7 +56,7 @@ public void GivenAListOfDependenciesTargetFrameworksWillBeOrdered()
// Assert
Assert.Equal(7, viewModel.DependencySets.Count);

var dependencySetsList = viewModel.DependencySets.Keys.ToList();
var dependencySetsList = viewModel.DependencySets.Select(kvp => kvp.Key).ToList();

Assert.Equal(".NETFramework 4.5", dependencySetsList[0]);
Assert.Equal(".NETFramework 4.6.2", dependencySetsList[1]);
Expand Down Expand Up @@ -91,6 +92,57 @@ public void GivenAListOfDependenciesPackageIdsWillBeOrdered()
Assert.Equal("cde", dependencyViewModels[2].Id);
Assert.Equal("def", dependencyViewModels[3].Id);
}

[Fact]
public void GivenAListOfDependenciesPackageIdsWillBeOrderedCaseInsensitively()
{
// Arrange
var dependencies = new[]
{
new PackageDependency { TargetFramework = null, Id = "Zebra" },
new PackageDependency { TargetFramework = null, Id = "apple" },
new PackageDependency { TargetFramework = null, Id = "Banana" },
new PackageDependency { TargetFramework = null, Id = "cherry" }
};

// Act
var viewModel = new DependencySetsViewModel(dependencies);

// Assert
Assert.Single(viewModel.DependencySets);
Assert.Equal(4, viewModel.DependencySets.First().Value.Count());

var dependencyViewModels = viewModel.DependencySets.First().Value.ToList();

Assert.Equal("apple", dependencyViewModels[0].Id);
Assert.Equal("Banana", dependencyViewModels[1].Id);
Assert.Equal("cherry", dependencyViewModels[2].Id);
Assert.Equal("Zebra", dependencyViewModels[3].Id);
}

[Fact]
public void GivenAListOfDependenciesNet10WillBeOrderedAfterNet9()
{
// Arrange
var dependencies = new[]
{
new PackageDependency { TargetFramework = "net10.0" },
new PackageDependency { TargetFramework = "net8.0" },
new PackageDependency { TargetFramework = "net9.0" }
};

// Act
var viewModel = new DependencySetsViewModel(dependencies);

// Assert
Assert.Equal(3, viewModel.DependencySets.Count);

var dependencySetsList = viewModel.DependencySets.Select(kvp => kvp.Key).ToList();

Assert.Equal("net8.0", dependencySetsList[0]);
Assert.Equal("net9.0", dependencySetsList[1]);
Assert.Equal("net10.0", dependencySetsList[2]);
}
}
}
}