Skip to content

Commit 21348d6

Browse files
authored
Add feature flag for new TFM heuristics (#8448)
Add feature flag for new TFM heuristics
1 parent bd9271e commit 21348d6

13 files changed

Lines changed: 222 additions & 12 deletions

File tree

src/NuGetGallery.Services/Configuration/FeatureFlagService.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public class FeatureFlagService : IFeatureFlagService
4040
private const string ShowEnable2FADialog = GalleryPrefix + "ShowEnable2FADialog";
4141
private const string Get2FADismissFeedback = GalleryPrefix + "Get2FADismissFeedback";
4242
private const string PackageRenamesFeatureName = GalleryPrefix + "PackageRenames";
43+
private const string PatternSetTfmHeuristicsFeatureName = GalleryPrefix + "PatternSetTfmHeuristics";
4344
private const string EmbeddedReadmeFlightName = GalleryPrefix + "EmbeddedReadmes";
4445
private const string LicenseMdRenderingFlightName = GalleryPrefix + "LicenseMdRendering";
4546
private const string MarkdigMdRenderingFlightName = GalleryPrefix + "MarkdigMdRendering";
@@ -220,6 +221,11 @@ public bool IsPackageRenamesEnabled(User user)
220221
return _client.IsEnabled(PackageRenamesFeatureName, user, defaultValue: false);
221222
}
222223

