Skip to content

Commit 681a2ed

Browse files
authored
Implement package type filtering on local folder search (#7505)
1 parent ba53358 commit 681a2ed

2 files changed

Lines changed: 102 additions & 1 deletion

File tree

src/NuGet.Core/NuGet.Protocol/LocalRepositories/LocalPackageSearchResource.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using System.Threading;
1212
using System.Threading.Tasks;
1313
using NuGet.Common;
14+
using NuGet.Packaging.Core;
1415
using NuGet.Protocol.Core.Types;
1516
using NuGet.Versioning;
1617

@@ -30,7 +31,7 @@ public LocalPackageSearchResource(FindLocalPackagesResource localResource)
3031
_localResource = localResource;
3132
}
3233

33-
public override bool SupportsPackageTypeFiltering => false;
34+
public override bool SupportsPackageTypeFiltering => true;
3435

3536
public async override Task<IEnumerable<IPackageSearchMetadata>> SearchAsync(
3637
string searchTerm,
@@ -63,6 +64,12 @@ public async override Task<IEnumerable<IPackageSearchMetadata>> SearchAsync(
6364
query = query.Where(package => ContainsAnyTerm(terms, package));
6465
}
6566

67+
// Filter on package type
68+
if (!string.IsNullOrEmpty(filters?.PackageType))
69+
{
70+
query = query.Where(package => MatchesPackageType(package, filters.PackageType));
71+
}
72+
6673
// Collapse to the highest version per id, if necessary
6774
var collapsedQuery = filters?.Filter == SearchFilterType.IsLatestVersion ||
6875
filters?.Filter == SearchFilterType.IsAbsoluteLatestVersion
@@ -103,6 +110,27 @@ private static bool ContainsAnyTerm(string[] terms, LocalPackageInfo package)
103110
return false;
104111
}
105112

113+
private static bool MatchesPackageType(LocalPackageInfo package, string packageType)
114+
{
115+
IReadOnlyList<PackageType> packageTypes = package.Nuspec.GetPackageTypes();
116+
117+
// A package that does not declare any package type is implicitly a "Dependency" package.
118+
if (packageTypes.Count == 0)
119+
{
120+
return PackageType.PackageTypeNameComparer.Equals(packageType, PackageType.Dependency.Name);
121+
}
122+
123+
foreach (PackageType type in packageTypes.NoAllocEnumerate())
124+
{
125+
if (PackageType.PackageTypeNameComparer.Equals(type.Name, packageType))
126+
{
127+
return true;
128+
}
129+
}
130+
131+
return false;
132+
}
133+
106134
private static bool ContainsTerm(string search, string property)
107135
{
108136
int? pos = property?.IndexOf(search, StringComparison.OrdinalIgnoreCase);

test/NuGet.Core.Tests/NuGet.Protocol.Tests/LocalResourceTests/LocalPackageSearchResourceTests.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Threading.Tasks;
99
using System.Xml.Linq;
1010
using NuGet.Common;
11+
using NuGet.Packaging.Core;
1112
using NuGet.Protocol.Core.Types;
1213
using NuGet.Protocol.Tests.Plugins.Helpers;
1314
using NuGet.Test.Utility;
@@ -732,5 +733,77 @@ public async Task LocalPackageSearchResource_SearchNoFilter_AllversionsMatch(boo
732733
Assert.Equal(0, testLogger.Errors);
733734
}
734735
}
736+
737+
[Fact]
738+
public void LocalPackageSearchResource_SupportsPackageTypeFiltering()
739+
{
740+
// Arrange
741+
var localResource = new FindLocalPackagesResourceV2(root: ".");
742+
var resource = new LocalPackageSearchResource(localResource);
743+
744+
// Act & Assert
745+
Assert.True(resource.SupportsPackageTypeFiltering);
746+
}
747+
748+
[Theory]
749+
[InlineData("Dependency")]
750+
[InlineData("dependency")]
751+
public async Task LocalPackageSearchResource_FilterOnPackageType_MatchesExplicitTypeAndImplicitDependencyAsync(string packageTypeFilter)
752+
{
753+
using (var root = TestDirectory.Create())
754+
{
755+
// Arrange
756+
var testLogger = new TestLogger();
757+
758+
// Package that explicitly declares the Dependency package type.
759+
var dependencyPackage = new SimpleTestPackageContext()
760+
{
761+
Id = "dependencyPackage",
762+
Version = "1.0.0",
763+
PackageTypes = { PackageType.Dependency }
764+
};
765+
766+
// Package that does not declare any package type (implicitly Dependency).
767+
var implicitPackage = new SimpleTestPackageContext()
768+
{
769+
Id = "implicitPackage",
770+
Version = "1.0.0"
771+
};
772+
773+
// Package with a different package type that should be filtered out.
774+
var toolPackage = new SimpleTestPackageContext()
775+
{
776+
Id = "toolPackage",
777+
Version = "1.0.0",
778+
PackageTypes = { PackageType.DotnetTool }
779+
};
780+
781+
await SimpleTestPackageUtility.CreatePackagesAsync(root, dependencyPackage, implicitPackage, toolPackage);
782+
783+
var localResource = new FindLocalPackagesResourceV2(root);
784+
var resource = new LocalPackageSearchResource(localResource);
785+
786+
var filter = new SearchFilter(includePrerelease: true)
787+
{
788+
PackageType = packageTypeFilter
789+
};
790+
791+
// Act
792+
var packages = (await resource.SearchAsync(
793+
searchTerm: "",
794+
filter,
795+
skip: 0,
796+
take: 30,
797+
log: testLogger,
798+
token: CancellationToken.None))
799+
.OrderBy(p => p.Identity.Id)
800+
.ToList();
801+
802+
// Assert
803+
Assert.Equal(2, packages.Count);
804+
Assert.Equal("dependencyPackage", packages[0].Identity.Id);
805+
Assert.Equal("implicitPackage", packages[1].Identity.Id);
806+
}
807+
}
735808
}
736809
}

0 commit comments

Comments
 (0)