Skip to content

Commit 38c0603

Browse files
committed
Start
Push more logic to mirror service Refactor search Refactor Azure Table search Clean
1 parent 5fc5072 commit 38c0603

15 files changed

+527
-23
lines changed

src/BaGet.Core/IUrlGenerator.cs

+19-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public interface IUrlGenerator
1111
/// Get the URL for the package source (also known as the "service index").
1212
/// See: https://docs.microsoft.com/en-us/nuget/api/service-index
1313
/// </summary>
14-
string GetServiceIndexUrl();
14+
string GetServiceIndexV3Url();
1515

1616
/// <summary>
1717
/// Get the URL for the root of the package content resource.
@@ -80,6 +80,13 @@ public interface IUrlGenerator
8080
/// <param name="id">The package's ID</param>
8181
string GetPackageVersionsUrl(string id);
8282

83+
/// <summary>
84+
/// Get the URL to download a package (.nupkg).
85+
/// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-content-nupkg
86+
/// </summary>
87+
/// <param name="package">The package to download</param>
88+
string GetPackageDownloadUrl(Package package);
89+
8390
/// <summary>
8491
/// Get the URL to download a package (.nupkg).
8592
/// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-content-nupkg
@@ -101,5 +108,16 @@ public interface IUrlGenerator
101108
/// <param name="id">The package's ID</param>
102109
/// <param name="version">The package's version</param>
103110
string GetPackageIconDownloadUrl(string id, NuGetVersion version);
111+
112+
/// <summary>
113+
/// Get the URL for the package source that implements the legacy NuGet V2 API.
114+
/// </summary>
115+
string GetServiceIndexV2Url();
116+
117+
/// <summary>
118+
/// Get the URL for the metadata of a single package version.
119+
/// </summary>
120+
/// <param name="package">The package to lookup</param>
121+
string GetPackageVersionV2Url(Package package);
104122
}
105123
}

src/BaGet.Core/Metadata/DefaultPackageMetadataService.cs

