diff --git a/src/BaGet.Core/BaGet.Core.csproj b/src/BaGet.Core/BaGet.Core.csproj
index 8c128749..4b5cc093 100644
--- a/src/BaGet.Core/BaGet.Core.csproj
+++ b/src/BaGet.Core/BaGet.Core.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/src/BaGet.Core/Configuration/MirrorOptions.cs b/src/BaGet.Core/Configuration/MirrorOptions.cs
index f5bb2946..09db895d 100644
--- a/src/BaGet.Core/Configuration/MirrorOptions.cs
+++ b/src/BaGet.Core/Configuration/MirrorOptions.cs
@@ -17,6 +17,11 @@ public class MirrorOptions : IValidatableObject
///
public Uri PackageSource { get; set; }
+ ///
+ /// Whether or not the package source is a v2 package source feed.
+ ///
+ public bool Legacy { get; set; }
+
///
/// The time before a download from the package source times out.
///
diff --git a/src/BaGet.Core/Extensions/DependencyInjectionExtensions.cs b/src/BaGet.Core/Extensions/DependencyInjectionExtensions.cs
index 7cf4650d..a4538920 100644
--- a/src/BaGet.Core/Extensions/DependencyInjectionExtensions.cs
+++ b/src/BaGet.Core/Extensions/DependencyInjectionExtensions.cs
@@ -99,11 +99,14 @@ private static void AddBaGetServices(this IServiceCollection services)
services.TryAddTransient();
services.TryAddTransient();
services.TryAddTransient();
+ services.TryAddTransient();
+ services.TryAddTransient();
services.TryAddTransient();
services.TryAddSingleton();
services.TryAddTransient();
services.TryAddTransient(IMirrorServiceFactory);
+ services.TryAddTransient(IMirrorNuGetClientFactory);
}
private static void AddDefaultProviders(this IServiceCollection services)
@@ -195,8 +198,16 @@ private static IMirrorService IMirrorServiceFactory(IServiceProvider provider)
{
var options = provider.GetRequiredService>();
var service = options.Value.Enabled ? typeof(MirrorService) : typeof(NullMirrorService);
-
+
return (IMirrorService)provider.GetRequiredService(service);
}
+
+ private static IMirrorNuGetClient IMirrorNuGetClientFactory(IServiceProvider provider)
+ {
+ var options = provider.GetRequiredService>();
+ var service = options.Value.Legacy ? typeof(MirrorV2Client) : typeof(MirrorV3Client);
+
+ return (IMirrorNuGetClient)provider.GetRequiredService(service);
+ }
}
}
diff --git a/src/BaGet.Core/Mirror/Clients/MirrorV2Client.cs b/src/BaGet.Core/Mirror/Clients/MirrorV2Client.cs
new file mode 100644
index 00000000..b5634db5
--- /dev/null
+++ b/src/BaGet.Core/Mirror/Clients/MirrorV2Client.cs
@@ -0,0 +1,114 @@
+using BaGet.Protocol.Models;
+using Microsoft.Extensions.Options;
+using NuGet.Common;
+using NuGet.Configuration;
+using NuGet.Protocol;
+using NuGet.Protocol.Core.Types;
+using NuGet.Versioning;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace BaGet.Core
+{
+ internal sealed class MirrorV2Client : IMirrorNuGetClient
+ {
+ private readonly ILogger _logger;
+ private readonly SourceCacheContext _cache;
+ private readonly SourceRepository _repository;
+
+ public MirrorV2Client(IOptionsSnapshot options)
+ {
+ if (options is null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
+
+ if (options.Value?.PackageSource?.AbsolutePath == null)
+ {
+ throw new ArgumentException("No mirror package source has been set.");
+ }
+
+ _logger = NullLogger.Instance;
+ _cache = new SourceCacheContext();
+ _repository = Repository.Factory.GetCoreV2(new PackageSource(options.Value.PackageSource.AbsoluteUri));
+ }
+
+ public async Task> ListPackageVersionsAsync(string id, bool includeUnlisted, CancellationToken cancellationToken)
+ {
+ var resource = await _repository.GetResourceAsync();
+ var versions = await resource.GetAllVersionsAsync(id, _cache, _logger, cancellationToken);
+
+ return versions.ToList();
+ }
+
+ public async Task> GetPackageMetadataAsync(string id, CancellationToken cancellationToken)
+ {
+ var resource = await _repository.GetResourceAsync();
+ var packages = await resource.GetMetadataAsync(id, includePrerelease: true, includeUnlisted: false, _cache, _logger, cancellationToken);
+
+ var result = new List();
+ foreach (var package in packages)
+ {
+ result.Add(new PackageMetadata
+ {
+ Authors = package.Authors,
+ Description = package.Description,
+ IconUrl = package.IconUrl?.AbsoluteUri,
+ LicenseUrl = package.LicenseUrl?.AbsoluteUri,
+ Listed = package.IsListed,
+ PackageId = id,
+ Summary = package.Summary,
+ Version = package.Identity.Version.ToString(),
+ Tags = package.Tags?.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries),
+ Title = package.Title,
+ RequireLicenseAcceptance = package.RequireLicenseAcceptance,
+ Published = package.Published?.UtcDateTime ?? DateTimeOffset.MinValue,
+ ProjectUrl = package.ProjectUrl?.AbsoluteUri,
+ DependencyGroups = GetDependencies(package),
+ });
+ }
+
+ return result;
+ }
+
+ public async Task DownloadPackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken)
+ {
+ var packageStream = new MemoryStream();
+ var resource = await _repository.GetResourceAsync();
+ await resource.CopyNupkgToStreamAsync(id, version, packageStream, _cache, _logger, cancellationToken);
+ packageStream.Seek(0, SeekOrigin.Begin);
+
+ return packageStream;
+ }
+
+ private IReadOnlyList GetDependencies(IPackageSearchMetadata package)
+ {
+ var groupItems = new List();
+ foreach (var set in package.DependencySets)
+ {
+ var item = new DependencyGroupItem
+ {
+ TargetFramework = set.TargetFramework.Framework,
+ Dependencies = new List()
+ };
+
+ foreach (var dependency in set.Packages)
+ {
+ item.Dependencies.Add(new DependencyItem
+ {
+ Id = dependency.Id,
+ Range = dependency.VersionRange.ToNormalizedString(),
+ });
+ }
+
+ groupItems.Add(item);
+ }
+
+ return groupItems;
+ }
+ }
+}
diff --git a/src/BaGet.Core/Mirror/Clients/MirrorV3Client.cs b/src/BaGet.Core/Mirror/Clients/MirrorV3Client.cs
new file mode 100644
index 00000000..09e928cc
--- /dev/null
+++ b/src/BaGet.Core/Mirror/Clients/MirrorV3Client.cs
@@ -0,0 +1,36 @@
+using BaGet.Protocol;
+using BaGet.Protocol.Models;
+using NuGet.Versioning;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace BaGet.Core
+{
+ internal sealed class MirrorV3Client : IMirrorNuGetClient
+ {
+ private readonly NuGetClient _client;
+
+ public MirrorV3Client(NuGetClient client)
+ {
+ _client = client ?? throw new ArgumentNullException(nameof(client));
+ }
+
+ public async Task DownloadPackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken)
+ {
+ return await _client.DownloadPackageAsync(id, version, cancellationToken);
+ }
+
+ public async Task> GetPackageMetadataAsync(string id, CancellationToken cancellationToken)
+ {
+ return await _client.GetPackageMetadataAsync(id, cancellationToken);
+ }
+
+ public async Task> ListPackageVersionsAsync(string id, bool includeUnlisted, CancellationToken cancellationToken)
+ {
+ return await _client.ListPackageVersionsAsync(id, includeUnlisted, cancellationToken);
+ }
+ }
+}
diff --git a/src/BaGet.Core/Mirror/IMirrorNuGetClient.cs b/src/BaGet.Core/Mirror/IMirrorNuGetClient.cs
new file mode 100644
index 00000000..4b51d088
--- /dev/null
+++ b/src/BaGet.Core/Mirror/IMirrorNuGetClient.cs
@@ -0,0 +1,16 @@
+using BaGet.Protocol.Models;
+using NuGet.Versioning;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace BaGet.Core
+{
+ public interface IMirrorNuGetClient
+ {
+ Task> ListPackageVersionsAsync(string id, bool includeUnlisted, CancellationToken cancellationToken);
+ Task> GetPackageMetadataAsync(string id, CancellationToken cancellationToken);
+ Task DownloadPackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken);
+ }
+}
diff --git a/src/BaGet.Core/Mirror/MirrorService.cs b/src/BaGet.Core/Mirror/MirrorService.cs
index 88bebadc..2bafa516 100644
--- a/src/BaGet.Core/Mirror/MirrorService.cs
+++ b/src/BaGet.Core/Mirror/MirrorService.cs
@@ -16,13 +16,13 @@ namespace BaGet.Core
public class MirrorService : IMirrorService
{
private readonly IPackageService _localPackages;
- private readonly NuGetClient _upstreamClient;
+ private readonly IMirrorNuGetClient _upstreamClient;
private readonly IPackageIndexingService _indexer;
private readonly ILogger _logger;
public MirrorService(
IPackageService localPackages,
- NuGetClient upstreamClient,
+ IMirrorNuGetClient upstreamClient,
IPackageIndexingService indexer,
ILogger logger)
{
@@ -32,6 +32,19 @@ public MirrorService(
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
+ public static MirrorService Create(
+ IPackageService localPackages,
+ NuGetClient client,
+ IPackageIndexingService indexer,
+ ILogger logger)
+ {
+ return new MirrorService(
+ localPackages,
+ new MirrorV3Client(client),
+ indexer,
+ logger);
+ }
+
public async Task> FindPackageVersionsOrNullAsync(
string id,
CancellationToken cancellationToken)
diff --git a/src/BaGet/appsettings.json b/src/BaGet/appsettings.json
index 3c3ee86f..d2ea0711 100644
--- a/src/BaGet/appsettings.json
+++ b/src/BaGet/appsettings.json
@@ -19,6 +19,8 @@
"Mirror": {
"Enabled": false,
+ // Uncomment this to use the NuGet v2 protocol
+ //"Legacy": true,
"PackageSource": "https://api.nuget.org/v3/index.json"
},
diff --git a/tests/BaGet.Core.Tests/Mirror/MirrorServiceTests.cs b/tests/BaGet.Core.Tests/Mirror/MirrorServiceTests.cs
index 0a10afd4..dc567431 100644
--- a/tests/BaGet.Core.Tests/Mirror/MirrorServiceTests.cs
+++ b/tests/BaGet.Core.Tests/Mirror/MirrorServiceTests.cs
@@ -61,7 +61,7 @@ public FactsBase()
_upstream = new Mock();
_indexer = new Mock();
- _target = new MirrorService(
+ _target = MirrorService.Create(
_packages.Object,
_upstream.Object,
_indexer.Object,