224+
public bool ArePatternSetTfmHeuristicsEnabled()
225+
{
226+
return _client.IsEnabled(PatternSetTfmHeuristicsFeatureName, defaultValue: false);
227+
}
228+
223229
public bool AreEmbeddedReadmesEnabled(User user)
224230
{
225231
return _client.IsEnabled(EmbeddedReadmeFlightName, user, defaultValue: false);

src/NuGetGallery.Services/Configuration/IFeatureFlagService.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,11 @@ public interface IFeatureFlagService
158158
/// </summary>
159159
bool IsPackageRenamesEnabled(User user);
160160

161+
/// <summary>
162+
/// Whether we're using pattern set based TFM determination on ingested packages
163+
/// </summary>
164+
bool ArePatternSetTfmHeuristicsEnabled();
165+
161166
/// <summary>
162167
/// Whether the user is able to publish the package with an embedded readme file.
163168
/// </summary>

src/NuGetGallery.Services/PackageManagement/IPackageService.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ Package FilterLatestPackageBySuffix(IReadOnlyCollection<Package> packages,
114114

115115
Package EnrichPackageFromNuGetPackage(Package package, PackageArchiveReader packageArchive, PackageMetadata packageMetadata, PackageStreamMetadata packageStreamMetadata, User user);
116116

117+
IEnumerable<NuGetFramework> GetSupportedFrameworks(PackageArchiveReader package);
118+
117119
IEnumerable<NuGetFramework> GetSupportedFrameworks(NuspecReader nuspecReader, IList<string> packageFiles);
118120

119121
Task PublishPackageAsync(string id, string version, bool commitChanges = true);

src/NuGetGallery.Services/PackageManagement/PackageService.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class PackageService : CorePackageService, IPackageService
3232
private readonly ISecurityPolicyService _securityPolicyService;
3333
private readonly IEntitiesContext _entitiesContext;
3434
private readonly IContentObjectService _contentObjectService;
35+
private readonly IFeatureFlagService _featureFlagService;
3536
private const int packagesDisplayed = 5;
3637

3738
public PackageService(
@@ -42,14 +43,16 @@ public PackageService(
4243
ITelemetryService telemetryService,
4344
ISecurityPolicyService securityPolicyService,
4445
IEntitiesContext entitiesContext,
45-
IContentObjectService contentObjectService)
46+
IContentObjectService contentObjectService,
47+
IFeatureFlagService featureFlagService)
4648
: base(packageRepository, packageRegistrationRepository, certificateRepository)
4749
{
4850
_auditingService = auditingService ?? throw new ArgumentNullException(nameof(auditingService));
4951
_telemetryService = telemetryService ?? throw new ArgumentNullException(nameof(telemetryService));
5052
_securityPolicyService = securityPolicyService ?? throw new ArgumentNullException(nameof(securityPolicyService));
5153
_entitiesContext = entitiesContext ?? throw new ArgumentNullException(nameof(entitiesContext));
5254
_contentObjectService = contentObjectService ?? throw new ArgumentNullException(nameof(contentObjectService));
55+
_featureFlagService = featureFlagService ?? throw new ArgumentNullException(nameof(featureFlagService));
5356
}
5457

5558
/// <summary>
@@ -702,8 +705,15 @@ public virtual Package EnrichPackageFromNuGetPackage(
702705
return package;
703706
}
704707

705-
public virtual IEnumerable<NuGetFramework> GetSupportedFrameworks(PackageArchiveReader package) =>
706-
GetSupportedFrameworks(package.NuspecReader, package.GetFiles().ToList());
708+
public virtual IEnumerable<NuGetFramework> GetSupportedFrameworks(PackageArchiveReader package)
709+
{
710+
if (_featureFlagService.ArePatternSetTfmHeuristicsEnabled())
711+
{
712+
return GetSupportedFrameworks(package.NuspecReader, package.GetFiles().ToList());
713+
}
714+
715+
return package.GetSupportedFrameworks();
716+
}
707717

708718
/// <summary>
709719
/// This method combines the logic used in restore operations to make a determination about the TFM supported by the package.

src/NuGetGallery/App_Data/Files/Content/flags.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
"NuGetGallery.ODataV2SearchNonHijacked": "Enabled",
2424
"NuGetGallery.ODataV2SearchCountNonHijacked": "Enabled",
2525
"NuGetGallery.DisplayVulnerabilities": "Enabled",
26-
"NuGetGallery.DisplayFuGetLinks": "Enabled"
26+
"NuGetGallery.DisplayFuGetLinks": "Enabled",
27+
"NuGetGallery.PatternSetTfmHeuristics": "Enabled"
2728
},
2829
"Flights": {
2930
"NuGetGallery.TyposquattingFlight": {

src/VerifyMicrosoftPackage/Application.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ private static PackageService GetPackageService()
266266
var securityPolicyService = new FakeSecurityPolicyService();
267267
var contextFake = new FakeEntitiesContext();
268268
var contentObjectService = new FakeContentObjectService();
269+
var featureFlagService = new FakeFeatureFlagService();
269270

270271
var packageService = new PackageService(
271272
packageRegistrationRepository,
@@ -275,7 +276,8 @@ private static PackageService GetPackageService()
275276
telemetryService,
276277
securityPolicyService,
277278
contextFake,
278-
contentObjectService);
279+
contentObjectService,
280+
featureFlagService);
279281

280282
return packageService;
281283
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Net.Http;
7+
using System.Security.Principal;
8+
using NuGet.Services.Entities;
9+
using NuGet.Versioning;
10+
using NuGetGallery;
11+
12+
namespace NuGet.VerifyMicrosoftPackage.Fakes
13+
{
14+
public class FakeFeatureFlagService : IFeatureFlagService
15+
{
16+
public bool IsAlternateStatisticsSourceEnabled() => throw new NotImplementedException();
17+
18+
public bool IsAsyncAccountDeleteEnabled() => throw new NotImplementedException();
19+
20+
public bool IsSelfServiceAccountDeleteEnabled() => throw new NotImplementedException();
21+
22+
public bool IsTyposquattingEnabled() => throw new NotImplementedException();
23+
24+
public bool IsTyposquattingEnabled(User user) => throw new NotImplementedException();
25+
26+
public bool IsPackagesAtomFeedEnabled() => throw new NotImplementedException();
27+
28+
public bool IsManageDeprecationEnabled(User user, PackageRegistration registration) => throw new NotImplementedException();
29+
30+
public bool IsManageDeprecationEnabled(User user, IEnumerable<Package> allVersions) => throw new NotImplementedException();
31+
32+
public bool IsManageDeprecationApiEnabled(User user) => throw new NotImplementedException();
33+
34+
public bool IsDisplayVulnerabilitiesEnabled() => throw new NotImplementedException();
35+
36+
public bool IsDisplayFuGetLinksEnabled() => throw new NotImplementedException();
37+
38+
public bool AreEmbeddedIconsEnabled(User user) => throw new NotImplementedException();
39+
40+
public bool IsForceFlatContainerIconsEnabled() => throw new NotImplementedException();
41+
42+
public bool IsSearchSideBySideEnabled(User user) => throw new NotImplementedException();
43+
44+
public bool IsGitHubUsageEnabled(User user) => throw new NotImplementedException();
45+
46+
public bool IsAdvancedSearchEnabled(User user) => throw new NotImplementedException();
47+
48+
public bool IsPackageDependentsEnabled(User user) => throw new NotImplementedException();
49+
50+
public bool IsODataDatabaseReadOnlyEnabled() => throw new NotImplementedException();
51+
52+
public bool IsABTestingEnabled(User user) => throw new NotImplementedException();
53+
54+
public bool IsPreviewHijackEnabled() => throw new NotImplementedException();
55+
56+
public bool IsGravatarProxyEnabled() => throw new NotImplementedException();
57+
58+
public bool ProxyGravatarEnSubdomain() => throw new NotImplementedException();
59+
60+
public bool AreDynamicODataCacheDurationsEnabled() => throw new NotImplementedException();
61+
62+
public bool IsShowEnable2FADialogEnabled() => throw new NotImplementedException();
63+
64+
public bool IsGet2FADismissFeedbackEnabled() => throw new NotImplementedException();
65+
66+
public bool IsPackageRenamesEnabled(User user) => throw new NotImplementedException();
67+
68+
public bool ArePatternSetTfmHeuristicsEnabled() => true;
69+
70+
public bool AreEmbeddedReadmesEnabled(User user) => throw new NotImplementedException();
71+
72+
public bool IsODataV1GetAllEnabled() => throw new NotImplementedException();
73+
74+
public bool IsODataV1GetAllCountEnabled() => throw new NotImplementedException();
75+
76+
public bool IsODataV1GetSpecificNonHijackedEnabled() => throw new NotImplementedException();
77+
78+
public bool IsODataV1FindPackagesByIdNonHijackedEnabled() => throw new NotImplementedException();
79+
80+
public bool IsODataV1FindPackagesByIdCountNonHijackedEnabled() => throw new NotImplementedException();
81+
82+
public bool IsODataV1SearchNonHijackedEnabled() => throw new NotImplementedException();
83+
84+
public bool IsODataV1SearchCountNonHijackedEnabled() => throw new NotImplementedException();
85+
86+
public bool IsODataV2GetAllNonHijackedEnabled() => throw new NotImplementedException();
87+
88+
public bool IsODataV2GetAllCountNonHijackedEnabled() => throw new NotImplementedException();
89+
90+
public bool IsODataV2GetSpecificNonHijackedEnabled() => throw new NotImplementedException();
91+
92+
public bool IsODataV2FindPackagesByIdNonHijackedEnabled() => throw new NotImplementedException();
93+
94+
public bool IsODataV2FindPackagesByIdCountNonHijackedEnabled() => throw new NotImplementedException();
95+
96+
public bool IsODataV2SearchNonHijackedEnabled() => throw new NotImplementedException();
97+
98+
public bool IsLicenseMdRenderingEnabled(User user) => throw new NotImplementedException();
99+
100+
public bool IsODataV2SearchCountNonHijackedEnabled() => throw new NotImplementedException();
101+
102+
public bool IsMarkdigMdRenderingEnabled() => throw new NotImplementedException();
103+
104+
public bool IsDeletePackageApiEnabled(User user) => throw new NotImplementedException();
105+
106+
public bool IsImageAllowlistEnabled() => throw new NotImplementedException();
107+
}
108+
}

src/VerifyMicrosoftPackage/VerifyMicrosoftPackage.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
<Compile Include="Fakes\FakeEntitiesContext.cs" />
4545
<Compile Include="Fakes\FakeEntityRepository.cs" />
4646
<Compile Include="Fakes\FakeSecurityPolicyService.cs" />
47+
<Compile Include="Fakes\FakeFeatureFlagService.cs" />
4748
<Compile Include="Fakes\FakeTelemetryService.cs" />
4849
<Compile Include="Program.cs" />
4950
<Compile Include="Properties\AssemblyInfo.cs" />

tests/NuGetGallery.Facts/NuGetGallery.Facts.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
<Compile Include="Services\StatusServiceFacts.cs" />
120120
<Compile Include="Telemetry\CustomerResourceIdEnricherFacts.cs" />
121121
<Compile Include="TestData\TestDataResourceUtility.cs" />
122+
<Compile Include="TestUtils\MockPackageArchiveReader.cs" />
122123
<Compile Include="UsernameValidationRegex.cs" />
123124
<Compile Include="Extensions\NumberExtensionsFacts.cs" />
124125
<Compile Include="Extensions\RouteExtensionsFacts.cs" />

tests/NuGetGallery.Facts/Services/PackageServiceFacts.cs

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ private static IPackageService CreateService(
3333
Mock<ISecurityPolicyService> securityPolicyService = null,
3434
Action<Mock<PackageService>> setup = null,
3535
Mock<IEntitiesContext> context = null,
36-
Mock<IContentObjectService> contentObjectService = null)
36+
Mock<IContentObjectService> contentObjectService = null,
37+
Mock<IFeatureFlagService> featureFlagService = null)
3738
{
3839
packageRegistrationRepository = packageRegistrationRepository ?? new Mock<IEntityRepository<PackageRegistration>>();
3940
packageRepository = packageRepository ?? new Mock<IEntityRepository<Package>>();
@@ -49,6 +50,12 @@ private static IPackageService CreateService(
4950
contentObjectService.Setup(x => x.QueryHintConfiguration).Returns(Mock.Of<IQueryHintConfiguration>());
5051
}
5152

53+
if (featureFlagService == null)
54+
{
55+
featureFlagService = new Mock<IFeatureFlagService>();
56+
featureFlagService.Setup(x => x.ArePatternSetTfmHeuristicsEnabled()).Returns(true);
57+
}
58+
5259
var packageService = new Mock<PackageService>(
5360
packageRegistrationRepository.Object,
5461
packageRepository.Object,
@@ -57,7 +64,8 @@ private static IPackageService CreateService(
5764
telemetryService.Object,
5865
securityPolicyService.Object,
5966
context.Object,
60-
contentObjectService.Object);
67+
contentObjectService.Object,
68+
featureFlagService.Object);
6169

6270
packageService.CallBase = true;
6371

@@ -970,6 +978,41 @@ private async Task WillNotSaveAnySupportedFrameworksWhenThereIsAnAnyTargetFramew
970978
Assert.Empty(package.SupportedFrameworks);
971979
}
972980

981+
[Theory]
982+
[InlineData(false, new[] { "net40", "net5.0", "netcore21" })]
983+
[InlineData(true, new[] { "net5.0", "netcore21" })]
984+
private void UsesTfmHeuristicsBasedOnFeatureFlag(bool useNewTfmHeuristics, IEnumerable<string> expectedSupportedTfms)
985+
{
986+
// arrange
987+
// - create a package that responds differently to each set of heuristics
988+
var nuspec =
989+
$@"<?xml version=""1.0""?>
990+
<package xmlns = ""http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"">
991+
<metadata>
992+
<id>Foo</id>
993+
<frameworkAssemblies>
994+
<frameworkAssembly assemblyName=""System"" targetFramework="".NETFramework4.0"" />
995+
</frameworkAssemblies>
996+
</metadata>
997+
</package>";
998+
var nuspecReader = new NuspecReader(XDocument.Parse(nuspec));
999+
var files = new List<string> { "lib/netcore2.1/_._", "lib/net5.0/_._" };
1000+
var package = new MockPackageArchiveReader(nuspecReader, files);
1001+
1002+
// - create feature flag services and package services for both scenarios
1003+
var featureFlagService = new Mock<IFeatureFlagService>();
1004+
featureFlagService.Setup(x => x.ArePatternSetTfmHeuristicsEnabled()).Returns(useNewTfmHeuristics);
1005+
1006+
// act
1007+
var supportedFrameworks = CreateService(featureFlagService: featureFlagService).GetSupportedFrameworks(package)
1008+
.Select(f => f.GetShortFolderName())
1009+
.OrderBy(f => f)
1010+
.ToList();
1011+
1012+
// assert
1013+
Assert.Equal<string>(expectedSupportedTfms, supportedFrameworks);
1014+
}
1015+
9731016
[Theory]
9741017
[MemberData(nameof(TargetFrameworkCases))]
9751018
private void DeterminesCorrectSupportedFrameworksFromFileList(bool isTools, List<string> files, List<string> expectedSupportedFrameworks)
@@ -991,9 +1034,7 @@ private void DeterminesCorrectSupportedFrameworksFromFileList(bool isTools, List
9911034
<id>Foo</id>
9921035
</metadata>
9931036
</package>";
994-
var xml = XDocument.Parse(nuspec);
995-
996-
var nuspecReader = new NuspecReader(xml);
1037+
var nuspecReader = new NuspecReader(XDocument.Parse(nuspec));
9971038

9981039
// act
9991040
var supportedFrameworks = CreateService().GetSupportedFrameworks(nuspecReader, files)

0 commit comments

Comments
 (0)