+1-7
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,13 @@ namespace BaGet.Core
1111
public class DefaultPackageMetadataService : IPackageMetadataService
1212
{
1313
private readonly IMirrorService _mirror;
14-
private readonly IPackageService _packages;
1514
private readonly RegistrationBuilder _builder;
1615

1716
public DefaultPackageMetadataService(
1817
IMirrorService mirror,
19-
IPackageService packages,
2018
RegistrationBuilder builder)
2119
{
2220
_mirror = mirror ?? throw new ArgumentNullException(nameof(mirror));
23-
_packages = packages ?? throw new ArgumentNullException(nameof(packages));
2421
_builder = builder ?? throw new ArgumentNullException(nameof(builder));
2522
}
2623

@@ -45,10 +42,7 @@ public async Task<RegistrationLeafResponse> GetRegistrationLeafOrNullAsync(
4542
NuGetVersion version,
4643
CancellationToken cancellationToken = default)
4744
{
48-
// Allow read-through caching to happen if it is configured.
49-
await _mirror.MirrorAsync(id, version, cancellationToken);
50-
51-
var package = await _packages.FindOrNullAsync(id, version, includeUnlisted: true, cancellationToken);
45+
var package = await _mirror.FindPackageOrNullAsync(id, version, cancellationToken);
5246
if (package == null)
5347
{
5448
return null;

src/BaGet.Core/Metadata/RegistrationBuilder.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public virtual RegistrationLeafResponse BuildLeaf(Package package)
5252
Listed = package.Listed,
5353
Published = package.Published,
5454
RegistrationLeafUrl = _url.GetRegistrationLeafUrl(id, version),
55-
PackageContentUrl = _url.GetPackageDownloadUrl(id, version),
55+
PackageContentUrl = _url.GetPackageDownloadUrl(package),
5656
RegistrationIndexUrl = _url.GetRegistrationIndexUrl(id)
5757
};
5858
}
@@ -61,7 +61,7 @@ private BaGetRegistrationIndexPageItem ToRegistrationIndexPageItem(Package packa
6161
new BaGetRegistrationIndexPageItem
6262
{
6363
RegistrationLeafUrl = _url.GetRegistrationLeafUrl(package.Id, package.Version),
64-
PackageContentUrl = _url.GetPackageDownloadUrl(package.Id, package.Version),
64+
PackageContentUrl = _url.GetPackageDownloadUrl(package),
6565
PackageMetadata = new BaGetPackageMetadata
6666
{
6767
PackageId = package.Id,
@@ -78,7 +78,7 @@ private BaGetRegistrationIndexPageItem ToRegistrationIndexPageItem(Package packa
7878
Listed = package.Listed,
7979
MinClientVersion = package.MinClientVersion,
8080
ReleaseNotes = package.ReleaseNotes,
81-
PackageContentUrl = _url.GetPackageDownloadUrl(package.Id, package.Version),
81+
PackageContentUrl = _url.GetPackageDownloadUrl(package),
8282
PackageTypes = package.PackageTypes.Select(t => t.Name).ToList(),
8383
ProjectUrl = package.ProjectUrlString,
8484
RepositoryUrl = package.RepositoryUrlString,

src/BaGet.Core/Mirror/DisabledMirrorService.cs

+8
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ public DisabledMirrorService(IPackageService packages)
1919
_packages = packages ?? throw new ArgumentNullException(nameof(packages));
2020
}
2121

22+
public async Task<Package> FindPackageOrNullAsync(
23+
string id,
24+
NuGetVersion version,
25+
CancellationToken cancellationToken)
26+
{
27+
return await _packages.FindOrNullAsync(id, version, includeUnlisted: true, cancellationToken);
28+
}
29+
2230
public async Task<IReadOnlyList<NuGetVersion>> FindPackageVersionsAsync(string id, CancellationToken cancellationToken)
2331
{
2432
var packages = await _packages.FindAsync(id, includeUnlisted: true, cancellationToken);

src/BaGet.Core/Mirror/IMirrorService.cs

+13
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@ public interface IMirrorService
3434
/// </returns>
3535
Task<IReadOnlyList<Package>> FindPackagesAsync(string id, CancellationToken cancellationToken);
3636

37+
/// <summary>
38+
/// Attempt to find a package's metadata using mirroring. This will merge
39+
/// results from the configured upstream source with the locally indexed packages.
40+
/// </summary>
41+
/// <param name="id">The package's id to lookup</param>
42+
/// <param name="version">The package's version to lookup</param>
43+
/// <param name="cancellationToken">The token to cancel the lookup</param>
44+
/// <returns>
45+
/// The metadata for single version of a package.
46+
/// Returns null if the package does not exist.
47+
/// </returns>
48+
Task<Package> FindPackageOrNullAsync(string id, NuGetVersion version, CancellationToken cancellationToken);
49+
3750
/// <summary>
3851
/// If the package is unknown, attempt to index it from an upstream source.
3952
/// </summary>

src/BaGet.Core/Mirror/MirrorService.cs

+10
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,16 @@ public async Task<IReadOnlyList<Package>> FindPackagesAsync(string id, Cancellat
6868
return result.Values.ToList();
6969
}
7070

71+
public async Task<Package> FindPackageOrNullAsync(
72+
string id,
73+
NuGetVersion version,
74+
CancellationToken cancellationToken)
75+
{
76+
await MirrorAsync(id, version, cancellationToken);
77+
78+
return await _localPackages.FindOrNullAsync(id, version, includeUnlisted: true, cancellationToken);
79+
}
80+
7181
public async Task MirrorAsync(string id, NuGetVersion version, CancellationToken cancellationToken)
7282
{
7383
if (await _localPackages.ExistsAsync(id, version, cancellationToken))

src/BaGet.Core/V2/IV2Builder.cs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System.Collections.Generic;
2+
using System.Xml.Linq;
3+
4+
namespace BaGet.Core
5+
{
6+
public interface IV2Builder
7+
{
8+
XElement BuildIndex();
9+
XElement BuildPackages(IReadOnlyList<Package> packages);
10+
XElement BuildPackage(Package package);
11+
}
12+
}

src/BaGet.Core/V2/V2Builder.cs

+219
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Xml.Linq;
4+
5+
namespace BaGet.Core
6+
{
7+
public class V2Builder : IV2Builder
8+
{
9+
private readonly IUrlGenerator _url;
10+
11+
public V2Builder(IUrlGenerator url)
12+
{
13+
_url = url;
14+
}
15+
16+
public XElement BuildIndex()
17+
{
18+
var serviceIndex = _url.GetServiceIndexV2Url();
19+
20+
return XElement.Parse($@"
21+
<service xmlns=""http://www.w3.org/2007/app"" xmlns:atom=""http://www.w3.org/2005/Atom"" xml:base=""{serviceIndex}"">
22+
<workspace>
23+
<atom:title type=""text"">Default</atom:title>
24+
<collection href=""Packages"">
25+
<atom:title type=""text"">Packages</atom:title>
26+
</collection>
27+
</workspace>
28+
</service>");
29+
}
30+
31+
public XElement BuildPackages(IReadOnlyList<Package> packages)
32+
{
33+
// See: https://joelverhagen.github.io/NuGetUndocs/#endpoint-find-packages-by-id
34+
var serviceIndex = _url.GetServiceIndexV2Url();
35+
36+
// TODO: Add <?xml version="1.0" encoding="utf-8"?> to top
37+
return new XElement(
38+
N.feed,
39+
new XAttribute(N.baze, XNamespace.Get(serviceIndex)),
40+
new XAttribute(N.m, NS.m),
41+
new XAttribute(N.d, NS.d),
42+
new XAttribute(N.georss, NS.georss),
43+
new XAttribute(N.gml, NS.gml),
44+
new XElement(N.m_count, packages.Count),
45+
46+
packages.Select(package =>
47+
{
48+
var packageV2Url = _url.GetPackageVersionV2Url(package);
49+
var downloadUrl = _url.GetPackageDownloadUrl(package);
50+
51+
return new XElement(
52+
N.entry,
53+
new XElement(N.id, packageV2Url),
54+
new XElement(N.title, package.Title),
55+
new XElement(
56+
N.content,
57+
new XAttribute("type", "application/zip"),
58+
new XAttribute("src", downloadUrl)
59+
),
60+
61+
BuildAuthor(package),
62+
BuildProperties(package)
63+
);
64+
})
65+
);
66+
}
67+
68+
public XElement BuildPackage(Package package)
69+
{
70+
// See: https://joelverhagen.github.io/NuGetUndocs/#endpoint-get-a-single-package
71+
var serviceIndex = _url.GetServiceIndexV2Url();
72+
var packageV2Url = _url.GetPackageVersionV2Url(package);
73+
var downloadUrl = _url.GetPackageDownloadUrl(package);
74+
75+
return new XElement(
76+
N.entry,
77+
new XAttribute(N.baze, XNamespace.Get(serviceIndex)),
78+
new XAttribute(N.m, NS.m),
79+
new XAttribute(N.d, NS.d),
80+
new XAttribute(N.georss, NS.georss),
81+
new XAttribute(N.gml, NS.gml),
82+
new XElement(N.id, packageV2Url),
83+
new XElement(N.title, package.Title),
84+
85+
new XElement(
86+
N.content,
87+
new XAttribute("type", "application/zip"),
88+
new XAttribute("src", downloadUrl)
89+
),
90+
91+
BuildAuthor(package),
92+
BuildProperties(package)
93+
);
94+
}
95+
96+
private XElement BuildProperties(Package package)
97+
{
98+
// See: https://joelverhagen.github.io/NuGetUndocs/#package-entity
99+
return new XElement(
100+
N.m_properties,
101+
new XElement(N.d_Id, package.Id),
102+
new XElement(N.d_Title, package.Title),
103+
new XElement(N.d_Version, package.OriginalVersionString),
104+
new XElement(N.d_NormalizedVersion, package.NormalizedVersionString),
105+
new XElement(N.d_Authors, string.Join(", ", package.Authors)),
106+
new XElement(N.d_Copyright, ""), // TODO
107+
new XElement(N.d_Description, package.Description),
108+
new XElement(
109+
N.d_DownloadCount,
110+
new XAttribute(N.m_type, "Edm.Int32"),
111+
package.Downloads),
112+
new XElement(N.d_LastEdited, package.Published),
113+
new XElement(N.d_Published, package.Published),
114+
new XElement(N.d_PackageHash, ""),
115+
new XElement(N.d_PackageHashAlgorithm, ""),
116+
new XElement(N.d_PackageSize, 0),
117+
new XElement(N.d_ProjectUrl, package.ProjectUrl),
118+
new XElement(N.d_IconUrl, package.IconUrl), // TODO, URL logic
119+
new XElement(N.d_LicenseUrl, package.LicenseUrl), // TODO
120+
new XElement(N.d_Tags, string.Join(", ", package.Tags)),
121+
new XElement(N.d_RequireLicenseAcceptance, package.RequireLicenseAcceptance),
122+
123+
BuildDependencies(package)
124+
);
125+
}
126+
127+
private XElement BuildAuthor(Package package)
128+
{
129+
// TODO: No authors?
130+
return new XElement(
131+
N.author,
132+
package.Authors.Select(author => new XElement(N.name, author))
133+
);
134+
}
135+
136+
private XElement BuildDependencies(Package package)
137+
{
138+
var flattenedDependencies = new List<string>();
139+
140+
flattenedDependencies.AddRange(
141+
package
142+
.Dependencies
143+
.Where(IsFrameworkDependency)
144+
.Select(dependency => dependency.TargetFramework)
145+
.Distinct()
146+
.Select(targetFramework => $"::{targetFramework}"));
147+
148+
flattenedDependencies.AddRange(
149+
package
150+
.Dependencies
151+
.Where(dependency => !IsFrameworkDependency(dependency))
152+
.Select(dependency => $"{dependency.Id}:{dependency.VersionRange}:{dependency.TargetFramework}"));
153+
154+
var result = string.Join("|", flattenedDependencies);
155+
156+
return new XElement(N.d_Dependencies, result);
157+
}
158+
159+
private bool IsFrameworkDependency(PackageDependency dependency)
160+
{
161+
return dependency.Id == null && dependency.VersionRange == null;
162+
}
163+
164+
private static class NS
165+
{
166+
public static readonly XNamespace xmlns = "http://www.w3.org/2005/Atom";
167+
//public static readonly XNamespace baze = "https://www.nuget.org/api/v2/curated-feeds/microsoftdotnet";
168+
public static readonly XNamespace m = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
169+
public static readonly XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices";
170+
public static readonly XNamespace georss = "http://www.georss.org/georss";
171+
public static readonly XNamespace gml = "http://www.opengis.net/gml";
172+
}
173+
174+
private static class N
175+
{
176+
public static readonly XName feed = NS.xmlns + "feed";
177+
public static readonly XName entry = NS.xmlns + "entry";
178+
public static readonly XName title = NS.xmlns + "title";
179+
public static readonly XName author = NS.xmlns + "author";
180+
public static readonly XName name = NS.xmlns + "name";
181+
public static readonly XName link = NS.xmlns + "link";
182+
public static readonly XName id = NS.xmlns + "id";
183+
public static readonly XName content = NS.xmlns + "content";
184+
185+
public static readonly XName m_count = NS.m + "count";
186+
public static readonly XName m_properties = NS.m + "properties";
187+
public static readonly XName m_type = NS.m + "type";
188+
189+
public static readonly XName d_Id = NS.d + "Id";
190+
public static readonly XName d_Title = NS.d + "Title";
191+
public static readonly XName d_Version = NS.d + "Version";
192+
public static readonly XName d_NormalizedVersion = NS.d + "NormalizedVersion";
193+
public static readonly XName d_Authors = NS.d + "Authors";
194+
public static readonly XName d_Copyright = NS.d + "Copyright";
195+
public static readonly XName d_Dependencies = NS.d + "Dependencies";
196+
public static readonly XName d_Description = NS.d + "Description";
197+
public static readonly XName d_IconUrl = NS.d + "IconUrl";
198+
public static readonly XName d_LicenseUrl = NS.d + "LicenseUrl";
199+
public static readonly XName d_ProjectUrl = NS.d + "ProjectUrl";
200+
public static readonly XName d_Tags = NS.d + "Tags";
201+
public static readonly XName d_ReportAbuseUrl = NS.d + "ReportAbuseUrl";
202+
public static readonly XName d_RequireLicenseAcceptance = NS.d + "RequireLicenseAcceptance";
203+
public static readonly XName d_DownloadCount = NS.d + "DownloadCount";
204+
public static readonly XName d_Created = NS.d + "Created";
205+
public static readonly XName d_LastEdited = NS.d + "LastEdited";
206+
public static readonly XName d_Published = NS.d + "Published";
207+
public static readonly XName d_PackageHash = NS.d + "PackageHash";
208+
public static readonly XName d_PackageHashAlgorithm = NS.d + "PackageHashAlgorithm";
209+
public static readonly XName d_MinClientVersion = NS.d + "MinClientVersion";
210+
public static readonly XName d_PackageSize = NS.d + "PackageSize";
211+
212+
public static readonly XName baze = XNamespace.Xmlns + "base";
213+
public static readonly XName m = XNamespace.Xmlns + "m";
214+
public static readonly XName d = XNamespace.Xmlns + "d";
215+
public static readonly XName georss = XNamespace.Xmlns + "georss";
216+
public static readonly XName gml = XNamespace.Xmlns + "gml";
217+
}
218+
}
219+
}

0 commit comments

Comments
 (0)