diff --git a/BaGet.sln b/BaGet.sln
index f904327e..b4879934 100644
--- a/BaGet.sln
+++ b/BaGet.sln
@@ -1,81 +1,81 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27130.2027
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BaGet", "src\BaGet\BaGet.csproj", "{284366CB-C68F-473E-908A-50A382616AE0}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BaGet.Core", "src\BaGet.Core\BaGet.Core.csproj", "{FFFACD28-C300-4046-BCFE-4A7899E88EA3}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BaGet.Azure", "src\BaGet.Azure\BaGet.Azure.csproj", "{716C970D-9614-4265-AC92-57E8B227B98E}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BaGet.Tools.AzureSearchImporter", "src\BaGet.Tools.AzureSearchImporter\BaGet.Tools.AzureSearchImporter.csproj", "{B232DAFE-5CE8-441F-ACC7-2BB54BCD094F}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BaGet.Core.Tests", "tests\BaGet.Core.Tests\BaGet.Core.Tests.csproj", "{89AB1AE2-6CAA-4809-8B74-D78CBE00B049}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BaGet.Tests", "tests\BaGet.Tests\BaGet.Tests.csproj", "{892A7A82-4283-4315-B7E5-6D5B70543000}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{26A0B557-53FB-4B9A-94C4-BCCF1BDCB0CC}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{C237857D-AD8E-4C52-974F-6A8155BB0C18}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaGet.Protocol", "src\BaGet.Protocol\BaGet.Protocol.csproj", "{A2D23427-9278-4D52-B31F-759212252832}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaGet.Protocol.Tests", "tests\BaGet.Protocol.Tests\BaGet.Protocol.Tests.csproj", "{AC764A9A-9EAF-422B-9223-D3290C3CFD79}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {284366CB-C68F-473E-908A-50A382616AE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {284366CB-C68F-473E-908A-50A382616AE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {284366CB-C68F-473E-908A-50A382616AE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {284366CB-C68F-473E-908A-50A382616AE0}.Release|Any CPU.Build.0 = Release|Any CPU
- {FFFACD28-C300-4046-BCFE-4A7899E88EA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {FFFACD28-C300-4046-BCFE-4A7899E88EA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {FFFACD28-C300-4046-BCFE-4A7899E88EA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {FFFACD28-C300-4046-BCFE-4A7899E88EA3}.Release|Any CPU.Build.0 = Release|Any CPU
- {716C970D-9614-4265-AC92-57E8B227B98E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {716C970D-9614-4265-AC92-57E8B227B98E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {716C970D-9614-4265-AC92-57E8B227B98E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {716C970D-9614-4265-AC92-57E8B227B98E}.Release|Any CPU.Build.0 = Release|Any CPU
- {B232DAFE-5CE8-441F-ACC7-2BB54BCD094F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B232DAFE-5CE8-441F-ACC7-2BB54BCD094F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B232DAFE-5CE8-441F-ACC7-2BB54BCD094F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B232DAFE-5CE8-441F-ACC7-2BB54BCD094F}.Release|Any CPU.Build.0 = Release|Any CPU
- {89AB1AE2-6CAA-4809-8B74-D78CBE00B049}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {89AB1AE2-6CAA-4809-8B74-D78CBE00B049}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {89AB1AE2-6CAA-4809-8B74-D78CBE00B049}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {89AB1AE2-6CAA-4809-8B74-D78CBE00B049}.Release|Any CPU.Build.0 = Release|Any CPU
- {892A7A82-4283-4315-B7E5-6D5B70543000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {892A7A82-4283-4315-B7E5-6D5B70543000}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {892A7A82-4283-4315-B7E5-6D5B70543000}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {892A7A82-4283-4315-B7E5-6D5B70543000}.Release|Any CPU.Build.0 = Release|Any CPU
- {A2D23427-9278-4D52-B31F-759212252832}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A2D23427-9278-4D52-B31F-759212252832}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A2D23427-9278-4D52-B31F-759212252832}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A2D23427-9278-4D52-B31F-759212252832}.Release|Any CPU.Build.0 = Release|Any CPU
- {AC764A9A-9EAF-422B-9223-D3290C3CFD79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {AC764A9A-9EAF-422B-9223-D3290C3CFD79}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AC764A9A-9EAF-422B-9223-D3290C3CFD79}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {AC764A9A-9EAF-422B-9223-D3290C3CFD79}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {284366CB-C68F-473E-908A-50A382616AE0} = {26A0B557-53FB-4B9A-94C4-BCCF1BDCB0CC}
- {FFFACD28-C300-4046-BCFE-4A7899E88EA3} = {26A0B557-53FB-4B9A-94C4-BCCF1BDCB0CC}
- {716C970D-9614-4265-AC92-57E8B227B98E} = {26A0B557-53FB-4B9A-94C4-BCCF1BDCB0CC}
- {B232DAFE-5CE8-441F-ACC7-2BB54BCD094F} = {26A0B557-53FB-4B9A-94C4-BCCF1BDCB0CC}
- {89AB1AE2-6CAA-4809-8B74-D78CBE00B049} = {C237857D-AD8E-4C52-974F-6A8155BB0C18}
- {892A7A82-4283-4315-B7E5-6D5B70543000} = {C237857D-AD8E-4C52-974F-6A8155BB0C18}
- {A2D23427-9278-4D52-B31F-759212252832} = {26A0B557-53FB-4B9A-94C4-BCCF1BDCB0CC}
- {AC764A9A-9EAF-422B-9223-D3290C3CFD79} = {C237857D-AD8E-4C52-974F-6A8155BB0C18}
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {1423C027-2C90-417F-8629-2A4CF107C055}
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27130.2027
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BaGet", "src\BaGet\BaGet.csproj", "{284366CB-C68F-473E-908A-50A382616AE0}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BaGet.Core", "src\BaGet.Core\BaGet.Core.csproj", "{FFFACD28-C300-4046-BCFE-4A7899E88EA3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BaGet.Azure", "src\BaGet.Azure\BaGet.Azure.csproj", "{716C970D-9614-4265-AC92-57E8B227B98E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BaGet.Tools.AzureSearchImporter", "src\BaGet.Tools.AzureSearchImporter\BaGet.Tools.AzureSearchImporter.csproj", "{B232DAFE-5CE8-441F-ACC7-2BB54BCD094F}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BaGet.Core.Tests", "tests\BaGet.Core.Tests\BaGet.Core.Tests.csproj", "{89AB1AE2-6CAA-4809-8B74-D78CBE00B049}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BaGet.Tests", "tests\BaGet.Tests\BaGet.Tests.csproj", "{892A7A82-4283-4315-B7E5-6D5B70543000}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{26A0B557-53FB-4B9A-94C4-BCCF1BDCB0CC}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{C237857D-AD8E-4C52-974F-6A8155BB0C18}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaGet.Protocol", "src\BaGet.Protocol\BaGet.Protocol.csproj", "{A2D23427-9278-4D52-B31F-759212252832}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaGet.Protocol.Tests", "tests\BaGet.Protocol.Tests\BaGet.Protocol.Tests.csproj", "{AC764A9A-9EAF-422B-9223-D3290C3CFD79}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {284366CB-C68F-473E-908A-50A382616AE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {284366CB-C68F-473E-908A-50A382616AE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {284366CB-C68F-473E-908A-50A382616AE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {284366CB-C68F-473E-908A-50A382616AE0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FFFACD28-C300-4046-BCFE-4A7899E88EA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FFFACD28-C300-4046-BCFE-4A7899E88EA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FFFACD28-C300-4046-BCFE-4A7899E88EA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FFFACD28-C300-4046-BCFE-4A7899E88EA3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {716C970D-9614-4265-AC92-57E8B227B98E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {716C970D-9614-4265-AC92-57E8B227B98E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {716C970D-9614-4265-AC92-57E8B227B98E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {716C970D-9614-4265-AC92-57E8B227B98E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B232DAFE-5CE8-441F-ACC7-2BB54BCD094F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B232DAFE-5CE8-441F-ACC7-2BB54BCD094F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B232DAFE-5CE8-441F-ACC7-2BB54BCD094F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B232DAFE-5CE8-441F-ACC7-2BB54BCD094F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {89AB1AE2-6CAA-4809-8B74-D78CBE00B049}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {89AB1AE2-6CAA-4809-8B74-D78CBE00B049}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {89AB1AE2-6CAA-4809-8B74-D78CBE00B049}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {89AB1AE2-6CAA-4809-8B74-D78CBE00B049}.Release|Any CPU.Build.0 = Release|Any CPU
+ {892A7A82-4283-4315-B7E5-6D5B70543000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {892A7A82-4283-4315-B7E5-6D5B70543000}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {892A7A82-4283-4315-B7E5-6D5B70543000}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {892A7A82-4283-4315-B7E5-6D5B70543000}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A2D23427-9278-4D52-B31F-759212252832}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A2D23427-9278-4D52-B31F-759212252832}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A2D23427-9278-4D52-B31F-759212252832}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A2D23427-9278-4D52-B31F-759212252832}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AC764A9A-9EAF-422B-9223-D3290C3CFD79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AC764A9A-9EAF-422B-9223-D3290C3CFD79}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AC764A9A-9EAF-422B-9223-D3290C3CFD79}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AC764A9A-9EAF-422B-9223-D3290C3CFD79}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {284366CB-C68F-473E-908A-50A382616AE0} = {26A0B557-53FB-4B9A-94C4-BCCF1BDCB0CC}
+ {FFFACD28-C300-4046-BCFE-4A7899E88EA3} = {26A0B557-53FB-4B9A-94C4-BCCF1BDCB0CC}
+ {716C970D-9614-4265-AC92-57E8B227B98E} = {26A0B557-53FB-4B9A-94C4-BCCF1BDCB0CC}
+ {B232DAFE-5CE8-441F-ACC7-2BB54BCD094F} = {26A0B557-53FB-4B9A-94C4-BCCF1BDCB0CC}
+ {89AB1AE2-6CAA-4809-8B74-D78CBE00B049} = {C237857D-AD8E-4C52-974F-6A8155BB0C18}
+ {892A7A82-4283-4315-B7E5-6D5B70543000} = {C237857D-AD8E-4C52-974F-6A8155BB0C18}
+ {A2D23427-9278-4D52-B31F-759212252832} = {26A0B557-53FB-4B9A-94C4-BCCF1BDCB0CC}
+ {AC764A9A-9EAF-422B-9223-D3290C3CFD79} = {C237857D-AD8E-4C52-974F-6A8155BB0C18}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {1423C027-2C90-417F-8629-2A4CF107C055}
+ EndGlobalSection
+EndGlobal
diff --git a/LICENSE b/LICENSE
index cc2ed50b..0f682f8d 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,19 +1,19 @@
-Copyright 2018 Loic Sharma
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+Copyright 2018 Loic Sharma
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
\ No newline at end of file
diff --git a/docs/readme.md b/docs/readme.md
index ade0ce39..c962291d 100644
--- a/docs/readme.md
+++ b/docs/readme.md
@@ -1,2 +1,2 @@
-Please use the [documentation website](https://loic-sharma.github.io/BaGet/)
+Please use the [documentation website](https://loic-sharma.github.io/BaGet/)
for the best browsing experience.
\ No newline at end of file
diff --git a/src/BaGet.Azure/BaGet.Azure.csproj b/src/BaGet.Azure/BaGet.Azure.csproj
index 4ce007b0..803e06b4 100644
--- a/src/BaGet.Azure/BaGet.Azure.csproj
+++ b/src/BaGet.Azure/BaGet.Azure.csproj
@@ -1,19 +1,19 @@
-
-
-
- netstandard2.0;net461
-
- The libraries to host BaGet on Azure.
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ netstandard2.0;net461
+
+ The libraries to host BaGet on Azure.
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BaGet.Azure/Configuration/AzureSearchOptions.cs b/src/BaGet.Azure/Configuration/AzureSearchOptions.cs
index a8e74bc1..1ba788de 100644
--- a/src/BaGet.Azure/Configuration/AzureSearchOptions.cs
+++ b/src/BaGet.Azure/Configuration/AzureSearchOptions.cs
@@ -1,13 +1,13 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace BaGet.Azure.Configuration
-{
- public class AzureSearchOptions : Core.Configuration.SearchOptions
- {
- [Required]
- public string AccountName { get; set; }
-
- [Required]
- public string ApiKey { get; set; }
- }
-}
+using System.ComponentModel.DataAnnotations;
+
+namespace BaGet.Azure.Configuration
+{
+ public class AzureSearchOptions : Core.Configuration.SearchOptions
+ {
+ [Required]
+ public string AccountName { get; set; }
+
+ [Required]
+ public string ApiKey { get; set; }
+ }
+}
diff --git a/src/BaGet.Azure/Configuration/BlobStorageOptions.cs b/src/BaGet.Azure/Configuration/BlobStorageOptions.cs
index 5649f32b..40799b03 100644
--- a/src/BaGet.Azure/Configuration/BlobStorageOptions.cs
+++ b/src/BaGet.Azure/Configuration/BlobStorageOptions.cs
@@ -1,16 +1,16 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace BaGet.Azure.Configuration
-{
- public class BlobStorageOptions
- {
- [Required]
- public string AccountName { get; set; }
-
- [Required]
- public string AccessKey { get; set; }
-
- [Required]
- public string Container { get; set; }
- }
-}
+using System.ComponentModel.DataAnnotations;
+
+namespace BaGet.Azure.Configuration
+{
+ public class BlobStorageOptions
+ {
+ [Required]
+ public string AccountName { get; set; }
+
+ [Required]
+ public string AccessKey { get; set; }
+
+ [Required]
+ public string Container { get; set; }
+ }
+}
diff --git a/src/BaGet.Azure/Extensions/ServiceCollectionExtensions.cs b/src/BaGet.Azure/Extensions/ServiceCollectionExtensions.cs
index 783e0680..fd62e396 100644
--- a/src/BaGet.Azure/Extensions/ServiceCollectionExtensions.cs
+++ b/src/BaGet.Azure/Extensions/ServiceCollectionExtensions.cs
@@ -1,67 +1,67 @@
-using BaGet.Azure.Configuration;
-using BaGet.Azure.Search;
-using BaGet.Core.Services;
-using Microsoft.Azure.Search;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Options;
-using Microsoft.WindowsAzure.Storage;
-using Microsoft.WindowsAzure.Storage.Auth;
-using Microsoft.WindowsAzure.Storage.Blob;
-
-namespace BaGet.Azure.Extensions
-{
- public static class ServiceCollectionExtensions
- {
- public static IServiceCollection AddBlobStorageService(this IServiceCollection services)
- {
- services.AddSingleton(provider =>
- {
- var options = provider.GetRequiredService>().Value;
-
- return new CloudStorageAccount(
- new StorageCredentials(
- options.AccountName,
- options.AccessKey),
- useHttps: true);
- });
-
- services.AddTransient(provider =>
- {
- var options = provider.GetRequiredService>().Value;
- var account = provider.GetRequiredService();
-
- var client = account.CreateCloudBlobClient();
-
- return client.GetContainerReference(options.Container);
- });
-
- services.AddTransient();
-
- return services;
- }
-
- public static IServiceCollection AddAzureSearch(this IServiceCollection services)
- {
- services.AddTransient();
- services.AddTransient();
-
- services.AddSingleton(provider =>
- {
- var options = provider.GetRequiredService>().Value;
- var credentials = new SearchCredentials(options.ApiKey);
-
- return new SearchServiceClient(options.AccountName, credentials);
- });
-
- services.AddSingleton(provider =>
- {
- var options = provider.GetRequiredService>().Value;
- var credentials = new SearchCredentials(options.ApiKey);
-
- return new SearchIndexClient(options.AccountName, PackageDocument.IndexName, credentials);
- });
-
- return services;
- }
- }
-}
+using BaGet.Azure.Configuration;
+using BaGet.Azure.Search;
+using BaGet.Core.Services;
+using Microsoft.Azure.Search;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Microsoft.WindowsAzure.Storage;
+using Microsoft.WindowsAzure.Storage.Auth;
+using Microsoft.WindowsAzure.Storage.Blob;
+
+namespace BaGet.Azure.Extensions
+{
+ public static class ServiceCollectionExtensions
+ {
+ public static IServiceCollection AddBlobStorageService(this IServiceCollection services)
+ {
+ services.AddSingleton(provider =>
+ {
+ var options = provider.GetRequiredService>().Value;
+
+ return new CloudStorageAccount(
+ new StorageCredentials(
+ options.AccountName,
+ options.AccessKey),
+ useHttps: true);
+ });
+
+ services.AddTransient(provider =>
+ {
+ var options = provider.GetRequiredService>().Value;
+ var account = provider.GetRequiredService();
+
+ var client = account.CreateCloudBlobClient();
+
+ return client.GetContainerReference(options.Container);
+ });
+
+ services.AddTransient();
+
+ return services;
+ }
+
+ public static IServiceCollection AddAzureSearch(this IServiceCollection services)
+ {
+ services.AddTransient();
+ services.AddTransient();
+
+ services.AddSingleton(provider =>
+ {
+ var options = provider.GetRequiredService>().Value;
+ var credentials = new SearchCredentials(options.ApiKey);
+
+ return new SearchServiceClient(options.AccountName, credentials);
+ });
+
+ services.AddSingleton(provider =>
+ {
+ var options = provider.GetRequiredService>().Value;
+ var credentials = new SearchCredentials(options.ApiKey);
+
+ return new SearchIndexClient(options.AccountName, PackageDocument.IndexName, credentials);
+ });
+
+ return services;
+ }
+ }
+}
diff --git a/src/BaGet.Azure/Search/AzureSearchService.cs b/src/BaGet.Azure/Search/AzureSearchService.cs
index 40810140..6c2e4831 100644
--- a/src/BaGet.Azure/Search/AzureSearchService.cs
+++ b/src/BaGet.Azure/Search/AzureSearchService.cs
@@ -1,85 +1,85 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using BaGet.Core.Services;
-using Microsoft.Azure.Search;
-using NuGet.Versioning;
-
-namespace BaGet.Azure.Search
-{
- using BaGet.Core.Entities;
- using SearchParameters = Microsoft.Azure.Search.Models.SearchParameters;
-
- public class AzureSearchService : ISearchService
- {
- private readonly BatchIndexer _indexer;
- private readonly SearchIndexClient _searchClient;
-
- public AzureSearchService(BatchIndexer indexer, SearchIndexClient searchClient)
- {
- _indexer = indexer ?? throw new ArgumentNullException(nameof(indexer));
- _searchClient = searchClient ?? throw new ArgumentNullException(nameof(searchClient));
- }
-
- public Task IndexAsync(Package package) => _indexer.IndexAsync(package.Id);
-
- public async Task> SearchAsync(string query, int skip = 0, int take = 20)
- {
- query = query.TrimEnd().TrimEnd('*') + '*';
-
- var search = await _searchClient.Documents.SearchAsync(query, new SearchParameters
- {
- Skip = skip,
- Top = take
- });
-
- var results = new List();
-
- foreach (var result in search.Results)
- {
- var document = result.Document;
- var versions = new List();
-
- if (document.Versions.Length != document.VersionDownloads.Length)
- {
- throw new InvalidOperationException($"Invalid document {document.Key} with mismatched versions");
- }
-
- for (var i = 0; i < document.Versions.Length; i++)
- {
- versions.Add(new SearchResultVersion(
- NuGetVersion.Parse(document.Versions[i]),
- long.Parse(document.VersionDownloads[i])));
- }
-
- results.Add(new SearchResult
- {
- Id = document.Id,
- Version = NuGetVersion.Parse(document.Version),
- Description = document.Description,
- Authors = document.Authors,
- IconUrl = document.IconUrl,
- LicenseUrl = document.LicenseUrl,
- Summary = document.Summary,
- Tags = document.Tags,
- Title = document.Title,
- TotalDownloads = document.TotalDownloads,
- Versions = versions.AsReadOnly()
- });
- }
-
- return results.AsReadOnly();
- }
-
- public async Task> AutocompleteAsync(string query, int skip = 0, int take = 20)
- {
- var search = await _searchClient.Documents.SearchAsync(query);
-
- return search.Results
- .Select(r => r.Document.Id)
- .ToList()
- .AsReadOnly();
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using BaGet.Core.Services;
+using Microsoft.Azure.Search;
+using NuGet.Versioning;
+
+namespace BaGet.Azure.Search
+{
+ using BaGet.Core.Entities;
+ using SearchParameters = Microsoft.Azure.Search.Models.SearchParameters;
+
+ public class AzureSearchService : ISearchService
+ {
+ private readonly BatchIndexer _indexer;
+ private readonly SearchIndexClient _searchClient;
+
+ public AzureSearchService(BatchIndexer indexer, SearchIndexClient searchClient)
+ {
+ _indexer = indexer ?? throw new ArgumentNullException(nameof(indexer));
+ _searchClient = searchClient ?? throw new ArgumentNullException(nameof(searchClient));
+ }
+
+ public Task IndexAsync(Package package) => _indexer.IndexAsync(package.Id);
+
+ public async Task> SearchAsync(string query, int skip = 0, int take = 20)
+ {
+ query = query.TrimEnd().TrimEnd('*') + '*';
+
+ var search = await _searchClient.Documents.SearchAsync(query, new SearchParameters
+ {
+ Skip = skip,
+ Top = take
+ });
+
+ var results = new List();
+
+ foreach (var result in search.Results)
+ {
+ var document = result.Document;
+ var versions = new List();
+
+ if (document.Versions.Length != document.VersionDownloads.Length)
+ {
+ throw new InvalidOperationException($"Invalid document {document.Key} with mismatched versions");
+ }
+
+ for (var i = 0; i < document.Versions.Length; i++)
+ {
+ versions.Add(new SearchResultVersion(
+ NuGetVersion.Parse(document.Versions[i]),
+ long.Parse(document.VersionDownloads[i])));
+ }
+
+ results.Add(new SearchResult
+ {
+ Id = document.Id,
+ Version = NuGetVersion.Parse(document.Version),
+ Description = document.Description,
+ Authors = document.Authors,
+ IconUrl = document.IconUrl,
+ LicenseUrl = document.LicenseUrl,
+ Summary = document.Summary,
+ Tags = document.Tags,
+ Title = document.Title,
+ TotalDownloads = document.TotalDownloads,
+ Versions = versions.AsReadOnly()
+ });
+ }
+
+ return results.AsReadOnly();
+ }
+
+ public async Task> AutocompleteAsync(string query, int skip = 0, int take = 20)
+ {
+ var search = await _searchClient.Documents.SearchAsync(query);
+
+ return search.Results
+ .Select(r => r.Document.Id)
+ .ToList()
+ .AsReadOnly();
+ }
+ }
+}
diff --git a/src/BaGet.Azure/Search/BatchIndexer.cs b/src/BaGet.Azure/Search/BatchIndexer.cs
index 2feebd5d..f16480b4 100644
--- a/src/BaGet.Azure/Search/BatchIndexer.cs
+++ b/src/BaGet.Azure/Search/BatchIndexer.cs
@@ -1,111 +1,111 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using BaGet.Core.Services;
-using Microsoft.Azure.Search;
-using Microsoft.Azure.Search.Models;
-using Microsoft.Extensions.Logging;
-
-namespace BaGet.Azure.Search
-{
- public class BatchIndexer
- {
- public const int MaxBatchSize = 1000;
-
- private readonly IPackageService _packageService;
- private readonly ISearchIndexClient _indexClient;
- private readonly ILogger _logger;
-
- public BatchIndexer(
- IPackageService packageService,
- SearchServiceClient searchClient,
- ILogger logger)
- {
- if (searchClient == null) throw new ArgumentNullException(nameof(searchClient));
-
- _indexClient = searchClient.Indexes.GetClient(PackageDocument.IndexName);
-
- _packageService = packageService ?? throw new ArgumentNullException(nameof(packageService));
- _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- }
-
- public async Task IndexAsync(params string[] packageIds)
- {
- if (packageIds == null) throw new ArgumentNullException(nameof(packageIds));
-
- var actions = new List>();
- var packageIdSet = new HashSet(packageIds, StringComparer.OrdinalIgnoreCase);
-
- if (packageIdSet.Count > MaxBatchSize)
- {
- throw new ArgumentException($"Cannot index more than {MaxBatchSize} packages at once");
- }
-
- _logger.LogInformation("Indexing {PackageCount} packages...", packageIdSet.Count);
-
- foreach (var packageId in packageIdSet)
- {
- var document = await BuildDocumentAsync(packageId);
-
- actions.Add(IndexAction.Upload(document));
- }
-
- var batch = IndexBatch.New(actions);
-
- // TODO: Add retry on IndexBatchException
- // See: https://docs.microsoft.com/en-us/azure/search/search-import-data-dotnet#import-data-to-the-index
- await _indexClient.Documents.IndexAsync(batch);
-
- _logger.LogInformation("Indexed {PackageCount} packages", packageIdSet.Count);
- }
-
- private async Task BuildDocumentAsync(string packageId)
- {
- if (packageId == null) throw new ArgumentNullException(nameof(packageId));
-
- var packages = await _packageService.FindAsync(packageId);
-
- if (packages.Count == 0)
- {
- _logger.LogError("Could not find package with id {PackageId}", packageId);
-
- throw new ArgumentException($"Invalid package id {packageId}", nameof(packageId));
- }
-
- var result = new PackageDocument();
-
- var latest = packages.OrderByDescending(p => p.Version).First();
- var versions = packages.OrderBy(p => p.Version).ToList();
-
- result.Key = EncodeKey(packageId.ToLowerInvariant());
- result.Id = latest.Id;
- result.Version = latest.VersionString;
- result.Description = latest.Description;
- result.Authors = latest.Authors;
- result.IconUrl = latest.IconUrlString;
- result.LicenseUrl = latest.LicenseUrlString;
- result.ProjectUrl = latest.ProjectUrlString;
- result.Published = latest.Published;
- result.Summary = latest.Summary;
- result.Tags = latest.Tags;
- result.Title = latest.Title;
- result.TotalDownloads = versions.Sum(p => p.Downloads);
- result.DownloadsMagnitude = result.TotalDownloads.ToString().Length;
- result.Versions = versions.Select(p => p.VersionString).ToArray();
- result.VersionDownloads = versions.Select(p => p.Downloads.ToString()).ToArray();
-
- return result;
- }
-
- private string EncodeKey(string key)
- {
- // Keys can only contain letters, digits, underscore(_), dash(-), or equal sign(=).
- var bytes = Encoding.UTF8.GetBytes(key);
- var base64 = Convert.ToBase64String(bytes);
-
- return base64.Replace('+', '-').Replace('/', '_');
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using BaGet.Core.Services;
+using Microsoft.Azure.Search;
+using Microsoft.Azure.Search.Models;
+using Microsoft.Extensions.Logging;
+
+namespace BaGet.Azure.Search
+{
+ public class BatchIndexer
+ {
+ public const int MaxBatchSize = 1000;
+
+ private readonly IPackageService _packageService;
+ private readonly ISearchIndexClient _indexClient;
+ private readonly ILogger _logger;
+
+ public BatchIndexer(
+ IPackageService packageService,
+ SearchServiceClient searchClient,
+ ILogger logger)
+ {
+ if (searchClient == null) throw new ArgumentNullException(nameof(searchClient));
+
+ _indexClient = searchClient.Indexes.GetClient(PackageDocument.IndexName);
+
+ _packageService = packageService ?? throw new ArgumentNullException(nameof(packageService));
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ }
+
+ public async Task IndexAsync(params string[] packageIds)
+ {
+ if (packageIds == null) throw new ArgumentNullException(nameof(packageIds));
+
+ var actions = new List>();
+ var packageIdSet = new HashSet(packageIds, StringComparer.OrdinalIgnoreCase);
+
+ if (packageIdSet.Count > MaxBatchSize)
+ {
+ throw new ArgumentException($"Cannot index more than {MaxBatchSize} packages at once");
+ }
+
+ _logger.LogInformation("Indexing {PackageCount} packages...", packageIdSet.Count);
+
+ foreach (var packageId in packageIdSet)
+ {
+ var document = await BuildDocumentAsync(packageId);
+
+ actions.Add(IndexAction.Upload(document));
+ }
+
+ var batch = IndexBatch.New(actions);
+
+ // TODO: Add retry on IndexBatchException
+ // See: https://docs.microsoft.com/en-us/azure/search/search-import-data-dotnet#import-data-to-the-index
+ await _indexClient.Documents.IndexAsync(batch);
+
+ _logger.LogInformation("Indexed {PackageCount} packages", packageIdSet.Count);
+ }
+
+ private async Task BuildDocumentAsync(string packageId)
+ {
+ if (packageId == null) throw new ArgumentNullException(nameof(packageId));
+
+ var packages = await _packageService.FindAsync(packageId);
+
+ if (packages.Count == 0)
+ {
+ _logger.LogError("Could not find package with id {PackageId}", packageId);
+
+ throw new ArgumentException($"Invalid package id {packageId}", nameof(packageId));
+ }
+
+ var result = new PackageDocument();
+
+ var latest = packages.OrderByDescending(p => p.Version).First();
+ var versions = packages.OrderBy(p => p.Version).ToList();
+
+ result.Key = EncodeKey(packageId.ToLowerInvariant());
+ result.Id = latest.Id;
+ result.Version = latest.VersionString;
+ result.Description = latest.Description;
+ result.Authors = latest.Authors;
+ result.IconUrl = latest.IconUrlString;
+ result.LicenseUrl = latest.LicenseUrlString;
+ result.ProjectUrl = latest.ProjectUrlString;
+ result.Published = latest.Published;
+ result.Summary = latest.Summary;
+ result.Tags = latest.Tags;
+ result.Title = latest.Title;
+ result.TotalDownloads = versions.Sum(p => p.Downloads);
+ result.DownloadsMagnitude = result.TotalDownloads.ToString().Length;
+ result.Versions = versions.Select(p => p.VersionString).ToArray();
+ result.VersionDownloads = versions.Select(p => p.Downloads.ToString()).ToArray();
+
+ return result;
+ }
+
+ private string EncodeKey(string key)
+ {
+ // Keys can only contain letters, digits, underscore(_), dash(-), or equal sign(=).
+ var bytes = Encoding.UTF8.GetBytes(key);
+ var base64 = Convert.ToBase64String(bytes);
+
+ return base64.Replace('+', '-').Replace('/', '_');
+ }
+ }
+}
diff --git a/src/BaGet.Azure/Search/PackageDocument.cs b/src/BaGet.Azure/Search/PackageDocument.cs
index 5de05be5..f902aa88 100644
--- a/src/BaGet.Azure/Search/PackageDocument.cs
+++ b/src/BaGet.Azure/Search/PackageDocument.cs
@@ -1,49 +1,49 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using Microsoft.Azure.Search;
-using Microsoft.Azure.Search.Models;
-
-namespace BaGet.Azure.Search
-{
- // See: https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-for-packages
- [SerializePropertyNamesAsCamelCase]
- public class PackageDocument
- {
- public const string IndexName = "packages";
-
- [Key]
- public string Key { get; set; }
-
- [IsSearchable, IsFilterable, IsSortable]
- public string Id { get; set; }
-
- [IsSearchable, IsFilterable, IsSortable]
- public string Version { get; set; }
-
- [IsSearchable]
- public string Description { get; set; }
- public string[] Authors { get; set; }
- public string IconUrl { get; set; }
- public string LicenseUrl { get; set; }
- public string ProjectUrl { get; set; }
- public DateTimeOffset Published { get; set; }
-
- [IsSearchable]
- public string Summary { get; set; }
-
- [IsSearchable, IsFilterable, IsFacetable]
- public string[] Tags { get; set; }
-
- [IsSearchable]
- public string Title { get; set; }
-
- [IsFilterable, IsSortable]
- public long TotalDownloads { get; set; }
-
- [IsFilterable, IsSortable]
- public int DownloadsMagnitude { get; set; }
-
- public string[] Versions { get; set; }
- public string[] VersionDownloads { get; set; }
- }
-}
+using System;
+using System.ComponentModel.DataAnnotations;
+using Microsoft.Azure.Search;
+using Microsoft.Azure.Search.Models;
+
+namespace BaGet.Azure.Search
+{
+ // See: https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-for-packages
+ [SerializePropertyNamesAsCamelCase]
+ public class PackageDocument
+ {
+ public const string IndexName = "packages";
+
+ [Key]
+ public string Key { get; set; }
+
+ [IsSearchable, IsFilterable, IsSortable]
+ public string Id { get; set; }
+
+ [IsSearchable, IsFilterable, IsSortable]
+ public string Version { get; set; }
+
+ [IsSearchable]
+ public string Description { get; set; }
+ public string[] Authors { get; set; }
+ public string IconUrl { get; set; }
+ public string LicenseUrl { get; set; }
+ public string ProjectUrl { get; set; }
+ public DateTimeOffset Published { get; set; }
+
+ [IsSearchable]
+ public string Summary { get; set; }
+
+ [IsSearchable, IsFilterable, IsFacetable]
+ public string[] Tags { get; set; }
+
+ [IsSearchable]
+ public string Title { get; set; }
+
+ [IsFilterable, IsSortable]
+ public long TotalDownloads { get; set; }
+
+ [IsFilterable, IsSortable]
+ public int DownloadsMagnitude { get; set; }
+
+ public string[] Versions { get; set; }
+ public string[] VersionDownloads { get; set; }
+ }
+}
diff --git a/src/BaGet.Core/BaGet.Core.csproj b/src/BaGet.Core/BaGet.Core.csproj
index 3d8c1d8a..482912f8 100644
--- a/src/BaGet.Core/BaGet.Core.csproj
+++ b/src/BaGet.Core/BaGet.Core.csproj
@@ -1,20 +1,20 @@
-
-
-
- netstandard2.0;net461
-
- The core libraries that power BaGet.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ netstandard2.0;net461
+
+ The core libraries that power BaGet.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BaGet.Core/Configuration/BaGetOptions.cs b/src/BaGet.Core/Configuration/BaGetOptions.cs
index f51d1291..92a6a004 100644
--- a/src/BaGet.Core/Configuration/BaGetOptions.cs
+++ b/src/BaGet.Core/Configuration/BaGetOptions.cs
@@ -1,41 +1,41 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace BaGet.Core.Configuration
-{
- public class BaGetOptions
- {
- ///
- /// The SHA-256 hash of the API Key required to authenticate package
- /// operations. If empty, package operations do not require authentication.
- ///
- public string ApiKeyHash { get; set; }
-
- ///
- /// The application root URL for usage in reverse proxy scenarios.
- ///
- public string PathBase { get; set; }
-
- ///
- /// If enabled, the database will be updated at app startup by running
- /// Entity Framework migrations. This is not recommended in production.
- ///
- public bool RunMigrationsAtStartup { get; set; } = true;
-
- ///
- /// How BaGet should interpret package deletion requests.
- ///
- public PackageDeletionBehavior PackageDeletionBehavior { get; set; } = PackageDeletionBehavior.Unlist;
-
- [Required]
- public DatabaseOptions Database { get; set; }
-
- [Required]
- public StorageOptions Storage { get; set; }
-
- [Required]
- public SearchOptions Search { get; set; }
-
- [Required]
- public MirrorOptions Mirror { get; set; }
- }
-}
+using System.ComponentModel.DataAnnotations;
+
+namespace BaGet.Core.Configuration
+{
+ public class BaGetOptions
+ {
+ ///
+ /// The SHA-256 hash of the API Key required to authenticate package
+ /// operations. If empty, package operations do not require authentication.
+ ///
+ public string ApiKeyHash { get; set; }
+
+ ///
+ /// The application root URL for usage in reverse proxy scenarios.
+ ///
+ public string PathBase { get; set; }
+
+ ///
+ /// If enabled, the database will be updated at app startup by running
+ /// Entity Framework migrations. This is not recommended in production.
+ ///
+ public bool RunMigrationsAtStartup { get; set; } = true;
+
+ ///
+ /// How BaGet should interpret package deletion requests.
+ ///
+ public PackageDeletionBehavior PackageDeletionBehavior { get; set; } = PackageDeletionBehavior.Unlist;
+
+ [Required]
+ public DatabaseOptions Database { get; set; }
+
+ [Required]
+ public StorageOptions Storage { get; set; }
+
+ [Required]
+ public SearchOptions Search { get; set; }
+
+ [Required]
+ public MirrorOptions Mirror { get; set; }
+ }
+}
diff --git a/src/BaGet.Core/Configuration/DatabaseOptions.cs b/src/BaGet.Core/Configuration/DatabaseOptions.cs
index 69b74d39..411e6f4d 100644
--- a/src/BaGet.Core/Configuration/DatabaseOptions.cs
+++ b/src/BaGet.Core/Configuration/DatabaseOptions.cs
@@ -1,18 +1,18 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace BaGet.Core.Configuration
-{
- public class DatabaseOptions
- {
- public DatabaseType Type { get; set; }
-
- [Required]
- public string ConnectionString { get; set; }
- }
-
- public enum DatabaseType
- {
- Sqlite,
- SqlServer,
- }
-}
+using System.ComponentModel.DataAnnotations;
+
+namespace BaGet.Core.Configuration
+{
+ public class DatabaseOptions
+ {
+ public DatabaseType Type { get; set; }
+
+ [Required]
+ public string ConnectionString { get; set; }
+ }
+
+ public enum DatabaseType
+ {
+ Sqlite,
+ SqlServer,
+ }
+}
diff --git a/src/BaGet.Core/Configuration/FileSystemStorageOptions.cs b/src/BaGet.Core/Configuration/FileSystemStorageOptions.cs
index 4df207e2..8bfbcd15 100644
--- a/src/BaGet.Core/Configuration/FileSystemStorageOptions.cs
+++ b/src/BaGet.Core/Configuration/FileSystemStorageOptions.cs
@@ -1,14 +1,14 @@
-using System.IO;
-
-namespace BaGet.Core.Configuration
-{
- public class FileSystemStorageOptions : StorageOptions
- {
- ///
- /// The path at which content will be stored. Defaults to the same path
- /// as the main BaGet executable. This path will be created if it does not
- /// exist at startup. Packages will be stored in a subfolder named "packages".
- ///
- public string Path { get; set; } = Directory.GetCurrentDirectory();
- }
-}
+using System.IO;
+
+namespace BaGet.Core.Configuration
+{
+ public class FileSystemStorageOptions : StorageOptions
+ {
+ ///
+ /// The path at which content will be stored. Defaults to the same path
+ /// as the main BaGet executable. This path will be created if it does not
+ /// exist at startup. Packages will be stored in a subfolder named "packages".
+ ///
+ public string Path { get; set; } = Directory.GetCurrentDirectory();
+ }
+}
diff --git a/src/BaGet.Core/Configuration/MirrorOptions.cs b/src/BaGet.Core/Configuration/MirrorOptions.cs
index 5a5038f1..096aef02 100644
--- a/src/BaGet.Core/Configuration/MirrorOptions.cs
+++ b/src/BaGet.Core/Configuration/MirrorOptions.cs
@@ -1,36 +1,36 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-
-namespace BaGet.Core.Configuration
-{
- public class MirrorOptions : IValidatableObject
- {
- ///
- /// If true, packages that aren't found locally will be indexed
- /// using the upstream source.
- ///
- public bool Enabled { get; set; }
-
- ///
- /// The v3 index that will be mirrored.
- ///
- public Uri PackageSource { get; set; }
-
- ///
- /// The time before a download from the package source times out.
- ///
- [Range(0, int.MaxValue)]
- public int PackageDownloadTimeoutSeconds { get; set; } = 600;
-
- public IEnumerable Validate(ValidationContext validationContext)
- {
- if (Enabled && PackageSource == null)
- {
- yield return new ValidationResult(
- $"The {nameof(PackageSource)} configuration is required if mirroring is enabled",
- new[] { nameof(PackageSource) });
- }
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace BaGet.Core.Configuration
+{
+ public class MirrorOptions : IValidatableObject
+ {
+ ///
+ /// If true, packages that aren't found locally will be indexed
+ /// using the upstream source.
+ ///
+ public bool Enabled { get; set; }
+
+ ///
+ /// The v3 index that will be mirrored.
+ ///
+ public Uri PackageSource { get; set; }
+
+ ///
+ /// The time before a download from the package source times out.
+ ///
+ [Range(0, int.MaxValue)]
+ public int PackageDownloadTimeoutSeconds { get; set; } = 600;
+
+ public IEnumerable Validate(ValidationContext validationContext)
+ {
+ if (Enabled && PackageSource == null)
+ {
+ yield return new ValidationResult(
+ $"The {nameof(PackageSource)} configuration is required if mirroring is enabled",
+ new[] { nameof(PackageSource) });
+ }
+ }
+ }
+}
diff --git a/src/BaGet.Core/Configuration/PackageDeletionBehavior.cs b/src/BaGet.Core/Configuration/PackageDeletionBehavior.cs
index 99681513..f6c6d227 100644
--- a/src/BaGet.Core/Configuration/PackageDeletionBehavior.cs
+++ b/src/BaGet.Core/Configuration/PackageDeletionBehavior.cs
@@ -1,22 +1,22 @@
-namespace BaGet.Core.Configuration
-{
- ///
- /// How BaGet should interpret package deletion requests.
- /// See: https://docs.microsoft.com/en-us/nuget/api/package-publish-resource#delete-a-package
- ///
- public enum PackageDeletionBehavior
- {
- ///
- /// Package "deletions" make the package undiscoverable. The package is still restorable
- /// by consumers that know its id and version. This is the recommended behavior as it prevents
- /// the "left pad" problem.
- ///
- Unlist,
-
- ///
- /// Removes the package from the database and storage. Existing consumers will no longer
- /// be able to restore the package.
- ///
- HardDelete,
- }
-}
+namespace BaGet.Core.Configuration
+{
+ ///
+ /// How BaGet should interpret package deletion requests.
+ /// See: https://docs.microsoft.com/en-us/nuget/api/package-publish-resource#delete-a-package
+ ///
+ public enum PackageDeletionBehavior
+ {
+ ///
+ /// Package "deletions" make the package undiscoverable. The package is still restorable
+ /// by consumers that know its id and version. This is the recommended behavior as it prevents
+ /// the "left pad" problem.
+ ///
+ Unlist,
+
+ ///
+ /// Removes the package from the database and storage. Existing consumers will no longer
+ /// be able to restore the package.
+ ///
+ HardDelete,
+ }
+}
diff --git a/src/BaGet.Core/Configuration/SearchOptions.cs b/src/BaGet.Core/Configuration/SearchOptions.cs
index a1a6f652..af662923 100644
--- a/src/BaGet.Core/Configuration/SearchOptions.cs
+++ b/src/BaGet.Core/Configuration/SearchOptions.cs
@@ -1,13 +1,13 @@
-namespace BaGet.Core.Configuration
-{
- public class SearchOptions
- {
- public SearchType Type { get; set; }
- }
-
- public enum SearchType
- {
- Database = 0,
- Azure = 1,
- }
-}
+namespace BaGet.Core.Configuration
+{
+ public class SearchOptions
+ {
+ public SearchType Type { get; set; }
+ }
+
+ public enum SearchType
+ {
+ Database = 0,
+ Azure = 1,
+ }
+}
diff --git a/src/BaGet.Core/Configuration/StorageOptions.cs b/src/BaGet.Core/Configuration/StorageOptions.cs
index 88073932..561fc955 100644
--- a/src/BaGet.Core/Configuration/StorageOptions.cs
+++ b/src/BaGet.Core/Configuration/StorageOptions.cs
@@ -1,13 +1,13 @@
-namespace BaGet.Core.Configuration
-{
- public class StorageOptions
- {
- public StorageType Type { get; set; }
- }
-
- public enum StorageType
- {
- FileSystem = 0,
- AzureBlobStorage = 1,
- }
-}
+namespace BaGet.Core.Configuration
+{
+ public class StorageOptions
+ {
+ public StorageType Type { get; set; }
+ }
+
+ public enum StorageType
+ {
+ FileSystem = 0,
+ AzureBlobStorage = 1,
+ }
+}
diff --git a/src/BaGet.Core/Entities/IContext.cs b/src/BaGet.Core/Entities/IContext.cs
index 490cca68..3d358226 100644
--- a/src/BaGet.Core/Entities/IContext.cs
+++ b/src/BaGet.Core/Entities/IContext.cs
@@ -1,22 +1,22 @@
-using System.Threading.Tasks;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-
-namespace BaGet.Core.Entities
-{
- public interface IContext
- {
- DatabaseFacade Database { get; }
-
- DbSet Packages { get; set; }
-
- ///
- /// Check whether a is due to a SQL unique constraint violation.
- ///
- /// The exception to inspect.
- /// Whether the exception was caused to SQL unique constraint violation.
- bool IsUniqueConstraintViolationException(DbUpdateException exception);
-
- Task SaveChangesAsync();
- }
-}
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+
+namespace BaGet.Core.Entities
+{
+ public interface IContext
+ {
+ DatabaseFacade Database { get; }
+
+ DbSet Packages { get; set; }
+
+ ///
+ /// Check whether a is due to a SQL unique constraint violation.
+ ///
+ /// The exception to inspect.
+ /// Whether the exception was caused to SQL unique constraint violation.
+ bool IsUniqueConstraintViolationException(DbUpdateException exception);
+
+ Task SaveChangesAsync();
+ }
+}
diff --git a/src/BaGet.Core/Entities/Package.cs b/src/BaGet.Core/Entities/Package.cs
index 54275aad..d23351bf 100644
--- a/src/BaGet.Core/Entities/Package.cs
+++ b/src/BaGet.Core/Entities/Package.cs
@@ -1,59 +1,59 @@
-using System;
-using System.Collections.Generic;
-using NuGet.Versioning;
-
-namespace BaGet.Core.Entities
-{
- // See NuGetGallery's: https://github.com/NuGet/NuGetGallery/blob/master/src/NuGetGallery.Core/Entities/Package.cs
- public class Package
- {
- public int Key { get; set; }
-
- public string Id { get; set; }
- public NuGetVersion Version { get; set; }
-
- public string[] Authors { get; set; }
- public string Description { get; set; }
- public long Downloads { get; set; }
- public bool HasReadme { get; set; }
- public string Language { get; set; }
- public bool Listed { get; set; }
- public string MinClientVersion { get; set; }
- public DateTime Published { get; set; }
- public bool RequireLicenseAcceptance { get; set; }
- public string Summary { get; set; }
- public string Title { get; set; }
-
- public Uri IconUrl { get; set; }
- public Uri LicenseUrl { get; set; }
- public Uri ProjectUrl { get; set; }
-
- public Uri RepositoryUrl { get; set; }
- public string RepositoryType { get; set; }
-
- public string[] Tags { get; set; }
-
- ///
- /// Used for optimistic concurrency.
- ///
- public byte[] RowVersion { get; set; }
-
- public List Dependencies { get; set; }
-
- public string VersionString
- {
- get => Version?.ToNormalizedString().ToLowerInvariant() ?? string.Empty;
- set
- {
- NuGetVersion.TryParse(value, out var version);
-
- Version = version;
- }
- }
-
- public string IconUrlString => IconUrl?.AbsoluteUri ?? string.Empty;
- public string LicenseUrlString => LicenseUrl?.AbsoluteUri ?? string.Empty;
- public string ProjectUrlString => ProjectUrl?.AbsoluteUri ?? string.Empty;
- public string RepositoryUrlString => RepositoryUrl?.AbsoluteUri ?? string.Empty;
- }
-}
+using System;
+using System.Collections.Generic;
+using NuGet.Versioning;
+
+namespace BaGet.Core.Entities
+{
+ // See NuGetGallery's: https://github.com/NuGet/NuGetGallery/blob/master/src/NuGetGallery.Core/Entities/Package.cs
+ public class Package
+ {
+ public int Key { get; set; }
+
+ public string Id { get; set; }
+ public NuGetVersion Version { get; set; }
+
+ public string[] Authors { get; set; }
+ public string Description { get; set; }
+ public long Downloads { get; set; }
+ public bool HasReadme { get; set; }
+ public string Language { get; set; }
+ public bool Listed { get; set; }
+ public string MinClientVersion { get; set; }
+ public DateTime Published { get; set; }
+ public bool RequireLicenseAcceptance { get; set; }
+ public string Summary { get; set; }
+ public string Title { get; set; }
+
+ public Uri IconUrl { get; set; }
+ public Uri LicenseUrl { get; set; }
+ public Uri ProjectUrl { get; set; }
+
+ public Uri RepositoryUrl { get; set; }
+ public string RepositoryType { get; set; }
+
+ public string[] Tags { get; set; }
+
+ ///
+ /// Used for optimistic concurrency.
+ ///
+ public byte[] RowVersion { get; set; }
+
+ public List Dependencies { get; set; }
+
+ public string VersionString
+ {
+ get => Version?.ToNormalizedString().ToLowerInvariant() ?? string.Empty;
+ set
+ {
+ NuGetVersion.TryParse(value, out var version);
+
+ Version = version;
+ }
+ }
+
+ public string IconUrlString => IconUrl?.AbsoluteUri ?? string.Empty;
+ public string LicenseUrlString => LicenseUrl?.AbsoluteUri ?? string.Empty;
+ public string ProjectUrlString => ProjectUrl?.AbsoluteUri ?? string.Empty;
+ public string RepositoryUrlString => RepositoryUrl?.AbsoluteUri ?? string.Empty;
+ }
+}
diff --git a/src/BaGet.Core/Entities/PackageDependency.cs b/src/BaGet.Core/Entities/PackageDependency.cs
index 56fffe81..04a5a20b 100644
--- a/src/BaGet.Core/Entities/PackageDependency.cs
+++ b/src/BaGet.Core/Entities/PackageDependency.cs
@@ -1,14 +1,14 @@
-namespace BaGet.Core.Entities
-{
- // See NuGetGallery.Core's: https://github.com/NuGet/NuGetGallery/blob/master/src/NuGetGallery.Core/Entities/PackageDependency.cs
- public class PackageDependency
- {
- public int Key { get; set; }
-
- public string Id { get; set; }
- public string VersionRange { get; set; }
- public string TargetFramework { get; set; }
-
- public Package Package { get; set; }
- }
-}
+namespace BaGet.Core.Entities
+{
+ // See NuGetGallery.Core's: https://github.com/NuGet/NuGetGallery/blob/master/src/NuGetGallery.Core/Entities/PackageDependency.cs
+ public class PackageDependency
+ {
+ public int Key { get; set; }
+
+ public string Id { get; set; }
+ public string VersionRange { get; set; }
+ public string TargetFramework { get; set; }
+
+ public Package Package { get; set; }
+ }
+}
diff --git a/src/BaGet.Core/Extensions/PackageArchiveReaderExtensions.cs b/src/BaGet.Core/Extensions/PackageArchiveReaderExtensions.cs
index 5f4cbf60..98349150 100644
--- a/src/BaGet.Core/Extensions/PackageArchiveReaderExtensions.cs
+++ b/src/BaGet.Core/Extensions/PackageArchiveReaderExtensions.cs
@@ -1,45 +1,45 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using NuGet.Packaging;
-
-namespace BaGet.Core.Extensions
-{
- public static class PackageArchiveReaderExtensions
- {
- private static readonly string[] OrderedReadmeFileNames = new[]
- {
- "readme.md",
- "readme.txt",
- };
-
- private static readonly HashSet ReadmeFileNames = new HashSet(
- OrderedReadmeFileNames,
- StringComparer.OrdinalIgnoreCase);
-
- public static bool HasReadme(this PackageArchiveReader package)
- => package.GetFiles().Any(ReadmeFileNames.Contains);
-
- public async static Task GetReadmeAsync(
- this PackageArchiveReader package,
- CancellationToken cancellationToken)
- {
- var packageFiles = package.GetFiles();
-
- foreach (var readmeFileName in OrderedReadmeFileNames)
- {
- var readmePath = packageFiles.FirstOrDefault(f => f.Equals(readmeFileName, StringComparison.OrdinalIgnoreCase));
-
- if (readmePath != null)
- {
- return await package.GetStreamAsync(readmePath, cancellationToken);
- }
- }
-
- throw new InvalidOperationException("Package does not have a readme!");
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using NuGet.Packaging;
+
+namespace BaGet.Core.Extensions
+{
+ public static class PackageArchiveReaderExtensions
+ {
+ private static readonly string[] OrderedReadmeFileNames = new[]
+ {
+ "readme.md",
+ "readme.txt",
+ };
+
+ private static readonly HashSet ReadmeFileNames = new HashSet(
+ OrderedReadmeFileNames,
+ StringComparer.OrdinalIgnoreCase);
+
+ public static bool HasReadme(this PackageArchiveReader package)
+ => package.GetFiles().Any(ReadmeFileNames.Contains);
+
+ public async static Task GetReadmeAsync(
+ this PackageArchiveReader package,
+ CancellationToken cancellationToken)
+ {
+ var packageFiles = package.GetFiles();
+
+ foreach (var readmeFileName in OrderedReadmeFileNames)
+ {
+ var readmePath = packageFiles.FirstOrDefault(f => f.Equals(readmeFileName, StringComparison.OrdinalIgnoreCase));
+
+ if (readmePath != null)
+ {
+ return await package.GetStreamAsync(readmePath, cancellationToken);
+ }
+ }
+
+ throw new InvalidOperationException("Package does not have a readme!");
+ }
+ }
+}
diff --git a/src/BaGet.Core/Extensions/StreamExtensions.cs b/src/BaGet.Core/Extensions/StreamExtensions.cs
index c4a30f50..82cf6e17 100644
--- a/src/BaGet.Core/Extensions/StreamExtensions.cs
+++ b/src/BaGet.Core/Extensions/StreamExtensions.cs
@@ -1,59 +1,59 @@
-using System;
-using System.IO;
-using System.Linq;
-using System.Security.Cryptography;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace BaGet.Core.Extensions
-{
- public static class StreamExtensions
- {
- // See: https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/IO/Stream.cs#L35
- private const int DefaultCopyBufferSize = 81920;
-
- ///
- /// Copies a stream to a file, and returns that file as a stream. The underlying file will be
- /// deleted when the resulting stream is disposed.
- ///
- /// The stream to be copied, at its current position.
- ///
- /// The copied stream, with its position reset to the beginning.
- public static async Task AsTemporaryFileStreamAsync(
- this Stream original,
- CancellationToken cancellationToken = default)
- {
- var result = new FileStream(
- Path.GetTempFileName(),
- FileMode.Create,
- FileAccess.ReadWrite,
- FileShare.None,
- DefaultCopyBufferSize,
- FileOptions.DeleteOnClose);
-
- try
- {
- await original.CopyToAsync(result, DefaultCopyBufferSize, cancellationToken);
- result.Position = 0;
- }
- catch (Exception)
- {
- result.Dispose();
- throw;
- }
-
- return result;
- }
-
- public static bool Matches(this Stream content, Stream target)
- {
- using (var sha256 = SHA256.Create())
- {
- var contentHash = sha256.ComputeHash(content);
- var targetHash = sha256.ComputeHash(target);
-
- return contentHash.SequenceEqual(targetHash);
- }
- }
- }
-}
+using System;
+using System.IO;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace BaGet.Core.Extensions
+{
+ public static class StreamExtensions
+ {
+ // See: https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/IO/Stream.cs#L35
+ private const int DefaultCopyBufferSize = 81920;
+
+ ///
+ /// Copies a stream to a file, and returns that file as a stream. The underlying file will be
+ /// deleted when the resulting stream is disposed.
+ ///
+ /// The stream to be copied, at its current position.
+ ///
+ /// The copied stream, with its position reset to the beginning.
+ public static async Task AsTemporaryFileStreamAsync(
+ this Stream original,
+ CancellationToken cancellationToken = default)
+ {
+ var result = new FileStream(
+ Path.GetTempFileName(),
+ FileMode.Create,
+ FileAccess.ReadWrite,
+ FileShare.None,
+ DefaultCopyBufferSize,
+ FileOptions.DeleteOnClose);
+
+ try
+ {
+ await original.CopyToAsync(result, DefaultCopyBufferSize, cancellationToken);
+ result.Position = 0;
+ }
+ catch (Exception)
+ {
+ result.Dispose();
+ throw;
+ }
+
+ return result;
+ }
+
+ public static bool Matches(this Stream content, Stream target)
+ {
+ using (var sha256 = SHA256.Create())
+ {
+ var contentHash = sha256.ComputeHash(content);
+ var targetHash = sha256.ComputeHash(target);
+
+ return contentHash.SequenceEqual(targetHash);
+ }
+ }
+ }
+}
diff --git a/src/BaGet.Core/Mirror/DownloadsImporter.cs b/src/BaGet.Core/Mirror/DownloadsImporter.cs
index 7e76250e..1a0f9205 100644
--- a/src/BaGet.Core/Mirror/DownloadsImporter.cs
+++ b/src/BaGet.Core/Mirror/DownloadsImporter.cs
@@ -1,66 +1,66 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using BaGet.Core.Entities;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging;
-
-namespace BaGet.Core.Mirror
-{
- public class DownloadsImporter
- {
- private const int BatchSize = 200;
-
- private readonly IContext _context;
- private readonly IPackageDownloadsSource _downloadsSource;
- private readonly ILogger _logger;
-
- public DownloadsImporter(
- IContext context,
- IPackageDownloadsSource downloadsSource,
- ILogger logger)
- {
- _context = context ?? throw new ArgumentNullException(nameof(context));
- _downloadsSource = downloadsSource ?? throw new ArgumentNullException(nameof(downloadsSource));
- _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- }
-
- public async Task ImportAsync()
- {
- var packageDownloads = await _downloadsSource.GetPackageDownloadsAsync();
- var packages = await _context.Packages.CountAsync();
- var batches = (packages / BatchSize) + 1;
-
- for (var batch = 0; batch < batches; batch++)
- {
- _logger.LogInformation("Importing batch {Batch}...", batch);
-
- foreach (var package in await GetBatch(batch))
- {
- var packageId = package.Id.ToLowerInvariant();
- var packageVersion = package.VersionString.ToLowerInvariant();
-
- if (!packageDownloads.ContainsKey(packageId) ||
- !packageDownloads[packageId].ContainsKey(packageVersion))
- {
- continue;
- }
-
- package.Downloads = packageDownloads[packageId][packageVersion];
- }
-
- await _context.SaveChangesAsync();
-
- _logger.LogInformation("Imported batch {Batch}", batch);
- }
- }
-
- private Task> GetBatch(int batch)
- => _context.Packages
- .OrderBy(p => p.Key)
- .Skip(batch * BatchSize)
- .Take(BatchSize)
- .ToListAsync();
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using BaGet.Core.Entities;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+
+namespace BaGet.Core.Mirror
+{
+ public class DownloadsImporter
+ {
+ private const int BatchSize = 200;
+
+ private readonly IContext _context;
+ private readonly IPackageDownloadsSource _downloadsSource;
+ private readonly ILogger _logger;
+
+ public DownloadsImporter(
+ IContext context,
+ IPackageDownloadsSource downloadsSource,
+ ILogger logger)
+ {
+ _context = context ?? throw new ArgumentNullException(nameof(context));
+ _downloadsSource = downloadsSource ?? throw new ArgumentNullException(nameof(downloadsSource));
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ }
+
+ public async Task ImportAsync()
+ {
+ var packageDownloads = await _downloadsSource.GetPackageDownloadsAsync();
+ var packages = await _context.Packages.CountAsync();
+ var batches = (packages / BatchSize) + 1;
+
+ for (var batch = 0; batch < batches; batch++)
+ {
+ _logger.LogInformation("Importing batch {Batch}...", batch);
+
+ foreach (var package in await GetBatch(batch))
+ {
+ var packageId = package.Id.ToLowerInvariant();
+ var packageVersion = package.VersionString.ToLowerInvariant();
+
+ if (!packageDownloads.ContainsKey(packageId) ||
+ !packageDownloads[packageId].ContainsKey(packageVersion))
+ {
+ continue;
+ }
+
+ package.Downloads = packageDownloads[packageId][packageVersion];
+ }
+
+ await _context.SaveChangesAsync();
+
+ _logger.LogInformation("Imported batch {Batch}", batch);
+ }
+ }
+
+ private Task> GetBatch(int batch)
+ => _context.Packages
+ .OrderBy(p => p.Key)
+ .Skip(batch * BatchSize)
+ .Take(BatchSize)
+ .ToListAsync();
+ }
+}
diff --git a/src/BaGet.Core/Mirror/FakeMirrorService.cs b/src/BaGet.Core/Mirror/FakeMirrorService.cs
index 7770529b..3ef157f4 100644
--- a/src/BaGet.Core/Mirror/FakeMirrorService.cs
+++ b/src/BaGet.Core/Mirror/FakeMirrorService.cs
@@ -1,29 +1,29 @@
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using BaGet.Core.Entities;
-using NuGet.Versioning;
-
-namespace BaGet.Core.Mirror
-{
- ///
- /// The mirror service used when mirroring has been disabled.
- ///
- public class FakeMirrorService : IMirrorService
- {
- public Task> FindPackageVersionsOrNullAsync(string id, CancellationToken cancellationToken)
- {
- return Task.FromResult>(null);
- }
-
- public Task> FindPackagesOrNullAsync(string id, CancellationToken cancellationToken)
- {
- return Task.FromResult>(null);
- }
-
- public Task MirrorAsync(string id, NuGetVersion version, CancellationToken cancellationToken)
- {
- return Task.CompletedTask;
- }
- }
-}
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using BaGet.Core.Entities;
+using NuGet.Versioning;
+
+namespace BaGet.Core.Mirror
+{
+ ///
+ /// The mirror service used when mirroring has been disabled.
+ ///
+ public class FakeMirrorService : IMirrorService
+ {
+ public Task> FindPackageVersionsOrNullAsync(string id, CancellationToken cancellationToken)
+ {
+ return Task.FromResult>(null);
+ }
+
+ public Task> FindPackagesOrNullAsync(string id, CancellationToken cancellationToken)
+ {
+ return Task.FromResult>(null);
+ }
+
+ public Task MirrorAsync(string id, NuGetVersion version, CancellationToken cancellationToken)
+ {
+ return Task.CompletedTask;
+ }
+ }
+}
diff --git a/src/BaGet.Core/Mirror/IMirrorService.cs b/src/BaGet.Core/Mirror/IMirrorService.cs
index 3ba67abd..48b4ff7b 100644
--- a/src/BaGet.Core/Mirror/IMirrorService.cs
+++ b/src/BaGet.Core/Mirror/IMirrorService.cs
@@ -1,47 +1,47 @@
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using BaGet.Core.Entities;
-using NuGet.Versioning;
-
-namespace BaGet.Core.Mirror
-{
- ///
- /// Indexes packages from an external source.
- ///
- public interface IMirrorService
- {
- ///
- /// Attempt to find a package's versions using mirroring. This will merge
- /// results from the configured upstream source with the locally indexed packages.
- ///
- /// The package's id to lookup
- /// The token to cancel the lookup
- ///
- /// The package's versions, or null if the package cannot be found on the
- /// configured upstream source. This includes unlisted versions.
- ///
- Task> FindPackageVersionsOrNullAsync(string id, CancellationToken cancellationToken);
-
- ///
- /// Attempt to find a package's metadata using mirroring. This will merge
- /// results from the configured upstream source with the locally indexed packages.
- ///
- /// The package's id to lookup
- /// The token to cancel the lookup
- ///
- /// The package's metadata, or null if the package cannot be found on the configured
- /// upstream source.
- ///
- Task> FindPackagesOrNullAsync(string id, CancellationToken cancellationToken);
-
- ///
- /// If the package is unknown, attempt to index it from an upstream source.
- ///
- /// The package's id
- /// The package's version
- /// The token to cancel the mirroring
- /// A task that completes when the package has been mirrored.
- Task MirrorAsync(string id, NuGetVersion version, CancellationToken cancellationToken);
- }
-}
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using BaGet.Core.Entities;
+using NuGet.Versioning;
+
+namespace BaGet.Core.Mirror
+{
+ ///
+ /// Indexes packages from an external source.
+ ///
+ public interface IMirrorService
+ {
+ ///
+ /// Attempt to find a package's versions using mirroring. This will merge
+ /// results from the configured upstream source with the locally indexed packages.
+ ///
+ /// The package's id to lookup
+ /// The token to cancel the lookup
+ ///
+ /// The package's versions, or null if the package cannot be found on the
+ /// configured upstream source. This includes unlisted versions.
+ ///
+ Task> FindPackageVersionsOrNullAsync(string id, CancellationToken cancellationToken);
+
+ ///
+ /// Attempt to find a package's metadata using mirroring. This will merge
+ /// results from the configured upstream source with the locally indexed packages.
+ ///
+ /// The package's id to lookup
+ /// The token to cancel the lookup
+ ///
+ /// The package's metadata, or null if the package cannot be found on the configured
+ /// upstream source.
+ ///
+ Task> FindPackagesOrNullAsync(string id, CancellationToken cancellationToken);
+
+ ///
+ /// If the package is unknown, attempt to index it from an upstream source.
+ ///
+ /// The package's id
+ /// The package's version
+ /// The token to cancel the mirroring
+ /// A task that completes when the package has been mirrored.
+ Task MirrorAsync(string id, NuGetVersion version, CancellationToken cancellationToken);
+ }
+}
diff --git a/src/BaGet.Core/Mirror/IPackageDownloader.cs b/src/BaGet.Core/Mirror/IPackageDownloader.cs
index 292abc10..49ed659f 100644
--- a/src/BaGet.Core/Mirror/IPackageDownloader.cs
+++ b/src/BaGet.Core/Mirror/IPackageDownloader.cs
@@ -1,18 +1,18 @@
-using System;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace BaGet.Core.Mirror
-{
- public interface IPackageDownloader
- {
- ///
- /// Attempt to download a package.
- ///
- /// The package to download.
- /// The token to cancel the download.
- /// The package, or null if it couldn't be downloaded.
- Task DownloadOrNullAsync(Uri packageUri, CancellationToken cancellationToken);
- }
-}
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace BaGet.Core.Mirror
+{
+ public interface IPackageDownloader
+ {
+ ///
+ /// Attempt to download a package.
+ ///
+ /// The package to download.
+ /// The token to cancel the download.
+ /// The package, or null if it couldn't be downloaded.
+ Task DownloadOrNullAsync(Uri packageUri, CancellationToken cancellationToken);
+ }
+}
diff --git a/src/BaGet.Core/Mirror/IPackageDownloadsSource.cs b/src/BaGet.Core/Mirror/IPackageDownloadsSource.cs
index fe37ac9e..fc051b92 100644
--- a/src/BaGet.Core/Mirror/IPackageDownloadsSource.cs
+++ b/src/BaGet.Core/Mirror/IPackageDownloadsSource.cs
@@ -1,10 +1,10 @@
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
-namespace BaGet.Core.Mirror
-{
- public interface IPackageDownloadsSource
- {
- Task>> GetPackageDownloadsAsync();
- }
-}
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace BaGet.Core.Mirror
+{
+ public interface IPackageDownloadsSource
+ {
+ Task>> GetPackageDownloadsAsync();
+ }
+}
diff --git a/src/BaGet.Core/Mirror/MirrorService.cs b/src/BaGet.Core/Mirror/MirrorService.cs
index 7beacf72..51ca342c 100644
--- a/src/BaGet.Core/Mirror/MirrorService.cs
+++ b/src/BaGet.Core/Mirror/MirrorService.cs
@@ -1,240 +1,240 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using BaGet.Core.Entities;
-using BaGet.Core.Services;
-using BaGet.Protocol;
-using Microsoft.Extensions.Logging;
-using NuGet.Versioning;
-
-namespace BaGet.Core.Mirror
-{
- using PackageIdentity = NuGet.Packaging.Core.PackageIdentity;
-
- public class MirrorService : IMirrorService
- {
- private readonly IPackageService _localPackages;
- private readonly IPackageMetadataService _upstreamFeed;
- private readonly IPackageDownloader _downloader;
- private readonly IPackageIndexingService _indexer;
- private readonly ILogger _logger;
-
- public MirrorService(
- IPackageService localPackages,
- IPackageMetadataService upstreamFeed,
- IPackageDownloader downloader,
- IPackageIndexingService indexer,
- ILogger logger)
- {
- _localPackages = localPackages ?? throw new ArgumentNullException(nameof(localPackages));
- _upstreamFeed = upstreamFeed ?? throw new ArgumentNullException(nameof(upstreamFeed));
- _downloader = downloader ?? throw new ArgumentNullException(nameof(downloader));
- _indexer = indexer ?? throw new ArgumentNullException(nameof(indexer));
- _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- }
-
- public async Task> FindPackageVersionsOrNullAsync(
- string id,
- CancellationToken cancellationToken)
- {
- var includeUnlisted = true;
- var versions = await _upstreamFeed.GetAllVersionsOrNullAsync(id, includeUnlisted, cancellationToken);
- if (versions == null)
- {
- return null;
- }
-
- // Merge the local package versions into the upstream package versions.
- var localPackages = await _localPackages.FindAsync(id, includeUnlisted);
- var localVersions = localPackages.Select(p => p.Version);
-
- return versions.Concat(localVersions).Distinct().ToList();
- }
-
- public async Task> FindPackagesOrNullAsync(string id, CancellationToken cancellationToken)
- {
- var upstreamPackageMetadata = await _upstreamFeed.GetAllMetadataOrNullAsync(id, cancellationToken);
- if (upstreamPackageMetadata == null)
- {
- return null;
- }
-
- var upstreamPackages = upstreamPackageMetadata.Select(ToPackage);
-
- // Return the upstream packages if there are no local packages matching the package id.
- var localPackages = await _localPackages.FindAsync(id, includeUnlisted: true);
- if (!localPackages.Any())
- {
- return upstreamPackages.ToList();
- }
-
- // Otherwise, merge the local packages into the upstream packages.
- var result = upstreamPackages.ToDictionary(p => new PackageIdentity(p.Id, p.Version));
- var local = localPackages.ToDictionary(p => new PackageIdentity(p.Id, p.Version));
-
- foreach (var localPackage in local)
- {
- result[localPackage.Key] = localPackage.Value;
- }
-
- return result.Values.ToList();
- }
-
- public async Task MirrorAsync(string id, NuGetVersion version, CancellationToken cancellationToken)
- {
- if (await _localPackages.ExistsAsync(id, version))
- {
- return;
- }
-
- _logger.LogInformation(
- "Package {PackageId} {PackageVersion} does not exist locally. Indexing from upstream feed...",
- id,
- version);
-
- await IndexFromSourceAsync(id, version, cancellationToken);
-
- _logger.LogInformation(
- "Finished indexing {PackageId} {PackageVersion} from the upstream feed",
- id,
- version);
- }
-
- private Package ToPackage(PackageMetadata metadata)
- {
- return new Package
- {
- Id = metadata.PackageId,
- Version = metadata.Version,
- Authors = ParseAuthors(metadata.Authors),
- Description = metadata.Description,
- Downloads = metadata.Downloads,
- HasReadme = metadata.HasReadme,
- Language = metadata.Language,
- Listed = metadata.Listed,
- MinClientVersion = metadata.MinClientVersion,
- Published = metadata.Published,
- RequireLicenseAcceptance = metadata.RequireLicenseAcceptance,
- Summary = metadata.Summary,
- Title = metadata.Title,
- IconUrl = ParseUri(metadata.IconUrl),
- LicenseUrl = ParseUri(metadata.LicenseUrl),
- ProjectUrl = ParseUri(metadata.ProjectUrl),
- RepositoryUrl = ParseUri(metadata.RepositoryUrl),
- RepositoryType = metadata.RepositoryType,
- Tags = metadata.Tags.ToArray(),
-
- Dependencies = FindDependencies(metadata)
- };
- }
-
- private Uri ParseUri(string uriString)
- {
- if (uriString == null) return null;
-
- if (!Uri.TryCreate(uriString, UriKind.Absolute, out var uri))
- {
- return null;
- }
-
- return uri;
- }
-
- private string[] ParseAuthors(string authors)
- {
- if (string.IsNullOrEmpty(authors)) return new string[0];
-
- return authors
- .Split(new[] { ',', ';', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
- .Select(a => a.Trim())
- .ToArray();
- }
-
- private List FindDependencies(PackageMetadata package)
- {
- if ((package.DependencyGroups?.Count ?? 0) == 0)
- {
- return new List();
- }
-
- return package.DependencyGroups
- .SelectMany(FindDependenciesFromDependencyGroup)
- .ToList();
- }
-
- private IEnumerable FindDependenciesFromDependencyGroup(DependencyGroupItem group)
- {
- // BaGet stores a dependency group with no dependencies as a package dependency
- // with no package id nor package version.
- if ((group.Dependencies?.Count ?? 0) == 0)
- {
- return new[]
- {
- new PackageDependency
- {
- Id = null,
- VersionRange = null,
- TargetFramework = group.TargetFramework
- }
- };
- }
-
- return group.Dependencies.Select(d => new PackageDependency
- {
- Id = d.Id,
- VersionRange = d.Range,
- TargetFramework = group.TargetFramework
- });
- }
-
- private async Task IndexFromSourceAsync(string id, NuGetVersion version, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- _logger.LogInformation(
- "Attempting to mirror package {PackageId} {PackageVersion}...",
- id,
- version);
-
- try
- {
- var packageUri = await _upstreamFeed.GetPackageContentUriAsync(id, version);
-
- using (var stream = await _downloader.DownloadOrNullAsync(packageUri, cancellationToken))
- {
- if (stream == null)
- {
- _logger.LogWarning(
- "Failed to download package {PackageId} {PackageVersion}",
- id,
- version);
-
- return;
- }
-
- _logger.LogInformation(
- "Downloaded package {PackageId} {PackageVersion}, indexing...",
- id,
- version);
-
- var result = await _indexer.IndexAsync(stream, cancellationToken);
-
- _logger.LogInformation(
- "Finished indexing package {PackageId} {PackageVersion} with result {Result}",
- packageUri,
- result);
- }
- }
- catch (Exception e)
- {
- _logger.LogError(
- e,
- "Failed to mirror package {PackageId} {PackageVersion}",
- id,
- version);
- }
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using BaGet.Core.Entities;
+using BaGet.Core.Services;
+using BaGet.Protocol;
+using Microsoft.Extensions.Logging;
+using NuGet.Versioning;
+
+namespace BaGet.Core.Mirror
+{
+ using PackageIdentity = NuGet.Packaging.Core.PackageIdentity;
+
+ public class MirrorService : IMirrorService
+ {
+ private readonly IPackageService _localPackages;
+ private readonly IPackageMetadataService _upstreamFeed;
+ private readonly IPackageDownloader _downloader;
+ private readonly IPackageIndexingService _indexer;
+ private readonly ILogger _logger;
+
+ public MirrorService(
+ IPackageService localPackages,
+ IPackageMetadataService upstreamFeed,
+ IPackageDownloader downloader,
+ IPackageIndexingService indexer,
+ ILogger logger)
+ {
+ _localPackages = localPackages ?? throw new ArgumentNullException(nameof(localPackages));
+ _upstreamFeed = upstreamFeed ?? throw new ArgumentNullException(nameof(upstreamFeed));
+ _downloader = downloader ?? throw new ArgumentNullException(nameof(downloader));
+ _indexer = indexer ?? throw new ArgumentNullException(nameof(indexer));
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ }
+
+ public async Task> FindPackageVersionsOrNullAsync(
+ string id,
+ CancellationToken cancellationToken)
+ {
+ var includeUnlisted = true;
+ var versions = await _upstreamFeed.GetAllVersionsOrNullAsync(id, includeUnlisted, cancellationToken);
+ if (versions == null)
+ {
+ return null;
+ }
+
+ // Merge the local package versions into the upstream package versions.
+ var localPackages = await _localPackages.FindAsync(id, includeUnlisted);
+ var localVersions = localPackages.Select(p => p.Version);
+
+ return versions.Concat(localVersions).Distinct().ToList();
+ }
+
+ public async Task> FindPackagesOrNullAsync(string id, CancellationToken cancellationToken)
+ {
+ var upstreamPackageMetadata = await _upstreamFeed.GetAllMetadataOrNullAsync(id, cancellationToken);
+ if (upstreamPackageMetadata == null)
+ {
+ return null;
+ }
+
+ var upstreamPackages = upstreamPackageMetadata.Select(ToPackage);
+
+ // Return the upstream packages if there are no local packages matching the package id.
+ var localPackages = await _localPackages.FindAsync(id, includeUnlisted: true);
+ if (!localPackages.Any())
+ {
+ return upstreamPackages.ToList();
+ }
+
+ // Otherwise, merge the local packages into the upstream packages.
+ var result = upstreamPackages.ToDictionary(p => new PackageIdentity(p.Id, p.Version));
+ var local = localPackages.ToDictionary(p => new PackageIdentity(p.Id, p.Version));
+
+ foreach (var localPackage in local)
+ {
+ result[localPackage.Key] = localPackage.Value;
+ }
+
+ return result.Values.ToList();
+ }
+
+ public async Task MirrorAsync(string id, NuGetVersion version, CancellationToken cancellationToken)
+ {
+ if (await _localPackages.ExistsAsync(id, version))
+ {
+ return;
+ }
+
+ _logger.LogInformation(
+ "Package {PackageId} {PackageVersion} does not exist locally. Indexing from upstream feed...",
+ id,
+ version);
+
+ await IndexFromSourceAsync(id, version, cancellationToken);
+
+ _logger.LogInformation(
+ "Finished indexing {PackageId} {PackageVersion} from the upstream feed",
+ id,
+ version);
+ }
+
+ private Package ToPackage(PackageMetadata metadata)
+ {
+ return new Package
+ {
+ Id = metadata.PackageId,
+ Version = metadata.Version,
+ Authors = ParseAuthors(metadata.Authors),
+ Description = metadata.Description,
+ Downloads = metadata.Downloads,
+ HasReadme = metadata.HasReadme,
+ Language = metadata.Language,
+ Listed = metadata.Listed,
+ MinClientVersion = metadata.MinClientVersion,
+ Published = metadata.Published,
+ RequireLicenseAcceptance = metadata.RequireLicenseAcceptance,
+ Summary = metadata.Summary,
+ Title = metadata.Title,
+ IconUrl = ParseUri(metadata.IconUrl),
+ LicenseUrl = ParseUri(metadata.LicenseUrl),
+ ProjectUrl = ParseUri(metadata.ProjectUrl),
+ RepositoryUrl = ParseUri(metadata.RepositoryUrl),
+ RepositoryType = metadata.RepositoryType,
+ Tags = metadata.Tags.ToArray(),
+
+ Dependencies = FindDependencies(metadata)
+ };
+ }
+
+ private Uri ParseUri(string uriString)
+ {
+ if (uriString == null) return null;
+
+ if (!Uri.TryCreate(uriString, UriKind.Absolute, out var uri))
+ {
+ return null;
+ }
+
+ return uri;
+ }
+
+ private string[] ParseAuthors(string authors)
+ {
+ if (string.IsNullOrEmpty(authors)) return new string[0];
+
+ return authors
+ .Split(new[] { ',', ';', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
+ .Select(a => a.Trim())
+ .ToArray();
+ }
+
+ private List FindDependencies(PackageMetadata package)
+ {
+ if ((package.DependencyGroups?.Count ?? 0) == 0)
+ {
+ return new List();
+ }
+
+ return package.DependencyGroups
+ .SelectMany(FindDependenciesFromDependencyGroup)
+ .ToList();
+ }
+
+ private IEnumerable FindDependenciesFromDependencyGroup(DependencyGroupItem group)
+ {
+ // BaGet stores a dependency group with no dependencies as a package dependency
+ // with no package id nor package version.
+ if ((group.Dependencies?.Count ?? 0) == 0)
+ {
+ return new[]
+ {
+ new PackageDependency
+ {
+ Id = null,
+ VersionRange = null,
+ TargetFramework = group.TargetFramework
+ }
+ };
+ }
+
+ return group.Dependencies.Select(d => new PackageDependency
+ {
+ Id = d.Id,
+ VersionRange = d.Range,
+ TargetFramework = group.TargetFramework
+ });
+ }
+
+ private async Task IndexFromSourceAsync(string id, NuGetVersion version, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ _logger.LogInformation(
+ "Attempting to mirror package {PackageId} {PackageVersion}...",
+ id,
+ version);
+
+ try
+ {
+ var packageUri = await _upstreamFeed.GetPackageContentUriAsync(id, version);
+
+ using (var stream = await _downloader.DownloadOrNullAsync(packageUri, cancellationToken))
+ {
+ if (stream == null)
+ {
+ _logger.LogWarning(
+ "Failed to download package {PackageId} {PackageVersion}",
+ id,
+ version);
+
+ return;
+ }
+
+ _logger.LogInformation(
+ "Downloaded package {PackageId} {PackageVersion}, indexing...",
+ id,
+ version);
+
+ var result = await _indexer.IndexAsync(stream, cancellationToken);
+
+ _logger.LogInformation(
+ "Finished indexing package {PackageId} {PackageVersion} with result {Result}",
+ packageUri,
+ result);
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.LogError(
+ e,
+ "Failed to mirror package {PackageId} {PackageVersion}",
+ id,
+ version);
+ }
+ }
+ }
+}
diff --git a/src/BaGet.Core/Mirror/PackageDownloader.cs b/src/BaGet.Core/Mirror/PackageDownloader.cs
index 743338cc..7507ed6b 100644
--- a/src/BaGet.Core/Mirror/PackageDownloader.cs
+++ b/src/BaGet.Core/Mirror/PackageDownloader.cs
@@ -1,82 +1,82 @@
-using System;
-using System.Diagnostics;
-using System.IO;
-using System.Net;
-using System.Net.Http;
-using System.Threading;
-using System.Threading.Tasks;
-using BaGet.Core.Extensions;
-using Microsoft.Extensions.Logging;
-
-namespace BaGet.Core.Mirror
-{
- // See: https://github.com/NuGet/NuGet.Jobs/blob/master/src/Validation.Common.Job/PackageDownloader.cs
- public class PackageDownloader : IPackageDownloader
- {
- private readonly HttpClient _httpClient;
- private readonly ILogger _logger;
-
- public PackageDownloader(HttpClient httpClient, ILogger logger)
- {
- _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
- _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- }
-
- public async Task DownloadOrNullAsync(Uri packageUri, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- _logger.LogInformation("Attempting to download package from {PackageUri}...", packageUri);
-
- Stream packageStream = null;
- var stopwatch = Stopwatch.StartNew();
-
- try
- {
- // Download the package from the network to a temporary file.
- using (var response = await _httpClient.GetAsync(packageUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken))
- {
- _logger.LogInformation(
- "Received response {StatusCode}: {ReasonPhrase} of type {ContentType} for request {PackageUri}",
- response.StatusCode,
- response.ReasonPhrase,
- response.Content.Headers.ContentType,
- packageUri);
-
- if (response.StatusCode != HttpStatusCode.OK)
- {
- _logger.LogWarning(
- $"Expected status code {HttpStatusCode.OK} for package download, actual: {{ResponseStatusCode}}",
- response.StatusCode);
-
- return null;
- }
-
- using (var networkStream = await response.Content.ReadAsStreamAsync())
- {
- packageStream = await networkStream.AsTemporaryFileStreamAsync(cancellationToken);
- }
- }
-
- _logger.LogInformation(
- "Downloaded {PackageSizeInBytes} bytes in {DownloadElapsedTime} seconds for request {PackageUri}",
- packageStream.Length,
- stopwatch.Elapsed.TotalSeconds,
- packageUri);
-
- return packageStream;
- }
- catch (Exception e)
- {
- _logger.LogError(
- e,
- "Exception thrown when trying to download package from {PackageUri}",
- packageUri);
-
- packageStream?.Dispose();
-
- return null;
- }
- }
- }
-}
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Net;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using BaGet.Core.Extensions;
+using Microsoft.Extensions.Logging;
+
+namespace BaGet.Core.Mirror
+{
+ // See: https://github.com/NuGet/NuGet.Jobs/blob/master/src/Validation.Common.Job/PackageDownloader.cs
+ public class PackageDownloader : IPackageDownloader
+ {
+ private readonly HttpClient _httpClient;
+ private readonly ILogger _logger;
+
+ public PackageDownloader(HttpClient httpClient, ILogger logger)
+ {
+ _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ }
+
+ public async Task DownloadOrNullAsync(Uri packageUri, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ _logger.LogInformation("Attempting to download package from {PackageUri}...", packageUri);
+
+ Stream packageStream = null;
+ var stopwatch = Stopwatch.StartNew();
+
+ try
+ {
+ // Download the package from the network to a temporary file.
+ using (var response = await _httpClient.GetAsync(packageUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken))
+ {
+ _logger.LogInformation(
+ "Received response {StatusCode}: {ReasonPhrase} of type {ContentType} for request {PackageUri}",
+ response.StatusCode,
+ response.ReasonPhrase,
+ response.Content.Headers.ContentType,
+ packageUri);
+
+ if (response.StatusCode != HttpStatusCode.OK)
+ {
+ _logger.LogWarning(
+ $"Expected status code {HttpStatusCode.OK} for package download, actual: {{ResponseStatusCode}}",
+ response.StatusCode);
+
+ return null;
+ }
+
+ using (var networkStream = await response.Content.ReadAsStreamAsync())
+ {
+ packageStream = await networkStream.AsTemporaryFileStreamAsync(cancellationToken);
+ }
+ }
+
+ _logger.LogInformation(
+ "Downloaded {PackageSizeInBytes} bytes in {DownloadElapsedTime} seconds for request {PackageUri}",
+ packageStream.Length,
+ stopwatch.Elapsed.TotalSeconds,
+ packageUri);
+
+ return packageStream;
+ }
+ catch (Exception e)
+ {
+ _logger.LogError(
+ e,
+ "Exception thrown when trying to download package from {PackageUri}",
+ packageUri);
+
+ packageStream?.Dispose();
+
+ return null;
+ }
+ }
+ }
+}
diff --git a/src/BaGet.Core/Mirror/PackageDownloadsJsonSource.cs b/src/BaGet.Core/Mirror/PackageDownloadsJsonSource.cs
index 8860470b..60a5d040 100644
--- a/src/BaGet.Core/Mirror/PackageDownloadsJsonSource.cs
+++ b/src/BaGet.Core/Mirror/PackageDownloadsJsonSource.cs
@@ -1,110 +1,110 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Net.Http;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using NuGet.Versioning;
-
-namespace BaGet.Core.Mirror
-{
- // See https://github.com/NuGet/NuGet.Services.Metadata/blob/master/src/NuGet.Indexing/Downloads.cs
- public class PackageDownloadsJsonSource : IPackageDownloadsSource
- {
- public const string PackageDownloadsV1Url = "https://nugetprod0.blob.core.windows.net/ng-search-data/downloads.v1.json";
-
- private readonly HttpClient _httpClient;
- private readonly ILogger _logger;
-
- public PackageDownloadsJsonSource(HttpClient httpClient, ILogger logger)
- {
- _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
- _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- }
-
- public async Task>> GetPackageDownloadsAsync()
- {
- _logger.LogInformation("Fetching package downloads...");
-
- var serializer = new JsonSerializer();
- var results = new Dictionary>();
-
- using (var downloadsStream = await GetDownloadsStreamAsync())
- using (var downloadStreamReader = new StreamReader(downloadsStream))
- using (var jsonReader = new JsonTextReader(downloadStreamReader))
- {
- _logger.LogInformation("Parsing package downloads...");
-
- jsonReader.Read();
-
- while (jsonReader.Read())
- {
- try
- {
- if (jsonReader.TokenType == JsonToken.StartArray)
- {
- // TODO: This line reads the entire document into memory...
- var record = JToken.ReadFrom(jsonReader);
- var id = string.Intern(record[0].ToString().ToLowerInvariant());
-
- // The second entry in each record should be an array of versions, if not move on to next entry.
- // This is a check to safe guard against invalid entries.
- if (record.Count() == 2 && record[1].Type != JTokenType.Array)
- {
- continue;
- }
-
- if (!results.ContainsKey(id))
- {
- results.Add(id, new Dictionary());
- }
-
- foreach (var token in record)
- {
- if (token != null && token.Count() == 2)
- {
- var version = string.Intern(NuGetVersion.Parse(token[0].ToString()).ToNormalizedString().ToLowerInvariant());
- var downloads = token[1].ToObject();
-
- results[id][version] = downloads;
- }
- }
- }
- }
- catch (JsonReaderException e)
- {
- _logger.LogError(e, "Invalid entry in downloads.v1.json");
- }
- }
-
- _logger.LogInformation("Parsed package downloads");
- }
-
- return results;
- }
-
- private async Task GetDownloadsStreamAsync()
- {
- _logger.LogInformation("Downloading downloads.v1.json...");
-
- var fileStream = File.Open(Path.GetTempFileName(), FileMode.Create);
- var response = await _httpClient.GetAsync(PackageDownloadsV1Url, HttpCompletionOption.ResponseHeadersRead);
-
- response.EnsureSuccessStatusCode();
-
- using (var networkStream = await response.Content.ReadAsStreamAsync())
- {
- await networkStream.CopyToAsync(fileStream);
- }
-
- fileStream.Seek(0, SeekOrigin.Begin);
-
- _logger.LogInformation("Downloaded downloads.v1.json");
-
- return fileStream;
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using NuGet.Versioning;
+
+namespace BaGet.Core.Mirror
+{
+ // See https://github.com/NuGet/NuGet.Services.Metadata/blob/master/src/NuGet.Indexing/Downloads.cs
+ public class PackageDownloadsJsonSource : IPackageDownloadsSource
+ {
+ public const string PackageDownloadsV1Url = "https://nugetprod0.blob.core.windows.net/ng-search-data/downloads.v1.json";
+
+ private readonly HttpClient _httpClient;
+ private readonly ILogger _logger;
+
+ public PackageDownloadsJsonSource(HttpClient httpClient, ILogger logger)
+ {
+ _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ }
+
+ public async Task>> GetPackageDownloadsAsync()
+ {
+ _logger.LogInformation("Fetching package downloads...");
+
+ var serializer = new JsonSerializer();
+ var results = new Dictionary>();
+
+ using (var downloadsStream = await GetDownloadsStreamAsync())
+ using (var downloadStreamReader = new StreamReader(downloadsStream))
+ using (var jsonReader = new JsonTextReader(downloadStreamReader))
+ {
+ _logger.LogInformation("Parsing package downloads...");
+
+ jsonReader.Read();
+
+ while (jsonReader.Read())
+ {
+ try
+ {
+ if (jsonReader.TokenType == JsonToken.StartArray)
+ {
+ // TODO: This line reads the entire document into memory...
+ var record = JToken.ReadFrom(jsonReader);
+ var id = string.Intern(record[0].ToString().ToLowerInvariant());
+
+ // The second entry in each record should be an array of versions, if not move on to next entry.
+ // This is a check to safe guard against invalid entries.
+ if (record.Count() == 2 && record[1].Type != JTokenType.Array)
+ {
+ continue;
+ }
+
+ if (!results.ContainsKey(id))
+ {
+ results.Add(id, new Dictionary());
+ }
+
+ foreach (var token in record)
+ {
+ if (token != null && token.Count() == 2)
+ {
+ var version = string.Intern(NuGetVersion.Parse(token[0].ToString()).ToNormalizedString().ToLowerInvariant());
+ var downloads = token[1].ToObject();
+
+ results[id][version] = downloads;
+ }
+ }
+ }
+ }
+ catch (JsonReaderException e)
+ {
+ _logger.LogError(e, "Invalid entry in downloads.v1.json");
+ }
+ }
+
+ _logger.LogInformation("Parsed package downloads");
+ }
+
+ return results;
+ }
+
+ private async Task GetDownloadsStreamAsync()
+ {
+ _logger.LogInformation("Downloading downloads.v1.json...");
+
+ var fileStream = File.Open(Path.GetTempFileName(), FileMode.Create);
+ var response = await _httpClient.GetAsync(PackageDownloadsV1Url, HttpCompletionOption.ResponseHeadersRead);
+
+ response.EnsureSuccessStatusCode();
+
+ using (var networkStream = await response.Content.ReadAsStreamAsync())
+ {
+ await networkStream.CopyToAsync(fileStream);
+ }
+
+ fileStream.Seek(0, SeekOrigin.Begin);
+
+ _logger.LogInformation("Downloaded downloads.v1.json");
+
+ return fileStream;
+ }
+ }
+}
diff --git a/src/BaGet.Core/Services/ApiKeyAuthenticationService.cs b/src/BaGet.Core/Services/ApiKeyAuthenticationService.cs
index 5ae3835a..53c2089f 100644
--- a/src/BaGet.Core/Services/ApiKeyAuthenticationService.cs
+++ b/src/BaGet.Core/Services/ApiKeyAuthenticationService.cs
@@ -1,56 +1,56 @@
-using System;
-using System.Security.Cryptography;
-using System.Text;
-using System.Threading.Tasks;
-using BaGet.Core.Configuration;
-using Microsoft.Extensions.Options;
-
-namespace BaGet.Core.Services
-{
- public class ApiKeyAuthenticationService : IAuthenticationService, IDisposable
- {
- private readonly string _apiKeyHash;
- private readonly SHA256 _sha256;
-
- public ApiKeyAuthenticationService(IOptionsSnapshot options)
- {
- if (options == null) throw new ArgumentNullException(nameof(options));
-
- if (string.IsNullOrEmpty(options.Value.ApiKeyHash))
- {
- _apiKeyHash = null;
- }
- else
- {
- _apiKeyHash = options.Value.ApiKeyHash.ToLowerInvariant();
- }
-
- _sha256 = SHA256.Create();
- }
-
- public Task AuthenticateAsync(string apiKey) => Task.FromResult(Authenticate(apiKey));
-
- private bool Authenticate(string apiKey)
- {
- // No authentication is necessary if there is no required API key.
- if (_apiKeyHash == null) return true;
-
- // Otherwise, an API key is required.
- if (string.IsNullOrEmpty(apiKey)) return false;
-
- return _apiKeyHash == ComputeSHA256Hash(apiKey);
- }
-
- private string ComputeSHA256Hash(string input)
- {
- var bytes = _sha256.ComputeHash(Encoding.ASCII.GetBytes(input));
-
- return BitConverter
- .ToString(bytes)
- .Replace("-", string.Empty)
- .ToLowerInvariant();
- }
-
- public void Dispose() => _sha256.Dispose();
- }
-}
+using System;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading.Tasks;
+using BaGet.Core.Configuration;
+using Microsoft.Extensions.Options;
+
+namespace BaGet.Core.Services
+{
+ public class ApiKeyAuthenticationService : IAuthenticationService, IDisposable
+ {
+ private readonly string _apiKeyHash;
+ private readonly SHA256 _sha256;
+
+ public ApiKeyAuthenticationService(IOptionsSnapshot options)
+ {
+ if (options == null) throw new ArgumentNullException(nameof(options));
+
+ if (string.IsNullOrEmpty(options.Value.ApiKeyHash))
+ {
+ _apiKeyHash = null;
+ }
+ else
+ {
+ _apiKeyHash = options.Value.ApiKeyHash.ToLowerInvariant();
+ }
+
+ _sha256 = SHA256.Create();
+ }
+
+ public Task AuthenticateAsync(string apiKey) => Task.FromResult(Authenticate(apiKey));
+
+ private bool Authenticate(string apiKey)
+ {
+ // No authentication is necessary if there is no required API key.
+ if (_apiKeyHash == null) return true;
+
+ // Otherwise, an API key is required.
+ if (string.IsNullOrEmpty(apiKey)) return false;
+
+ return _apiKeyHash == ComputeSHA256Hash(apiKey);
+ }
+
+ private string ComputeSHA256Hash(string input)
+ {
+ var bytes = _sha256.ComputeHash(Encoding.ASCII.GetBytes(input));
+
+ return BitConverter
+ .ToString(bytes)
+ .Replace("-", string.Empty)
+ .ToLowerInvariant();
+ }
+
+ public void Dispose() => _sha256.Dispose();
+ }
+}
diff --git a/src/BaGet.Core/Services/DatabaseSearchService.cs b/src/BaGet.Core/Services/DatabaseSearchService.cs
index 7e375759..a02e2df8 100644
--- a/src/BaGet.Core/Services/DatabaseSearchService.cs
+++ b/src/BaGet.Core/Services/DatabaseSearchService.cs
@@ -1,92 +1,92 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using BaGet.Core.Entities;
-using Microsoft.EntityFrameworkCore;
-
-namespace BaGet.Core.Services
-{
- public class DatabaseSearchService : ISearchService
- {
- private readonly IContext _context;
-
- public DatabaseSearchService(IContext context)
- {
- _context = context ?? throw new ArgumentNullException(nameof(context));
- }
-
- public Task IndexAsync(Package package) => Task.CompletedTask;
-
- public async Task> SearchAsync(string query, int skip = 0, int take = 20)
- {
- IQueryable search = _context.Packages;
-
- if (!string.IsNullOrEmpty(query))
- {
- query = query.ToLower();
- search = search.Where(p => p.Id.ToLower().Contains(query));
- }
-
- var packages = await _context.Packages
- .Where(p =>
- search.Select(p2 => p2.Id)
- .OrderBy(id => id)
- .Distinct()
- .Skip(skip)
- .Take(take)
- .Contains(p.Id))
- .GroupBy(p => p.Id)
- .ToListAsync();
-
- var result = new List();
-
- foreach (var package in packages)
- {
- var versions = package.OrderByDescending(p => p.Version).ToList();
- var latest = versions.First();
-
- var versionResults = versions.Select(p => new SearchResultVersion(p.Version, p.Downloads));
-
- result.Add(new SearchResult
- {
- Id = latest.Id,
- Version = latest.Version,
- Description = latest.Description,
- Authors = latest.Authors,
- IconUrl = latest.IconUrlString,
- LicenseUrl = latest.LicenseUrlString,
- ProjectUrl = latest.ProjectUrlString,
- Summary = latest.Summary,
- Tags = latest.Tags,
- Title = latest.Title,
- TotalDownloads = versions.Sum(p => p.Downloads),
- Versions = versionResults.ToList().AsReadOnly(),
- });
- }
-
- return result.AsReadOnly();
- }
-
- public async Task> AutocompleteAsync(string query, int skip = 0, int take = 20)
- {
- IQueryable search = _context.Packages;
-
- if (!string.IsNullOrEmpty(query))
- {
- query = query.ToLower();
- search = search.Where(p => p.Id.ToLower().Contains(query));
- }
-
- var results = await search.Where(p => p.Listed)
- .OrderByDescending(p => p.Downloads)
- .Skip(skip)
- .Take(take)
- .Select(p => p.Id)
- .Distinct()
- .ToListAsync();
-
- return results.AsReadOnly();
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using BaGet.Core.Entities;
+using Microsoft.EntityFrameworkCore;
+
+namespace BaGet.Core.Services
+{
+ public class DatabaseSearchService : ISearchService
+ {
+ private readonly IContext _context;
+
+ public DatabaseSearchService(IContext context)
+ {
+ _context = context ?? throw new ArgumentNullException(nameof(context));
+ }
+
+ public Task IndexAsync(Package package) => Task.CompletedTask;
+
+ public async Task> SearchAsync(string query, int skip = 0, int take = 20)
+ {
+ IQueryable search = _context.Packages;
+
+ if (!string.IsNullOrEmpty(query))
+ {
+ query = query.ToLower();
+ search = search.Where(p => p.Id.ToLower().Contains(query));
+ }
+
+ var packages = await _context.Packages
+ .Where(p =>
+ search.Select(p2 => p2.Id)
+ .OrderBy(id => id)
+ .Distinct()
+ .Skip(skip)
+ .Take(take)
+ .Contains(p.Id))
+ .GroupBy(p => p.Id)
+ .ToListAsync();
+
+ var result = new List();
+
+ foreach (var package in packages)
+ {
+ var versions = package.OrderByDescending(p => p.Version).ToList();
+ var latest = versions.First();
+
+ var versionResults = versions.Select(p => new SearchResultVersion(p.Version, p.Downloads));
+
+ result.Add(new SearchResult
+ {
+ Id = latest.Id,
+ Version = latest.Version,
+ Description = latest.Description,
+ Authors = latest.Authors,
+ IconUrl = latest.IconUrlString,
+ LicenseUrl = latest.LicenseUrlString,
+ ProjectUrl = latest.ProjectUrlString,
+ Summary = latest.Summary,
+ Tags = latest.Tags,
+ Title = latest.Title,
+ TotalDownloads = versions.Sum(p => p.Downloads),
+ Versions = versionResults.ToList().AsReadOnly(),
+ });
+ }
+
+ return result.AsReadOnly();
+ }
+
+ public async Task> AutocompleteAsync(string query, int skip = 0, int take = 20)
+ {
+ IQueryable search = _context.Packages;
+
+ if (!string.IsNullOrEmpty(query))
+ {
+ query = query.ToLower();
+ search = search.Where(p => p.Id.ToLower().Contains(query));
+ }
+
+ var results = await search.Where(p => p.Listed)
+ .OrderByDescending(p => p.Downloads)
+ .Skip(skip)
+ .Take(take)
+ .Select(p => p.Id)
+ .Distinct()
+ .ToListAsync();
+
+ return results.AsReadOnly();
+ }
+ }
+}
diff --git a/src/BaGet.Core/Services/IAuthenticationService.cs b/src/BaGet.Core/Services/IAuthenticationService.cs
index 56a6759e..987e84d0 100644
--- a/src/BaGet.Core/Services/IAuthenticationService.cs
+++ b/src/BaGet.Core/Services/IAuthenticationService.cs
@@ -1,9 +1,9 @@
-using System.Threading.Tasks;
-
-namespace BaGet.Core.Services
-{
- public interface IAuthenticationService
- {
- Task AuthenticateAsync(string apiKey);
- }
-}
+using System.Threading.Tasks;
+
+namespace BaGet.Core.Services
+{
+ public interface IAuthenticationService
+ {
+ Task AuthenticateAsync(string apiKey);
+ }
+}
diff --git a/src/BaGet.Core/Services/IPackageDeletionService.cs b/src/BaGet.Core/Services/IPackageDeletionService.cs
index 79eb1132..6c8fd2d5 100644
--- a/src/BaGet.Core/Services/IPackageDeletionService.cs
+++ b/src/BaGet.Core/Services/IPackageDeletionService.cs
@@ -1,18 +1,18 @@
-using System.Threading;
-using System.Threading.Tasks;
-using NuGet.Versioning;
-
-namespace BaGet.Core.Services
-{
- public interface IPackageDeletionService
- {
- ///
- /// Attempt to delete a package.
- ///
- /// The id of the package to delete.
- /// The version of the package to delete.
- ///
- /// False if the package does not exist.
- Task TryDeletePackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken);
- }
-}
+using System.Threading;
+using System.Threading.Tasks;
+using NuGet.Versioning;
+
+namespace BaGet.Core.Services
+{
+ public interface IPackageDeletionService
+ {
+ ///
+ /// Attempt to delete a package.
+ ///
+ /// The id of the package to delete.
+ /// The version of the package to delete.
+ ///
+ /// False if the package does not exist.
+ Task TryDeletePackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken);
+ }
+}
diff --git a/src/BaGet.Core/Services/IPackageService.cs b/src/BaGet.Core/Services/IPackageService.cs
index 58736a7b..09b18bb3 100644
--- a/src/BaGet.Core/Services/IPackageService.cs
+++ b/src/BaGet.Core/Services/IPackageService.cs
@@ -1,95 +1,95 @@
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using BaGet.Core.Entities;
-using NuGet.Versioning;
-
-namespace BaGet.Core.Services
-{
- ///
- /// The result of attempting to add the package to the database.
- /// See
- ///
- public enum PackageAddResult
- {
- ///
- /// Failed to add the package as it already exists.
- ///
- PackageAlreadyExists,
-
- ///
- /// The package was added successfully.
- ///
- Success
- }
-
- ///
- /// The "source of truth" for packages' state. Packages' content
- /// are stored by the .
- ///
- public interface IPackageService
- {
- ///
- /// Attempt to add a new package to the database.
- ///
- /// The package to add to the database.
- /// The result of attempting to add the package to the database.
- Task AddAsync(Package package);
-
- ///
- /// Attempt to find a package with the given id and version.
- ///
- /// The package's id.
- /// The package's version.
- /// Whether unlisted results should be included.
- /// The package found, or null.
- Task FindOrNullAsync(string id, NuGetVersion version, bool includeUnlisted = false);
-
- ///
- /// Attempt to find all packages with a given id.
- ///
- /// The packages' id.
- /// Whether unlisted results should be included.
- /// The packages found. Always non-null.
- Task> FindAsync(string id, bool includeUnlisted = false);
-
- ///
- /// Determine whether a package exists in the database (even if the package is unlisted).
- ///
- /// The package id to search.
- /// The package version to search.
- /// Whether the package exists in the database.
- Task ExistsAsync(string id, NuGetVersion version = null);
-
- ///
- /// Unlist a package, making it undiscoverable.
- ///
- /// The id of the package to unlist.
- /// The version of the package to unlist.
- /// False if the package does not exist.
- Task UnlistPackageAsync(string id, NuGetVersion version);
-
- ///
- /// Relist a package, making it discoverable.
- ///
- /// The id of the package to relist.
- /// The version of the package to relist.
- /// False if the package does not exist.
- Task RelistPackageAsync(string id, NuGetVersion version);
-
- ///
- /// Increment a package's download count.
- ///
- /// The id of the package to update.
- /// The id of the package to update.
- /// False if the package does not exist.
- Task AddDownloadAsync(string id, NuGetVersion version);
-
- ///
- /// Completely remove the package from the database.
- ///
- /// The id of the package to remove.
- /// The version of the pacakge to remove.
- /// False if the package doesn't exist.
- Task HardDeletePackageAsync(string id, NuGetVersion version);
- }
-}
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using BaGet.Core.Entities;
+using NuGet.Versioning;
+
+namespace BaGet.Core.Services
+{
+ ///
+ /// The result of attempting to add the package to the database.
+ /// See
+ ///
+ public enum PackageAddResult
+ {
+ ///
+ /// Failed to add the package as it already exists.
+ ///
+ PackageAlreadyExists,
+
+ ///
+ /// The package was added successfully.
+ ///
+ Success
+ }
+
+ ///
+ /// The "source of truth" for packages' state. Packages' content
+ /// are stored by the .
+ ///
+ public interface IPackageService
+ {
+ ///
+ /// Attempt to add a new package to the database.
+ ///
+ /// The package to add to the database.
+ /// The result of attempting to add the package to the database.
+ Task AddAsync(Package package);
+
+ ///
+ /// Attempt to find a package with the given id and version.
+ ///
+ /// The package's id.
+ /// The package's version.
+ /// Whether unlisted results should be included.
+ /// The package found, or null.
+ Task FindOrNullAsync(string id, NuGetVersion version, bool includeUnlisted = false);
+
+ ///
+ /// Attempt to find all packages with a given id.
+ ///
+ /// The packages' id.
+ /// Whether unlisted results should be included.
+ /// The packages found. Always non-null.
+ Task> FindAsync(string id, bool includeUnlisted = false);
+
+ ///
+ /// Determine whether a package exists in the database (even if the package is unlisted).
+ ///
+ /// The package id to search.
+ /// The package version to search.
+ /// Whether the package exists in the database.
+ Task ExistsAsync(string id, NuGetVersion version = null);
+
+ ///
+ /// Unlist a package, making it undiscoverable.
+ ///
+ /// The id of the package to unlist.
+ /// The version of the package to unlist.
+ /// False if the package does not exist.
+ Task UnlistPackageAsync(string id, NuGetVersion version);
+
+ ///
+ /// Relist a package, making it discoverable.
+ ///
+ /// The id of the package to relist.
+ /// The version of the package to relist.
+ /// False if the package does not exist.
+ Task RelistPackageAsync(string id, NuGetVersion version);
+
+ ///
+ /// Increment a package's download count.
+ ///
+ /// The id of the package to update.
+ /// The id of the package to update.
+ /// False if the package does not exist.
+ Task AddDownloadAsync(string id, NuGetVersion version);
+
+ ///
+ /// Completely remove the package from the database.
+ ///
+ /// The id of the package to remove.
+ /// The version of the pacakge to remove.
+ /// False if the package doesn't exist.
+ Task HardDeletePackageAsync(string id, NuGetVersion version);
+ }
+}
diff --git a/src/BaGet.Core/Services/IPackageStorageService.cs b/src/BaGet.Core/Services/IPackageStorageService.cs
index b060cffc..da3ecd10 100644
--- a/src/BaGet.Core/Services/IPackageStorageService.cs
+++ b/src/BaGet.Core/Services/IPackageStorageService.cs
@@ -1,69 +1,69 @@
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using BaGet.Core.Entities;
-using NuGet.Versioning;
-
-namespace BaGet.Core.Services
-{
- ///
- /// Stores packages' content. Packages' state are stored by the
- /// .
- ///
- public interface IPackageStorageService
- {
- ///
- /// Persist a package's content to storage. This operation MUST fail if a package
- /// with the same id/version but different content has already been stored.
- ///
- /// The package's metadata.
- /// The package's nupkg stream.
- /// The package's nuspec stream.
- /// The package's readme stream, or null if none.
- ///
- ///
- Task SavePackageContentAsync(
- Package package,
- Stream packageStream,
- Stream nuspecStream,
- Stream readmeStream,
- CancellationToken cancellationToken);
-
- ///
- /// Retrieve a package's nupkg stream.
- ///
- /// The package's id.
- /// The package's version.
- ///
- /// The package's nupkg stream.
- Task GetPackageStreamAsync(string id, NuGetVersion version, CancellationToken cancellationToken);
-
- ///
- /// Retrieve a package's nuspec stream.
- ///
- /// The package's id.
- /// The package's version.
- ///
- /// The package's nuspec stream.
- Task GetNuspecStreamAsync(string id, NuGetVersion version, CancellationToken cancellationToken);
-
- ///
- /// Retrieve a package's readme stream.
- ///
- /// The package's id.
- /// The package's version.
- ///
- /// The package's readme stream.
- Task GetReadmeStreamAsync(string id, NuGetVersion version, CancellationToken cancellationToken);
-
- ///
- /// Remove a package's content from storage. This operation SHOULD succeed
- /// even if the package does not exist.
- ///
- /// The package's id.
- /// The package's version.
- ///
- ///
- Task DeleteAsync(string id, NuGetVersion version, CancellationToken cancellationToken);
- }
-}
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using BaGet.Core.Entities;
+using NuGet.Versioning;
+
+namespace BaGet.Core.Services
+{
+ ///
+ /// Stores packages' content. Packages' state are stored by the
+ /// .
+ ///
+ public interface IPackageStorageService
+ {
+ ///
+ /// Persist a package's content to storage. This operation MUST fail if a package
+ /// with the same id/version but different content has already been stored.
+ ///
+ /// The package's metadata.
+ /// The package's nupkg stream.
+ /// The package's nuspec stream.
+ /// The package's readme stream, or null if none.
+ ///
+ ///
+ Task SavePackageContentAsync(
+ Package package,
+ Stream packageStream,
+ Stream nuspecStream,
+ Stream readmeStream,
+ CancellationToken cancellationToken);
+
+ ///
+ /// Retrieve a package's nupkg stream.
+ ///
+ /// The package's id.
+ /// The package's version.
+ ///
+ /// The package's nupkg stream.
+ Task GetPackageStreamAsync(string id, NuGetVersion version, CancellationToken cancellationToken);
+
+ ///
+ /// Retrieve a package's nuspec stream.
+ ///
+ /// The package's id.
+ /// The package's version.
+ ///
+ /// The package's nuspec stream.
+ Task GetNuspecStreamAsync(string id, NuGetVersion version, CancellationToken cancellationToken);
+
+ ///
+ /// Retrieve a package's readme stream.
+ ///
+ /// The package's id.
+ /// The package's version.
+ ///
+ /// The package's readme stream.
+ Task GetReadmeStreamAsync(string id, NuGetVersion version, CancellationToken cancellationToken);
+
+ ///
+ /// Remove a package's content from storage. This operation SHOULD succeed
+ /// even if the package does not exist.
+ ///
+ /// The package's id.
+ /// The package's version.
+ ///
+ ///
+ Task DeleteAsync(string id, NuGetVersion version, CancellationToken cancellationToken);
+ }
+}
diff --git a/src/BaGet.Core/Services/ISearchService.cs b/src/BaGet.Core/Services/ISearchService.cs
index f5745595..316e08d6 100644
--- a/src/BaGet.Core/Services/ISearchService.cs
+++ b/src/BaGet.Core/Services/ISearchService.cs
@@ -1,49 +1,49 @@
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using BaGet.Core.Entities;
-using NuGet.Versioning;
-
-namespace BaGet.Core.Services
-{
- public interface ISearchService
- {
- Task IndexAsync(Package package);
-
- Task> SearchAsync(string query, int skip = 0, int take = 20);
-
- Task> AutocompleteAsync(string query, int skip = 0, int take = 20);
- }
-
- public class SearchResult
- {
- public string Id { get; set; }
-
- public NuGetVersion Version { get; set; }
-
- public string Description { get; set; }
- public IReadOnlyList Authors { get; set; }
- public string IconUrl { get; set; }
- public string LicenseUrl { get; set; }
- public string ProjectUrl { get; set; }
- public string Summary { get; set; }
- public string[] Tags { get; set; }
- public string Title { get; set; }
- public long TotalDownloads { get; set; }
-
- public IReadOnlyList Versions { get; set; }
- }
-
- public class SearchResultVersion
- {
- public SearchResultVersion(NuGetVersion version, long downloads)
- {
- Version = version ?? throw new ArgumentNullException(nameof(version));
- Downloads = downloads;
- }
-
- public NuGetVersion Version { get; }
-
- public long Downloads { get; }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using BaGet.Core.Entities;
+using NuGet.Versioning;
+
+namespace BaGet.Core.Services
+{
+ public interface ISearchService
+ {
+ Task IndexAsync(Package package);
+
+ Task> SearchAsync(string query, int skip = 0, int take = 20);
+
+ Task> AutocompleteAsync(string query, int skip = 0, int take = 20);
+ }
+
+ public class SearchResult
+ {
+ public string Id { get; set; }
+
+ public NuGetVersion Version { get; set; }
+
+ public string Description { get; set; }
+ public IReadOnlyList Authors { get; set; }
+ public string IconUrl { get; set; }
+ public string LicenseUrl { get; set; }
+ public string ProjectUrl { get; set; }
+ public string Summary { get; set; }
+ public string[] Tags { get; set; }
+ public string Title { get; set; }
+ public long TotalDownloads { get; set; }
+
+ public IReadOnlyList Versions { get; set; }
+ }
+
+ public class SearchResultVersion
+ {
+ public SearchResultVersion(NuGetVersion version, long downloads)
+ {
+ Version = version ?? throw new ArgumentNullException(nameof(version));
+ Downloads = downloads;
+ }
+
+ public NuGetVersion Version { get; }
+
+ public long Downloads { get; }
+ }
+}
diff --git a/src/BaGet.Core/Services/PackageDeletionService.cs b/src/BaGet.Core/Services/PackageDeletionService.cs
index f763aee5..11cf6f9b 100644
--- a/src/BaGet.Core/Services/PackageDeletionService.cs
+++ b/src/BaGet.Core/Services/PackageDeletionService.cs
@@ -1,93 +1,93 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using BaGet.Core.Configuration;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using NuGet.Versioning;
-
-namespace BaGet.Core.Services
-{
- public class PackageDeletionService : IPackageDeletionService
- {
- private readonly IPackageService _packages;
- private readonly IPackageStorageService _storage;
- private readonly BaGetOptions _options;
- private readonly ILogger _logger;
-
- public PackageDeletionService(
- IPackageService packages,
- IPackageStorageService storage,
- IOptionsSnapshot options,
- ILogger logger)
- {
- _packages = packages ?? throw new ArgumentNullException(nameof(packages));
- _storage = storage ?? throw new ArgumentNullException(nameof(storage));
- _options = options?.Value ?? throw new ArgumentNullException(nameof(options));
- _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- }
-
- public async Task TryDeletePackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken)
- {
- switch (_options.PackageDeletionBehavior)
- {
- case PackageDeletionBehavior.Unlist:
- return await TryUnlistPackageAsync(id, version);
-
- case PackageDeletionBehavior.HardDelete:
- return await TryHardDeletePackageAsync(id, version, cancellationToken);
-
- default:
- throw new InvalidOperationException($"Unknown deletion behavior '{_options.PackageDeletionBehavior}'");
- }
- }
-
- private async Task TryUnlistPackageAsync(string id, NuGetVersion version)
- {
- _logger.LogInformation("Unlisting package {PackageId} {PackageVersion}...", id, version);
-
- if (!await _packages.UnlistPackageAsync(id, version))
- {
- _logger.LogWarning("Could not find package {PackageId} {PackageVersion}", id, version);
-
- return false;
- }
-
- _logger.LogInformation("Unlisted package {PackageId} {PackageVersion}", id, version);
-
- return true;
- }
-
- private async Task TryHardDeletePackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken)
- {
- _logger.LogInformation(
- "Hard deleting package {PackageId} {PackageVersion} from the database...",
- id,
- version);
-
- var found = await _packages.HardDeletePackageAsync(id, version);
- if (!found)
- {
- _logger.LogWarning(
- "Could not find package {PackageId} {PackageVersion} in the database",
- id,
- version);
- }
-
- // Delete the package from storage. This is necessary even if the package isn't
- // in the database to ensure that the storage is consistent with the database.
- _logger.LogInformation("Hard deleting package {PackageId} {PackageVersion} from storage...",
- id,
- version);
-
- await _storage.DeleteAsync(id, version, cancellationToken);
-
- _logger.LogInformation(
- "Hard deleted package {PackageId} {PackageVersion} from storage",
- id,
- version);
-
- return found;
- }
- }
-}
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using BaGet.Core.Configuration;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using NuGet.Versioning;
+
+namespace BaGet.Core.Services
+{
+ public class PackageDeletionService : IPackageDeletionService
+ {
+ private readonly IPackageService _packages;
+ private readonly IPackageStorageService _storage;
+ private readonly BaGetOptions _options;
+ private readonly ILogger _logger;
+
+ public PackageDeletionService(
+ IPackageService packages,
+ IPackageStorageService storage,
+ IOptionsSnapshot options,
+ ILogger logger)
+ {
+ _packages = packages ?? throw new ArgumentNullException(nameof(packages));
+ _storage = storage ?? throw new ArgumentNullException(nameof(storage));
+ _options = options?.Value ?? throw new ArgumentNullException(nameof(options));
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ }
+
+ public async Task TryDeletePackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken)
+ {
+ switch (_options.PackageDeletionBehavior)
+ {
+ case PackageDeletionBehavior.Unlist:
+ return await TryUnlistPackageAsync(id, version);
+
+ case PackageDeletionBehavior.HardDelete:
+ return await TryHardDeletePackageAsync(id, version, cancellationToken);
+
+ default:
+ throw new InvalidOperationException($"Unknown deletion behavior '{_options.PackageDeletionBehavior}'");
+ }
+ }
+
+ private async Task TryUnlistPackageAsync(string id, NuGetVersion version)
+ {
+ _logger.LogInformation("Unlisting package {PackageId} {PackageVersion}...", id, version);
+
+ if (!await _packages.UnlistPackageAsync(id, version))
+ {
+ _logger.LogWarning("Could not find package {PackageId} {PackageVersion}", id, version);
+
+ return false;
+ }
+
+ _logger.LogInformation("Unlisted package {PackageId} {PackageVersion}", id, version);
+
+ return true;
+ }
+
+ private async Task TryHardDeletePackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken)
+ {
+ _logger.LogInformation(
+ "Hard deleting package {PackageId} {PackageVersion} from the database...",
+ id,
+ version);
+
+ var found = await _packages.HardDeletePackageAsync(id, version);
+ if (!found)
+ {
+ _logger.LogWarning(
+ "Could not find package {PackageId} {PackageVersion} in the database",
+ id,
+ version);
+ }
+
+ // Delete the package from storage. This is necessary even if the package isn't
+ // in the database to ensure that the storage is consistent with the database.
+ _logger.LogInformation("Hard deleting package {PackageId} {PackageVersion} from storage...",
+ id,
+ version);
+
+ await _storage.DeleteAsync(id, version, cancellationToken);
+
+ _logger.LogInformation(
+ "Hard deleted package {PackageId} {PackageVersion} from storage",
+ id,
+ version);
+
+ return found;
+ }
+ }
+}
diff --git a/src/BaGet.Core/Services/PackageService.cs b/src/BaGet.Core/Services/PackageService.cs
index 39b7a703..f805315e 100644
--- a/src/BaGet.Core/Services/PackageService.cs
+++ b/src/BaGet.Core/Services/PackageService.cs
@@ -1,128 +1,128 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using BaGet.Core.Entities;
-using Microsoft.EntityFrameworkCore;
-using NuGet.Versioning;
-
-namespace BaGet.Core.Services
-{
- public class PackageService : IPackageService
- {
- private readonly IContext _context;
-
- public PackageService(IContext context)
- {
- _context = context ?? throw new ArgumentNullException(nameof(context));
- }
-
- public async Task AddAsync(Package package)
- {
- try
- {
- _context.Packages.Add(package);
-
- await _context.SaveChangesAsync();
-
- return PackageAddResult.Success;
- }
- catch (DbUpdateException e)
- when (_context.IsUniqueConstraintViolationException(e))
- {
- return PackageAddResult.PackageAlreadyExists;
- }
- }
-
- public Task ExistsAsync(string id, NuGetVersion version = null)
- {
- var query = _context.Packages.Where(p => p.Id == id);
-
- if (version != null)
- {
- query = query.Where(p => p.VersionString == version.ToNormalizedString());
- }
-
- return query.AnyAsync();
- }
-
- public async Task> FindAsync(string id, bool includeUnlisted = false)
- {
- var query = _context.Packages.Include(a => a.Dependencies).Where(p => p.Id == id);
-
- if (!includeUnlisted)
- {
- query = query.Where(p => p.Listed);
- }
-
- return (await query.ToListAsync()).AsReadOnly();
- }
-
- public Task FindOrNullAsync(string id, NuGetVersion version, bool includeUnlisted = false)
- {
- var query = _context.Packages
- .Include(a => a.Dependencies)
- .Where(p => p.Id == id)
- .Where(p => p.VersionString == version.ToNormalizedString());
-
- if (!includeUnlisted)
- {
- query = query.Where(p => p.Listed);
- }
-
- return query.FirstOrDefaultAsync();
- }
-
- public Task UnlistPackageAsync(string id, NuGetVersion version)
- {
- return TryUpdatePackageAsync(id, version, p => p.Listed = false);
- }
-
- public Task RelistPackageAsync(string id, NuGetVersion version)
- {
- return TryUpdatePackageAsync(id, version, p => p.Listed = true);
- }
-
- public Task AddDownloadAsync(string id, NuGetVersion version)
- {
- return TryUpdatePackageAsync(id, version, p => p.Downloads += 1);
- }
-
- public async Task HardDeletePackageAsync(string id, NuGetVersion version)
- {
- var package = await _context.Packages
- .Where(p => p.Id == id)
- .Where(p => p.VersionString == version.ToNormalizedString())
- .Include(p => p.Dependencies)
- .FirstOrDefaultAsync();
-
- if (package == null)
- {
- return false;
- }
-
- _context.Packages.Remove(package);
- await _context.SaveChangesAsync();
-
- return true;
- }
-
- private async Task TryUpdatePackageAsync(string id, NuGetVersion version, Action action)
- {
- var package = await _context.Packages
- .Where(p => p.Id == id)
- .Where(p => p.VersionString == version.ToNormalizedString())
- .FirstOrDefaultAsync();
-
- if (package != null)
- {
- action(package);
- await _context.SaveChangesAsync();
-
- return true;
- }
-
- return false;
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using BaGet.Core.Entities;
+using Microsoft.EntityFrameworkCore;
+using NuGet.Versioning;
+
+namespace BaGet.Core.Services
+{
+ public class PackageService : IPackageService
+ {
+ private readonly IContext _context;
+
+ public PackageService(IContext context)
+ {
+ _context = context ?? throw new ArgumentNullException(nameof(context));
+ }
+
+ public async Task AddAsync(Package package)
+ {
+ try
+ {
+ _context.Packages.Add(package);
+
+ await _context.SaveChangesAsync();
+
+ return PackageAddResult.Success;
+ }
+ catch (DbUpdateException e)
+ when (_context.IsUniqueConstraintViolationException(e))
+ {
+ return PackageAddResult.PackageAlreadyExists;
+ }
+ }
+
+ public Task ExistsAsync(string id, NuGetVersion version = null)
+ {
+ var query = _context.Packages.Where(p => p.Id == id);
+
+ if (version != null)
+ {
+ query = query.Where(p => p.VersionString == version.ToNormalizedString());
+ }
+
+ return query.AnyAsync();
+ }
+
+ public async Task> FindAsync(string id, bool includeUnlisted = false)
+ {
+ var query = _context.Packages.Include(a => a.Dependencies).Where(p => p.Id == id);
+
+ if (!includeUnlisted)
+ {
+ query = query.Where(p => p.Listed);
+ }
+
+ return (await query.ToListAsync()).AsReadOnly();
+ }
+
+ public Task FindOrNullAsync(string id, NuGetVersion version, bool includeUnlisted = false)
+ {
+ var query = _context.Packages
+ .Include(a => a.Dependencies)
+ .Where(p => p.Id == id)
+ .Where(p => p.VersionString == version.ToNormalizedString());
+
+ if (!includeUnlisted)
+ {
+ query = query.Where(p => p.Listed);
+ }
+
+ return query.FirstOrDefaultAsync();
+ }
+
+ public Task UnlistPackageAsync(string id, NuGetVersion version)
+ {
+ return TryUpdatePackageAsync(id, version, p => p.Listed = false);
+ }
+
+ public Task RelistPackageAsync(string id, NuGetVersion version)
+ {
+ return TryUpdatePackageAsync(id, version, p => p.Listed = true);
+ }
+
+ public Task AddDownloadAsync(string id, NuGetVersion version)
+ {
+ return TryUpdatePackageAsync(id, version, p => p.Downloads += 1);
+ }
+
+ public async Task HardDeletePackageAsync(string id, NuGetVersion version)
+ {
+ var package = await _context.Packages
+ .Where(p => p.Id == id)
+ .Where(p => p.VersionString == version.ToNormalizedString())
+ .Include(p => p.Dependencies)
+ .FirstOrDefaultAsync();
+
+ if (package == null)
+ {
+ return false;
+ }
+
+ _context.Packages.Remove(package);
+ await _context.SaveChangesAsync();
+
+ return true;
+ }
+
+ private async Task TryUpdatePackageAsync(string id, NuGetVersion version, Action action)
+ {
+ var package = await _context.Packages
+ .Where(p => p.Id == id)
+ .Where(p => p.VersionString == version.ToNormalizedString())
+ .FirstOrDefaultAsync();
+
+ if (package != null)
+ {
+ action(package);
+ await _context.SaveChangesAsync();
+
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/src/BaGet.Core/readme.md b/src/BaGet.Core/readme.md
index 2387533c..fa74efcf 100644
--- a/src/BaGet.Core/readme.md
+++ b/src/BaGet.Core/readme.md
@@ -1,3 +1,3 @@
-# BaGet.Core
-
+# BaGet.Core
+
Contains BaGet's core logic.
\ No newline at end of file
diff --git a/src/BaGet.Tools.AzureSearchImporter/BaGet.Tools.AzureSearchImporter.csproj b/src/BaGet.Tools.AzureSearchImporter/BaGet.Tools.AzureSearchImporter.csproj
index c5152816..cb6b83cf 100644
--- a/src/BaGet.Tools.AzureSearchImporter/BaGet.Tools.AzureSearchImporter.csproj
+++ b/src/BaGet.Tools.AzureSearchImporter/BaGet.Tools.AzureSearchImporter.csproj
@@ -1,30 +1,30 @@
-
-
-
- Exe
- netcoreapp2.1
-
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ Exe
+ netcoreapp2.1
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BaGet.Tools.AzureSearchImporter/Entities/IndexerContext.cs b/src/BaGet.Tools.AzureSearchImporter/Entities/IndexerContext.cs
index dcb2820e..732c9f17 100644
--- a/src/BaGet.Tools.AzureSearchImporter/Entities/IndexerContext.cs
+++ b/src/BaGet.Tools.AzureSearchImporter/Entities/IndexerContext.cs
@@ -1,30 +1,30 @@
-using Microsoft.EntityFrameworkCore;
-
-namespace BaGet.Tools.AzureSearchImporter.Entities
-{
- public class IndexerContext : DbContext
- {
- public IndexerContext(DbContextOptions options)
- : base(options)
- {}
-
- public DbSet PackageIds { get; set; }
-
- protected override void OnModelCreating(ModelBuilder builder)
- {
- builder.Entity()
- .HasKey(p => p.Key);
-
- builder.Entity()
- .Property(p => p.Value)
- .HasColumnType("TEXT COLLATE NOCASE");
-
- builder.Entity()
- .HasIndex(p => p.Value)
- .IsUnique();
-
- builder.Entity()
- .HasIndex(p => p.Done);
- }
- }
-}
+using Microsoft.EntityFrameworkCore;
+
+namespace BaGet.Tools.AzureSearchImporter.Entities
+{
+ public class IndexerContext : DbContext
+ {
+ public IndexerContext(DbContextOptions options)
+ : base(options)
+ {}
+
+ public DbSet PackageIds { get; set; }
+
+ protected override void OnModelCreating(ModelBuilder builder)
+ {
+ builder.Entity()
+ .HasKey(p => p.Key);
+
+ builder.Entity()
+ .Property(p => p.Value)
+ .HasColumnType("TEXT COLLATE NOCASE");
+
+ builder.Entity()
+ .HasIndex(p => p.Value)
+ .IsUnique();
+
+ builder.Entity()
+ .HasIndex(p => p.Done);
+ }
+ }
+}
diff --git a/src/BaGet.Tools.AzureSearchImporter/Entities/IndexerContextFactory.cs b/src/BaGet.Tools.AzureSearchImporter/Entities/IndexerContextFactory.cs
index c6d35f56..da35befd 100644
--- a/src/BaGet.Tools.AzureSearchImporter/Entities/IndexerContextFactory.cs
+++ b/src/BaGet.Tools.AzureSearchImporter/Entities/IndexerContextFactory.cs
@@ -1,19 +1,19 @@
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Design;
-
-namespace BaGet.Tools.AzureSearchImporter.Entities
-{
- class IndexerContextFactory : IDesignTimeDbContextFactory
- {
- public const string ConnectionString = "Data Source=indexer.db";
-
- public IndexerContext CreateDbContext(string[] args)
- {
- var optionsBuilder = new DbContextOptionsBuilder();
-
- optionsBuilder.UseSqlite(ConnectionString);
-
- return new IndexerContext(optionsBuilder.Options);
- }
- }
-}
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Design;
+
+namespace BaGet.Tools.AzureSearchImporter.Entities
+{
+ class IndexerContextFactory : IDesignTimeDbContextFactory
+ {
+ public const string ConnectionString = "Data Source=indexer.db";
+
+ public IndexerContext CreateDbContext(string[] args)
+ {
+ var optionsBuilder = new DbContextOptionsBuilder();
+
+ optionsBuilder.UseSqlite(ConnectionString);
+
+ return new IndexerContext(optionsBuilder.Options);
+ }
+ }
+}
diff --git a/src/BaGet.Tools.AzureSearchImporter/Entities/PackageId.cs b/src/BaGet.Tools.AzureSearchImporter/Entities/PackageId.cs
index 48bcffc8..bb0f53ac 100644
--- a/src/BaGet.Tools.AzureSearchImporter/Entities/PackageId.cs
+++ b/src/BaGet.Tools.AzureSearchImporter/Entities/PackageId.cs
@@ -1,11 +1,11 @@
-namespace BaGet.Tools.AzureSearchImporter.Entities
-{
- public class PackageId
- {
- public int Key { get; set; }
-
- public string Value { get; set; }
-
- public bool Done { get; set; }
- }
+namespace BaGet.Tools.AzureSearchImporter.Entities
+{
+ public class PackageId
+ {
+ public int Key { get; set; }
+
+ public string Value { get; set; }
+
+ public bool Done { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/BaGet.Tools.AzureSearchImporter/Importer.cs b/src/BaGet.Tools.AzureSearchImporter/Importer.cs
index fc18cd15..fff42e03 100644
--- a/src/BaGet.Tools.AzureSearchImporter/Importer.cs
+++ b/src/BaGet.Tools.AzureSearchImporter/Importer.cs
@@ -1,70 +1,70 @@
-using System;
-using System.Linq;
-using System.Threading.Tasks;
-using BaGet.Azure.Search;
-using BaGet.Tools.AzureSearchImporter.Entities;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging;
-using MoreLinq;
-
-namespace BaGet.Tools.AzureSearchImporter
-{
- public class Importer
- {
- private const int ImportBatchSize = 200;
-
- private readonly IndexerContext _context;
- private readonly BatchIndexer _indexer;
- private readonly ILogger _logger;
-
- public Importer(IndexerContext context, BatchIndexer indexer, ILogger logger)
- {
- _context = context ?? throw new ArgumentException(nameof(context));
- _indexer = indexer ?? throw new ArgumentNullException(nameof(indexer));
- _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- }
-
- public async Task ImportAsync(int skip = 0)
- {
- _logger.LogInformation("Starting import with skip {Skip}...", skip);
-
- var batchCount = 1;
- var left = await _context.PackageIds
- .Where(p => !p.Done)
- .CountAsync();
-
- _logger.LogInformation("{PackageIdsLeft} package ids left to import", left);
-
- while (true)
- {
- _logger.LogInformation("Importing batch {BatchCount}...", batchCount);
-
- var batch = await _context.PackageIds
- .Where(p => !p.Done)
- .OrderBy(p => p.Key)
- .Skip(skip)
- .Take(ImportBatchSize)
- .ToListAsync();
-
- if (batch.Count == 0)
- {
- break;
- }
-
- await _indexer.IndexAsync(batch.Select(p => p.Value).ToArray());
-
- foreach (var package in batch)
- {
- package.Done = true;
- }
-
- await _context.SaveChangesAsync();
-
- _logger.LogInformation("Imported batch {BatchCount}", batchCount);
- batchCount++;
- }
-
- _logger.LogInformation("Finished importing");
- }
- }
-}
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using BaGet.Azure.Search;
+using BaGet.Tools.AzureSearchImporter.Entities;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+using MoreLinq;
+
+namespace BaGet.Tools.AzureSearchImporter
+{
+ public class Importer
+ {
+ private const int ImportBatchSize = 200;
+
+ private readonly IndexerContext _context;
+ private readonly BatchIndexer _indexer;
+ private readonly ILogger _logger;
+
+ public Importer(IndexerContext context, BatchIndexer indexer, ILogger logger)
+ {
+ _context = context ?? throw new ArgumentException(nameof(context));
+ _indexer = indexer ?? throw new ArgumentNullException(nameof(indexer));
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ }
+
+ public async Task ImportAsync(int skip = 0)
+ {
+ _logger.LogInformation("Starting import with skip {Skip}...", skip);
+
+ var batchCount = 1;
+ var left = await _context.PackageIds
+ .Where(p => !p.Done)
+ .CountAsync();
+
+ _logger.LogInformation("{PackageIdsLeft} package ids left to import", left);
+
+ while (true)
+ {
+ _logger.LogInformation("Importing batch {BatchCount}...", batchCount);
+
+ var batch = await _context.PackageIds
+ .Where(p => !p.Done)
+ .OrderBy(p => p.Key)
+ .Skip(skip)
+ .Take(ImportBatchSize)
+ .ToListAsync();
+
+ if (batch.Count == 0)
+ {
+ break;
+ }
+
+ await _indexer.IndexAsync(batch.Select(p => p.Value).ToArray());
+
+ foreach (var package in batch)
+ {
+ package.Done = true;
+ }
+
+ await _context.SaveChangesAsync();
+
+ _logger.LogInformation("Imported batch {BatchCount}", batchCount);
+ batchCount++;
+ }
+
+ _logger.LogInformation("Finished importing");
+ }
+ }
+}
diff --git a/src/BaGet.Tools.AzureSearchImporter/Initializer.cs b/src/BaGet.Tools.AzureSearchImporter/Initializer.cs
index 6999b2a2..733be192 100644
--- a/src/BaGet.Tools.AzureSearchImporter/Initializer.cs
+++ b/src/BaGet.Tools.AzureSearchImporter/Initializer.cs
@@ -1,99 +1,99 @@
-using System;
-using System.Linq;
-using System.Threading.Tasks;
-using BaGet.Azure.Search;
-using BaGet.Core.Entities;
-using BaGet.Tools.AzureSearchImporter.Entities;
-using Microsoft.Azure.Search;
-using Microsoft.Azure.Search.Models;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging;
-using MoreLinq;
-
-namespace BaGet.Tools.AzureSearchImporter
-{
- public class Initializer
- {
- public const int InitializationBatchSize = 100;
-
- private readonly IContext _bagetContext;
- private readonly IndexerContext _indexerContext;
- private readonly SearchServiceClient _searchClient;
- private readonly ILogger _logger;
-
- public Initializer(
- IContext bagetContext,
- IndexerContext indexerContext,
- SearchServiceClient searchClient,
- ILogger logger)
- {
- _bagetContext = bagetContext ?? throw new ArgumentNullException(nameof(bagetContext));
- _indexerContext = indexerContext ?? throw new ArgumentNullException(nameof(indexerContext));
- _searchClient = searchClient ?? throw new ArgumentNullException(nameof(searchClient));
- _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- }
-
- public Task InitializeAsync()
- => Task.WhenAll(
- InitializeIndex(),
- InitializeStateAsync());
-
- private async Task InitializeIndex()
- {
- if (await _searchClient.Indexes.ExistsAsync(PackageDocument.IndexName))
- {
- _logger.LogInformation("Search index already exists");
- return;
- }
-
- _logger.LogInformation("Search index does not exist, creating...");
-
- await _searchClient.Indexes.CreateAsync(new Index
- {
- Name = PackageDocument.IndexName,
- Fields = FieldBuilder.BuildForType()
- });
-
- _logger.LogInformation("Created search index");
- }
-
- private async Task InitializeStateAsync()
- {
- if (await _indexerContext.PackageIds.AnyAsync())
- {
- _logger.LogInformation("Indexer state is already initialized");
- return;
- }
-
- _logger.LogInformation("Unitialized state. Finding packages to track in indexer state...");
-
- var packageIds = await _bagetContext.Packages
- .Select(p => p.Id)
- .Distinct()
- .ToListAsync();
-
- _logger.LogInformation("Found {PackageIdCount} package ids to track in indexer state", packageIds.Count);
-
- var batchCount = 1;
-
- foreach (var batch in packageIds.Batch(InitializationBatchSize))
- {
- foreach (var packageId in batch)
- {
- _indexerContext.PackageIds.Add(new PackageId
- {
- Value = packageId,
- Done = false,
- });
- }
-
- _logger.LogInformation("Saving package id batch {BatchCount} to indexer state...", batchCount);
-
- await _indexerContext.SaveChangesAsync();
- batchCount++;
- }
-
- _logger.LogInformation("Finished adding {PackageIdCount} package ids to indexer state");
- }
- }
-}
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using BaGet.Azure.Search;
+using BaGet.Core.Entities;
+using BaGet.Tools.AzureSearchImporter.Entities;
+using Microsoft.Azure.Search;
+using Microsoft.Azure.Search.Models;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+using MoreLinq;
+
+namespace BaGet.Tools.AzureSearchImporter
+{
+ public class Initializer
+ {
+ public const int InitializationBatchSize = 100;
+
+ private readonly IContext _bagetContext;
+ private readonly IndexerContext _indexerContext;
+ private readonly SearchServiceClient _searchClient;
+ private readonly ILogger _logger;
+
+ public Initializer(
+ IContext bagetContext,
+ IndexerContext indexerContext,
+ SearchServiceClient searchClient,
+ ILogger logger)
+ {
+ _bagetContext = bagetContext ?? throw new ArgumentNullException(nameof(bagetContext));
+ _indexerContext = indexerContext ?? throw new ArgumentNullException(nameof(indexerContext));
+ _searchClient = searchClient ?? throw new ArgumentNullException(nameof(searchClient));
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ }
+
+ public Task InitializeAsync()
+ => Task.WhenAll(
+ InitializeIndex(),
+ InitializeStateAsync());
+
+ private async Task InitializeIndex()
+ {
+ if (await _searchClient.Indexes.ExistsAsync(PackageDocument.IndexName))
+ {
+ _logger.LogInformation("Search index already exists");
+ return;
+ }
+
+ _logger.LogInformation("Search index does not exist, creating...");
+
+ await _searchClient.Indexes.CreateAsync(new Index
+ {
+ Name = PackageDocument.IndexName,
+ Fields = FieldBuilder.BuildForType()
+ });
+
+ _logger.LogInformation("Created search index");
+ }
+
+ private async Task InitializeStateAsync()
+ {
+ if (await _indexerContext.PackageIds.AnyAsync())
+ {
+ _logger.LogInformation("Indexer state is already initialized");
+ return;
+ }
+
+ _logger.LogInformation("Unitialized state. Finding packages to track in indexer state...");
+
+ var packageIds = await _bagetContext.Packages
+ .Select(p => p.Id)
+ .Distinct()
+ .ToListAsync();
+
+ _logger.LogInformation("Found {PackageIdCount} package ids to track in indexer state", packageIds.Count);
+
+ var batchCount = 1;
+
+ foreach (var batch in packageIds.Batch(InitializationBatchSize))
+ {
+ foreach (var packageId in batch)
+ {
+ _indexerContext.PackageIds.Add(new PackageId
+ {
+ Value = packageId,
+ Done = false,
+ });
+ }
+
+ _logger.LogInformation("Saving package id batch {BatchCount} to indexer state...", batchCount);
+
+ await _indexerContext.SaveChangesAsync();
+ batchCount++;
+ }
+
+ _logger.LogInformation("Finished adding {PackageIdCount} package ids to indexer state");
+ }
+ }
+}
diff --git a/src/BaGet.Tools.AzureSearchImporter/Migrations/20180415185938_Initial.Designer.cs b/src/BaGet.Tools.AzureSearchImporter/Migrations/20180415185938_Initial.Designer.cs
index 4d31be92..03e58faf 100644
--- a/src/BaGet.Tools.AzureSearchImporter/Migrations/20180415185938_Initial.Designer.cs
+++ b/src/BaGet.Tools.AzureSearchImporter/Migrations/20180415185938_Initial.Designer.cs
@@ -1,44 +1,44 @@
-//
-using BaGet.Tools.AzureSearchImporter.Entities;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage;
-using System;
-
-namespace BaGet.Tools.AzureSearchImporter.Migrations
-{
- [DbContext(typeof(IndexerContext))]
- [Migration("20180415185938_Initial")]
- partial class Initial
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "2.0.1-rtm-125");
-
- modelBuilder.Entity("BaGet.Tools.AzureSearchImporter.Entities.PackageId", b =>
- {
- b.Property("Key")
- .ValueGeneratedOnAdd();
-
- b.Property("Done");
-
- b.Property("Value")
- .HasColumnType("TEXT COLLATE NOCASE");
-
- b.HasKey("Key");
-
- b.HasIndex("Done");
-
- b.HasIndex("Value")
- .IsUnique();
-
- b.ToTable("PackageIds");
- });
-#pragma warning restore 612, 618
- }
- }
-}
+//
+using BaGet.Tools.AzureSearchImporter.Entities;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage;
+using System;
+
+namespace BaGet.Tools.AzureSearchImporter.Migrations
+{
+ [DbContext(typeof(IndexerContext))]
+ [Migration("20180415185938_Initial")]
+ partial class Initial
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "2.0.1-rtm-125");
+
+ modelBuilder.Entity("BaGet.Tools.AzureSearchImporter.Entities.PackageId", b =>
+ {
+ b.Property("Key")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Done");
+
+ b.Property("Value")
+ .HasColumnType("TEXT COLLATE NOCASE");
+
+ b.HasKey("Key");
+
+ b.HasIndex("Done");
+
+ b.HasIndex("Value")
+ .IsUnique();
+
+ b.ToTable("PackageIds");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/src/BaGet.Tools.AzureSearchImporter/Migrations/20180415185938_Initial.cs b/src/BaGet.Tools.AzureSearchImporter/Migrations/20180415185938_Initial.cs
index 647952f9..ea6e5d3f 100644
--- a/src/BaGet.Tools.AzureSearchImporter/Migrations/20180415185938_Initial.cs
+++ b/src/BaGet.Tools.AzureSearchImporter/Migrations/20180415185938_Initial.cs
@@ -1,43 +1,43 @@
-using Microsoft.EntityFrameworkCore.Migrations;
-using System;
-using System.Collections.Generic;
-
-namespace BaGet.Tools.AzureSearchImporter.Migrations
-{
- public partial class Initial : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.CreateTable(
- name: "PackageIds",
- columns: table => new
- {
- Key = table.Column(nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- Done = table.Column(nullable: false),
- Value = table.Column(type: "TEXT COLLATE NOCASE", nullable: true)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_PackageIds", x => x.Key);
- });
-
- migrationBuilder.CreateIndex(
- name: "IX_PackageIds_Done",
- table: "PackageIds",
- column: "Done");
-
- migrationBuilder.CreateIndex(
- name: "IX_PackageIds_Value",
- table: "PackageIds",
- column: "Value",
- unique: true);
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropTable(
- name: "PackageIds");
- }
- }
-}
+using Microsoft.EntityFrameworkCore.Migrations;
+using System;
+using System.Collections.Generic;
+
+namespace BaGet.Tools.AzureSearchImporter.Migrations
+{
+ public partial class Initial : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateTable(
+ name: "PackageIds",
+ columns: table => new
+ {
+ Key = table.Column(nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ Done = table.Column(nullable: false),
+ Value = table.Column(type: "TEXT COLLATE NOCASE", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_PackageIds", x => x.Key);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_PackageIds_Done",
+ table: "PackageIds",
+ column: "Done");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_PackageIds_Value",
+ table: "PackageIds",
+ column: "Value",
+ unique: true);
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "PackageIds");
+ }
+ }
+}
diff --git a/src/BaGet.Tools.AzureSearchImporter/Migrations/IndexerContextModelSnapshot.cs b/src/BaGet.Tools.AzureSearchImporter/Migrations/IndexerContextModelSnapshot.cs
index 9bf7838b..278d0e5a 100644
--- a/src/BaGet.Tools.AzureSearchImporter/Migrations/IndexerContextModelSnapshot.cs
+++ b/src/BaGet.Tools.AzureSearchImporter/Migrations/IndexerContextModelSnapshot.cs
@@ -1,43 +1,43 @@
-//
-using BaGet.Tools.AzureSearchImporter.Entities;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage;
-using System;
-
-namespace BaGet.Tools.AzureSearchImporter.Migrations
-{
- [DbContext(typeof(IndexerContext))]
- partial class IndexerContextModelSnapshot : ModelSnapshot
- {
- protected override void BuildModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "2.0.1-rtm-125");
-
- modelBuilder.Entity("BaGet.Tools.AzureSearchImporter.Entities.PackageId", b =>
- {
- b.Property("Key")
- .ValueGeneratedOnAdd();
-
- b.Property("Done");
-
- b.Property("Value")
- .HasColumnType("TEXT COLLATE NOCASE");
-
- b.HasKey("Key");
-
- b.HasIndex("Done");
-
- b.HasIndex("Value")
- .IsUnique();
-
- b.ToTable("PackageIds");
- });
-#pragma warning restore 612, 618
- }
- }
-}
+//
+using BaGet.Tools.AzureSearchImporter.Entities;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage;
+using System;
+
+namespace BaGet.Tools.AzureSearchImporter.Migrations
+{
+ [DbContext(typeof(IndexerContext))]
+ partial class IndexerContextModelSnapshot : ModelSnapshot
+ {
+ protected override void BuildModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "2.0.1-rtm-125");
+
+ modelBuilder.Entity("BaGet.Tools.AzureSearchImporter.Entities.PackageId", b =>
+ {
+ b.Property("Key")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Done");
+
+ b.Property("Value")
+ .HasColumnType("TEXT COLLATE NOCASE");
+
+ b.HasKey("Key");
+
+ b.HasIndex("Done");
+
+ b.HasIndex("Value")
+ .IsUnique();
+
+ b.ToTable("PackageIds");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/src/BaGet.Tools.AzureSearchImporter/Program.cs b/src/BaGet.Tools.AzureSearchImporter/Program.cs
index d864a378..7f93280f 100644
--- a/src/BaGet.Tools.AzureSearchImporter/Program.cs
+++ b/src/BaGet.Tools.AzureSearchImporter/Program.cs
@@ -1,85 +1,85 @@
-using System;
-using System.Threading.Tasks;
-using BaGet.Azure.Extensions;
-using BaGet.Core.Configuration;
-using BaGet.Core.Services;
-using BaGet.Extensions;
-using BaGet.Tools.AzureSearchImporter.Entities;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-
-namespace BaGet.Tools.AzureSearchImporter
-{
- class Program
- {
- public static void Main(string[] args)
- => MainAsync(args)
- .GetAwaiter()
- .GetResult();
-
- private async static Task MainAsync(string[] args)
- {
- // Parse the skip from arguments.
- int skip = 0;
-
- if (args.Length > 0)
- {
- int.TryParse(args[args.Length - 1], out skip);
- }
-
- // Prepare the job.
- var provider = GetServiceProvider(GetConfiguration());
- var scopeFactory = provider.GetRequiredService();
- var initializer = provider.GetRequiredService();
- var importer = provider.GetRequiredService();
-
- using (var scope = scopeFactory.CreateScope())
- {
- scope.ServiceProvider
- .GetRequiredService()
- .Database
- .Migrate();
- }
-
- // Initialize the state and start importing packages to the search index.
- await initializer.InitializeAsync();
- await importer.ImportAsync(skip);
- }
-
- private static IConfiguration GetConfiguration()
- => new ConfigurationBuilder()
- .SetBasePath(Environment.CurrentDirectory)
- .AddJsonFile("appsettings.json")
- .Build();
-
- private static IServiceProvider GetServiceProvider(IConfiguration configuration)
- {
- var services = new ServiceCollection();
-
- services.Configure(configuration);
- services.ConfigureAzure(configuration);
-
- services.AddLogging(logging =>
- {
- logging.AddFilter(DbLoggerCategory.Database.Command.Name, LogLevel.Warning);
- logging.AddConsole();
- });
-
- services.AddBaGetContext();
- services.AddDbContext((provider, options) =>
- {
- options.UseSqlite(IndexerContextFactory.ConnectionString);
- });
-
- services.AddTransient();
- services.AddAzureSearch();
-
- services.AddTransient();
- services.AddTransient();
-
- return services.BuildServiceProvider();
- }
- }
-}
+using System;
+using System.Threading.Tasks;
+using BaGet.Azure.Extensions;
+using BaGet.Core.Configuration;
+using BaGet.Core.Services;
+using BaGet.Extensions;
+using BaGet.Tools.AzureSearchImporter.Entities;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+namespace BaGet.Tools.AzureSearchImporter
+{
+ class Program
+ {
+ public static void Main(string[] args)
+ => MainAsync(args)
+ .GetAwaiter()
+ .GetResult();
+
+ private async static Task MainAsync(string[] args)
+ {
+ // Parse the skip from arguments.
+ int skip = 0;
+
+ if (args.Length > 0)
+ {
+ int.TryParse(args[args.Length - 1], out skip);
+ }
+
+ // Prepare the job.
+ var provider = GetServiceProvider(GetConfiguration());
+ var scopeFactory = provider.GetRequiredService();
+ var initializer = provider.GetRequiredService();
+ var importer = provider.GetRequiredService();
+
+ using (var scope = scopeFactory.CreateScope())
+ {
+ scope.ServiceProvider
+ .GetRequiredService()
+ .Database
+ .Migrate();
+ }
+
+ // Initialize the state and start importing packages to the search index.
+ await initializer.InitializeAsync();
+ await importer.ImportAsync(skip);
+ }
+
+ private static IConfiguration GetConfiguration()
+ => new ConfigurationBuilder()
+ .SetBasePath(Environment.CurrentDirectory)
+ .AddJsonFile("appsettings.json")
+ .Build();
+
+ private static IServiceProvider GetServiceProvider(IConfiguration configuration)
+ {
+ var services = new ServiceCollection();
+
+ services.Configure(configuration);
+ services.ConfigureAzure(configuration);
+
+ services.AddLogging(logging =>
+ {
+ logging.AddFilter(DbLoggerCategory.Database.Command.Name, LogLevel.Warning);
+ logging.AddConsole();
+ });
+
+ services.AddBaGetContext();
+ services.AddDbContext((provider, options) =>
+ {
+ options.UseSqlite(IndexerContextFactory.ConnectionString);
+ });
+
+ services.AddTransient();
+ services.AddAzureSearch();
+
+ services.AddTransient();
+ services.AddTransient();
+
+ return services.BuildServiceProvider();
+ }
+ }
+}
diff --git a/src/BaGet/BaGet.csproj b/src/BaGet/BaGet.csproj
index 74b76288..be959660 100644
--- a/src/BaGet/BaGet.csproj
+++ b/src/BaGet/BaGet.csproj
@@ -1,54 +1,54 @@
-
-
-
-