Skip to content

Commit 1641b29

Browse files
authored
Merge pull request #13 from ai-traders/fix-no-deps
Fix missing dependencies in registration
2 parents 64bf093 + 9e2e791 commit 1641b29

File tree

15 files changed

+444
-10
lines changed

15 files changed

+444
-10
lines changed

CHANGELOG.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
### 0.2.0 (2018-Nov-11)
1+
### 0.2.1 (2018-Oct-22)
2+
3+
* Fixed missing dependencies in V3 endpoints \#12
4+
5+
### 0.2.0 (2018-Oct-11)
26

37
* Added V2 implementation from LiGet
48
* Added compatibility mode with LiGet to keep the same endpoints
@@ -7,7 +11,7 @@
711
* added importer to complete migration from LiGet
812
* fix/adjust for deployments with root-owned volumes
913

10-
### 0.1.0 (2018-Nov-09)
14+
### 0.1.0 (2018-Oct-09)
1115

1216
First release with a changelog.
1317
- added unit, integration tests and e2e tests with paket and nuget cli.

e2e/test_proxy/01_proxy.bats

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,8 @@ load '/opt/bats-assert/load.bash'
2222
assert_equal "$status" 0
2323
assert [ -e 'paket-constraint/packages/log4net/log4net.2.0.8.nupkg' ]
2424
}
25+
26+
@test "paket.lock has dependencies" {
27+
run /bin/bash -c "cat paket-constraint/paket.lock"
28+
assert_output --partial "(>="
29+
}

src/BaGet.Core/Legacy/PackageExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public static string ToDependenciesString(this IEnumerable<Entities.PackageDepen
3636
return string.Join("|", texts);
3737
}
3838

39-
private static bool IsFrameworkDependency(Entities.PackageDependency dependency)
39+
public static bool IsFrameworkDependency(this Entities.PackageDependency dependency)
4040
{
4141
return dependency.Id == null && dependency.VersionRange == null;
4242
}

src/BaGet.Web/Controllers/Registration/CacheRegistrationIndexModule.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ private RegistrationIndexLeaf ToRegistrationIndexLeaf(HttpRequest request, IPack
103103
catalogEntry: new CatalogEntry(
104104
package: package,
105105
catalogUri: $"https://api.nuget.org/v3/catalog0/data/2015.02.01.06.24.15/{package.Identity.Id}.{package.Identity.Version}.json",
106-
packageContent: request.PackageDownload(package.Identity.Id, package.Identity.Version, "cache")),
106+
packageContent: request.PackageDownload(package.Identity.Id, package.Identity.Version, "cache"),
107+
getRegistrationUrl: id => new System.Uri(request.PackageRegistration(id, "cache"))),
107108
packageContent: request.PackageDownload(package.Identity.Id, package.Identity.Version, "cache"));
108109

109110
}

src/BaGet.Web/Controllers/Registration/RegistrationIndexModule.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public RegistrationIndexModule(IPackageService packageService)
3232
{
3333
string id = routeData.As<string>("id");
3434
// Documentation: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource
35-
var packages = await _packages.FindAsync(id);
35+
var packages = await _packages.FindAsync(id, includeUnlisted: false, includeDependencies: true);
3636
var versions = packages.Select(p => p.Version).ToList();
3737

3838
if (!packages.Any())
@@ -71,7 +71,7 @@ await res.AsJson(new
7171
return;
7272
}
7373

74-
var package = await _packages.FindAsync(id, nugetVersion);
74+
var package = await _packages.FindAsync(id, nugetVersion, false, includeDependencies: false);
7575

7676
if (package == null)
7777
{
@@ -98,7 +98,8 @@ private RegistrationIndexLeaf ToRegistrationIndexLeaf(HttpRequest request, Packa
9898
catalogEntry: new CatalogEntry(
9999
package: package,
100100
catalogUri: $"https://api.nuget.org/v3/catalog0/data/2015.02.01.06.24.15/{package.Id}.{package.Version}.json",
101-
packageContent: request.PackageDownload(package.Id, package.Version, "")),
101+
packageContent: request.PackageDownload(package.Id, package.Version, ""),
102+
getRegistrationUrl: id => new System.Uri(request.PackageRegistration(id, ""))),
102103
packageContent: request.PackageDownload(package.Id, package.Version, ""));
103104
}
104105
}

src/BaGet.Web/Models/CatalogEntry.cs

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
11
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
24
using BaGet.Core.Entities;
5+
using BaGet.Core.Legacy;
36
using Newtonsoft.Json;
7+
using NuGet.Frameworks;
48
using NuGet.Protocol.Core.Types;
59

610
namespace BaGet.Web.Models
711
{
812
public class CatalogEntry
913
{
10-
public CatalogEntry(Package package, string catalogUri, string packageContent)
14+
public CatalogEntry(Package package, string catalogUri, string packageContent, Func<string, Uri> getRegistrationUrl)
1115
{
1216
if (package == null) throw new ArgumentNullException(nameof(package));
1317

1418
CatalogUri = catalogUri ?? throw new ArgumentNullException(nameof(catalogUri));
1519

1620
PackageId = package.Id;
1721
Version = package.VersionString;
18-
Authors = string.Join(", ", package.Authors);
22+
Authors = package.Authors == null ? null : string.Join(", ", package.Authors);
1923
Description = package.Description;
2024
Downloads = package.Downloads;
2125
HasReadme = package.HasReadme;
@@ -33,9 +37,66 @@ public CatalogEntry(Package package, string catalogUri, string packageContent)
3337
Summary = package.Summary;
3438
Tags = package.Tags;
3539
Title = package.Title;
40+
DependencyGroups = ToDependencyGroups(package.Dependencies, catalogUri, getRegistrationUrl);
3641
}
3742

38-
public CatalogEntry(IPackageSearchMetadata package, string catalogUri, string packageContent)
43+
public static DependencyGroup[] ToDependencyGroups(List<Core.Entities.PackageDependency> dependencies,
44+
string catalogUri, Func<string, Uri> getRegistrationUrl)
45+
{
46+
if(dependencies == null || !dependencies.Any())
47+
return null;
48+
49+
var groups = new List<DependencyGroup>();
50+
var frameworkDeps = dependencies.Where(d => d.IsFrameworkDependency()).Select(d => d.TargetFramework).Distinct();
51+
foreach(var frameworkDep in frameworkDeps) {
52+
var g = new DependencyGroup() {
53+
CatalogUrl = catalogUri + $"#dependencygroup/.{frameworkDep}",
54+
TargetFramework = frameworkDep
55+
};
56+
groups.Add(g);
57+
}
58+
// empty string key implies no target framework
59+
Dictionary<string, List<PackageDependency>> dependenciesByFramework = new Dictionary<string, List<PackageDependency>>();
60+
foreach (var packageDependency in dependencies.Where(d => !d.IsFrameworkDependency()))
61+
{
62+
var dep = new PackageDependency() {
63+
Id = packageDependency.Id,
64+
Range = packageDependency.VersionRange
65+
};
66+
string framework = packageDependency.TargetFramework == null ? "" : packageDependency.TargetFramework;
67+
List<PackageDependency> deps = new List<PackageDependency>();
68+
if (!dependenciesByFramework.TryGetValue(framework, out deps)) {
69+
deps = new List<PackageDependency>();
70+
dependenciesByFramework.Add(framework, deps);
71+
}
72+
deps.Add(dep);
73+
}
74+
var perFrameworkDeps =
75+
dependenciesByFramework.GroupBy(d => d.Key)
76+
.Select(grouppedDeps =>
77+
{
78+
var framework = string.IsNullOrEmpty(grouppedDeps.Key) ? null : grouppedDeps.Key;
79+
string catalogForGroup = catalogUri + "#dependencygroup";
80+
if(framework != null)
81+
catalogForGroup = catalogUri + $"#dependencygroup/.{framework}";
82+
var g = new DependencyGroup() {
83+
CatalogUrl = catalogForGroup,
84+
TargetFramework = framework,
85+
Dependencies = grouppedDeps.SelectMany(d => d.Value)
86+
.Select(d => new PackageDependency() {
87+
CatalogUrl = catalogUri + $"#dependencygroup/.{grouppedDeps.Key}/{d.Id}",
88+
Id = d.Id,
89+
Range = d.Range,
90+
Registration = getRegistrationUrl(d.Id).AbsoluteUri
91+
}).ToArray()
92+
};
93+
return g;
94+
});
95+
96+
return groups.Concat(perFrameworkDeps).ToArray();
97+
}
98+
99+
public CatalogEntry(IPackageSearchMetadata package, string catalogUri, string packageContent, Func<string, Uri> getRegistrationUrl)
39100
{
40101
if (package == null) throw new ArgumentNullException(nameof(package));
41102

@@ -61,6 +122,36 @@ public CatalogEntry(IPackageSearchMetadata package, string catalogUri, string pa
61122
Summary = package.Summary;
62123
Tags = package.Tags == null ? null : package.Tags.Split(',');
63124
Title = package.Title;
125+
DependencyGroups = ToDependencyGroups(package.DependencySets, catalogUri, getRegistrationUrl);
126+
}
127+
128+
public static DependencyGroup[] ToDependencyGroups(IEnumerable<NuGet.Packaging.PackageDependencyGroup> dependencies, string catalogUri,
129+
Func<string, Uri> getRegistrationUrl)
130+
{
131+
return dependencies.Select(grouppedDeps => {
132+
string targetFramework;
133+
if(grouppedDeps.TargetFramework == null || grouppedDeps.TargetFramework.Equals(NuGetFramework.AnyFramework))
134+
targetFramework = null;
135+
else
136+
targetFramework = grouppedDeps.TargetFramework.GetShortFolderName();
137+
string catalogForGroup = catalogUri + "#dependencygroup";
138+
if(targetFramework != null)
139+
catalogForGroup = catalogUri + $"#dependencygroup/.{targetFramework}";
140+
var g = new DependencyGroup() {
141+
CatalogUrl = catalogForGroup,
142+
TargetFramework = targetFramework,
143+
Dependencies = grouppedDeps.Packages
144+
.Select(d => new PackageDependency() {
145+
CatalogUrl = catalogUri + $"#dependencygroup/.{targetFramework}/{d.Id}",
146+
Id = d.Id,
147+
Range = d.VersionRange.ToNormalizedString(),
148+
Registration = getRegistrationUrl(d.Id).AbsoluteUri
149+
}).ToArray()
150+
};
151+
if(g.Dependencies.Length == 0)
152+
g.Dependencies = null;
153+
return g;
154+
}).ToArray();
64155
}
65156

66157
private string NullSafeToString(object prop)
@@ -95,5 +186,7 @@ private string NullSafeToString(object prop)
95186
public string Summary { get; }
96187
public string[] Tags { get; }
97188
public string Title { get; }
189+
190+
public DependencyGroup[] DependencyGroups { get; }
98191
}
99192
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using Newtonsoft.Json;
2+
3+
namespace BaGet.Web.Models
4+
{
5+
// see https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#package-dependency-group
6+
public class DependencyGroup
7+
{
8+
public DependencyGroup() {
9+
Type = "PackageDependencyGroup";
10+
}
11+
12+
[JsonProperty(PropertyName = "@id")]
13+
public string CatalogUrl { get; set; }
14+
15+
[JsonProperty(PropertyName = "@type")]
16+
public string Type { get; set; }
17+
18+
public string TargetFramework { get; set; }
19+
20+
public PackageDependency[] Dependencies { get; set; }
21+
}
22+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using Newtonsoft.Json;
2+
3+
namespace BaGet.Web.Models
4+
{
5+
// https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#package-dependency
6+
public class PackageDependency
7+
{
8+
public PackageDependency() {
9+
Type = "PackageDependency";
10+
}
11+
12+
[JsonProperty(PropertyName = "@id")]
13+
public string CatalogUrl { get; set; }
14+
15+
[JsonProperty(PropertyName = "@type")]
16+
public string Type { get; set; }
17+
18+
public string Id { get; set; }
19+
20+
public string Range { get; set; }
21+
22+
public string Registration { get; set; }
23+
}
24+
}

tasks.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ case "${command}" in
6868
rm -rf e2e/data/db/*
6969
rm -rf e2e/data/packages/*
7070
rm -rf e2e/data/cache/*
71+
rm e2e/test_*/nuget*/*/ -rf
7172
ide --idefile Idefile.e2e-docker "./e2e/run.sh"
7273
;;
7374
stress_docker)
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using BaGet.Core.Mirror;
6+
using Microsoft.AspNetCore.TestHost;
7+
using Microsoft.Extensions.DependencyInjection;
8+
using Microsoft.Extensions.Logging;
9+
using Moq;
10+
using Newtonsoft.Json.Linq;
11+
using NuGet.Frameworks;
12+
using NuGet.Packaging;
13+
using NuGet.Protocol;
14+
using NuGet.Protocol.Core.Types;
15+
using NuGet.Versioning;
16+
using Xunit;
17+
using Xunit.Abstractions;
18+
19+
namespace BaGet.Tests.Controllers
20+
{
21+
public class CacheRegistrationIndexModuleTest
22+
{
23+
protected readonly ITestOutputHelper Helper;
24+
25+
readonly string RegistrationIndexUrlFormatString = "cache/v3/registration/{0}/index.json";
26+
27+
public CacheRegistrationIndexModuleTest(ITestOutputHelper helper)
28+
{
29+
Helper = helper ?? throw new ArgumentNullException(nameof(helper));
30+
}
31+
32+
[Fact]
33+
public async Task RegistrationIndexCatalogEntryShouldContainDependencyGroups()
34+
{
35+
var pkgService = new Mock<IMirrorService>(MockBehavior.Strict);
36+
Mock<IPackageSearchMetadata> abcPackage = new Mock<IPackageSearchMetadata>();
37+
abcPackage.SetupGet(a => a.Identity).Returns(new NuGet.Packaging.Core.PackageIdentity("abc", NuGetVersion.Parse("1.2.3")));
38+
abcPackage.SetupGet(a => a.DependencySets).Returns(new PackageDependencyGroup[1] {
39+
new PackageDependencyGroup(NuGetFramework.Parse("netstandard2.0"), new NuGet.Packaging.Core.PackageDependency[0])
40+
});
41+
pkgService.Setup(p => p.FindUpstreamMetadataAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
42+
.ReturnsAsync(new List<IPackageSearchMetadata>() {
43+
abcPackage.Object
44+
});
45+
using (TestServer server = TestServerBuilder.Create()
46+
.TraceToTestOutputHelper(Helper, LogLevel.Error)
47+
.WithMock(typeof(IMirrorService), pkgService)
48+
.Build())
49+
{
50+
var services = server.Host.Services;
51+
Assert.Equal(pkgService.Object, services.GetRequiredService<IMirrorService>());
52+
// https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-pages-and-leaves
53+
var response = await server.CreateClient().GetAsync(string.Format(RegistrationIndexUrlFormatString, "abc"));
54+
Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode);
55+
var jsonString = await response.Content.ReadAsStringAsync();
56+
var actual = JObject.Parse(jsonString);
57+
var groups = actual["items"][0]["items"][0]["catalogEntry"]["dependencyGroups"];
58+
var expected = new JArray(
59+
new JObject {
60+
{ "@id", $"https://api.nuget.org/v3/catalog0/data/2015.02.01.06.24.15/abc.1.2.3.json#dependencygroup/.netstandard2.0" },
61+
{ "@type", "PackageDependencyGroup" },
62+
{ "targetFramework", "netstandard2.0" },
63+
{ "dependencies", null }
64+
}
65+
);
66+
string message = "Actual part of response: \n" + groups.ToString();
67+
Assert.True(JToken.DeepEquals(expected, groups), message);
68+
}
69+
}
70+
}
71+
}

0 commit comments

Comments
 (0)