From 4b6fa1ea948e859de1bd0dbb4929593c6450211a Mon Sep 17 00:00:00 2001 From: Kannan Chithambaranathan Date: Mon, 22 Aug 2022 17:13:40 +0530 Subject: [PATCH 1/7] Project Updated 1) Project Migrated to .Net 6.0 2) The Language Version is Updated. 3) Global Using Included. 4) File Scope namespace changed. 5) All Pacakges are Upto date. Pending Work: Need to migrate the Azure deprecated Packages. --- .editorconfig | 52 +- BaGet.sln | 6 +- BaGet.sln.DotSettings | 2 + .../BaGet.Protocol.Samples.Tests.csproj | 10 +- .../.config/dotnet-tools.json | 12 + .../BaGetWebApplication.csproj | 2 +- samples/BaGetWebApplication/GlobalUsings.cs | 6 + samples/BaGetWebApplication/Program.cs | 43 +- samples/BaGetWebApplication/Startup.cs | 40 - .../AliyunApplicationExtensions.cs | 56 +- src/BaGet.Aliyun/AliyunStorageOptions.cs | 27 +- src/BaGet.Aliyun/AliyunStorageService.cs | 129 ++- src/BaGet.Aliyun/BaGet.Aliyun.csproj | 7 +- src/BaGet.Aws/AwsApplicationExtensions.cs | 141 ++- src/BaGet.Aws/BaGet.Aws.csproj | 12 +- src/BaGet.Aws/S3StorageOptions.cs | 31 +- src/BaGet.Aws/S3StorageService.cs | 143 ++- src/BaGet.Azure/AzureApplicationExtensions.cs | 262 +++--- src/BaGet.Azure/BaGet.Azure.csproj | 12 +- .../BaGet.Azure.csproj.DotSettings | 6 + .../Configuration/AzureBlobStorageOptions.cs | 90 +- .../Configuration/AzureSearchOptions.cs | 15 +- .../Configuration/AzureTableOptions.cs | 11 +- .../Extensions/StorageExceptionExtensions.cs | 38 +- src/BaGet.Azure/GlobalUsings.cs | 27 + .../Search/AzureSearchBatchIndexer.cs | 122 ++- src/BaGet.Azure/Search/AzureSearchIndexer.cs | 50 +- src/BaGet.Azure/Search/AzureSearchService.cs | 348 ++++--- .../Search/ExactMatchCustomAnalyzer.cs | 24 +- src/BaGet.Azure/Search/IndexActionBuilder.cs | 182 ++-- src/BaGet.Azure/Search/PackageDocument.cs | 148 ++- src/BaGet.Azure/Search/SearchFilters.cs | 49 +- src/BaGet.Azure/Storage/BlobStorageService.cs | 123 ++- src/BaGet.Azure/Table/PackageEntity.cs | 171 ++-- .../Table/PackageEntityExtensions.cs | 141 ++- .../Table/TableOperationBuilder.cs | 213 +++-- src/BaGet.Azure/Table/TablePackageDatabase.cs | 306 +++---- src/BaGet.Azure/Table/TableSearchService.cs | 300 +++---- .../ApiKeyAuthenticationService.cs | 38 +- .../Authentication/IAuthenticationService.cs | 12 +- src/BaGet.Core/BaGet.Core.csproj | 16 +- src/BaGet.Core/BaGetApplication.cs | 18 +- src/BaGet.Core/Configuration/BaGetOptions.cs | 101 +-- src/BaGet.Core/Configuration/BindOptions.cs | 34 +- .../Configuration/DatabaseOptions.cs | 15 +- .../Configuration/FileSystemStorageOptions.cs | 38 +- src/BaGet.Core/Configuration/MirrorOptions.cs | 59 +- .../Configuration/PackageDeletionBehavior.cs | 35 +- src/BaGet.Core/Configuration/SearchOptions.cs | 11 +- .../Configuration/StorageOptions.cs | 11 +- .../Content/DefaultPackageContentService.cs | 132 ++- .../Content/IPackageContentService.cs | 146 ++- src/BaGet.Core/Entities/AbstractContext.cs | 305 ++++--- .../Converters/StringArrayComparer.cs | 25 +- .../Converters/StringArrayToJsonConverter.cs | 21 +- .../Converters/UriToStringConverter.cs | 22 +- src/BaGet.Core/Entities/IContext.cs | 51 +- src/BaGet.Core/Entities/NullContext.cs | 40 +- src/BaGet.Core/Entities/Package.cs | 115 ++- src/BaGet.Core/Entities/PackageDependency.cs | 33 +- src/BaGet.Core/Entities/PackageType.cs | 19 +- src/BaGet.Core/Entities/SemVerLevel.cs | 25 +- src/BaGet.Core/Entities/TargetFramework.cs | 15 +- .../Extensions/BaGetApplicationExtensions.cs | 54 +- ...DependencyInjectionExtensions.Providers.cs | 217 +++-- .../DependencyInjectionExtensions.cs | 345 ++++--- src/BaGet.Core/Extensions/IProvider.cs | 70 +- .../PackageArchiveReaderExtensions.cs | 331 ++++--- src/BaGet.Core/Extensions/StreamExtensions.cs | 88 +- src/BaGet.Core/Extensions/SystemTime.cs | 17 +- src/BaGet.Core/IPackageDatabase.cs | 192 ++-- src/BaGet.Core/IPackageService.cs | 118 ++- src/BaGet.Core/IUrlGenerator.cs | 173 ++-- .../Indexing/FrameworkCompatibilityService.cs | 122 ++- .../IFrameworkCompatibilityService.cs | 23 +- .../Indexing/IPackageDeletionService.cs | 27 +- .../Indexing/IPackageIndexingService.cs | 61 +- .../Indexing/ISymbolIndexingService.cs | 61 +- .../Indexing/PackageDeletionService.cs | 128 ++- .../Indexing/PackageIndexingService.cs | 261 +++--- .../Indexing/SymbolIndexingService.cs | 295 +++--- .../Metadata/BaGetPackageMetadata.cs | 48 +- .../Metadata/BaGetRegistrationIndexPage.cs | 60 +- .../BaGetRegistrationIndexPageItem.cs | 53 +- .../BaGetRegistrationIndexResponse.cs | 72 +- .../Metadata/DefaultPackageMetadataService.cs | 77 +- .../Metadata/IPackageMetadataService.cs | 55 +- .../Metadata/PackageRegistration.cs | 50 +- .../Metadata/RegistrationBuilder.cs | 192 ++-- src/BaGet.Core/PackageDatabase.cs | 226 +++-- src/BaGet.Core/PackageService.cs | 222 +++-- src/BaGet.Core/Search/AutocompleteRequest.cs | 61 +- .../Search/DatabaseSearchService.cs | 328 ++++--- src/BaGet.Core/Search/DependentsResponse.cs | 72 +- src/BaGet.Core/Search/ISearchIndexer.cs | 24 +- .../Search/ISearchResponseBuilder.cs | 18 +- src/BaGet.Core/Search/ISearchService.cs | 83 +- src/BaGet.Core/Search/NullSearchIndexer.cs | 20 +- src/BaGet.Core/Search/NullSearchService.cs | 102 +-- src/BaGet.Core/Search/SearchRequest.cs | 69 +- .../Search/SearchResponseBuilder.cs | 130 ++- src/BaGet.Core/Search/VersionsRequest.cs | 37 +- .../ServiceIndex/BaGetServiceIndex.cs | 71 +- .../ServiceIndex/IServiceIndexService.cs | 29 +- src/BaGet.Core/Storage/FileStorageService.cs | 169 ++-- .../Storage/IPackageStorageService.cs | 120 ++- src/BaGet.Core/Storage/IStorageService.cs | 138 ++- .../Storage/ISymbolStorageService.cs | 55 +- src/BaGet.Core/Storage/NullStorageService.cs | 54 +- .../Storage/PackageStorageService.cs | 371 ++++---- .../Storage/SymbolStorageService.cs | 113 ++- .../Clients/DisabledUpstreamClient.cs | 53 +- .../Upstream/Clients/V2UpstreamClient.cs | 315 ++++--- .../Upstream/Clients/V3UpstreamClient.cs | 277 +++--- src/BaGet.Core/Upstream/DownloadsImporter.cs | 92 +- .../Upstream/IPackageDownloadsSource.cs | 12 +- src/BaGet.Core/Upstream/IUpstreamClient.cs | 84 +- .../Upstream/PackageDownloadsJsonSource.cs | 139 ++- .../Validation/RequiredIfAttribute.cs | 236 +++-- .../Validation/ValidateBaGetOptions.cs | 70 +- .../Validation/ValidateStartupOptions.cs | 84 +- .../BaGet.Database.MySql.csproj | 7 +- .../Migrations/20181212113156_Initial.cs | 1 - .../20190303071640_AddSearchDimensions.cs | 1 - ...14215810_AddOriginalVersionStringColumn.cs | 3 +- ...00109085155_AddReleaseNotesStringColumn.cs | 3 +- ...20200210004047_AddHasEmbeddedIconColumn.cs | 3 +- ...10919191554_RemoveReleaseNotesMaxLength.cs | 3 +- .../MySqlApplicationExtensions.cs | 36 +- src/BaGet.Database.MySql/MySqlContext.cs | 43 +- .../BaGet.Database.PostgreSql.csproj | 6 +- .../Migrations/20190311172227_Initial.cs | 3 +- .../PostgreSqlApplicationExtensions.cs | 38 +- .../PostgreSqlContext.cs | 103 ++- .../BaGet.Database.SqlServer.csproj | 6 +- .../Migrations/20180804082816_Initial.cs | 1 - .../SqlServerApplicationExtensions.cs | 38 +- .../SqlServerContext.cs | 50 +- .../BaGet.Database.Sqlite.csproj | 6 +- .../Migrations/20180804082808_Initial.cs | 1 - .../SqliteApplicationExtensions.cs | 38 +- src/BaGet.Database.Sqlite/SqliteContext.cs | 83 +- src/BaGet.Gcp/BaGet.Gcp.csproj | 6 +- .../GoogleCloudApplicationExtensions.cs | 46 +- src/BaGet.Gcp/GoogleCloudStorageOptions.cs | 13 +- src/BaGet.Gcp/GoogleCloudStorageService.cs | 136 ++- src/BaGet.Protocol/BaGet.Protocol.csproj | 12 +- src/BaGet.Protocol/Catalog/CatalogClient.cs | 62 +- .../Catalog/CatalogProcessor.cs | 320 ++++--- .../Catalog/CatalogProcessorOptions.cs | 55 +- src/BaGet.Protocol/Catalog/FileCursor.cs | 83 +- src/BaGet.Protocol/Catalog/ICatalogClient.cs | 93 +- .../Catalog/ICatalogLeafProcessor.cs | 61 +- src/BaGet.Protocol/Catalog/ICursor.cs | 45 +- .../Catalog/NullCatalogClient.cs | 49 +- src/BaGet.Protocol/Catalog/NullCursor.cs | 33 +- .../Catalog/RawCatalogClient.cs | 96 +- .../PackageDependencyRangeJsonConverter.cs | 78 +- .../StringOrStringArrayJsonConverter.cs | 79 +- .../Extensions/CatalogModelExtensions.cs | 384 ++++---- .../Extensions/HttpClientExtensions.cs | 108 ++- .../NuGetClientFactoryExtensions.cs | 55 +- .../PackageContentModelExtensions.cs | 33 +- .../PackageMetadataModelExtensions.cs | 91 +- .../Extensions/SearchModelExtensions.cs | 43 +- .../Extensions/ServiceIndexModelExtensions.cs | 129 ++- src/BaGet.Protocol/Models/AlternatePackage.cs | 47 +- .../Models/AutocompleteContext.cs | 19 +- .../Models/AutocompleteResponse.cs | 42 +- src/BaGet.Protocol/Models/CatalogIndex.cs | 52 +- src/BaGet.Protocol/Models/CatalogLeaf.cs | 102 +-- src/BaGet.Protocol/Models/CatalogLeafItem.cs | 69 +- src/BaGet.Protocol/Models/CatalogPage.cs | 62 +- src/BaGet.Protocol/Models/CatalogPageItem.cs | 49 +- .../Models/DependencyGroupItem.cs | 36 +- src/BaGet.Protocol/Models/DependencyItem.cs | 37 +- src/BaGet.Protocol/Models/ICatalogLeafItem.cs | 44 +- .../Models/PackageDeleteCatalogLeaf.cs | 24 +- .../Models/PackageDeprecation.cs | 58 +- .../Models/PackageDetailsCatalogLeaf.cs | 330 ++++--- src/BaGet.Protocol/Models/PackageMetadata.cs | 247 +++-- .../Models/PackageNotFoundException.cs | 48 +- .../Models/PackageVersionsResponse.cs | 26 +- .../Models/ProtocolException.cs | 83 +- .../Models/RegistrationIndexPage.cs | 72 +- .../Models/RegistrationIndexPageItem.cs | 45 +- .../Models/RegistrationIndexResponse.cs | 72 +- .../Models/RegistrationLeafResponse.cs | 101 +-- .../Models/RegistrationPageResponse.cs | 19 +- src/BaGet.Protocol/Models/SearchContext.cs | 29 +- src/BaGet.Protocol/Models/SearchResponse.cs | 42 +- src/BaGet.Protocol/Models/SearchResult.cs | 160 ++-- .../Models/SearchResultPackageType.cs | 25 +- .../Models/SearchResultVersion.cs | 45 +- src/BaGet.Protocol/Models/ServiceIndexItem.cs | 45 +- .../Models/ServiceIndexResponse.cs | 36 +- src/BaGet.Protocol/NuGetClient.cs | 848 +++++++++--------- src/BaGet.Protocol/NuGetClientFactory.cs | 349 ++++--- .../PackageContent/IPackageContentClient.cs | 94 +- .../PackageContent/PackageContentClient.cs | 81 +- .../PackageContent/RawPackageContentClient.cs | 124 ++- .../PackageMetadata/IPackageMetadataClient.cs | 71 +- .../PackageMetadata/PackageMetadataClient.cs | 76 +- .../RawPackageMetadataClient.cs | 81 +- .../Search/AutocompleteClient.cs | 70 +- .../Search/IAutocompleteClient.cs | 81 +- src/BaGet.Protocol/Search/ISearchClient.cs | 53 +- .../Search/NullAutocompleteClient.cs | 36 +- .../Search/RawAutocompleteClient.cs | 109 ++- src/BaGet.Protocol/Search/RawSearchClient.cs | 136 ++- src/BaGet.Protocol/Search/SearchClient.cs | 48 +- .../ServiceIndex/IServiceIndexClient.cs | 29 +- .../ServiceIndex/RawServiceIndexClient.cs | 55 +- .../ServiceIndex/ServiceIndexClient.cs | 30 +- src/BaGet.Web/BaGet.Web.csproj | 10 +- src/BaGet.Web/BaGet.Web.csproj.DotSettings | 4 + src/BaGet.Web/BaGetEndpointBuilder.cs | 244 +++-- src/BaGet.Web/BaGetUrlGenerator.cs | 315 ++++--- .../Controllers/PackageContentController.cs | 140 ++- .../Controllers/PackageMetadataController.cs | 74 +- .../Controllers/PackagePublishController.cs | 207 +++-- src/BaGet.Web/Controllers/SearchController.cs | 141 ++- .../Controllers/ServiceIndexController.cs | 39 +- src/BaGet.Web/Controllers/SymbolController.cs | 124 ++- .../Extensions/HttpRequestExtensions.cs | 55 +- .../IApplicationBuilderExtensions.cs | 16 +- src/BaGet.Web/Extensions/IHostExtensions.cs | 61 +- .../IServiceCollectionExtensions.cs | 35 +- src/BaGet.Web/Extensions/RazorExtensions.cs | 13 +- src/BaGet.Web/GlobalUsings.cs | 21 + src/BaGet.Web/NavLinkTagHelper.cs | 52 +- src/BaGet.Web/OperationCancelledMiddleware.cs | 75 +- src/BaGet.Web/Pages/Index.cshtml | 1 - src/BaGet.Web/Pages/Index.cshtml.cs | 95 +- src/BaGet.Web/Pages/Package.cshtml | 2 + src/BaGet.Web/Pages/Package.cshtml.cs | 378 ++++---- src/BaGet.Web/Pages/_ViewImports.cshtml | 4 - src/BaGet.Web/Routes.cs | 43 +- src/BaGet/BaGet.csproj | 14 +- src/BaGet/ConfigureBaGetOptions.cs | 189 ++-- src/BaGet/ConfigureRazorRuntimeCompilation.cs | 40 +- src/BaGet/Program.cs | 113 ++- src/BaGet/Startup.cs | 162 ++-- src/Directory.Build.props | 14 +- .../BaGet.Core.Tests/BaGet.Core.Tests.csproj | 18 +- .../BaGet.Protocol.Tests.csproj | 18 +- .../RawCatalogClientTests.cs | 1 - .../RawPackageMetadataClientTests.cs | 1 - .../Support/ProtocolFixture.cs | 1 - tests/BaGet.Tests/ApiIntegrationTests.cs | 405 +++++---- tests/BaGet.Tests/BaGet.Tests.csproj | 28 +- .../BaGetClientIntegrationTests.cs | 339 ++++--- tests/BaGet.Tests/HostIntegrationTests.cs | 101 ++- tests/BaGet.Tests/MirrorIntegrationTests.cs | 149 ++- .../NuGetClientIntegrationTests.cs | 495 +++++----- tests/BaGet.Tests/Support/BaGetApplication.cs | 213 +++-- .../HttpSourceResourceProviderTestHost.cs | 59 +- tests/BaGet.Tests/Support/StreamExtensions.cs | 29 +- tests/BaGet.Tests/Support/TestResources.cs | 71 +- .../BaGet.Tests/Support/TestableHttpSource.cs | 75 +- tests/BaGet.Tests/Support/XunitLogger.cs | 51 +- .../Support/XunitLoggerProvider.cs | 27 +- tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj | 20 +- 263 files changed, 11393 insertions(+), 12167 deletions(-) create mode 100644 BaGet.sln.DotSettings create mode 100644 samples/BaGetWebApplication/.config/dotnet-tools.json create mode 100644 samples/BaGetWebApplication/GlobalUsings.cs delete mode 100644 samples/BaGetWebApplication/Startup.cs create mode 100644 src/BaGet.Azure/BaGet.Azure.csproj.DotSettings create mode 100644 src/BaGet.Azure/GlobalUsings.cs create mode 100644 src/BaGet.Web/BaGet.Web.csproj.DotSettings create mode 100644 src/BaGet.Web/GlobalUsings.cs diff --git a/.editorconfig b/.editorconfig index df9dfe6f..84873213 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,6 +6,28 @@ indent_style = space charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_readonly_field = true:warning +dotnet_style_predefined_type_for_locals_parameters_members = true:warning +dotnet_style_predefined_type_for_member_access = true:warning [*.{htm,html,js,ts,tsx,css,sass,scss,less,svg,vue}] indent_size = 2 @@ -53,7 +75,7 @@ dotnet_naming_style.pascal_case_style.capitalization = pascal_case # Use PascalCase for constant fields dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields -dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style dotnet_naming_symbols.constant_fields.applicable_kinds = field dotnet_naming_symbols.constant_fields.applicable_accessibilities = * dotnet_naming_symbols.constant_fields.required_modifiers = const @@ -94,3 +116,31 @@ csharp_space_between_method_call_empty_parameter_list_parentheses = false # Wrapping preferences csharp_preserve_single_line_statements = true csharp_preserve_single_line_blocks = true +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_indent_labels = one_less_than_current +csharp_style_throw_expression = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent diff --git a/BaGet.sln b/BaGet.sln index af6601d2..ce69b704 100644 --- a/BaGet.sln +++ b/BaGet.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29104.9 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.32804.182 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BaGet", "src\BaGet\BaGet.csproj", "{284366CB-C68F-473E-908A-50A382616AE0}" EndProject @@ -50,7 +50,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BaGet.Aliyun", "src\BaGet.A EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BaGetWebApplication", "samples\BaGetWebApplication\BaGetWebApplication.csproj", "{E5AFE55D-0932-46A9-BFA3-C8A034037377}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaGet.Web.Tests", "tests\BaGet.Web.Tests\BaGet.Web.Tests.csproj", "{BEEAAA73-36CD-4178-9C9F-6D036586E7BF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BaGet.Web.Tests", "tests\BaGet.Web.Tests\BaGet.Web.Tests.csproj", "{BEEAAA73-36CD-4178-9C9F-6D036586E7BF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/BaGet.sln.DotSettings b/BaGet.sln.DotSettings new file mode 100644 index 00000000..d8c3bd8c --- /dev/null +++ b/BaGet.sln.DotSettings @@ -0,0 +1,2 @@ + + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> \ No newline at end of file diff --git a/samples/BaGet.Protocol.Samples.Tests/BaGet.Protocol.Samples.Tests.csproj b/samples/BaGet.Protocol.Samples.Tests/BaGet.Protocol.Samples.Tests.csproj index 51c3b796..6b390535 100644 --- a/samples/BaGet.Protocol.Samples.Tests/BaGet.Protocol.Samples.Tests.csproj +++ b/samples/BaGet.Protocol.Samples.Tests/BaGet.Protocol.Samples.Tests.csproj @@ -1,17 +1,17 @@ - netcoreapp3.1 + net6.0 false IDE0007 - - - - + + + + all runtime; build; native; contentfiles; analyzers diff --git a/samples/BaGetWebApplication/.config/dotnet-tools.json b/samples/BaGetWebApplication/.config/dotnet-tools.json new file mode 100644 index 00000000..98091c92 --- /dev/null +++ b/samples/BaGetWebApplication/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-ef": { + "version": "6.0.8", + "commands": [ + "dotnet-ef" + ] + } + } +} \ No newline at end of file diff --git a/samples/BaGetWebApplication/BaGetWebApplication.csproj b/samples/BaGetWebApplication/BaGetWebApplication.csproj index 3123a668..605ea314 100644 --- a/samples/BaGetWebApplication/BaGetWebApplication.csproj +++ b/samples/BaGetWebApplication/BaGetWebApplication.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net6.0 diff --git a/samples/BaGetWebApplication/GlobalUsings.cs b/samples/BaGetWebApplication/GlobalUsings.cs new file mode 100644 index 00000000..3a050c72 --- /dev/null +++ b/samples/BaGetWebApplication/GlobalUsings.cs @@ -0,0 +1,6 @@ +// Global using directives + +global using BaGet; +global using BaGet.Web; +global using Microsoft.AspNetCore.Builder; +global using Microsoft.Extensions.Hosting; \ No newline at end of file diff --git a/samples/BaGetWebApplication/Program.cs b/samples/BaGetWebApplication/Program.cs index a91fee8b..e58c6bcb 100644 --- a/samples/BaGetWebApplication/Program.cs +++ b/samples/BaGetWebApplication/Program.cs @@ -1,25 +1,28 @@ -using System.Threading.Tasks; -using BaGet.Web; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; +var builder = WebApplication.CreateBuilder(args); -namespace BaGetWebApplication +builder.Services.AddBaGetWebApplication(app => { - public class Program - { - public static async Task Main(string[] args) - { - var host = CreateHostBuilder(args).Build(); + // Use SQLite as BaGet's database and store packages on the local file system. + app.AddSqliteDatabase(); + app.AddFileStorage(); +}); - await host.RunMigrationsAsync(); - await host.RunAsync(); - } +var app = builder.Build(); - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); - } +if (app.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); } + +app.UseStaticFiles(); +app.UseRouting(); + +app.UseEndpoints(endpoints => +{ + // Add BaGet's endpoints. + new BaGetEndpointBuilder().MapEndpoints(endpoints); +}); + +await app.RunMigrationsAsync(); + +await app.RunAsync(); diff --git a/samples/BaGetWebApplication/Startup.cs b/samples/BaGetWebApplication/Startup.cs deleted file mode 100644 index fe11415a..00000000 --- a/samples/BaGetWebApplication/Startup.cs +++ /dev/null @@ -1,40 +0,0 @@ -using BaGet; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace BaGetWebApplication -{ - public class Startup - { - public void ConfigureServices(IServiceCollection services) - { - services.AddBaGetWebApplication(app => - { - // Use SQLite as BaGet's database and store packages on the local file system. - app.AddSqliteDatabase(); - app.AddFileStorage(); - }); - } - - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseStaticFiles(); - app.UseRouting(); - - app.UseEndpoints(endpoints => - { - // Add BaGet's endpoints. - var baget = new BaGetEndpointBuilder(); - - baget.MapEndpoints(endpoints); - }); - } - } -} diff --git a/src/BaGet.Aliyun/AliyunApplicationExtensions.cs b/src/BaGet.Aliyun/AliyunApplicationExtensions.cs index eb8a6e74..e14289cb 100644 --- a/src/BaGet.Aliyun/AliyunApplicationExtensions.cs +++ b/src/BaGet.Aliyun/AliyunApplicationExtensions.cs @@ -1,4 +1,3 @@ -using System; using Aliyun.OSS; using BaGet.Aliyun; using BaGet.Core; @@ -6,41 +5,40 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; -namespace BaGet +namespace BaGet; + +public static class AliyunApplicationExtensions { - public static class AliyunApplicationExtensions + public static BaGetApplication AddAliyunOssStorage(this BaGetApplication app) { - public static BaGetApplication AddAliyunOssStorage(this BaGetApplication app) - { - app.Services.AddBaGetOptions(nameof(BaGetOptions.Storage)); + app.Services.AddBaGetOptions(nameof(BaGetOptions.Storage)); - app.Services.AddTransient(); - app.Services.TryAddTransient(provider => provider.GetRequiredService()); + app.Services.AddTransient(); + app.Services.TryAddTransient(provider => provider.GetRequiredService()); - app.Services.AddSingleton(provider => - { - var options = provider.GetRequiredService>().Value; + app.Services.AddSingleton(provider => + { + var options = provider.GetRequiredService>().Value; - return new OssClient(options.Endpoint, options.AccessKey, options.AccessKeySecret); - }); + return new OssClient(options.Endpoint, options.AccessKey, options.AccessKeySecret); + }); - app.Services.AddProvider((provider, config) => - { - if (!config.HasStorageType("AliyunOss")) return null; + app.Services.AddProvider((provider, config) => + { + if (!config.HasStorageType("AliyunOss")) return null; - return provider.GetRequiredService(); - }); + return provider.GetRequiredService(); + }); - return app; - } + return app; + } - public static BaGetApplication AddAliyunOssStorage( - this BaGetApplication app, - Action configure) - { - app.AddAliyunOssStorage(); - app.Services.Configure(configure); - return app; - } + public static BaGetApplication AddAliyunOssStorage( + this BaGetApplication app, + Action configure) + { + app.AddAliyunOssStorage(); + app.Services.Configure(configure); + return app; } -} +} \ No newline at end of file diff --git a/src/BaGet.Aliyun/AliyunStorageOptions.cs b/src/BaGet.Aliyun/AliyunStorageOptions.cs index 2357bdd6..9dc86a15 100644 --- a/src/BaGet.Aliyun/AliyunStorageOptions.cs +++ b/src/BaGet.Aliyun/AliyunStorageOptions.cs @@ -1,21 +1,20 @@ using System.ComponentModel.DataAnnotations; -namespace BaGet.Aliyun +namespace BaGet.Aliyun; + +public class AliyunStorageOptions { - public class AliyunStorageOptions - { - [Required] - public string AccessKey { get; set; } + [Required] + public string AccessKey { get; set; } - [Required] - public string AccessKeySecret { get; set; } + [Required] + public string AccessKeySecret { get; set; } - [Required] - public string Endpoint { get; set; } + [Required] + public string Endpoint { get; set; } - [Required] - public string Bucket { get; set; } + [Required] + public string Bucket { get; set; } - public string Prefix { get; set; } - } -} + public string Prefix { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Aliyun/AliyunStorageService.cs b/src/BaGet.Aliyun/AliyunStorageService.cs index 9ea3e635..3509fb27 100644 --- a/src/BaGet.Aliyun/AliyunStorageService.cs +++ b/src/BaGet.Aliyun/AliyunStorageService.cs @@ -1,93 +1,88 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; using Aliyun.OSS; using BaGet.Core; using Microsoft.Extensions.Options; -namespace BaGet.Aliyun +namespace BaGet.Aliyun; + +public class AliyunStorageService : IStorageService { - public class AliyunStorageService : IStorageService + private const string Separator = "/"; + private readonly string _bucket; + private readonly string _prefix; + private readonly OssClient _client; + + public AliyunStorageService(IOptionsSnapshot options, OssClient client) { - private const string Separator = "/"; - private readonly string _bucket; - private readonly string _prefix; - private readonly OssClient _client; + if (options == null) + throw new ArgumentNullException(nameof(options)); - public AliyunStorageService(IOptionsSnapshot options, OssClient client) - { - if (options == null) - throw new ArgumentNullException(nameof(options)); + _bucket = options.Value.Bucket; + _prefix = options.Value.Prefix; + _client = client ?? throw new ArgumentNullException(nameof(client)); - _bucket = options.Value.Bucket; - _prefix = options.Value.Prefix; - _client = client ?? throw new ArgumentNullException(nameof(client)); + if (!string.IsNullOrEmpty(_prefix) && !_prefix.EndsWith(Separator)) + _prefix += Separator; + } - if (!string.IsNullOrEmpty(_prefix) && !_prefix.EndsWith(Separator)) - _prefix += Separator; - } + private string PrepareKey(string path) + { + return _prefix + path.Replace("\\", Separator); + } - private string PrepareKey(string path) + public async Task GetAsync(string path, CancellationToken cancellationToken = default) + { + try { - return _prefix + path.Replace("\\", Separator); - } + var ossObject = await Task.Factory.FromAsync(_client.BeginGetObject, _client.EndGetObject, _bucket, PrepareKey(path), null); - public async Task GetAsync(string path, CancellationToken cancellationToken = default) + return ossObject.ResponseStream; + } + catch (Exception) { - try - { - var ossObject = await Task.Factory.FromAsync(_client.BeginGetObject, _client.EndGetObject, _bucket, PrepareKey(path), null); - - return ossObject.ResponseStream; - } - catch (Exception) - { - // TODO - throw; - } + // TODO + throw; } + } - public Task GetDownloadUriAsync(string path, CancellationToken cancellationToken = default) - { - var uri = _client.GeneratePresignedUri(_bucket, PrepareKey(path)); + public Task GetDownloadUriAsync(string path, CancellationToken cancellationToken = default) + { + var uri = _client.GeneratePresignedUri(_bucket, PrepareKey(path)); - return Task.FromResult(uri); - } + return Task.FromResult(uri); + } - public async Task PutAsync(string path, Stream content, string contentType, CancellationToken cancellationToken = default) - { - // TODO: Uploads should be idempotent. This should fail if and only if the blob - // already exists but has different content. + public async Task PutAsync(string path, Stream content, string contentType, CancellationToken cancellationToken = default) + { + // TODO: Uploads should be idempotent. This should fail if and only if the blob + // already exists but has different content. - var metadata = new ObjectMetadata - { - ContentType = contentType, - }; + var metadata = new ObjectMetadata + { + ContentType = contentType, + }; - var putResult = await Task.Factory.FromAsync(_client.BeginPutObject, _client.EndPutObject, _bucket, PrepareKey(path), content, metadata); + var putResult = await Task.Factory.FromAsync(_client.BeginPutObject, _client.EndPutObject, _bucket, PrepareKey(path), content, metadata); - switch (putResult.HttpStatusCode) - { - case System.Net.HttpStatusCode.OK: - return StoragePutResult.Success; + switch (putResult.HttpStatusCode) + { + case System.Net.HttpStatusCode.OK: + return StoragePutResult.Success; - // TODO: check sdk documents - //case System.Net.HttpStatusCode.Conflict: - // return StoragePutResult.Conflict; + // TODO: check sdk documents + //case System.Net.HttpStatusCode.Conflict: + // return StoragePutResult.Conflict; - //case System.Net.HttpStatusCode.Found: - // return StoragePutResult.AlreadyExists; + //case System.Net.HttpStatusCode.Found: + // return StoragePutResult.AlreadyExists; - default: - return StoragePutResult.Success; - } + default: + return StoragePutResult.Success; } + } - public Task DeleteAsync(string path, CancellationToken cancellationToken = default) - { - _client.DeleteObject(_bucket, PrepareKey(path)); - return Task.CompletedTask; - } + public Task DeleteAsync(string path, CancellationToken cancellationToken = default) + { + _client.DeleteObject(_bucket, PrepareKey(path)); + return Task.CompletedTask; } -} +} \ No newline at end of file diff --git a/src/BaGet.Aliyun/BaGet.Aliyun.csproj b/src/BaGet.Aliyun/BaGet.Aliyun.csproj index ad7896fc..b5ea641f 100644 --- a/src/BaGet.Aliyun/BaGet.Aliyun.csproj +++ b/src/BaGet.Aliyun/BaGet.Aliyun.csproj @@ -1,16 +1,17 @@ + NuGet;Alibaba;Cloud The libraries to host BaGet on Alibaba Cloud (Aliyun). - netstandard2.0 + net6.0 - + - + \ No newline at end of file diff --git a/src/BaGet.Aws/AwsApplicationExtensions.cs b/src/BaGet.Aws/AwsApplicationExtensions.cs index 6b07f868..1d3e4fce 100644 --- a/src/BaGet.Aws/AwsApplicationExtensions.cs +++ b/src/BaGet.Aws/AwsApplicationExtensions.cs @@ -1,5 +1,3 @@ -using System; -using System.Threading.Tasks; using Amazon; using Amazon.Runtime; using Amazon.S3; @@ -9,88 +7,87 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; -namespace BaGet +namespace BaGet; + +public static class AwsApplicationExtensions { - public static class AwsApplicationExtensions + public static BaGetApplication AddAwsS3Storage(this BaGetApplication app) { - public static BaGetApplication AddAwsS3Storage(this BaGetApplication app) - { - app.Services.AddBaGetOptions(nameof(BaGetOptions.Storage)); + app.Services.AddBaGetOptions(nameof(BaGetOptions.Storage)); - app.Services.AddTransient(); - app.Services.TryAddTransient(provider => provider.GetRequiredService()); + app.Services.AddTransient(); + app.Services.TryAddTransient(provider => provider.GetRequiredService()); - app.Services.AddProvider((provider, config) => - { - if (!config.HasStorageType("AwsS3")) return null; + app.Services.AddProvider((provider, config) => + { + if (!config.HasStorageType("AwsS3")) return null; + + return provider.GetRequiredService(); + }); - return provider.GetRequiredService(); - }); + app.Services.AddSingleton(provider => + { + var options = provider.GetRequiredService>().Value; - app.Services.AddSingleton(provider => + var config = new AmazonS3Config { - var options = provider.GetRequiredService>().Value; - - var config = new AmazonS3Config - { - RegionEndpoint = RegionEndpoint.GetBySystemName(options.Region) - }; - - if (options.UseInstanceProfile) - { - var credentials = FallbackCredentialsFactory.GetCredentials(); - return new AmazonS3Client(credentials, config); - } - - if (!string.IsNullOrEmpty(options.AssumeRoleArn)) - { - var credentials = FallbackCredentialsFactory.GetCredentials(); - var assumedCredentials = AssumeRoleAsync( - credentials, - options.AssumeRoleArn, - $"BaGet-Session-{Guid.NewGuid()}") - .GetAwaiter() - .GetResult(); - - return new AmazonS3Client(assumedCredentials, config); - } - - if (!string.IsNullOrEmpty(options.AccessKey)) - { - return new AmazonS3Client( - new BasicAWSCredentials( - options.AccessKey, - options.SecretKey), - config); - } - - return new AmazonS3Client(config); - }); - - return app; - } + RegionEndpoint = RegionEndpoint.GetBySystemName(options.Region) + }; - public static BaGetApplication AddAwsS3Storage(this BaGetApplication app, Action configure) - { - app.AddAwsS3Storage(); - app.Services.Configure(configure); - return app; - } + if (options.UseInstanceProfile) + { + var credentials = FallbackCredentialsFactory.GetCredentials(); + return new AmazonS3Client(credentials, config); + } - private static async Task AssumeRoleAsync( - AWSCredentials credentials, - string roleArn, - string roleSessionName) - { - var assumedCredentials = new AssumeRoleAWSCredentials(credentials, roleArn, roleSessionName); - var immutableCredentials = await credentials.GetCredentialsAsync(); + if (!string.IsNullOrEmpty(options.AssumeRoleArn)) + { + var credentials = FallbackCredentialsFactory.GetCredentials(); + var assumedCredentials = AssumeRoleAsync( + credentials, + options.AssumeRoleArn, + $"BaGet-Session-{Guid.NewGuid()}") + .GetAwaiter() + .GetResult(); + + return new AmazonS3Client(assumedCredentials, config); + } - if (string.IsNullOrWhiteSpace(immutableCredentials.Token)) + if (!string.IsNullOrEmpty(options.AccessKey)) { - throw new InvalidOperationException($"Unable to assume role {roleArn}"); + return new AmazonS3Client( + new BasicAWSCredentials( + options.AccessKey, + options.SecretKey), + config); } - return assumedCredentials; + return new AmazonS3Client(config); + }); + + return app; + } + + public static BaGetApplication AddAwsS3Storage(this BaGetApplication app, Action configure) + { + app.AddAwsS3Storage(); + app.Services.Configure(configure); + return app; + } + + private static async Task AssumeRoleAsync( + AWSCredentials credentials, + string roleArn, + string roleSessionName) + { + var assumedCredentials = new AssumeRoleAWSCredentials(credentials, roleArn, roleSessionName); + var immutableCredentials = await credentials.GetCredentialsAsync(); + + if (string.IsNullOrWhiteSpace(immutableCredentials.Token)) + { + throw new InvalidOperationException($"Unable to assume role {roleArn}"); } + + return assumedCredentials; } -} +} \ No newline at end of file diff --git a/src/BaGet.Aws/BaGet.Aws.csproj b/src/BaGet.Aws/BaGet.Aws.csproj index 5094cd3d..0685a977 100644 --- a/src/BaGet.Aws/BaGet.Aws.csproj +++ b/src/BaGet.Aws/BaGet.Aws.csproj @@ -1,20 +1,20 @@ - + - netstandard2.0 + net6.0 NuGet;Amazon;Cloud The libraries to host BaGet on AWS. - - - + + + - + \ No newline at end of file diff --git a/src/BaGet.Aws/S3StorageOptions.cs b/src/BaGet.Aws/S3StorageOptions.cs index 025340d2..96b16975 100644 --- a/src/BaGet.Aws/S3StorageOptions.cs +++ b/src/BaGet.Aws/S3StorageOptions.cs @@ -1,26 +1,25 @@ using System.ComponentModel.DataAnnotations; using BaGet.Core; -namespace BaGet.Aws +namespace BaGet.Aws; + +public class S3StorageOptions { - public class S3StorageOptions - { - [RequiredIf(nameof(SecretKey), null, IsInverted = true)] - public string AccessKey { get; set; } + [RequiredIf(nameof(SecretKey), null, IsInverted = true)] + public string AccessKey { get; set; } - [RequiredIf(nameof(AccessKey), null, IsInverted = true)] - public string SecretKey { get; set; } + [RequiredIf(nameof(AccessKey), null, IsInverted = true)] + public string SecretKey { get; set; } - [Required] - public string Region { get; set; } + [Required] + public string Region { get; set; } - [Required] - public string Bucket { get; set; } + [Required] + public string Bucket { get; set; } - public string Prefix { get; set; } + public string Prefix { get; set; } - public bool UseInstanceProfile { get; set; } + public bool UseInstanceProfile { get; set; } - public string AssumeRoleArn { get; set; } - } -} + public string AssumeRoleArn { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Aws/S3StorageService.cs b/src/BaGet.Aws/S3StorageService.cs index bee08c5f..a10cbadd 100644 --- a/src/BaGet.Aws/S3StorageService.cs +++ b/src/BaGet.Aws/S3StorageService.cs @@ -1,102 +1,97 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; using Amazon.S3; using Amazon.S3.Model; using BaGet.Core; using Microsoft.Extensions.Options; -namespace BaGet.Aws +namespace BaGet.Aws; + +public class S3StorageService : IStorageService { - public class S3StorageService : IStorageService + private const string Separator = "/"; + private readonly string _bucket; + private readonly string _prefix; + private readonly AmazonS3Client _client; + + public S3StorageService(IOptionsSnapshot options, AmazonS3Client client) { - private const string Separator = "/"; - private readonly string _bucket; - private readonly string _prefix; - private readonly AmazonS3Client _client; + if (options == null) + throw new ArgumentNullException(nameof(options)); - public S3StorageService(IOptionsSnapshot options, AmazonS3Client client) - { - if (options == null) - throw new ArgumentNullException(nameof(options)); + _bucket = options.Value.Bucket; + _prefix = options.Value.Prefix; + _client = client ?? throw new ArgumentNullException(nameof(client)); - _bucket = options.Value.Bucket; - _prefix = options.Value.Prefix; - _client = client ?? throw new ArgumentNullException(nameof(client)); + if (!string.IsNullOrEmpty(_prefix) && !_prefix.EndsWith(Separator)) + _prefix += Separator; + } - if (!string.IsNullOrEmpty(_prefix) && !_prefix.EndsWith(Separator)) - _prefix += Separator; - } + private string PrepareKey(string path) + { + return _prefix + path.Replace("\\", Separator); + } - private string PrepareKey(string path) - { - return _prefix + path.Replace("\\", Separator); - } + public async Task GetAsync(string path, CancellationToken cancellationToken = default) + { + var stream = new MemoryStream(); - public async Task GetAsync(string path, CancellationToken cancellationToken = default) + try { - var stream = new MemoryStream(); - - try - { - using (var request = await _client.GetObjectAsync(_bucket, PrepareKey(path), cancellationToken)) - { - await request.ResponseStream.CopyToAsync(stream); - } - - stream.Seek(0, SeekOrigin.Begin); - } - catch (Exception) + using (var request = await _client.GetObjectAsync(_bucket, PrepareKey(path), cancellationToken)) { - stream.Dispose(); - - // TODO - throw; + await request.ResponseStream.CopyToAsync(stream); } - return stream; + stream.Seek(0, SeekOrigin.Begin); } - - public Task GetDownloadUriAsync(string path, CancellationToken cancellationToken = default) + catch (Exception) { - var url = _client.GetPreSignedURL(new GetPreSignedUrlRequest - { - BucketName = _bucket, - Key = PrepareKey(path) - }); + stream.Dispose(); - return Task.FromResult(new Uri(url)); + // TODO + throw; } - public async Task PutAsync(string path, Stream content, string contentType, CancellationToken cancellationToken = default) + return stream; + } + + public Task GetDownloadUriAsync(string path, CancellationToken cancellationToken = default) + { + var url = _client.GetPreSignedURL(new GetPreSignedUrlRequest { - // TODO: Uploads should be idempotent. This should fail if and only if the blob - // already exists but has different content. + BucketName = _bucket, + Key = PrepareKey(path) + }); - using (var seekableContent = new MemoryStream()) - { - await content.CopyToAsync(seekableContent, 4096, cancellationToken); - - seekableContent.Seek(0, SeekOrigin.Begin); - - await _client.PutObjectAsync(new PutObjectRequest - { - BucketName = _bucket, - Key = PrepareKey(path), - InputStream = seekableContent, - ContentType = contentType, - AutoResetStreamPosition = false, - AutoCloseStream = false - }, cancellationToken); - } + return Task.FromResult(new Uri(url)); + } - return StoragePutResult.Success; - } + public async Task PutAsync(string path, Stream content, string contentType, CancellationToken cancellationToken = default) + { + // TODO: Uploads should be idempotent. This should fail if and only if the blob + // already exists but has different content. - public async Task DeleteAsync(string path, CancellationToken cancellationToken = default) + using (var seekableContent = new MemoryStream()) { - await _client.DeleteObjectAsync(_bucket, PrepareKey(path), cancellationToken); + await content.CopyToAsync(seekableContent, 4096, cancellationToken); + + seekableContent.Seek(0, SeekOrigin.Begin); + + await _client.PutObjectAsync(new PutObjectRequest + { + BucketName = _bucket, + Key = PrepareKey(path), + InputStream = seekableContent, + ContentType = contentType, + AutoResetStreamPosition = false, + AutoCloseStream = false + }, cancellationToken); } + + return StoragePutResult.Success; + } + + public async Task DeleteAsync(string path, CancellationToken cancellationToken = default) + { + await _client.DeleteObjectAsync(_bucket, PrepareKey(path), cancellationToken); } -} +} \ No newline at end of file diff --git a/src/BaGet.Azure/AzureApplicationExtensions.cs b/src/BaGet.Azure/AzureApplicationExtensions.cs index f8916840..de5ea672 100644 --- a/src/BaGet.Azure/AzureApplicationExtensions.cs +++ b/src/BaGet.Azure/AzureApplicationExtensions.cs @@ -1,184 +1,162 @@ -using System; -using BaGet.Azure; -using BaGet.Core; -using Microsoft.Azure.Cosmos.Table; -using Microsoft.Azure.Search; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Options; -using Microsoft.WindowsAzure.Storage.Blob; - -namespace BaGet +namespace BaGet.Azure; + +public static class AzureApplicationExtensions { - using CloudStorageAccount = Microsoft.WindowsAzure.Storage.CloudStorageAccount; - using StorageCredentials = Microsoft.WindowsAzure.Storage.Auth.StorageCredentials; + public static BaGetApplication AddAzureTableDatabase(this BaGetApplication app) + { + app.Services.AddBaGetOptions(nameof(BaGetOptions.Database)); - using TableStorageAccount = Microsoft.Azure.Cosmos.Table.CloudStorageAccount; + app.Services.AddTransient(); + app.Services.AddTransient(); + app.Services.AddTransient(); + app.Services.TryAddTransient(provider => provider.GetRequiredService()); + app.Services.TryAddTransient(provider => provider.GetRequiredService()); + app.Services.TryAddTransient(provider => provider.GetRequiredService()); - public static class AzureApplicationExtensions - { - public static BaGetApplication AddAzureTableDatabase(this BaGetApplication app) + app.Services.AddSingleton(provider => { - app.Services.AddBaGetOptions(nameof(BaGetOptions.Database)); + var options = provider.GetRequiredService>().Value; - app.Services.AddTransient(); - app.Services.AddTransient(); - app.Services.AddTransient(); - app.Services.TryAddTransient(provider => provider.GetRequiredService()); - app.Services.TryAddTransient(provider => provider.GetRequiredService()); - app.Services.TryAddTransient(provider => provider.GetRequiredService()); - - app.Services.AddSingleton(provider => - { - var options = provider.GetRequiredService>().Value; + return TableStorageAccount.Parse(options.ConnectionString); + }); - return TableStorageAccount.Parse(options.ConnectionString); - }); + app.Services.AddTransient(provider => + { + var account = provider.GetRequiredService(); - app.Services.AddTransient(provider => - { - var account = provider.GetRequiredService(); + return account.CreateCloudTableClient(); + }); - return account.CreateCloudTableClient(); - }); + app.Services.AddProvider((provider, config) => + { + if (!config.HasDatabaseType("AzureTable")) return null; - app.Services.AddProvider((provider, config) => - { - if (!config.HasDatabaseType("AzureTable")) return null; + return provider.GetRequiredService(); + }); - return provider.GetRequiredService(); - }); + app.Services.AddProvider((provider, config) => + { + if (!config.HasSearchType("Database")) return null; + if (!config.HasDatabaseType("AzureTable")) return null; - app.Services.AddProvider((provider, config) => - { - if (!config.HasSearchType("Database")) return null; - if (!config.HasDatabaseType("AzureTable")) return null; + return provider.GetRequiredService(); + }); - return provider.GetRequiredService(); - }); + app.Services.AddProvider((provider, config) => + { + if (!config.HasSearchType("Database")) return null; + if (!config.HasDatabaseType("AzureTable")) return null; - app.Services.AddProvider((provider, config) => - { - if (!config.HasSearchType("Database")) return null; - if (!config.HasDatabaseType("AzureTable")) return null; + return provider.GetRequiredService(); + }); - return provider.GetRequiredService(); - }); + return app; + } - return app; - } + public static BaGetApplication AddAzureTableDatabase(this BaGetApplication app, Action configure) + { + app.AddAzureTableDatabase(); + app.Services.Configure(configure); + return app; + } - public static BaGetApplication AddAzureTableDatabase( - this BaGetApplication app, - Action configure) - { - app.AddAzureTableDatabase(); - app.Services.Configure(configure); - return app; - } + public static BaGetApplication AddAzureBlobStorage(this BaGetApplication app) + { + app.Services.AddBaGetOptions(nameof(BaGetOptions.Storage)); + app.Services.AddTransient(); + app.Services.TryAddTransient(provider => provider.GetRequiredService()); - public static BaGetApplication AddAzureBlobStorage(this BaGetApplication app) + app.Services.AddSingleton(provider => { - app.Services.AddBaGetOptions(nameof(BaGetOptions.Storage)); - app.Services.AddTransient(); - app.Services.TryAddTransient(provider => provider.GetRequiredService()); + var options = provider.GetRequiredService>().Value; - app.Services.AddSingleton(provider => + if (!string.IsNullOrEmpty(options.ConnectionString)) { - var options = provider.GetRequiredService>().Value; + return CloudStorageAccount.Parse(options.ConnectionString); + } - if (!string.IsNullOrEmpty(options.ConnectionString)) - { - return CloudStorageAccount.Parse(options.ConnectionString); - } + return new CloudStorageAccount( + new StorageCredentials( + options.AccountName, + options.AccessKey), + useHttps: true); + }); - return new CloudStorageAccount( - new StorageCredentials( - options.AccountName, - options.AccessKey), - useHttps: true); - }); - - app.Services.AddTransient(provider => - { - var options = provider.GetRequiredService>().Value; - var account = provider.GetRequiredService(); + app.Services.AddTransient(provider => + { + var options = provider.GetRequiredService>().Value; + var account = provider.GetRequiredService(); - var client = account.CreateCloudBlobClient(); + var client = account.CreateCloudBlobClient(); - return client.GetContainerReference(options.Container); - }); + return client.GetContainerReference(options.Container); + }); - app.Services.AddProvider((provider, config) => - { - if (!config.HasStorageType("AzureBlobStorage")) return null; + app.Services.AddProvider((provider, config) => + { + if (!config.HasStorageType("AzureBlobStorage")) return null; - return provider.GetRequiredService(); - }); + return provider.GetRequiredService(); + }); - return app; - } + return app; + } - public static BaGetApplication AddAzureBlobStorage( - this BaGetApplication app, - Action configure) - { - app.AddAzureBlobStorage(); - app.Services.Configure(configure); - return app; - } + public static BaGetApplication AddAzureBlobStorage(this BaGetApplication app, Action configure) + { + app.AddAzureBlobStorage(); + app.Services.Configure(configure); + return app; + } - public static BaGetApplication AddAzureSearch(this BaGetApplication app) - { - app.Services.AddBaGetOptions(nameof(BaGetOptions.Search)); + public static BaGetApplication AddAzureSearch(this BaGetApplication app) + { + app.Services.AddBaGetOptions(nameof(BaGetOptions.Search)); - app.Services.AddTransient(); - app.Services.AddTransient(); - app.Services.AddTransient(); - app.Services.AddTransient(); - app.Services.TryAddTransient(provider => provider.GetRequiredService()); - app.Services.TryAddTransient(provider => provider.GetRequiredService()); + app.Services.AddTransient(); + app.Services.AddTransient(); + app.Services.AddTransient(); + app.Services.AddTransient(); + app.Services.TryAddTransient(provider => provider.GetRequiredService()); + app.Services.TryAddTransient(provider => provider.GetRequiredService()); - app.Services.AddSingleton(provider => - { - var options = provider.GetRequiredService>().Value; - var credentials = new SearchCredentials(options.ApiKey); + app.Services.AddSingleton(provider => + { + var options = provider.GetRequiredService>().Value; + var credentials = new SearchCredentials(options.ApiKey); - return new SearchServiceClient(options.AccountName, credentials); - }); + return new SearchServiceClient(options.AccountName, credentials); + }); - app.Services.AddSingleton(provider => - { - var options = provider.GetRequiredService>().Value; - var credentials = new SearchCredentials(options.ApiKey); + app.Services.AddSingleton(provider => + { + var options = provider.GetRequiredService>().Value; + var credentials = new SearchCredentials(options.ApiKey); - return new SearchIndexClient(options.AccountName, PackageDocument.IndexName, credentials); - }); + return new SearchIndexClient(options.AccountName, PackageDocument.IndexName, credentials); + }); - app.Services.AddProvider((provider, config) => - { - if (!config.HasSearchType("AzureSearch")) return null; + app.Services.AddProvider((provider, config) => + { + if (!config.HasSearchType("AzureSearch")) return null; - return provider.GetRequiredService(); - }); + return provider.GetRequiredService(); + }); - app.Services.AddProvider((provider, config) => - { - if (!config.HasSearchType("AzureSearch")) return null; + app.Services.AddProvider((provider, config) => + { + if (!config.HasSearchType("AzureSearch")) return null; - return provider.GetRequiredService(); - }); + return provider.GetRequiredService(); + }); - return app; - } + return app; + } - public static BaGetApplication AddAzureSearch( - this BaGetApplication app, - Action configure) - { - app.AddAzureSearch(); - app.Services.Configure(configure); - return app; - } + public static BaGetApplication AddAzureSearch(this BaGetApplication app, Action configure) + { + app.AddAzureSearch(); + app.Services.Configure(configure); + return app; } } diff --git a/src/BaGet.Azure/BaGet.Azure.csproj b/src/BaGet.Azure/BaGet.Azure.csproj index 7b567f6a..89064869 100644 --- a/src/BaGet.Azure/BaGet.Azure.csproj +++ b/src/BaGet.Azure/BaGet.Azure.csproj @@ -1,21 +1,21 @@ - netstandard2.0 + net6.0 NuGet;Azure;Cloud The libraries to host BaGet on Azure. - - - - + + + + - + \ No newline at end of file diff --git a/src/BaGet.Azure/BaGet.Azure.csproj.DotSettings b/src/BaGet.Azure/BaGet.Azure.csproj.DotSettings new file mode 100644 index 00000000..0223a3c2 --- /dev/null +++ b/src/BaGet.Azure/BaGet.Azure.csproj.DotSettings @@ -0,0 +1,6 @@ + + True + True + True + True + True \ No newline at end of file diff --git a/src/BaGet.Azure/Configuration/AzureBlobStorageOptions.cs b/src/BaGet.Azure/Configuration/AzureBlobStorageOptions.cs index 89055c68..302dc0d0 100644 --- a/src/BaGet.Azure/Configuration/AzureBlobStorageOptions.cs +++ b/src/BaGet.Azure/Configuration/AzureBlobStorageOptions.cs @@ -1,62 +1,58 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; +namespace BaGet.Azure; -namespace BaGet.Azure +/// +/// BaGet's configurations to use Azure Blob Storage to store packages. +/// See: https://loic-sharma.github.io/BaGet/quickstart/azure/#azure-blob-storage +/// +public class AzureBlobStorageOptions : IValidatableObject { /// - /// BaGet's configurations to use Azure Blob Storage to store packages. - /// See: https://loic-sharma.github.io/BaGet/quickstart/azure/#azure-blob-storage + /// The Azure Blob Storage connection string. + /// If provided, ignores and . /// - public class AzureBlobStorageOptions : IValidatableObject + public string ConnectionString { get; set; } + + /// + /// The Azure Blob Storage account name. Ignored if is provided. + /// + public string AccountName { get; set; } + + /// + /// The Azure Blob Storage access key. Ignored if is provided. + /// + public string AccessKey { get; set; } + + /// + /// The Azure Blob Storage container name. + /// + public string Container { get; set; } + + public IEnumerable Validate(ValidationContext validationContext) { - /// - /// The Azure Blob Storage connection string. - /// If provided, ignores and . - /// - public string ConnectionString { get; set; } - - /// - /// The Azure Blob Storage account name. Ignored if is provided. - /// - public string AccountName { get; set; } - - /// - /// The Azure Blob Storage access key. Ignored if is provided. - /// - public string AccessKey { get; set; } - - /// - /// The Azure Blob Storage container name. - /// - public string Container { get; set; } - - public IEnumerable Validate(ValidationContext validationContext) - { - const string helpUrl = "https://loic-sharma.github.io/BaGet/quickstart/azure/#azure-blob-storage"; + const string helpUrl = "https://loic-sharma.github.io/BaGet/quickstart/azure/#azure-blob-storage"; - if (string.IsNullOrEmpty(ConnectionString)) + if (string.IsNullOrEmpty(ConnectionString)) + { + if (string.IsNullOrEmpty(AccountName)) { - if (string.IsNullOrEmpty(AccountName)) - { - yield return new ValidationResult( - $"The {nameof(AccountName)} configuration is required. See {helpUrl}", - new[] { nameof(AccountName) }); - } - - if (string.IsNullOrEmpty(AccessKey)) - { - yield return new ValidationResult( - $"The {nameof(AccessKey)} configuration is required. See {helpUrl}", - new[] { nameof(AccessKey) }); - } + yield return new ValidationResult( + $"The {nameof(AccountName)} configuration is required. See {helpUrl}", + new[] { nameof(AccountName) }); } - if (string.IsNullOrEmpty(Container)) + if (string.IsNullOrEmpty(AccessKey)) { yield return new ValidationResult( - $"The {nameof(Container)} configuration is required. See {helpUrl}", - new[] { nameof(Container) }); + $"The {nameof(AccessKey)} configuration is required. See {helpUrl}", + new[] { nameof(AccessKey) }); } } + + if (string.IsNullOrEmpty(Container)) + { + yield return new ValidationResult( + $"The {nameof(Container)} configuration is required. See {helpUrl}", + new[] { nameof(Container) }); + } } } diff --git a/src/BaGet.Azure/Configuration/AzureSearchOptions.cs b/src/BaGet.Azure/Configuration/AzureSearchOptions.cs index f92c0681..f67eb1d6 100644 --- a/src/BaGet.Azure/Configuration/AzureSearchOptions.cs +++ b/src/BaGet.Azure/Configuration/AzureSearchOptions.cs @@ -1,13 +1,10 @@ -using System.ComponentModel.DataAnnotations; +namespace BaGet.Azure; -namespace BaGet.Azure +public class AzureSearchOptions { - public class AzureSearchOptions - { - [Required] - public string AccountName { get; set; } + [Required] + public string AccountName { get; set; } - [Required] - public string ApiKey { get; set; } - } + [Required] + public string ApiKey { get; set; } } diff --git a/src/BaGet.Azure/Configuration/AzureTableOptions.cs b/src/BaGet.Azure/Configuration/AzureTableOptions.cs index 373eb9b3..53405017 100644 --- a/src/BaGet.Azure/Configuration/AzureTableOptions.cs +++ b/src/BaGet.Azure/Configuration/AzureTableOptions.cs @@ -1,10 +1,7 @@ -using System.ComponentModel.DataAnnotations; +namespace BaGet.Azure; -namespace BaGet.Azure +public class AzureTableOptions { - public class AzureTableOptions - { - [Required] - public string ConnectionString { get; set; } - } + [Required] + public string ConnectionString { get; set; } } diff --git a/src/BaGet.Azure/Extensions/StorageExceptionExtensions.cs b/src/BaGet.Azure/Extensions/StorageExceptionExtensions.cs index a7f50c13..2ccbcf1b 100644 --- a/src/BaGet.Azure/Extensions/StorageExceptionExtensions.cs +++ b/src/BaGet.Azure/Extensions/StorageExceptionExtensions.cs @@ -1,30 +1,24 @@ -using System.Net; +namespace BaGet.Azure; -namespace BaGet.Azure +internal static class StorageExceptionExtensions { - using StorageException = Microsoft.WindowsAzure.Storage.StorageException; - using TableStorageException = Microsoft.Azure.Cosmos.Table.StorageException; - - internal static class StorageExceptionExtensions + public static bool IsAlreadyExistsException(this StorageException_ e) { - public static bool IsAlreadyExistsException(this StorageException e) - { - return e?.RequestInformation?.HttpStatusCode == (int?)HttpStatusCode.Conflict; - } + return e?.RequestInformation?.HttpStatusCode == (int?)HttpStatusCode.Conflict; + } - public static bool IsNotFoundException(this TableStorageException e) - { - return e?.RequestInformation?.HttpStatusCode == (int?)HttpStatusCode.NotFound; - } + public static bool IsNotFoundException(this TableStorageException e) + { + return e?.RequestInformation?.HttpStatusCode == (int?)HttpStatusCode.NotFound; + } - public static bool IsAlreadyExistsException(this TableStorageException e) - { - return e?.RequestInformation?.HttpStatusCode == (int?)HttpStatusCode.Conflict; - } + public static bool IsAlreadyExistsException(this TableStorageException e) + { + return e?.RequestInformation?.HttpStatusCode == (int?)HttpStatusCode.Conflict; + } - public static bool IsPreconditionFailedException(this TableStorageException e) - { - return e?.RequestInformation?.HttpStatusCode == (int?)HttpStatusCode.PreconditionFailed; - } + public static bool IsPreconditionFailedException(this TableStorageException e) + { + return e?.RequestInformation?.HttpStatusCode == (int?)HttpStatusCode.PreconditionFailed; } } diff --git a/src/BaGet.Azure/GlobalUsings.cs b/src/BaGet.Azure/GlobalUsings.cs new file mode 100644 index 00000000..253effb4 --- /dev/null +++ b/src/BaGet.Azure/GlobalUsings.cs @@ -0,0 +1,27 @@ +// Global using directives + +global using BaGet.Core; +global using BaGet.Protocol.Models; +global using Microsoft.Azure.Cosmos.Table; +global using Microsoft.Azure.Documents; +global using Microsoft.Azure.Search; +global using Microsoft.Azure.Search.Models; +global using Microsoft.Azure.Storage.Blob; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.Options; +global using Microsoft.Rest.Azure; +global using Newtonsoft.Json; +global using NuGet.Versioning; +global using System.ComponentModel.DataAnnotations; +global using System.Net; +global using System.Text; +global using SearchResult = BaGet.Protocol.Models.SearchResult; +global using TableStorageAccount = Microsoft.Azure.Cosmos.Table.CloudStorageAccount; +global using StorageException = Microsoft.Azure.Cosmos.Table.StorageException; +global using TableStorageException = Microsoft.Azure.Cosmos.Table.StorageException; +global using AccessCondition = Microsoft.Azure.Storage.AccessCondition; +global using StorageCredentials = Microsoft.Azure.Storage.Auth.StorageCredentials; +global using CloudStorageAccount = Microsoft.Azure.Storage.CloudStorageAccount; +global using StorageException_ = Microsoft.Azure.Storage.StorageException; diff --git a/src/BaGet.Azure/Search/AzureSearchBatchIndexer.cs b/src/BaGet.Azure/Search/AzureSearchBatchIndexer.cs index 062d67d8..8e357554 100644 --- a/src/BaGet.Azure/Search/AzureSearchBatchIndexer.cs +++ b/src/BaGet.Azure/Search/AzureSearchBatchIndexer.cs @@ -1,88 +1,68 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Azure.Search; -using Microsoft.Azure.Search.Models; -using Microsoft.Extensions.Logging; -using Microsoft.Rest.Azure; +namespace BaGet.Azure; -namespace BaGet.Azure +public class AzureSearchBatchIndexer { - public class AzureSearchBatchIndexer - { - /// - /// Azure Search accepts batches of up to 1000 documents. - /// - public const int MaxBatchSize = 1000; + /// + /// Azure Search accepts batches of up to 1000 documents. + /// + public const int MaxBatchSize = 1000; - private readonly ISearchIndexClient _indexClient; - private readonly ILogger _logger; + private readonly ISearchIndexClient _indexClient; + private readonly ILogger _logger; - public AzureSearchBatchIndexer( - SearchServiceClient searchClient, - ILogger logger) - { - if (searchClient == null) throw new ArgumentNullException(nameof(searchClient)); + public AzureSearchBatchIndexer(SearchServiceClient searchClient, ILogger logger) + { + if (searchClient == null) throw new ArgumentNullException(nameof(searchClient)); - _indexClient = searchClient.Indexes.GetClient(PackageDocument.IndexName); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + _indexClient = searchClient.Indexes.GetClient(PackageDocument.IndexName); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public async Task IndexAsync( - IReadOnlyList> batch, - CancellationToken cancellationToken) + public async Task IndexAsync(IReadOnlyList> batch, CancellationToken cancellationToken) + { + if (batch.Count > MaxBatchSize) { - if (batch.Count > MaxBatchSize) - { - throw new ArgumentException( - $"Batch cannot have more than {MaxBatchSize} elements", - nameof(batch)); - } + throw new ArgumentException($"Batch cannot have more than {MaxBatchSize} elements", nameof(batch)); + } - IList indexingResults = null; - Exception innerException = null; + IList indexingResults = null; + Exception innerException = null; - try - { - await _indexClient.Documents.IndexAsync( - IndexBatch.New(batch), - cancellationToken: cancellationToken); + try + { + await _indexClient.Documents.IndexAsync(IndexBatch.New(batch), cancellationToken: cancellationToken); - _logger.LogInformation("Pushed batch of {DocumentCount} documents", batch.Count); + _logger.LogInformation("Pushed batch of {DocumentCount} documents", batch.Count); - } - catch (IndexBatchException ex) - { - _logger.LogError(ex, "An exception was thrown when pushing batch of documents"); - indexingResults = ex.IndexingResults; - innerException = ex; - } - catch (CloudException ex) when (ex.Response.StatusCode == HttpStatusCode.RequestEntityTooLarge && batch.Count > 1) - { - var halfCount = batch.Count / 2; - var halfA = batch.Take(halfCount).ToList(); - var halfB = batch.Skip(halfCount).ToList(); + } + catch (IndexBatchException ex) + { + _logger.LogError(ex, "An exception was thrown when pushing batch of documents"); + indexingResults = ex.IndexingResults; + innerException = ex; + } + catch (CloudException ex) when (ex.Response.StatusCode == HttpStatusCode.RequestEntityTooLarge && batch.Count > 1) + { + var halfCount = batch.Count / 2; + var halfA = batch.Take(halfCount).ToList(); + var halfB = batch.Skip(halfCount).ToList(); - _logger.LogWarning( - 0, - ex, - "The request body for a batch of {BatchSize} was too large. Splitting into two batches of size " + - "{HalfA} and {HalfB}.", - batch.Count, - halfA.Count, - halfB.Count); + _logger.LogWarning( + 0, + ex, + "The request body for a batch of {BatchSize} was too large. Splitting into two batches of size " + + "{HalfA} and {HalfB}.", + batch.Count, + halfA.Count, + halfB.Count); - await IndexAsync(halfA, cancellationToken); - await IndexAsync(halfB, cancellationToken); - } + await IndexAsync(halfA, cancellationToken); + await IndexAsync(halfB, cancellationToken); + } - if (indexingResults != null && indexingResults.Any(result => !result.Succeeded)) - { - throw new InvalidOperationException("Failed to pushed batch of documents documents"); - } + if (indexingResults != null && indexingResults.Any(result => !result.Succeeded)) + { + throw new InvalidOperationException("Failed to pushed batch of documents documents"); } } } diff --git a/src/BaGet.Azure/Search/AzureSearchIndexer.cs b/src/BaGet.Azure/Search/AzureSearchIndexer.cs index 46be8c95..ebe35853 100644 --- a/src/BaGet.Azure/Search/AzureSearchIndexer.cs +++ b/src/BaGet.Azure/Search/AzureSearchIndexer.cs @@ -1,40 +1,26 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using BaGet.Core; -using Microsoft.Extensions.Logging; +namespace BaGet.Azure; -namespace BaGet.Azure +public class AzureSearchIndexer : ISearchIndexer { - public class AzureSearchIndexer : ISearchIndexer - { - private readonly IPackageDatabase _packages; - private readonly IndexActionBuilder _actionBuilder; - private readonly AzureSearchBatchIndexer _batchIndexer; - private readonly ILogger _logger; + private readonly IPackageDatabase _packages; + private readonly IndexActionBuilder _actionBuilder; + private readonly AzureSearchBatchIndexer _batchIndexer; + private readonly ILogger _logger; - public AzureSearchIndexer( - IPackageDatabase packages, - IndexActionBuilder actionBuilder, - AzureSearchBatchIndexer batchIndexer, - ILogger logger) - { - _packages = packages ?? throw new ArgumentNullException(nameof(packages)); - _actionBuilder = actionBuilder ?? throw new ArgumentNullException(nameof(actionBuilder)); - _batchIndexer = batchIndexer ?? throw new ArgumentNullException(nameof(batchIndexer)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + public AzureSearchIndexer(IPackageDatabase packages, IndexActionBuilder actionBuilder, AzureSearchBatchIndexer batchIndexer, ILogger logger) + { + _packages = packages ?? throw new ArgumentNullException(nameof(packages)); + _actionBuilder = actionBuilder ?? throw new ArgumentNullException(nameof(actionBuilder)); + _batchIndexer = batchIndexer ?? throw new ArgumentNullException(nameof(batchIndexer)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public async Task IndexAsync(Package package, CancellationToken cancellationToken) - { - var packages = await _packages.FindAsync(package.Id, includeUnlisted: false, cancellationToken); + public async Task IndexAsync(Package package, CancellationToken cancellationToken) + { + var packages = await _packages.FindAsync(package.Id, includeUnlisted: false, cancellationToken); - var actions = _actionBuilder.UpdatePackage( - new PackageRegistration( - package.Id, - packages)); + var actions = _actionBuilder.UpdatePackage(new PackageRegistration(package.Id, packages)); - await _batchIndexer.IndexAsync(actions, cancellationToken); - } + await _batchIndexer.IndexAsync(actions, cancellationToken); } } diff --git a/src/BaGet.Azure/Search/AzureSearchService.cs b/src/BaGet.Azure/Search/AzureSearchService.cs index 6ce1f63f..79f0fa57 100644 --- a/src/BaGet.Azure/Search/AzureSearchService.cs +++ b/src/BaGet.Azure/Search/AzureSearchService.cs @@ -1,225 +1,199 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using BaGet.Core; -using BaGet.Protocol.Models; -using Microsoft.Azure.Search; -using NuGet.Versioning; - -namespace BaGet.Azure +namespace BaGet.Azure; + +public class AzureSearchService : ISearchService { - using QueryType = Microsoft.Azure.Search.Models.QueryType; - using SearchParameters = Microsoft.Azure.Search.Models.SearchParameters; + private readonly SearchIndexClient _searchClient; + private readonly IUrlGenerator _url; + private readonly IFrameworkCompatibilityService _frameworks; - public class AzureSearchService : ISearchService + public AzureSearchService(SearchIndexClient searchClient, IUrlGenerator url, IFrameworkCompatibilityService frameworks) { - private readonly SearchIndexClient _searchClient; - private readonly IUrlGenerator _url; - private readonly IFrameworkCompatibilityService _frameworks; - - public AzureSearchService( - SearchIndexClient searchClient, - IUrlGenerator url, - IFrameworkCompatibilityService frameworks) - { - _searchClient = searchClient ?? throw new ArgumentNullException(nameof(searchClient)); - _url = url ?? throw new ArgumentNullException(nameof(url)); - _frameworks = frameworks ?? throw new ArgumentNullException(nameof(frameworks)); - } + _searchClient = searchClient ?? throw new ArgumentNullException(nameof(searchClient)); + _url = url ?? throw new ArgumentNullException(nameof(url)); + _frameworks = frameworks ?? throw new ArgumentNullException(nameof(frameworks)); + } - public async Task SearchAsync( - SearchRequest request, - CancellationToken cancellationToken) + public async Task SearchAsync(SearchRequest request, CancellationToken cancellationToken) + { + var searchText = BuildSeachQuery(request.Query, request.PackageType, request.Framework); + var filter = BuildSearchFilter(request.IncludePrerelease, request.IncludeSemVer2); + var parameters = new SearchParameters { - var searchText = BuildSeachQuery(request.Query, request.PackageType, request.Framework); - var filter = BuildSearchFilter(request.IncludePrerelease, request.IncludeSemVer2); - var parameters = new SearchParameters - { - IncludeTotalResultCount = true, - QueryType = QueryType.Full, - Skip = request.Skip, - Top = request.Take, - Filter = filter - }; - - var response = await _searchClient.Documents.SearchAsync( - searchText, - parameters, - cancellationToken: cancellationToken); + IncludeTotalResultCount = true, + QueryType = QueryType.Full, + Skip = request.Skip, + Top = request.Take, + Filter = filter + }; - var results = new List(); + var response = await _searchClient.Documents.SearchAsync( + searchText, + parameters, + cancellationToken: cancellationToken); - foreach (var result in response.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"); - } + var results = new List(); - for (var i = 0; i < document.Versions.Length; i++) - { - var version = NuGetVersion.Parse(document.Versions[i]); + foreach (var result in response.Results) + { + var document = result.Document; + var versions = new List(); - versions.Add(new SearchResultVersion - { - RegistrationLeafUrl = _url.GetRegistrationLeafUrl(document.Id, version), - Version = document.Versions[i], - Downloads = long.Parse(document.VersionDownloads[i]), - }); - } + if (document.Versions.Length != document.VersionDownloads.Length) + { + throw new InvalidOperationException($"Invalid document {document.Key} with mismatched versions"); + } - var iconUrl = document.HasEmbeddedIcon - ? _url.GetPackageIconDownloadUrl(document.Id, NuGetVersion.Parse(document.Version)) - : document.IconUrl; + for (var i = 0; i < document.Versions.Length; i++) + { + var version = NuGetVersion.Parse(document.Versions[i]); - results.Add(new SearchResult + versions.Add(new SearchResultVersion { - PackageId = document.Id, - Version = document.Version, - Description = document.Description, - Authors = document.Authors, - IconUrl = iconUrl, - LicenseUrl = document.LicenseUrl, - ProjectUrl = document.ProjectUrl, - RegistrationIndexUrl = _url.GetRegistrationIndexUrl(document.Id), - Summary = document.Summary, - Tags = document.Tags, - Title = document.Title, - TotalDownloads = document.TotalDownloads, - Versions = versions + RegistrationLeafUrl = _url.GetRegistrationLeafUrl(document.Id, version), + Version = document.Versions[i], + Downloads = long.Parse(document.VersionDownloads[i]), }); } - return new SearchResponse - { - TotalHits = response.Count.Value, - Data = results, - Context = SearchContext.Default(_url.GetPackageMetadataResourceUrl()) - }; - } + var iconUrl = document.HasEmbeddedIcon + ? _url.GetPackageIconDownloadUrl(document.Id, NuGetVersion.Parse(document.Version)) + : document.IconUrl; - public async Task AutocompleteAsync( - AutocompleteRequest request, - CancellationToken cancellationToken) - { - // TODO: Do a prefix search on the package id field. - // TODO: Support prerelease, semver2, and package type filters. - // See: https://github.com/loic-sharma/BaGet/issues/291 - var parameters = new SearchParameters + results.Add(new SearchResult { - IncludeTotalResultCount = true, - Skip = request.Skip, - Top = request.Take, - }; - - var response = await _searchClient.Documents.SearchAsync( - request.Query, - parameters, - cancellationToken: cancellationToken); - - var results = response.Results - .Select(r => r.Document.Id) - .ToList() - .AsReadOnly(); - - return new AutocompleteResponse - { - TotalHits = response.Count.Value, - Data = results, - Context = AutocompleteContext.Default - }; + PackageId = document.Id, + Version = document.Version, + Description = document.Description, + Authors = document.Authors, + IconUrl = iconUrl, + LicenseUrl = document.LicenseUrl, + ProjectUrl = document.ProjectUrl, + RegistrationIndexUrl = _url.GetRegistrationIndexUrl(document.Id), + Summary = document.Summary, + Tags = document.Tags, + Title = document.Title, + TotalDownloads = document.TotalDownloads, + Versions = versions + }); } - public Task ListPackageVersionsAsync( - VersionsRequest request, - CancellationToken cancellationToken) + return new SearchResponse { - // TODO: Support versions autocomplete. - // See: https://github.com/loic-sharma/BaGet/issues/291 - throw new NotImplementedException(); - } + TotalHits = response.Count.Value, + Data = results, + Context = SearchContext.Default(_url.GetPackageMetadataResourceUrl()) + }; + } - public async Task FindDependentsAsync( - string packageId, - CancellationToken cancellationToken) + public async Task AutocompleteAsync(AutocompleteRequest request, CancellationToken cancellationToken) + { + // TODO: Do a prefix search on the package id field. + // TODO: Support prerelease, semver2, and package type filters. + // See: https://github.com/loic-sharma/BaGet/issues/291 + var parameters = new SearchParameters { - // TODO: Escape packageId. - var query = $"dependencies:{packageId.ToLowerInvariant()}"; - var parameters = new SearchParameters - { - IncludeTotalResultCount = true, - QueryType = QueryType.Full, - Skip = 0, - Top = 20, - }; - - var response = await _searchClient.Documents.SearchAsync(query, parameters, cancellationToken: cancellationToken); - var results = response.Results - .Select(r => new PackageDependent - { - Id = r.Document.Id, - Description = r.Document.Description, - TotalDownloads = r.Document.TotalDownloads - }) - .ToList() - .AsReadOnly(); - - return new DependentsResponse - { - TotalHits = response.Count.Value, - Data = results - }; - } - - private string BuildSeachQuery(string query, string packageType, string framework) + IncludeTotalResultCount = true, + Skip = request.Skip, + Top = request.Take, + }; + + var response = await _searchClient.Documents.SearchAsync( + request.Query, + parameters, + cancellationToken: cancellationToken); + + var results = response.Results + .Select(r => r.Document.Id) + .ToList() + .AsReadOnly(); + + return new AutocompleteResponse { - var queryBuilder = new StringBuilder(); + TotalHits = response.Count.Value, + Data = results, + Context = AutocompleteContext.Default + }; + } - if (!string.IsNullOrEmpty(query)) - { - queryBuilder.Append(query.TrimEnd().TrimEnd('*')); - queryBuilder.Append('*'); - } + public Task ListPackageVersionsAsync(VersionsRequest request, CancellationToken cancellationToken) + { + // TODO: Support versions autocomplete. + // See: https://github.com/loic-sharma/BaGet/issues/291 + throw new NotImplementedException(); + } - if (!string.IsNullOrEmpty(packageType)) + public async Task FindDependentsAsync(string packageId, CancellationToken cancellationToken) + { + // TODO: Escape packageId. + var query = $"dependencies:{packageId.ToLowerInvariant()}"; + var parameters = new SearchParameters + { + IncludeTotalResultCount = true, + QueryType = QueryType.Full, + Skip = 0, + Top = 20, + }; + + var response = await _searchClient.Documents.SearchAsync(query, parameters, cancellationToken: cancellationToken); + var results = response.Results + .Select(r => new PackageDependent { - queryBuilder.Append(" +packageTypes:"); - queryBuilder.Append(packageType); - } + Id = r.Document.Id, + Description = r.Document.Description, + TotalDownloads = r.Document.TotalDownloads + }) + .ToList() + .AsReadOnly(); + + return new DependentsResponse + { + TotalHits = response.Count.Value, + Data = results + }; + } - if (!string.IsNullOrEmpty(framework)) - { - var frameworks = _frameworks.FindAllCompatibleFrameworks(framework); + private string BuildSeachQuery(string query, string packageType, string framework) + { + var queryBuilder = new StringBuilder(); - queryBuilder.Append(" +frameworks:("); - queryBuilder.Append(string.Join(" ", frameworks)); - queryBuilder.Append(')'); - } + if (!string.IsNullOrEmpty(query)) + { + queryBuilder.Append(query.TrimEnd().TrimEnd('*')); + queryBuilder.Append('*'); + } - return queryBuilder.ToString(); + if (!string.IsNullOrEmpty(packageType)) + { + queryBuilder.Append(" +packageTypes:"); + queryBuilder.Append(packageType); } - private string BuildSearchFilter(bool includePrerelease, bool includeSemVer2) + if (!string.IsNullOrEmpty(framework)) { - var searchFilters = SearchFilters.Default; + var frameworks = _frameworks.FindAllCompatibleFrameworks(framework); - if (includePrerelease) - { - searchFilters |= SearchFilters.IncludePrerelease; - } + queryBuilder.Append(" +frameworks:("); + queryBuilder.Append(string.Join(" ", frameworks)); + queryBuilder.Append(')'); + } - if (includeSemVer2) - { - searchFilters |= SearchFilters.IncludeSemVer2; - } + return queryBuilder.ToString(); + } - return $"searchFilters eq '{searchFilters}'"; + private string BuildSearchFilter(bool includePrerelease, bool includeSemVer2) + { + var searchFilters = SearchFilters.Default; + + if (includePrerelease) + { + searchFilters |= SearchFilters.IncludePrerelease; } + + if (includeSemVer2) + { + searchFilters |= SearchFilters.IncludeSemVer2; + } + + return $"searchFilters eq '{searchFilters}'"; } } diff --git a/src/BaGet.Azure/Search/ExactMatchCustomAnalyzer.cs b/src/BaGet.Azure/Search/ExactMatchCustomAnalyzer.cs index 962ef04c..24e8f6f1 100644 --- a/src/BaGet.Azure/Search/ExactMatchCustomAnalyzer.cs +++ b/src/BaGet.Azure/Search/ExactMatchCustomAnalyzer.cs @@ -1,21 +1,11 @@ -using System.Collections.Generic; -using Microsoft.Azure.Search.Models; +namespace BaGet.Azure; -namespace BaGet.Azure +/// +/// A custom analyzer for case insensitive exact match. +/// +public static class ExactMatchCustomAnalyzer { - /// - /// A custom analyzer for case insensitive exact match. - /// - public static class ExactMatchCustomAnalyzer - { - public const string Name = "baget-exact-match-analyzer"; + public const string Name = "baget-exact-match-analyzer"; - public static CustomAnalyzer Instance = new CustomAnalyzer( - Name, - TokenizerName.Keyword, - new List - { - TokenFilterName.Lowercase - }); - } + public static CustomAnalyzer Instance = new(Name, TokenizerName.Keyword, new List { TokenFilterName.Lowercase }); } diff --git a/src/BaGet.Azure/Search/IndexActionBuilder.cs b/src/BaGet.Azure/Search/IndexActionBuilder.cs index 4888e034..2a07ff23 100644 --- a/src/BaGet.Azure/Search/IndexActionBuilder.cs +++ b/src/BaGet.Azure/Search/IndexActionBuilder.cs @@ -1,118 +1,104 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using BaGet.Core; -using Microsoft.Azure.Search.Models; - -namespace BaGet.Azure +namespace BaGet.Azure; + +public class IndexActionBuilder { - public class IndexActionBuilder + public virtual IReadOnlyList> AddPackage(PackageRegistration registration) { - public virtual IReadOnlyList> AddPackage( - PackageRegistration registration) - { - return AddOrUpdatePackage(registration, isUpdate: false); - } + return AddOrUpdatePackage(registration, isUpdate: false); + } - public virtual IReadOnlyList> UpdatePackage( - PackageRegistration registration) - { - return AddOrUpdatePackage(registration, isUpdate: true); - } + public virtual IReadOnlyList> UpdatePackage(PackageRegistration registration) + { + return AddOrUpdatePackage(registration, isUpdate: true); + } + + private IReadOnlyList> AddOrUpdatePackage(PackageRegistration registration, bool isUpdate) + { + var encodedId = EncodePackageId(registration.PackageId.ToLowerInvariant()); + var result = new List>(); - private IReadOnlyList> AddOrUpdatePackage( - PackageRegistration registration, - bool isUpdate) + for (var i = 0; i < 4; i++) { - var encodedId = EncodePackageId(registration.PackageId.ToLowerInvariant()); - var result = new List>(); + var includePrerelease = (i & 1) != 0; + var includeSemVer2 = (i & 2) != 0; + var searchFilters = (SearchFilters)i; - for (var i = 0; i < 4; i++) - { - var includePrerelease = (i & 1) != 0; - var includeSemVer2 = (i & 2) != 0; - var searchFilters = (SearchFilters)i; + var documentKey = $"{encodedId}-{searchFilters}"; + var filtered = registration.Packages.Where(p => p.Listed); - var documentKey = $"{encodedId}-{searchFilters}"; - var filtered = registration.Packages.Where(p => p.Listed); + if (!includePrerelease) + { + filtered = filtered.Where(p => !p.IsPrerelease); + } - if (!includePrerelease) - { - filtered = filtered.Where(p => !p.IsPrerelease); - } + if (!includeSemVer2) + { + filtered = filtered.Where(p => p.SemVerLevel != SemVerLevel.SemVer2); + } - if (!includeSemVer2) + var versions = filtered.OrderBy(p => p.Version).ToList(); + if (versions.Count == 0) + { + if (isUpdate) { - filtered = filtered.Where(p => p.SemVerLevel != SemVerLevel.SemVer2); - } + var action = IndexAction.Delete( + new KeyedDocument + { + Key = documentKey + }); - var versions = filtered.OrderBy(p => p.Version).ToList(); - if (versions.Count == 0) - { - if (isUpdate) - { - var action = IndexAction.Delete( - new KeyedDocument - { - Key = documentKey - }); - - result.Add(action); - } - - continue; + result.Add(action); } - var latest = versions.Last(); - var dependencies = latest - .Dependencies - .Select(d => d.Id?.ToLowerInvariant()) - .Where(d => d != null) - .Distinct() - .ToArray(); - - var document = new PackageDocument(); - - document.Key = $"{encodedId}-{searchFilters}"; - document.Id = latest.Id; - document.Version = latest.Version.ToFullString(); - document.Description = latest.Description; - document.Authors = latest.Authors; - document.HasEmbeddedIcon = latest.HasEmbeddedIcon; - document.IconUrl = latest.IconUrlString; - document.LicenseUrl = latest.LicenseUrlString; - document.ProjectUrl = latest.ProjectUrlString; - document.Published = latest.Published; - document.Summary = latest.Summary; - document.Tags = latest.Tags; - document.Title = latest.Title; - document.TotalDownloads = versions.Sum(p => p.Downloads); - document.DownloadsMagnitude = document.TotalDownloads.ToString().Length; - document.Versions = versions.Select(p => p.Version.ToFullString()).ToArray(); - document.VersionDownloads = versions.Select(p => p.Downloads.ToString()).ToArray(); - document.Dependencies = dependencies; - document.PackageTypes = latest.PackageTypes.Select(t => t.Name).ToArray(); - document.Frameworks = latest.TargetFrameworks.Select(f => f.Moniker.ToLowerInvariant()).ToArray(); - document.SearchFilters = searchFilters.ToString(); - - result.Add( - isUpdate - ? IndexAction.MergeOrUpload(document) - : IndexAction.Upload(document)); + continue; } - return result; + var latest = versions.Last(); + var dependencies = latest + .Dependencies + .Select(d => d.Id?.ToLowerInvariant()) + .Where(d => d != null) + .Distinct() + .ToArray(); + + var document = new PackageDocument + { + Key = $"{encodedId}-{searchFilters}", + Id = latest.Id, + Version = latest.Version.ToFullString(), + Description = latest.Description, + Authors = latest.Authors, + HasEmbeddedIcon = latest.HasEmbeddedIcon, + IconUrl = latest.IconUrlString, + LicenseUrl = latest.LicenseUrlString, + ProjectUrl = latest.ProjectUrlString, + Published = latest.Published, + Summary = latest.Summary, + Tags = latest.Tags, + Title = latest.Title, + TotalDownloads = versions.Sum(p => p.Downloads), + Versions = versions.Select(p => p.Version.ToFullString()).ToArray(), + VersionDownloads = versions.Select(p => p.Downloads.ToString()).ToArray(), + Dependencies = dependencies, + PackageTypes = latest.PackageTypes.Select(t => t.Name).ToArray(), + Frameworks = latest.TargetFrameworks.Select(f => f.Moniker.ToLowerInvariant()).ToArray(), + SearchFilters = searchFilters.ToString(), + DownloadsMagnitude = versions.Sum(p => p.Downloads).ToString().Length + }; + + result.Add(isUpdate ? IndexAction.MergeOrUpload(document) : IndexAction.Upload(document)); } - private string EncodePackageId(string key) - { - // Keys can only contain letters, digits, underscore(_), dash(-), or equal sign(=). - // TODO: Align with NuGet.org's algorithm. - var bytes = Encoding.UTF8.GetBytes(key); - var base64 = Convert.ToBase64String(bytes); + return result; + } - return base64.Replace('+', '-').Replace('/', '_'); - } + private string EncodePackageId(string key) + { + // Keys can only contain letters, digits, underscore(_), dash(-), or equal sign(=). + // TODO: Align with NuGet.org's algorithm. + 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 65c1c3b6..04da8cc9 100644 --- a/src/BaGet.Azure/Search/PackageDocument.cs +++ b/src/BaGet.Azure/Search/PackageDocument.cs @@ -1,80 +1,74 @@ -using System; -using System.ComponentModel.DataAnnotations; -using Microsoft.Azure.Search; -using Microsoft.Azure.Search.Models; +namespace BaGet.Azure; -namespace BaGet.Azure +// See: https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-for-packages +[SerializePropertyNamesAsCamelCase] +public class PackageDocument : KeyedDocument { - // See: https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-for-packages - [SerializePropertyNamesAsCamelCase] - public class PackageDocument : KeyedDocument - { - public const string IndexName = "packages"; - - [IsSearchable, IsFilterable, IsSortable] - public string Id { get; set; } - - /// - /// The package's full versions after normalization, including any SemVer 2.0.0 build metadata. - /// - [IsSearchable, IsFilterable, IsSortable] - public string Version { get; set; } - - [IsSearchable] - public string Description { get; set; } - public string[] Authors { get; set; } - public bool HasEmbeddedIcon { 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; } - - /// - /// The package's full versions after normalization, including any SemVer 2.0.0 build metadata. - /// - public string[] Versions { get; set; } - public string[] VersionDownloads { get; set; } - - [IsSearchable] - [Analyzer(ExactMatchCustomAnalyzer.Name)] - public string[] Dependencies { get; set; } - - [IsSearchable] - [Analyzer(ExactMatchCustomAnalyzer.Name)] - public string[] PackageTypes { get; set; } - - [IsSearchable] - [Analyzer(ExactMatchCustomAnalyzer.Name)] - public string[] Frameworks { get; set; } - - [IsFilterable] - public string SearchFilters { get; set; } - } - - [SerializePropertyNamesAsCamelCase] - public class KeyedDocument : IKeyedDocument - { - [Key] - public string Key { get; set; } - } - - public interface IKeyedDocument - { - string Key { get; set; } - } + public const string IndexName = "packages"; + + [IsSearchable, IsFilterable, IsSortable] + public string Id { get; set; } + + /// + /// The package's full versions after normalization, including any SemVer 2.0.0 build metadata. + /// + [IsSearchable, IsFilterable, IsSortable] + public string Version { get; set; } + + [IsSearchable] + public string Description { get; set; } + public string[] Authors { get; set; } + public bool HasEmbeddedIcon { 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; } + + /// + /// The package's full versions after normalization, including any SemVer 2.0.0 build metadata. + /// + public string[] Versions { get; set; } + public string[] VersionDownloads { get; set; } + + [IsSearchable] + [Analyzer(ExactMatchCustomAnalyzer.Name)] + public string[] Dependencies { get; set; } + + [IsSearchable] + [Analyzer(ExactMatchCustomAnalyzer.Name)] + public string[] PackageTypes { get; set; } + + [IsSearchable] + [Analyzer(ExactMatchCustomAnalyzer.Name)] + public string[] Frameworks { get; set; } + + [IsFilterable] + public string SearchFilters { get; set; } +} + +[SerializePropertyNamesAsCamelCase] +public class KeyedDocument : IKeyedDocument +{ + [Key] + public string Key { get; set; } +} + +public interface IKeyedDocument +{ + string Key { get; set; } } diff --git a/src/BaGet.Azure/Search/SearchFilters.cs b/src/BaGet.Azure/Search/SearchFilters.cs index 9e3e90b5..20f59bd8 100644 --- a/src/BaGet.Azure/Search/SearchFilters.cs +++ b/src/BaGet.Azure/Search/SearchFilters.cs @@ -1,31 +1,28 @@ -using System; +namespace BaGet.Azure; -namespace BaGet.Azure +// Based off: https://github.com/NuGet/NuGet.Services.Metadata/blob/d3e6b7a3aa8ec9cb8b32bf860f2d4f0d6766ed92/src/NuGet.Services.AzureSearch/VersionList/SearchFilters.cs#L9 +[Flags] +public enum SearchFilters { - // Based off: https://github.com/NuGet/NuGet.Services.Metadata/blob/d3e6b7a3aa8ec9cb8b32bf860f2d4f0d6766ed92/src/NuGet.Services.AzureSearch/VersionList/SearchFilters.cs#L9 - [Flags] - public enum SearchFilters - { - /// - /// Exclude SemVer 2.0.0 and prerelease packages. - /// - Default = 0, + /// + /// Exclude SemVer 2.0.0 and prerelease packages. + /// + Default = 0, - /// - /// Include packages that have prerelease versions. Note that a package's dependency version ranges do not - /// affect the prerelease status of the package. This is in contrast of . - /// - IncludePrerelease = 1 << 0, + /// + /// Include packages that have prerelease versions. Note that a package's dependency version ranges do not + /// affect the prerelease status of the package. This is in contrast of . + /// + IncludePrerelease = 1 << 0, - /// - /// Include SemVer 2.0.0 packages. Note that SemVer 2.0.0 dependency version ranges make a package into a SemVer - /// 2.0.0 even if the package's own version string is SemVer 1.0.0. - /// - IncludeSemVer2 = 1 << 1, + /// + /// Include SemVer 2.0.0 packages. Note that SemVer 2.0.0 dependency version ranges make a package into a SemVer + /// 2.0.0 even if the package's own version string is SemVer 1.0.0. + /// + IncludeSemVer2 = 1 << 1, - /// - /// Include package that have prerelease versions and include SemVer 2.0.0 packages. - /// - IncludePrereleaseAndSemVer2 = IncludePrerelease | IncludeSemVer2, - } -} + /// + /// Include package that have prerelease versions and include SemVer 2.0.0 packages. + /// + IncludePrereleaseAndSemVer2 = IncludePrerelease | IncludeSemVer2, +} \ No newline at end of file diff --git a/src/BaGet.Azure/Storage/BlobStorageService.cs b/src/BaGet.Azure/Storage/BlobStorageService.cs index fe07efb3..d48599a4 100644 --- a/src/BaGet.Azure/Storage/BlobStorageService.cs +++ b/src/BaGet.Azure/Storage/BlobStorageService.cs @@ -1,85 +1,74 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using BaGet.Core; -using Microsoft.WindowsAzure.Storage; -using Microsoft.WindowsAzure.Storage.Blob; -namespace BaGet.Azure + +namespace BaGet.Azure; + +// See: https://github.com/NuGet/NuGetGallery/blob/master/src/NuGetGallery.Core/Services/CloudBlobCoreFileStorageService.cs +public class BlobStorageService : IStorageService { - // See: https://github.com/NuGet/NuGetGallery/blob/master/src/NuGetGallery.Core/Services/CloudBlobCoreFileStorageService.cs - public class BlobStorageService : IStorageService - { - private readonly CloudBlobContainer _container; + private readonly CloudBlobContainer _container; - public BlobStorageService(CloudBlobContainer container) - { - _container = container ?? throw new ArgumentNullException(nameof(container)); - } + public BlobStorageService(CloudBlobContainer container) + { + _container = container ?? throw new ArgumentNullException(nameof(container)); + } - public async Task GetAsync(string path, CancellationToken cancellationToken) - { - return await _container - .GetBlockBlobReference(path) - .OpenReadAsync(cancellationToken); - } + public async Task GetAsync(string path, CancellationToken cancellationToken) + { + return await _container + .GetBlockBlobReference(path) + .OpenReadAsync(cancellationToken); + } - public Task GetDownloadUriAsync(string path, CancellationToken cancellationToken) + public Task GetDownloadUriAsync(string path, CancellationToken cancellationToken) + { + // TODO: Make expiry time configurable. + var blob = _container.GetBlockBlobReference(path); + var accessPolicy = new SharedAccessBlobPolicy { - // TODO: Make expiry time configurable. - var blob = _container.GetBlockBlobReference(path); - var accessPolicy = new SharedAccessBlobPolicy - { - SharedAccessExpiryTime = DateTimeOffset.Now.Add(TimeSpan.FromMinutes(10)), - Permissions = SharedAccessBlobPermissions.Read - }; + SharedAccessExpiryTime = DateTimeOffset.Now.Add(TimeSpan.FromMinutes(10)), + Permissions = SharedAccessBlobPermissions.Read + }; - var signature = blob.GetSharedAccessSignature(accessPolicy); - var result = new Uri(blob.Uri, signature); + var signature = blob.GetSharedAccessSignature(accessPolicy); + var result = new Uri(blob.Uri, signature); - return Task.FromResult(result); - } + return Task.FromResult(result); + } - public async Task PutAsync( - string path, - Stream content, - string contentType, - CancellationToken cancellationToken) - { - var blob = _container.GetBlockBlobReference(path); - var condition = AccessCondition.GenerateIfNotExistsCondition(); + public async Task PutAsync(string path, Stream content, string contentType, CancellationToken cancellationToken) + { + var blob = _container.GetBlockBlobReference(path); + var condition = AccessCondition.GenerateIfNotExistsCondition(); - blob.Properties.ContentType = contentType; + blob.Properties.ContentType = contentType; - try - { - await blob.UploadFromStreamAsync( - content, - condition, - options: null, - operationContext: null, - cancellationToken: cancellationToken); + try + { + await blob.UploadFromStreamAsync( + content, + condition, + options: null, + operationContext: null, + cancellationToken: cancellationToken); - return StoragePutResult.Success; - } - catch (StorageException e) when (e.IsAlreadyExistsException()) + return StoragePutResult.Success; + } + catch (StorageException e) when (e.IsAlreadyExistsException()) + { + using (var targetStream = await blob.OpenReadAsync(cancellationToken)) { - using (var targetStream = await blob.OpenReadAsync(cancellationToken)) - { - content.Position = 0; - return content.Matches(targetStream) - ? StoragePutResult.AlreadyExists - : StoragePutResult.Conflict; - } + content.Position = 0; + return content.Matches(targetStream) + ? StoragePutResult.AlreadyExists + : StoragePutResult.Conflict; } } + } - public async Task DeleteAsync(string path, CancellationToken cancellationToken) - { - await _container - .GetBlockBlobReference(path) - .DeleteIfExistsAsync(cancellationToken); - } + public async Task DeleteAsync(string path, CancellationToken cancellationToken) + { + await _container + .GetBlockBlobReference(path) + .DeleteIfExistsAsync(cancellationToken); } } diff --git a/src/BaGet.Azure/Table/PackageEntity.cs b/src/BaGet.Azure/Table/PackageEntity.cs index 46e9a053..bbe334fe 100644 --- a/src/BaGet.Azure/Table/PackageEntity.cs +++ b/src/BaGet.Azure/Table/PackageEntity.cs @@ -1,106 +1,101 @@ -using System; -using BaGet.Core; -using Microsoft.Azure.Cosmos.Table; +namespace BaGet.Azure; -namespace BaGet.Azure +/// +/// The Azure Table Storage entity that maps to a . +/// The is the and +/// the is the . +/// +public class PackageEntity : TableEntity, IDownloadCount, IListed { - /// - /// The Azure Table Storage entity that maps to a . - /// The is the and - /// the is the . - /// - public class PackageEntity : TableEntity, IDownloadCount, IListed + public PackageEntity() { - public PackageEntity() - { - } + } - public string Id { get; set; } - public string NormalizedVersion { get; set; } - public string OriginalVersion { get; set; } - public string Authors { get; set; } - public string Description { get; set; } - public long Downloads { get; set; } - public bool HasReadme { get; set; } - public bool HasEmbeddedIcon { get; set; } - public bool IsPrerelease { 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 int SemVerLevel { get; set; } - public string ReleaseNotes { get; set; } - public string Summary { get; set; } - public string Title { get; set; } + public string Id { get; set; } + public string NormalizedVersion { get; set; } + public string OriginalVersion { get; set; } + public string Authors { get; set; } + public string Description { get; set; } + public long Downloads { get; set; } + public bool HasReadme { get; set; } + public bool HasEmbeddedIcon { get; set; } + public bool IsPrerelease { 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 int SemVerLevel { get; set; } + public string ReleaseNotes { get; set; } + public string Summary { get; set; } + public string Title { get; set; } - public string IconUrl { get; set; } - public string LicenseUrl { get; set; } - public string ProjectUrl { get; set; } + public string IconUrl { get; set; } + public string LicenseUrl { get; set; } + public string ProjectUrl { get; set; } - public string RepositoryUrl { get; set; } - public string RepositoryType { get; set; } + public string RepositoryUrl { get; set; } + public string RepositoryType { get; set; } - public string Tags { get; set; } - public string Dependencies { get; set; } - public string PackageTypes { get; set; } - public string TargetFrameworks { get; set; } - } + public string Tags { get; set; } + public string Dependencies { get; set; } + public string PackageTypes { get; set; } + public string TargetFrameworks { get; set; } +} - /// - /// A single item in . - /// - public class DependencyModel - { - public string Id { get; set; } - public string VersionRange { get; set; } - public string TargetFramework { get; set; } - } +/// +/// A single item in . +/// +public class DependencyModel +{ + public string Id { get; set; } + public string VersionRange { get; set; } + public string TargetFramework { get; set; } +} - /// - /// A single item in . - /// - public class PackageTypeModel - { - public string Name { get; set; } - public string Version { get; set; } - } +/// +/// A single item in . +/// +public class PackageTypeModel +{ + public string Name { get; set; } + public string Version { get; set; } +} - /// - /// The Azure Table Storage entity to update the column. - /// The is the and - /// the is the . - /// - public class PackageListingEntity : TableEntity, IListed +/// +/// The Azure Table Storage entity to update the column. +/// The is the and +/// the is the . +/// +public class PackageListingEntity : TableEntity, IListed +{ + public PackageListingEntity() { - public PackageListingEntity() - { - } - - public bool Listed { get; set; } } - /// - /// The Azure Table Storage entity to update the column. - /// The is the and - /// the is the . - /// - public class PackageDownloadsEntity : TableEntity, IDownloadCount - { - public PackageDownloadsEntity() - { - } - - public long Downloads { get; set; } - } + public bool Listed { get; set; } +} - internal interface IListed +/// +/// The Azure Table Storage entity to update the column. +/// The is the and +/// the is the . +/// +public class PackageDownloadsEntity : TableEntity, IDownloadCount +{ + public PackageDownloadsEntity() { - bool Listed { get; set; } } - public interface IDownloadCount - { - long Downloads { get; set; } - } + public long Downloads { get; set; } +} + +internal interface IListed +{ + bool Listed { get; set; } +} + +public interface IDownloadCount +{ + long Downloads { get; set; } } diff --git a/src/BaGet.Azure/Table/PackageEntityExtensions.cs b/src/BaGet.Azure/Table/PackageEntityExtensions.cs index 52d2b87f..cba79078 100644 --- a/src/BaGet.Azure/Table/PackageEntityExtensions.cs +++ b/src/BaGet.Azure/Table/PackageEntityExtensions.cs @@ -1,85 +1,78 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using BaGet.Core; -using Newtonsoft.Json; +namespace BaGet.Azure; -namespace BaGet.Azure +public static class PackageEntityExtensions { - public static class PackageEntityExtensions + public static Package AsPackage(this PackageEntity entity) { - public static Package AsPackage(this PackageEntity entity) + return new Package { - return new Package - { - Id = entity.Id, - NormalizedVersionString = entity.NormalizedVersion, - OriginalVersionString = entity.OriginalVersion, + Id = entity.Id, + NormalizedVersionString = entity.NormalizedVersion, + OriginalVersionString = entity.OriginalVersion, - // TODO: Convert to System.Text.Json - Authors = JsonConvert.DeserializeObject(entity.Authors), - Description = entity.Description, - Downloads = entity.Downloads, - HasReadme = entity.HasReadme, - HasEmbeddedIcon = entity.HasEmbeddedIcon, - IsPrerelease = entity.IsPrerelease, - Language = entity.Language, - Listed = entity.Listed, - MinClientVersion = entity.MinClientVersion, - Published = entity.Published, - RequireLicenseAcceptance = entity.RequireLicenseAcceptance, - SemVerLevel = (SemVerLevel)entity.SemVerLevel, - Summary = entity.Summary, - Title = entity.Title, - ReleaseNotes = entity.ReleaseNotes, - IconUrl = ParseUri(entity.IconUrl), - LicenseUrl = ParseUri(entity.LicenseUrl), - ProjectUrl = ParseUri(entity.ProjectUrl), - RepositoryUrl = ParseUri(entity.RepositoryUrl), - RepositoryType = entity.RepositoryType, - Tags = JsonConvert.DeserializeObject(entity.Tags), - Dependencies = ParseDependencies(entity.Dependencies), - PackageTypes = ParsePackageTypes(entity.PackageTypes), - TargetFrameworks = ParseTargetFrameworks(entity.TargetFrameworks), - }; - } + // TODO: Convert to System.Text.Json + Authors = JsonConvert.DeserializeObject(entity.Authors), + Description = entity.Description, + Downloads = entity.Downloads, + HasReadme = entity.HasReadme, + HasEmbeddedIcon = entity.HasEmbeddedIcon, + IsPrerelease = entity.IsPrerelease, + Language = entity.Language, + Listed = entity.Listed, + MinClientVersion = entity.MinClientVersion, + Published = entity.Published, + RequireLicenseAcceptance = entity.RequireLicenseAcceptance, + SemVerLevel = (SemVerLevel)entity.SemVerLevel, + Summary = entity.Summary, + Title = entity.Title, + ReleaseNotes = entity.ReleaseNotes, + IconUrl = ParseUri(entity.IconUrl), + LicenseUrl = ParseUri(entity.LicenseUrl), + ProjectUrl = ParseUri(entity.ProjectUrl), + RepositoryUrl = ParseUri(entity.RepositoryUrl), + RepositoryType = entity.RepositoryType, + Tags = JsonConvert.DeserializeObject(entity.Tags), + Dependencies = ParseDependencies(entity.Dependencies), + PackageTypes = ParsePackageTypes(entity.PackageTypes), + TargetFrameworks = ParseTargetFrameworks(entity.TargetFrameworks), + }; + } - private static Uri ParseUri(string input) - { - return string.IsNullOrEmpty(input) ? null : new Uri(input); - } + private static Uri ParseUri(string input) + { + return string.IsNullOrEmpty(input) ? null : new Uri(input); + } - private static List ParseDependencies(string input) - { - // TODO: Convert to System.Text.Json - return JsonConvert.DeserializeObject>(input) - .Select(e => new PackageDependency - { - Id = e.Id, - VersionRange = e.VersionRange, - TargetFramework = e.TargetFramework, - }) - .ToList(); - } + private static List ParseDependencies(string input) + { + // TODO: Convert to System.Text.Json + return JsonConvert.DeserializeObject>(input) + .Select(e => new PackageDependency + { + Id = e.Id, + VersionRange = e.VersionRange, + TargetFramework = e.TargetFramework, + }) + .ToList(); + } - private static List ParsePackageTypes(string input) - { - // TODO: Convert to System.Text.Json - return JsonConvert.DeserializeObject>(input) - .Select(e => new PackageType - { - Name = e.Name, - Version = e.Version - }) - .ToList(); - } + private static List ParsePackageTypes(string input) + { + // TODO: Convert to System.Text.Json + return JsonConvert.DeserializeObject>(input) + .Select(e => new PackageType + { + Name = e.Name, + Version = e.Version + }) + .ToList(); + } - private static List ParseTargetFrameworks(string targetFrameworks) - { - // TODO: Convert to System.Text.Json - return JsonConvert.DeserializeObject>(targetFrameworks) - .Select(f => new TargetFramework { Moniker = f }) - .ToList(); - } + private static List ParseTargetFrameworks(string targetFrameworks) + { + // TODO: Convert to System.Text.Json + return JsonConvert.DeserializeObject>(targetFrameworks) + .Select(f => new TargetFramework { Moniker = f }) + .ToList(); } } diff --git a/src/BaGet.Azure/Table/TableOperationBuilder.cs b/src/BaGet.Azure/Table/TableOperationBuilder.cs index db4742ad..cdfbfa29 100644 --- a/src/BaGet.Azure/Table/TableOperationBuilder.cs +++ b/src/BaGet.Azure/Table/TableOperationBuilder.cs @@ -1,130 +1,125 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using BaGet.Core; -using Microsoft.Azure.Cosmos.Table; -using Newtonsoft.Json; -using NuGet.Versioning; - -namespace BaGet.Azure +namespace BaGet.Azure; + +public class TableOperationBuilder { - public class TableOperationBuilder + public TableOperation AddPackage(Package package) { - public TableOperation AddPackage(Package package) - { - if (package == null) throw new ArgumentNullException(nameof(package)); - - var version = package.Version; - var normalizedVersion = version.ToNormalizedString(); - - var entity = new PackageEntity - { - PartitionKey = package.Id.ToLowerInvariant(), - RowKey = normalizedVersion.ToLowerInvariant(), - - Id = package.Id, - NormalizedVersion = normalizedVersion, - OriginalVersion = version.ToFullString(), - Authors = JsonConvert.SerializeObject(package.Authors), - Description = package.Description, - Downloads = package.Downloads, - HasReadme = package.HasReadme, - HasEmbeddedIcon = package.HasEmbeddedIcon, - IsPrerelease = package.IsPrerelease, - Language = package.Language, - Listed = package.Listed, - MinClientVersion = package.MinClientVersion, - Published = package.Published, - RequireLicenseAcceptance = package.RequireLicenseAcceptance, - SemVerLevel = (int)package.SemVerLevel, - Summary = package.Summary, - Title = package.Title, - IconUrl = package.IconUrlString, - LicenseUrl = package.LicenseUrlString, - ReleaseNotes = package.ReleaseNotes, - ProjectUrl = package.ProjectUrlString, - RepositoryUrl = package.RepositoryUrlString, - RepositoryType = package.RepositoryType, - Tags = JsonConvert.SerializeObject(package.Tags), - Dependencies = SerializeList(package.Dependencies, AsDependencyModel), - PackageTypes = SerializeList(package.PackageTypes, AsPackageTypeModel), - TargetFrameworks = SerializeList(package.TargetFrameworks, f => f.Moniker) - }; - - return TableOperation.Insert(entity); - } - - public TableOperation UpdateDownloads(string packageId, NuGetVersion packageVersion, long downloads) - { - var entity = new PackageDownloadsEntity(); + if (package == null) throw new ArgumentNullException(nameof(package)); - entity.PartitionKey = packageId.ToLowerInvariant(); - entity.RowKey = packageVersion.ToNormalizedString().ToLowerInvariant(); - entity.Downloads = downloads; - entity.ETag = "*"; + var version = package.Version; + var normalizedVersion = version.ToNormalizedString(); - return TableOperation.Merge(entity); - } - - public TableOperation HardDeletePackage(string packageId, NuGetVersion packageVersion) + var entity = new PackageEntity { - var entity = new PackageEntity(); + PartitionKey = package.Id.ToLowerInvariant(), + RowKey = normalizedVersion.ToLowerInvariant(), + + Id = package.Id, + NormalizedVersion = normalizedVersion, + OriginalVersion = version.ToFullString(), + Authors = JsonConvert.SerializeObject(package.Authors), + Description = package.Description, + Downloads = package.Downloads, + HasReadme = package.HasReadme, + HasEmbeddedIcon = package.HasEmbeddedIcon, + IsPrerelease = package.IsPrerelease, + Language = package.Language, + Listed = package.Listed, + MinClientVersion = package.MinClientVersion, + Published = package.Published, + RequireLicenseAcceptance = package.RequireLicenseAcceptance, + SemVerLevel = (int)package.SemVerLevel, + Summary = package.Summary, + Title = package.Title, + IconUrl = package.IconUrlString, + LicenseUrl = package.LicenseUrlString, + ReleaseNotes = package.ReleaseNotes, + ProjectUrl = package.ProjectUrlString, + RepositoryUrl = package.RepositoryUrlString, + RepositoryType = package.RepositoryType, + Tags = JsonConvert.SerializeObject(package.Tags), + Dependencies = SerializeList(package.Dependencies, AsDependencyModel), + PackageTypes = SerializeList(package.PackageTypes, AsPackageTypeModel), + TargetFrameworks = SerializeList(package.TargetFrameworks, f => f.Moniker) + }; + + return TableOperation.Insert(entity); + } - entity.PartitionKey = packageId.ToLowerInvariant(); - entity.RowKey = packageVersion.ToNormalizedString().ToLowerInvariant(); - entity.ETag = "*"; + public TableOperation UpdateDownloads(string packageId, NuGetVersion packageVersion, long downloads) + { + var entity = new PackageDownloadsEntity + { + PartitionKey = packageId.ToLowerInvariant(), + RowKey = packageVersion.ToNormalizedString().ToLowerInvariant(), + Downloads = downloads, + ETag = "*" + }; - return TableOperation.Delete(entity); - } + return TableOperation.Merge(entity); + } - public TableOperation UnlistPackage(string packageId, NuGetVersion packageVersion) + public TableOperation HardDeletePackage(string packageId, NuGetVersion packageVersion) + { + var entity = new PackageEntity { - var entity = new PackageListingEntity(); + PartitionKey = packageId.ToLowerInvariant(), + RowKey = packageVersion.ToNormalizedString().ToLowerInvariant(), + ETag = "*" + }; - entity.PartitionKey = packageId.ToLowerInvariant(); - entity.RowKey = packageVersion.ToNormalizedString().ToLowerInvariant(); - entity.Listed = false; - entity.ETag = "*"; - - return TableOperation.Merge(entity); - } + return TableOperation.Delete(entity); + } - public TableOperation RelistPackage(string packageId, NuGetVersion packageVersion) + public TableOperation UnlistPackage(string packageId, NuGetVersion packageVersion) + { + var entity = new PackageListingEntity { - var entity = new PackageListingEntity(); + PartitionKey = packageId.ToLowerInvariant(), + RowKey = packageVersion.ToNormalizedString().ToLowerInvariant(), + Listed = false, + ETag = "*" + }; - entity.PartitionKey = packageId.ToLowerInvariant(); - entity.RowKey = packageVersion.ToNormalizedString().ToLowerInvariant(); - entity.Listed = true; - entity.ETag = "*"; - - return TableOperation.Merge(entity); - } + return TableOperation.Merge(entity); + } - private static string SerializeList(IReadOnlyList objects, Func map) + public TableOperation RelistPackage(string packageId, NuGetVersion packageVersion) + { + var entity = new PackageListingEntity { - var data = objects.Select(map).ToList(); + PartitionKey = packageId.ToLowerInvariant(), + RowKey = packageVersion.ToNormalizedString().ToLowerInvariant(), + Listed = true, + ETag = "*" + }; + + return TableOperation.Merge(entity); + } + + private static string SerializeList(IReadOnlyList objects, Func map) + { + var data = objects.Select(map).ToList(); - return JsonConvert.SerializeObject(data); - } + return JsonConvert.SerializeObject(data); + } - public static DependencyModel AsDependencyModel(PackageDependency dependency) + public static DependencyModel AsDependencyModel(PackageDependency dependency) + { + return new DependencyModel { - return new DependencyModel - { - Id = dependency.Id, - VersionRange = dependency.VersionRange, - TargetFramework = dependency.TargetFramework - }; - } - - public static PackageTypeModel AsPackageTypeModel(PackageType packageType) + Id = dependency.Id, + VersionRange = dependency.VersionRange, + TargetFramework = dependency.TargetFramework + }; + } + + public static PackageTypeModel AsPackageTypeModel(PackageType packageType) + { + return new PackageTypeModel { - return new PackageTypeModel - { - Name = packageType.Name, - Version = packageType.Version - }; - } + Name = packageType.Name, + Version = packageType.Version + }; } } diff --git a/src/BaGet.Azure/Table/TablePackageDatabase.cs b/src/BaGet.Azure/Table/TablePackageDatabase.cs index 83174df4..d7862354 100644 --- a/src/BaGet.Azure/Table/TablePackageDatabase.cs +++ b/src/BaGet.Azure/Table/TablePackageDatabase.cs @@ -1,215 +1,191 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using BaGet.Core; -using Microsoft.Azure.Cosmos.Table; -using Microsoft.Extensions.Logging; -using NuGet.Versioning; - -namespace BaGet.Azure +namespace BaGet.Azure; + +/// +/// Stores the metadata of packages using Azure Table Storage. +/// +public class TablePackageDatabase : IPackageDatabase { - /// - /// Stores the metadata of packages using Azure Table Storage. - /// - public class TablePackageDatabase : IPackageDatabase + private const string TableName = "Packages"; + private const int MaxPreconditionFailures = 5; + + private readonly TableOperationBuilder _operationBuilder; + private readonly CloudTable _table; + private readonly ILogger _logger; + + public TablePackageDatabase(TableOperationBuilder operationBuilder, CloudTableClient client, ILogger logger) { - private const string TableName = "Packages"; - private const int MaxPreconditionFailures = 5; + _operationBuilder = operationBuilder ?? throw new ArgumentNullException(nameof(operationBuilder)); + _table = client?.GetTableReference(TableName) ?? throw new ArgumentNullException(nameof(client)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - private readonly TableOperationBuilder _operationBuilder; - private readonly CloudTable _table; - private readonly ILogger _logger; + public async Task AddAsync(Package package, CancellationToken cancellationToken) + { + try + { + var operation = _operationBuilder.AddPackage(package); - public TablePackageDatabase( - TableOperationBuilder operationBuilder, - CloudTableClient client, - ILogger logger) + await _table.ExecuteAsync(operation, cancellationToken); + } + catch (StorageException e) when (e.IsAlreadyExistsException()) { - _operationBuilder = operationBuilder ?? throw new ArgumentNullException(nameof(operationBuilder)); - _table = client?.GetTableReference(TableName) ?? throw new ArgumentNullException(nameof(client)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + return PackageAddResult.PackageAlreadyExists; } - public async Task AddAsync(Package package, CancellationToken cancellationToken) + return PackageAddResult.Success; + } + + public async Task AddDownloadAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + { + var attempt = 0; + + while (true) { try { - var operation = _operationBuilder.AddPackage(package); + var operation = TableOperation.Retrieve( + id.ToLowerInvariant(), + version.ToNormalizedString().ToLowerInvariant()); + + var result = await _table.ExecuteAsync(operation, cancellationToken); + var entity = result.Result as PackageDownloadsEntity; - await _table.ExecuteAsync(operation, cancellationToken); + if (entity == null) + { + return; + } + + entity.Downloads += 1; + + await _table.ExecuteAsync(TableOperation.Merge(entity), cancellationToken); + return; } - catch (StorageException e) when (e.IsAlreadyExistsException()) + catch (StorageException e) + when (attempt < MaxPreconditionFailures && e.IsPreconditionFailedException()) { - return PackageAddResult.PackageAlreadyExists; + attempt++; + _logger.LogWarning( + e, + $"Retrying due to precondition failure, attempt {{Attempt}} of {MaxPreconditionFailures}..", + attempt); } - - return PackageAddResult.Success; } + } - public async Task AddDownloadAsync( - string id, - NuGetVersion version, - CancellationToken cancellationToken) - { - var attempt = 0; + public async Task ExistsAsync(string id, CancellationToken cancellationToken) + { + var filter = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, id.ToLowerInvariant()); + var query = new TableQuery() + .Select(MinimalColumnSet) + .Where(filter) + .Take(1); - while (true) - { - try - { - var operation = TableOperation.Retrieve( - id.ToLowerInvariant(), - version.ToNormalizedString().ToLowerInvariant()); + var result = await _table.ExecuteQuerySegmentedAsync(query, token: null, cancellationToken); - var result = await _table.ExecuteAsync(operation, cancellationToken); - var entity = result.Result as PackageDownloadsEntity; + return result.Results.Any(); + } - if (entity == null) - { - return; - } + public async Task ExistsAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + { + var operation = TableOperation.Retrieve( + id.ToLowerInvariant(), + version.ToNormalizedString().ToLowerInvariant(), + MinimalColumnSet); - entity.Downloads += 1; + var execution = await _table.ExecuteAsync(operation, cancellationToken); - await _table.ExecuteAsync(TableOperation.Merge(entity), cancellationToken); - return; - } - catch (StorageException e) - when (attempt < MaxPreconditionFailures && e.IsPreconditionFailedException()) - { - attempt++; - _logger.LogWarning( - e, - $"Retrying due to precondition failure, attempt {{Attempt}} of {MaxPreconditionFailures}..", - attempt); - } - } - } + return execution.Result is PackageEntity; + } - public async Task ExistsAsync(string id, CancellationToken cancellationToken) + public async Task> FindAsync(string id, bool includeUnlisted, CancellationToken cancellationToken) + { + var filter = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, id.ToLowerInvariant()); + if (!includeUnlisted) { - var filter = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, id.ToLowerInvariant()); - var query = new TableQuery() - .Select(MinimalColumnSet) - .Where(filter) - .Take(1); - - var result = await _table.ExecuteQuerySegmentedAsync(query, token: null, cancellationToken); - - return result.Results.Any(); + filter = TableQuery.CombineFilters( + filter, + TableOperators.And, + TableQuery.GenerateFilterConditionForBool(nameof(PackageEntity.Listed), QueryComparisons.Equal, true)); } - public async Task ExistsAsync( - string id, - NuGetVersion version, - CancellationToken cancellationToken) - { - var operation = TableOperation.Retrieve( - id.ToLowerInvariant(), - version.ToNormalizedString().ToLowerInvariant(), - MinimalColumnSet); - - var execution = await _table.ExecuteAsync(operation, cancellationToken); + var query = new TableQuery().Where(filter); + var results = new List(); - return execution.Result is PackageEntity; - } + // Request 500 results at a time from the server. + TableContinuationToken token = null; + query.TakeCount = 500; - public async Task> FindAsync(string id, bool includeUnlisted, CancellationToken cancellationToken) + do { - var filter = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, id.ToLowerInvariant()); - if (!includeUnlisted) - { - filter = TableQuery.CombineFilters( - filter, - TableOperators.And, - TableQuery.GenerateFilterConditionForBool(nameof(PackageEntity.Listed), QueryComparisons.Equal, true)); - } + var segment = await _table.ExecuteQuerySegmentedAsync(query, token, cancellationToken); - var query = new TableQuery().Where(filter); - var results = new List(); + token = segment.ContinuationToken; - // Request 500 results at a time from the server. - TableContinuationToken token = null; - query.TakeCount = 500; + // Write out the properties for each entity returned. + results.AddRange(segment.Results.Select(r => r.AsPackage())); + } + while (token != null); - do - { - var segment = await _table.ExecuteQuerySegmentedAsync(query, token, cancellationToken); + return results.OrderBy(p => p.Version).ToList(); + } - token = segment.ContinuationToken; + public async Task FindOrNullAsync(string id, NuGetVersion version, bool includeUnlisted, CancellationToken cancellationToken) + { + var operation = TableOperation.Retrieve( + id.ToLowerInvariant(), + version.ToNormalizedString().ToLowerInvariant()); - // Write out the properties for each entity returned. - results.AddRange(segment.Results.Select(r => r.AsPackage())); - } - while (token != null); + var result = await _table.ExecuteAsync(operation, cancellationToken); + var entity = result.Result as PackageEntity; - return results.OrderBy(p => p.Version).ToList(); + if (entity == null) + { + return null; } - public async Task FindOrNullAsync( - string id, - NuGetVersion version, - bool includeUnlisted, - CancellationToken cancellationToken) + // Filter out the package if it's unlisted. + if (!includeUnlisted && !entity.Listed) { - var operation = TableOperation.Retrieve( - id.ToLowerInvariant(), - version.ToNormalizedString().ToLowerInvariant()); + return null; + } - var result = await _table.ExecuteAsync(operation, cancellationToken); - var entity = result.Result as PackageEntity; + return entity.AsPackage(); + } - if (entity == null) - { - return null; - } + public async Task HardDeletePackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + { + return await TryUpdatePackageAsync( + _operationBuilder.HardDeletePackage(id, version), + cancellationToken); + } - // Filter out the package if it's unlisted. - if (!includeUnlisted && !entity.Listed) - { - return null; - } + public async Task RelistPackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + { + return await TryUpdatePackageAsync( + _operationBuilder.RelistPackage(id, version), + cancellationToken); + } - return entity.AsPackage(); - } + public async Task UnlistPackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + { + return await TryUpdatePackageAsync( + _operationBuilder.UnlistPackage(id, version), + cancellationToken); + } - public async Task HardDeletePackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken) - { - return await TryUpdatePackageAsync( - _operationBuilder.HardDeletePackage(id, version), - cancellationToken); - } + private List MinimalColumnSet => new List { "PartitionKey" }; - public async Task RelistPackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + private async Task TryUpdatePackageAsync(TableOperation operation, CancellationToken cancellationToken) + { + try { - return await TryUpdatePackageAsync( - _operationBuilder.RelistPackage(id, version), - cancellationToken); + await _table.ExecuteAsync(operation, cancellationToken); } - - public async Task UnlistPackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + catch (StorageException e) when (e.IsNotFoundException()) { - return await TryUpdatePackageAsync( - _operationBuilder.UnlistPackage(id, version), - cancellationToken); + return false; } - private List MinimalColumnSet => new List { "PartitionKey" }; - - private async Task TryUpdatePackageAsync(TableOperation operation, CancellationToken cancellationToken) - { - try - { - await _table.ExecuteAsync(operation, cancellationToken); - } - catch (StorageException e) when (e.IsNotFoundException()) - { - return false; - } - - return true; - } + return true; } } diff --git a/src/BaGet.Azure/Table/TableSearchService.cs b/src/BaGet.Azure/Table/TableSearchService.cs index c5beb10a..d1e3b555 100644 --- a/src/BaGet.Azure/Table/TableSearchService.cs +++ b/src/BaGet.Azure/Table/TableSearchService.cs @@ -1,209 +1,175 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using BaGet.Core; -using BaGet.Protocol.Models; -using Microsoft.Azure.Cosmos.Table; - -namespace BaGet.Azure +namespace BaGet.Azure; + +public class TableSearchService : ISearchService { - public class TableSearchService : ISearchService + private const string TableName = "Packages"; + + private readonly CloudTable _table; + private readonly ISearchResponseBuilder _responseBuilder; + + public TableSearchService(CloudTableClient client, ISearchResponseBuilder responseBuilder) { - private const string TableName = "Packages"; + _table = client?.GetTableReference(TableName) ?? throw new ArgumentNullException(nameof(client)); + _responseBuilder = responseBuilder ?? throw new ArgumentNullException(nameof(responseBuilder)); + } - private readonly CloudTable _table; - private readonly ISearchResponseBuilder _responseBuilder; + public async Task SearchAsync(SearchRequest request, CancellationToken cancellationToken) + { + var results = await SearchAsync( + request.Query, + request.Skip, + request.Take, + request.IncludePrerelease, + request.IncludeSemVer2, + cancellationToken); + + return _responseBuilder.BuildSearch(results); + } - public TableSearchService( - CloudTableClient client, - ISearchResponseBuilder responseBuilder) - { - _table = client?.GetTableReference(TableName) ?? throw new ArgumentNullException(nameof(client)); - _responseBuilder = responseBuilder ?? throw new ArgumentNullException(nameof(responseBuilder)); - } + public async Task AutocompleteAsync(AutocompleteRequest request, CancellationToken cancellationToken) + { + var results = await SearchAsync( + request.Query, + request.Skip, + request.Take, + request.IncludePrerelease, + request.IncludeSemVer2, + cancellationToken); - public async Task SearchAsync( - SearchRequest request, - CancellationToken cancellationToken) - { - var results = await SearchAsync( - request.Query, - request.Skip, - request.Take, - request.IncludePrerelease, - request.IncludeSemVer2, - cancellationToken); - - return _responseBuilder.BuildSearch(results); - } + var packageIds = results.Select(p => p.PackageId).ToList(); - public async Task AutocompleteAsync( - AutocompleteRequest request, - CancellationToken cancellationToken) - { - var results = await SearchAsync( - request.Query, - request.Skip, - request.Take, - request.IncludePrerelease, - request.IncludeSemVer2, - cancellationToken); + return _responseBuilder.BuildAutocomplete(packageIds); + } - var packageIds = results.Select(p => p.PackageId).ToList(); + public Task ListPackageVersionsAsync(VersionsRequest request, CancellationToken cancellationToken) + { + // TODO: Support versions autocomplete. + // See: https://github.com/loic-sharma/BaGet/issues/291 + var response = _responseBuilder.BuildAutocomplete(new List()); - return _responseBuilder.BuildAutocomplete(packageIds); - } + return Task.FromResult(response); + } - public Task ListPackageVersionsAsync( - VersionsRequest request, - CancellationToken cancellationToken) - { - // TODO: Support versions autocomplete. - // See: https://github.com/loic-sharma/BaGet/issues/291 - var response = _responseBuilder.BuildAutocomplete(new List()); + public Task FindDependentsAsync(string packageId, CancellationToken cancellationToken) + { + var response = _responseBuilder.BuildDependents(new List()); - return Task.FromResult(response); - } + return Task.FromResult(response); + } - public Task FindDependentsAsync(string packageId, CancellationToken cancellationToken) - { - var response = _responseBuilder.BuildDependents(new List()); + private async Task> SearchAsync(string searchText, int skip, int take, bool includePrerelease, bool includeSemVer2, CancellationToken cancellationToken) + { + var query = new TableQuery(); + query = query.Where(GenerateSearchFilter(searchText, includePrerelease, includeSemVer2)); + query.TakeCount = 500; + + var results = await LoadPackagesAsync(query, maxPartitions: skip + take, cancellationToken); + + return results + .GroupBy(p => p.Id, StringComparer.OrdinalIgnoreCase) + .Select(group => new PackageRegistration(group.Key, group.ToList())) + .Skip(skip) + .Take(take) + .ToList(); + } - return Task.FromResult(response); - } + private async Task> LoadPackagesAsync(TableQuery query, int maxPartitions, CancellationToken cancellationToken) + { + var results = new List(); - private async Task> SearchAsync( - string searchText, - int skip, - int take, - bool includePrerelease, - bool includeSemVer2, - CancellationToken cancellationToken) + var partitions = 0; + string lastPartitionKey = null; + TableContinuationToken token = null; + do { - var query = new TableQuery(); - query = query.Where(GenerateSearchFilter(searchText, includePrerelease, includeSemVer2)); - query.TakeCount = 500; - - var results = await LoadPackagesAsync(query, maxPartitions: skip + take, cancellationToken); - - return results - .GroupBy(p => p.Id, StringComparer.OrdinalIgnoreCase) - .Select(group => new PackageRegistration(group.Key, group.ToList())) - .Skip(skip) - .Take(take) - .ToList(); - } + var segment = await _table.ExecuteQuerySegmentedAsync(query, token, cancellationToken); - private async Task> LoadPackagesAsync( - TableQuery query, - int maxPartitions, - CancellationToken cancellationToken) - { - var results = new List(); + token = segment.ContinuationToken; - var partitions = 0; - string lastPartitionKey = null; - TableContinuationToken token = null; - do + foreach (var result in segment.Results) { - var segment = await _table.ExecuteQuerySegmentedAsync(query, token, cancellationToken); - - token = segment.ContinuationToken; - - foreach (var result in segment.Results) + if (lastPartitionKey != result.PartitionKey) { - if (lastPartitionKey != result.PartitionKey) - { - lastPartitionKey = result.PartitionKey; - partitions++; + lastPartitionKey = result.PartitionKey; + partitions++; - if (partitions > maxPartitions) - { - break; - } + if (partitions > maxPartitions) + { + break; } - - results.Add(result.AsPackage()); } - } - while (token != null); - return results; + results.Add(result.AsPackage()); + } } + while (token != null); + + return results; + } + + private string GenerateSearchFilter(string searchText, bool includePrerelease, bool includeSemVer2) + { + var result = ""; - private string GenerateSearchFilter(string searchText, bool includePrerelease, bool includeSemVer2) + if (!string.IsNullOrWhiteSpace(searchText)) { - var result = ""; + // Filter to rows where the "searchText" prefix matches on the partition key. + var prefix = searchText.TrimEnd().Split(separator: null).Last(); - if (!string.IsNullOrWhiteSpace(searchText)) - { - // Filter to rows where the "searchText" prefix matches on the partition key. - var prefix = searchText.TrimEnd().Split(separator: null).Last(); + var prefixLower = prefix; + var prefixUpper = prefix + "~"; - var prefixLower = prefix; - var prefixUpper = prefix + "~"; + var partitionLowerFilter = TableQuery.GenerateFilterCondition( + "PartitionKey", + QueryComparisons.GreaterThanOrEqual, + prefixLower); - var partitionLowerFilter = TableQuery.GenerateFilterCondition( - "PartitionKey", - QueryComparisons.GreaterThanOrEqual, - prefixLower); + var partitionUpperFilter = TableQuery.GenerateFilterCondition( + "PartitionKey", + QueryComparisons.LessThanOrEqual, + prefixUpper); - var partitionUpperFilter = TableQuery.GenerateFilterCondition( - "PartitionKey", - QueryComparisons.LessThanOrEqual, - prefixUpper); + result = GenerateAnd(partitionLowerFilter, partitionUpperFilter); + } - result = GenerateAnd(partitionLowerFilter, partitionUpperFilter); - } + // Filter to rows that are listed. + result = GenerateAnd(result, GenerateIsTrue(nameof(PackageEntity.Listed))); - // Filter to rows that are listed. + if (!includePrerelease) + { result = GenerateAnd( result, - GenerateIsTrue(nameof(PackageEntity.Listed))); - - if (!includePrerelease) - { - result = GenerateAnd( - result, - GenerateIsFalse(nameof(PackageEntity.IsPrerelease))); - } + GenerateIsFalse(nameof(PackageEntity.IsPrerelease))); + } - if (!includeSemVer2) - { - result = GenerateAnd( - result, - TableQuery.GenerateFilterConditionForInt( - nameof(PackageEntity.SemVerLevel), - QueryComparisons.Equal, - 0)); - } + if (!includeSemVer2) + { + result = GenerateAnd(result, TableQuery.GenerateFilterConditionForInt(nameof(PackageEntity.SemVerLevel), QueryComparisons.Equal, 0)); + } - return result; + return result; - string GenerateAnd(string left, string right) - { - if (string.IsNullOrEmpty(left)) return right; + string GenerateAnd(string left, string right) + { + if (string.IsNullOrEmpty(left)) return right; - return TableQuery.CombineFilters(left, TableOperators.And, right); - } + return TableQuery.CombineFilters(left, TableOperators.And, right); + } - string GenerateIsTrue(string propertyName) - { - return TableQuery.GenerateFilterConditionForBool( - propertyName, - QueryComparisons.Equal, - givenValue: true); - } + string GenerateIsTrue(string propertyName) + { + return TableQuery.GenerateFilterConditionForBool( + propertyName, + QueryComparisons.Equal, + givenValue: true); + } - string GenerateIsFalse(string propertyName) - { - return TableQuery.GenerateFilterConditionForBool( - propertyName, - QueryComparisons.Equal, - givenValue: false); - } + string GenerateIsFalse(string propertyName) + { + return TableQuery.GenerateFilterConditionForBool( + propertyName, + QueryComparisons.Equal, + givenValue: false); } } } diff --git a/src/BaGet.Core/Authentication/ApiKeyAuthenticationService.cs b/src/BaGet.Core/Authentication/ApiKeyAuthenticationService.cs index 7c4c63de..3cae9099 100644 --- a/src/BaGet.Core/Authentication/ApiKeyAuthenticationService.cs +++ b/src/BaGet.Core/Authentication/ApiKeyAuthenticationService.cs @@ -1,30 +1,26 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Options; -namespace BaGet.Core +namespace BaGet.Core; + +public class ApiKeyAuthenticationService : IAuthenticationService { - public class ApiKeyAuthenticationService : IAuthenticationService - { - private readonly string _apiKey; + private readonly string _apiKey; - public ApiKeyAuthenticationService(IOptionsSnapshot options) - { - if (options == null) throw new ArgumentNullException(nameof(options)); + public ApiKeyAuthenticationService(IOptionsSnapshot options) + { + if (options == null) throw new ArgumentNullException(nameof(options)); - _apiKey = string.IsNullOrEmpty(options.Value.ApiKey) ? null : options.Value.ApiKey; - } + _apiKey = string.IsNullOrEmpty(options.Value.ApiKey) ? null : options.Value.ApiKey; + } - public Task AuthenticateAsync(string apiKey, CancellationToken cancellationToken) - => Task.FromResult(Authenticate(apiKey)); + public Task AuthenticateAsync(string apiKey, CancellationToken cancellationToken) + => Task.FromResult(Authenticate(apiKey)); - private bool Authenticate(string apiKey) - { - // No authentication is necessary if there is no required API key. - if (_apiKey == null) return true; + private bool Authenticate(string apiKey) + { + // No authentication is necessary if there is no required API key. + if (_apiKey == null) return true; - return _apiKey == apiKey; - } + return _apiKey == apiKey; } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Authentication/IAuthenticationService.cs b/src/BaGet.Core/Authentication/IAuthenticationService.cs index 3a3a0242..64a37614 100644 --- a/src/BaGet.Core/Authentication/IAuthenticationService.cs +++ b/src/BaGet.Core/Authentication/IAuthenticationService.cs @@ -1,10 +1,6 @@ -using System.Threading; -using System.Threading.Tasks; +namespace BaGet.Core; -namespace BaGet.Core +public interface IAuthenticationService { - public interface IAuthenticationService - { - Task AuthenticateAsync(string apiKey, CancellationToken cancellationToken); - } -} + Task AuthenticateAsync(string apiKey, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/BaGet.Core/BaGet.Core.csproj b/src/BaGet.Core/BaGet.Core.csproj index e8878842..328a1654 100644 --- a/src/BaGet.Core/BaGet.Core.csproj +++ b/src/BaGet.Core/BaGet.Core.csproj @@ -1,23 +1,23 @@ - netstandard2.0 + net6.0 NuGet The core libraries that power BaGet. - - - - - - + + + + + + - + \ No newline at end of file diff --git a/src/BaGet.Core/BaGetApplication.cs b/src/BaGet.Core/BaGetApplication.cs index 9894b41a..d3783bb9 100644 --- a/src/BaGet.Core/BaGetApplication.cs +++ b/src/BaGet.Core/BaGetApplication.cs @@ -1,15 +1,13 @@ -using System; using Microsoft.Extensions.DependencyInjection; -namespace BaGet.Core +namespace BaGet.Core; + +public class BaGetApplication { - public class BaGetApplication + public BaGetApplication(IServiceCollection services) { - public BaGetApplication(IServiceCollection services) - { - Services = services ?? throw new ArgumentNullException(nameof(services)); - } - - public IServiceCollection Services { get; } + Services = services ?? throw new ArgumentNullException(nameof(services)); } -} + + public IServiceCollection Services { get; } +} \ No newline at end of file diff --git a/src/BaGet.Core/Configuration/BaGetOptions.cs b/src/BaGet.Core/Configuration/BaGetOptions.cs index efd19ee8..3515fe3d 100644 --- a/src/BaGet.Core/Configuration/BaGetOptions.cs +++ b/src/BaGet.Core/Configuration/BaGetOptions.cs @@ -1,54 +1,51 @@ -using System.ComponentModel.DataAnnotations; +namespace BaGet.Core; -namespace BaGet.Core +public class BaGetOptions { - public class BaGetOptions - { - /// - /// The API Key required to authenticate package - /// operations. If empty, package operations do not require authentication. - /// - public string ApiKey { 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; - - /// - /// If enabled, pushing a package that already exists will replace the - /// existing package. - /// - public bool AllowPackageOverwrites { get; set; } = false; - - /// - /// If true, disables package pushing, deleting, and re-listing. - /// - public bool IsReadOnlyMode { get; set; } = false; - - /// - /// The URLs the BaGet server will use. - /// As per documentation here (Server URLs). - /// - public string Urls { get; set; } - - public DatabaseOptions Database { get; set; } - - public StorageOptions Storage { get; set; } - - public SearchOptions Search { get; set; } - - public MirrorOptions Mirror { get; set; } - } -} + /// + /// The API Key required to authenticate package + /// operations. If empty, package operations do not require authentication. + /// + public string ApiKey { 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; + + /// + /// If enabled, pushing a package that already exists will replace the + /// existing package. + /// + public bool AllowPackageOverwrites { get; set; } = false; + + /// + /// If true, disables package pushing, deleting, and re-listing. + /// + public bool IsReadOnlyMode { get; set; } = false; + + /// + /// The URLs the BaGet server will use. + /// As per documentation here (Server URLs). + /// + public string Urls { get; set; } + + public DatabaseOptions Database { get; set; } + + public StorageOptions Storage { get; set; } + + public SearchOptions Search { get; set; } + + public MirrorOptions Mirror { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Core/Configuration/BindOptions.cs b/src/BaGet.Core/Configuration/BindOptions.cs index ca6fd47a..b16c8ebe 100644 --- a/src/BaGet.Core/Configuration/BindOptions.cs +++ b/src/BaGet.Core/Configuration/BindOptions.cs @@ -1,26 +1,24 @@ -using System; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Options; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// Automatically binds configs to options. +/// +/// The options to bind to. +public class BindOptions : IConfigureOptions where TOptions : class { + private readonly IConfiguration _config; + /// - /// Automatically binds configs to options. + /// Automatically bind these configurations to the options. /// - /// The options to bind to. - public class BindOptions : IConfigureOptions where TOptions : class + /// The configs to automatically bind to options. + public BindOptions(IConfiguration config) { - private readonly IConfiguration _config; - - /// - /// Automatically bind these configurations to the options. - /// - /// The configs to automatically bind to options. - public BindOptions(IConfiguration config) - { - _config = config ?? throw new ArgumentNullException(nameof(config)); - } - - public void Configure(TOptions options) => _config.Bind(options); + _config = config ?? throw new ArgumentNullException(nameof(config)); } -} + + public void Configure(TOptions options) => _config.Bind(options); +} \ No newline at end of file diff --git a/src/BaGet.Core/Configuration/DatabaseOptions.cs b/src/BaGet.Core/Configuration/DatabaseOptions.cs index 0b95a710..e25e8474 100644 --- a/src/BaGet.Core/Configuration/DatabaseOptions.cs +++ b/src/BaGet.Core/Configuration/DatabaseOptions.cs @@ -1,12 +1,11 @@ using System.ComponentModel.DataAnnotations; -namespace BaGet.Core +namespace BaGet.Core; + +public class DatabaseOptions { - public class DatabaseOptions - { - public string Type { get; set; } + public string Type { get; set; } - [Required] - public string ConnectionString { get; set; } - } -} + [Required] + public string ConnectionString { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Core/Configuration/FileSystemStorageOptions.cs b/src/BaGet.Core/Configuration/FileSystemStorageOptions.cs index a74dba3d..9d5c2384 100644 --- a/src/BaGet.Core/Configuration/FileSystemStorageOptions.cs +++ b/src/BaGet.Core/Configuration/FileSystemStorageOptions.cs @@ -1,28 +1,24 @@ -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.IO; -using System.Linq; -namespace BaGet.Core +namespace BaGet.Core; + +public class FileSystemStorageOptions : IValidatableObject { - public class FileSystemStorageOptions : IValidatableObject - { - /// - /// 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; } + /// + /// 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; } - public IEnumerable Validate(ValidationContext validationContext) + public IEnumerable Validate(ValidationContext validationContext) + { + // Convert an empty storage path to the current working directory. + if (string.IsNullOrEmpty(Path)) { - // Convert an empty storage path to the current working directory. - if (string.IsNullOrEmpty(Path)) - { - Path = Directory.GetCurrentDirectory(); - } - - return Enumerable.Empty(); + Path = Directory.GetCurrentDirectory(); } + + return Enumerable.Empty(); } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Configuration/MirrorOptions.cs b/src/BaGet.Core/Configuration/MirrorOptions.cs index 09db895d..c374d55a 100644 --- a/src/BaGet.Core/Configuration/MirrorOptions.cs +++ b/src/BaGet.Core/Configuration/MirrorOptions.cs @@ -1,41 +1,38 @@ -using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -namespace BaGet.Core +namespace BaGet.Core; + +public class MirrorOptions : IValidatableObject { - public class MirrorOptions : IValidatableObject - { - /// - /// If true, packages that aren't found locally will be indexed - /// using the upstream source. - /// - public bool Enabled { get; set; } + /// + /// 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 v3 index that will be mirrored. + /// + public Uri PackageSource { get; set; } - /// - /// Whether or not the package source is a v2 package source feed. - /// - public bool Legacy { get; set; } + /// + /// Whether or not the package source is a v2 package source feed. + /// + public bool Legacy { get; set; } - /// - /// The time before a download from the package source times out. - /// - [Range(0, int.MaxValue)] - public int PackageDownloadTimeoutSeconds { get; set; } = 600; + /// + /// 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) + public IEnumerable Validate(ValidationContext validationContext) + { + if (Enabled && PackageSource == null) { - if (Enabled && PackageSource == null) - { - yield return new ValidationResult( - $"The {nameof(PackageSource)} configuration is required if mirroring is enabled", - new[] { nameof(PackageSource) }); - } + yield return new ValidationResult( + $"The {nameof(PackageSource)} configuration is required if mirroring is enabled", + new[] { nameof(PackageSource) }); } } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Configuration/PackageDeletionBehavior.cs b/src/BaGet.Core/Configuration/PackageDeletionBehavior.cs index 01c68d7f..0f6b4542 100644 --- a/src/BaGet.Core/Configuration/PackageDeletionBehavior.cs +++ b/src/BaGet.Core/Configuration/PackageDeletionBehavior.cs @@ -1,22 +1,21 @@ -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// 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 { /// - /// How BaGet should interpret package deletion requests. - /// See: https://docs.microsoft.com/en-us/nuget/api/package-publish-resource#delete-a-package + /// 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. /// - 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, + Unlist, - /// - /// Removes the package from the database and storage. Existing consumers will no longer - /// be able to restore the package. - /// - HardDelete, - } -} + /// + /// Removes the package from the database and storage. Existing consumers will no longer + /// be able to restore the package. + /// + HardDelete, +} \ No newline at end of file diff --git a/src/BaGet.Core/Configuration/SearchOptions.cs b/src/BaGet.Core/Configuration/SearchOptions.cs index 52f430cb..6822f85d 100644 --- a/src/BaGet.Core/Configuration/SearchOptions.cs +++ b/src/BaGet.Core/Configuration/SearchOptions.cs @@ -1,7 +1,6 @@ -namespace BaGet.Core +namespace BaGet.Core; + +public class SearchOptions { - public class SearchOptions - { - public string Type { get; set; } - } -} + public string Type { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Core/Configuration/StorageOptions.cs b/src/BaGet.Core/Configuration/StorageOptions.cs index be8c8a5d..f3fe03c5 100644 --- a/src/BaGet.Core/Configuration/StorageOptions.cs +++ b/src/BaGet.Core/Configuration/StorageOptions.cs @@ -1,7 +1,6 @@ -namespace BaGet.Core +namespace BaGet.Core; + +public class StorageOptions { - public class StorageOptions - { - public string Type { get; set; } - } -} + public string Type { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Core/Content/DefaultPackageContentService.cs b/src/BaGet.Core/Content/DefaultPackageContentService.cs index 8d0a76d1..de8603e7 100644 --- a/src/BaGet.Core/Content/DefaultPackageContentService.cs +++ b/src/BaGet.Core/Content/DefaultPackageContentService.cs @@ -1,92 +1,86 @@ -using System; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; using NuGet.Versioning; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// Implements the NuGet Package Content resource in NuGet's V3 protocol. +/// +public class DefaultPackageContentService : IPackageContentService { - /// - /// Implements the NuGet Package Content resource in NuGet's V3 protocol. - /// - public class DefaultPackageContentService : IPackageContentService + private readonly IPackageService _packages; + private readonly IPackageStorageService _storage; + + public DefaultPackageContentService( + IPackageService packages, + IPackageStorageService storage) { - private readonly IPackageService _packages; - private readonly IPackageStorageService _storage; + _packages = packages ?? throw new ArgumentNullException(nameof(packages)); + _storage = storage ?? throw new ArgumentNullException(nameof(storage)); + } - public DefaultPackageContentService( - IPackageService packages, - IPackageStorageService storage) + public async Task GetPackageVersionsOrNullAsync( + string id, + CancellationToken cancellationToken = default) + { + var versions = await _packages.FindPackageVersionsAsync(id, cancellationToken); + if (!versions.Any()) { - _packages = packages ?? throw new ArgumentNullException(nameof(packages)); - _storage = storage ?? throw new ArgumentNullException(nameof(storage)); + return null; } - public async Task GetPackageVersionsOrNullAsync( - string id, - CancellationToken cancellationToken = default) + return new PackageVersionsResponse { - var versions = await _packages.FindPackageVersionsAsync(id, cancellationToken); - if (!versions.Any()) - { - return null; - } - - return new PackageVersionsResponse - { - Versions = versions - .Select(v => v.ToNormalizedString()) - .Select(v => v.ToLowerInvariant()) - .ToList() - }; - } + Versions = versions + .Select(v => v.ToNormalizedString()) + .Select(v => v.ToLowerInvariant()) + .ToList() + }; + } - public async Task GetPackageContentStreamOrNullAsync( - string id, - NuGetVersion version, - CancellationToken cancellationToken = default) + public async Task GetPackageContentStreamOrNullAsync( + string id, + NuGetVersion version, + CancellationToken cancellationToken = default) + { + if (!await _packages.ExistsAsync(id, version, cancellationToken)) { - if (!await _packages.ExistsAsync(id, version, cancellationToken)) - { - return null; - } - - await _packages.AddDownloadAsync(id, version, cancellationToken); - return await _storage.GetPackageStreamAsync(id, version, cancellationToken); + return null; } - public async Task GetPackageManifestStreamOrNullAsync(string id, NuGetVersion version, CancellationToken cancellationToken = default) - { - if (!await _packages.ExistsAsync(id, version, cancellationToken)) - { - return null; - } + await _packages.AddDownloadAsync(id, version, cancellationToken); + return await _storage.GetPackageStreamAsync(id, version, cancellationToken); + } - return await _storage.GetNuspecStreamAsync(id, version, cancellationToken); + public async Task GetPackageManifestStreamOrNullAsync(string id, NuGetVersion version, CancellationToken cancellationToken = default) + { + if (!await _packages.ExistsAsync(id, version, cancellationToken)) + { + return null; } - public async Task GetPackageReadmeStreamOrNullAsync(string id, NuGetVersion version, CancellationToken cancellationToken = default) - { - var package = await _packages.FindPackageOrNullAsync(id, version, cancellationToken); - if (package == null || !package.HasReadme) - { - return null; - } + return await _storage.GetNuspecStreamAsync(id, version, cancellationToken); + } - return await _storage.GetReadmeStreamAsync(id, version, cancellationToken); + public async Task GetPackageReadmeStreamOrNullAsync(string id, NuGetVersion version, CancellationToken cancellationToken = default) + { + var package = await _packages.FindPackageOrNullAsync(id, version, cancellationToken); + if (package == null || !package.HasReadme) + { + return null; } - public async Task GetPackageIconStreamOrNullAsync(string id, NuGetVersion version, CancellationToken cancellationToken = default) - { - var package = await _packages.FindPackageOrNullAsync(id, version, cancellationToken); - if (package == null || !package.HasEmbeddedIcon) - { - return null; - } + return await _storage.GetReadmeStreamAsync(id, version, cancellationToken); + } - return await _storage.GetIconStreamAsync(id, version, cancellationToken); + public async Task GetPackageIconStreamOrNullAsync(string id, NuGetVersion version, CancellationToken cancellationToken = default) + { + var package = await _packages.FindPackageOrNullAsync(id, version, cancellationToken); + if (package == null || !package.HasEmbeddedIcon) + { + return null; } + + return await _storage.GetIconStreamAsync(id, version, cancellationToken); } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Content/IPackageContentService.cs b/src/BaGet.Core/Content/IPackageContentService.cs index 31abcafd..a5118700 100644 --- a/src/BaGet.Core/Content/IPackageContentService.cs +++ b/src/BaGet.Core/Content/IPackageContentService.cs @@ -1,85 +1,81 @@ -using System.IO; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; using NuGet.Versioning; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// The Package Content resource, used to download NuGet packages and to fetch other metadata. +/// +/// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource +/// +public interface IPackageContentService { /// - /// The Package Content resource, used to download NuGet packages and to fetch other metadata. - /// - /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource + /// Get a package's versions, or null if the package does not exist. + /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#enumerate-package-versions /// - public interface IPackageContentService - { - /// - /// Get a package's versions, or null if the package does not exist. - /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#enumerate-package-versions - /// - /// The package ID. - /// A token to cancel the task. - /// The package's versions, or null if the package does not exist. - Task GetPackageVersionsOrNullAsync( - string packageId, - CancellationToken cancellationToken); + /// The package ID. + /// A token to cancel the task. + /// The package's versions, or null if the package does not exist. + Task GetPackageVersionsOrNullAsync( + string packageId, + CancellationToken cancellationToken); - /// - /// Download a package, or null if the package does not exist. - /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-content-nupkg - /// - /// The package ID. - /// The package's version. - /// A token to cancel the task. - /// - /// The package's content stream, or null if the package does not exist. The stream may not be seekable. - /// - Task GetPackageContentStreamOrNullAsync( - string packageId, - NuGetVersion packageVersion, - CancellationToken cancellationToken); + /// + /// Download a package, or null if the package does not exist. + /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-content-nupkg + /// + /// The package ID. + /// The package's version. + /// A token to cancel the task. + /// + /// The package's content stream, or null if the package does not exist. The stream may not be seekable. + /// + Task GetPackageContentStreamOrNullAsync( + string packageId, + NuGetVersion packageVersion, + CancellationToken cancellationToken); - /// - /// Download a package's manifest (nuspec), or null if the package does not exist. - /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-manifest-nuspec - /// - /// The package id. - /// The package's version. - /// A token to cancel the task. - /// - /// The package's manifest stream, or null if the package does not exist. The stream may not be seekable. - /// - Task GetPackageManifestStreamOrNullAsync( - string packageId, - NuGetVersion packageVersion, - CancellationToken cancellationToken); + /// + /// Download a package's manifest (nuspec), or null if the package does not exist. + /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-manifest-nuspec + /// + /// The package id. + /// The package's version. + /// A token to cancel the task. + /// + /// The package's manifest stream, or null if the package does not exist. The stream may not be seekable. + /// + Task GetPackageManifestStreamOrNullAsync( + string packageId, + NuGetVersion packageVersion, + CancellationToken cancellationToken); - /// - /// Download a package's readme, or null if the package or readme does not exist. - /// - /// The package id. - /// The package's version. - /// A token to cancel the task. - /// - /// The package's readme stream, or null if the package or readme does not exist. The stream may not be seekable. - /// - Task GetPackageReadmeStreamOrNullAsync( - string id, - NuGetVersion version, - CancellationToken cancellationToken); + /// + /// Download a package's readme, or null if the package or readme does not exist. + /// + /// The package id. + /// The package's version. + /// A token to cancel the task. + /// + /// The package's readme stream, or null if the package or readme does not exist. The stream may not be seekable. + /// + Task GetPackageReadmeStreamOrNullAsync( + string id, + NuGetVersion version, + CancellationToken cancellationToken); - /// - /// Download a package's icon, or null if the package or icon does not exist. - /// - /// The package id. - /// The package's version. - /// A token to cancel the task. - /// - /// The package's icon stream, or null if the package or icon does not exist. The stream may not be seekable. - /// - Task GetPackageIconStreamOrNullAsync( - string id, - NuGetVersion version, - CancellationToken cancellationToken); - } -} + /// + /// Download a package's icon, or null if the package or icon does not exist. + /// + /// The package id. + /// The package's version. + /// A token to cancel the task. + /// + /// The package's icon stream, or null if the package or icon does not exist. The stream may not be seekable. + /// + Task GetPackageIconStreamOrNullAsync( + string id, + NuGetVersion version, + CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/BaGet.Core/Entities/AbstractContext.cs b/src/BaGet.Core/Entities/AbstractContext.cs index 7c8c2482..45e94e96 100644 --- a/src/BaGet.Core/Entities/AbstractContext.cs +++ b/src/BaGet.Core/Entities/AbstractContext.cs @@ -1,160 +1,157 @@ -using System.Threading; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -namespace BaGet.Core +namespace BaGet.Core; + +public abstract class AbstractContext : DbContext, IContext where TContext : DbContext { - public abstract class AbstractContext : DbContext, IContext where TContext : DbContext + public const int DefaultMaxStringLength = 4000; + + public const int MaxPackageIdLength = 128; + public const int MaxPackageVersionLength = 64; + public const int MaxPackageMinClientVersionLength = 44; + public const int MaxPackageLanguageLength = 20; + public const int MaxPackageTitleLength = 256; + public const int MaxPackageTypeNameLength = 512; + public const int MaxPackageTypeVersionLength = 64; + public const int MaxRepositoryTypeLength = 100; + public const int MaxTargetFrameworkLength = 256; + + public const int MaxPackageDependencyVersionRangeLength = 256; + + public AbstractContext(DbContextOptions options) + : base(options) + { } + + public DbSet Packages { get; set; } + public DbSet PackageDependencies { get; set; } + public DbSet PackageTypes { get; set; } + public DbSet TargetFrameworks { get; set; } + + public Task SaveChangesAsync() => SaveChangesAsync(default); + + public virtual async Task RunMigrationsAsync(CancellationToken cancellationToken) + => await Database.MigrateAsync(cancellationToken); + + public abstract bool IsUniqueConstraintViolationException(DbUpdateException exception); + + public virtual bool SupportsLimitInSubqueries => true; + + protected override void OnModelCreating(ModelBuilder builder) + { + builder.Entity(BuildPackageEntity); + builder.Entity(BuildPackageDependencyEntity); + builder.Entity(BuildPackageTypeEntity); + builder.Entity(BuildTargetFrameworkEntity); + } + + private void BuildPackageEntity(EntityTypeBuilder package) { - public const int DefaultMaxStringLength = 4000; - - public const int MaxPackageIdLength = 128; - public const int MaxPackageVersionLength = 64; - public const int MaxPackageMinClientVersionLength = 44; - public const int MaxPackageLanguageLength = 20; - public const int MaxPackageTitleLength = 256; - public const int MaxPackageTypeNameLength = 512; - public const int MaxPackageTypeVersionLength = 64; - public const int MaxRepositoryTypeLength = 100; - public const int MaxTargetFrameworkLength = 256; - - public const int MaxPackageDependencyVersionRangeLength = 256; - - public AbstractContext(DbContextOptions options) - : base(options) - { } - - public DbSet Packages { get; set; } - public DbSet PackageDependencies { get; set; } - public DbSet PackageTypes { get; set; } - public DbSet TargetFrameworks { get; set; } - - public Task SaveChangesAsync() => SaveChangesAsync(default); - - public virtual async Task RunMigrationsAsync(CancellationToken cancellationToken) - => await Database.MigrateAsync(cancellationToken); - - public abstract bool IsUniqueConstraintViolationException(DbUpdateException exception); - - public virtual bool SupportsLimitInSubqueries => true; - - protected override void OnModelCreating(ModelBuilder builder) - { - builder.Entity(BuildPackageEntity); - builder.Entity(BuildPackageDependencyEntity); - builder.Entity(BuildPackageTypeEntity); - builder.Entity(BuildTargetFrameworkEntity); - } - - private void BuildPackageEntity(EntityTypeBuilder package) - { - package.HasKey(p => p.Key); - package.HasIndex(p => p.Id); - package.HasIndex(p => new { p.Id, p.NormalizedVersionString }) - .IsUnique(); - - package.Property(p => p.Id) - .HasMaxLength(MaxPackageIdLength) - .IsRequired(); - - package.Property(p => p.NormalizedVersionString) - .HasColumnName("Version") - .HasMaxLength(MaxPackageVersionLength) - .IsRequired(); - - package.Property(p => p.OriginalVersionString) - .HasColumnName("OriginalVersion") - .HasMaxLength(MaxPackageVersionLength); - - package.Property(p => p.ReleaseNotes) - .HasColumnName("ReleaseNotes"); - - package.Property(p => p.Authors) - .HasMaxLength(DefaultMaxStringLength) - .HasConversion(StringArrayToJsonConverter.Instance) - .Metadata.SetValueComparer(StringArrayComparer.Instance); - - package.Property(p => p.IconUrl) - .HasConversion(UriToStringConverter.Instance) - .HasMaxLength(DefaultMaxStringLength); - - package.Property(p => p.LicenseUrl) - .HasConversion(UriToStringConverter.Instance) - .HasMaxLength(DefaultMaxStringLength); - - package.Property(p => p.ProjectUrl) - .HasConversion(UriToStringConverter.Instance) - .HasMaxLength(DefaultMaxStringLength); - - package.Property(p => p.RepositoryUrl) - .HasConversion(UriToStringConverter.Instance) - .HasMaxLength(DefaultMaxStringLength); - - package.Property(p => p.Tags) - .HasMaxLength(DefaultMaxStringLength) - .HasConversion(StringArrayToJsonConverter.Instance) - .Metadata.SetValueComparer(StringArrayComparer.Instance); - - package.Property(p => p.Description).HasMaxLength(DefaultMaxStringLength); - package.Property(p => p.Language).HasMaxLength(MaxPackageLanguageLength); - package.Property(p => p.MinClientVersion).HasMaxLength(MaxPackageMinClientVersionLength); - package.Property(p => p.Summary).HasMaxLength(DefaultMaxStringLength); - package.Property(p => p.Title).HasMaxLength(MaxPackageTitleLength); - package.Property(p => p.RepositoryType).HasMaxLength(MaxRepositoryTypeLength); - - package.Ignore(p => p.Version); - package.Ignore(p => p.IconUrlString); - package.Ignore(p => p.LicenseUrlString); - package.Ignore(p => p.ProjectUrlString); - package.Ignore(p => p.RepositoryUrlString); - - // TODO: This is needed to make the dependency to package relationship required. - // Unfortunately, this would generate a migration that drops a foreign key, which - // isn't supported by SQLite. The migrations will be need to be recreated for this. - // Consumers will need to recreate their database and reindex all their packages. - // To make this transition easier, I'd like to finish this change: - // https://github.com/loic-sharma/BaGet/pull/174 - //package.HasMany(p => p.Dependencies) - // .WithOne(d => d.Package) - // .IsRequired(); - - package.HasMany(p => p.PackageTypes) - .WithOne(d => d.Package) - .IsRequired(); - - package.HasMany(p => p.TargetFrameworks) - .WithOne(d => d.Package) - .IsRequired(); - - package.Property(p => p.RowVersion).IsRowVersion(); - } - - private void BuildPackageDependencyEntity(EntityTypeBuilder dependency) - { - dependency.HasKey(d => d.Key); - dependency.HasIndex(d => d.Id); - - dependency.Property(d => d.Id).HasMaxLength(MaxPackageIdLength); - dependency.Property(d => d.VersionRange).HasMaxLength(MaxPackageDependencyVersionRangeLength); - dependency.Property(d => d.TargetFramework).HasMaxLength(MaxTargetFrameworkLength); - } - - private void BuildPackageTypeEntity(EntityTypeBuilder type) - { - type.HasKey(d => d.Key); - type.HasIndex(d => d.Name); - - type.Property(d => d.Name).HasMaxLength(MaxPackageTypeNameLength); - type.Property(d => d.Version).HasMaxLength(MaxPackageTypeVersionLength); - } - - private void BuildTargetFrameworkEntity(EntityTypeBuilder targetFramework) - { - targetFramework.HasKey(f => f.Key); - targetFramework.HasIndex(f => f.Moniker); - - targetFramework.Property(f => f.Moniker).HasMaxLength(MaxTargetFrameworkLength); - } + package.HasKey(p => p.Key); + package.HasIndex(p => p.Id); + package.HasIndex(p => new { p.Id, p.NormalizedVersionString }) + .IsUnique(); + + package.Property(p => p.Id) + .HasMaxLength(MaxPackageIdLength) + .IsRequired(); + + package.Property(p => p.NormalizedVersionString) + .HasColumnName("Version") + .HasMaxLength(MaxPackageVersionLength) + .IsRequired(); + + package.Property(p => p.OriginalVersionString) + .HasColumnName("OriginalVersion") + .HasMaxLength(MaxPackageVersionLength); + + package.Property(p => p.ReleaseNotes) + .HasColumnName("ReleaseNotes"); + + package.Property(p => p.Authors) + .HasMaxLength(DefaultMaxStringLength) + .HasConversion(StringArrayToJsonConverter.Instance) + .Metadata.SetValueComparer(StringArrayComparer.Instance); + + package.Property(p => p.IconUrl) + .HasConversion(UriToStringConverter.Instance) + .HasMaxLength(DefaultMaxStringLength); + + package.Property(p => p.LicenseUrl) + .HasConversion(UriToStringConverter.Instance) + .HasMaxLength(DefaultMaxStringLength); + + package.Property(p => p.ProjectUrl) + .HasConversion(UriToStringConverter.Instance) + .HasMaxLength(DefaultMaxStringLength); + + package.Property(p => p.RepositoryUrl) + .HasConversion(UriToStringConverter.Instance) + .HasMaxLength(DefaultMaxStringLength); + + package.Property(p => p.Tags) + .HasMaxLength(DefaultMaxStringLength) + .HasConversion(StringArrayToJsonConverter.Instance) + .Metadata.SetValueComparer(StringArrayComparer.Instance); + + package.Property(p => p.Description).HasMaxLength(DefaultMaxStringLength); + package.Property(p => p.Language).HasMaxLength(MaxPackageLanguageLength); + package.Property(p => p.MinClientVersion).HasMaxLength(MaxPackageMinClientVersionLength); + package.Property(p => p.Summary).HasMaxLength(DefaultMaxStringLength); + package.Property(p => p.Title).HasMaxLength(MaxPackageTitleLength); + package.Property(p => p.RepositoryType).HasMaxLength(MaxRepositoryTypeLength); + + package.Ignore(p => p.Version); + package.Ignore(p => p.IconUrlString); + package.Ignore(p => p.LicenseUrlString); + package.Ignore(p => p.ProjectUrlString); + package.Ignore(p => p.RepositoryUrlString); + + // TODO: This is needed to make the dependency to package relationship required. + // Unfortunately, this would generate a migration that drops a foreign key, which + // isn't supported by SQLite. The migrations will be need to be recreated for this. + // Consumers will need to recreate their database and reindex all their packages. + // To make this transition easier, I'd like to finish this change: + // https://github.com/loic-sharma/BaGet/pull/174 + //package.HasMany(p => p.Dependencies) + // .WithOne(d => d.Package) + // .IsRequired(); + + package.HasMany(p => p.PackageTypes) + .WithOne(d => d.Package) + .IsRequired(); + + package.HasMany(p => p.TargetFrameworks) + .WithOne(d => d.Package) + .IsRequired(); + + package.Property(p => p.RowVersion).IsRowVersion(); + } + + private void BuildPackageDependencyEntity(EntityTypeBuilder dependency) + { + dependency.HasKey(d => d.Key); + dependency.HasIndex(d => d.Id); + + dependency.Property(d => d.Id).HasMaxLength(MaxPackageIdLength); + dependency.Property(d => d.VersionRange).HasMaxLength(MaxPackageDependencyVersionRangeLength); + dependency.Property(d => d.TargetFramework).HasMaxLength(MaxTargetFrameworkLength); + } + + private void BuildPackageTypeEntity(EntityTypeBuilder type) + { + type.HasKey(d => d.Key); + type.HasIndex(d => d.Name); + + type.Property(d => d.Name).HasMaxLength(MaxPackageTypeNameLength); + type.Property(d => d.Version).HasMaxLength(MaxPackageTypeVersionLength); + } + + private void BuildTargetFrameworkEntity(EntityTypeBuilder targetFramework) + { + targetFramework.HasKey(f => f.Key); + targetFramework.HasIndex(f => f.Moniker); + + targetFramework.Property(f => f.Moniker).HasMaxLength(MaxTargetFrameworkLength); } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Entities/Converters/StringArrayComparer.cs b/src/BaGet.Core/Entities/Converters/StringArrayComparer.cs index e76bcc62..483767e8 100644 --- a/src/BaGet.Core/Entities/Converters/StringArrayComparer.cs +++ b/src/BaGet.Core/Entities/Converters/StringArrayComparer.cs @@ -1,19 +1,16 @@ -using System; -using System.Linq; using Microsoft.EntityFrameworkCore.ChangeTracking; -namespace BaGet.Core +namespace BaGet.Core; + +public class StringArrayComparer : ValueComparer { - public class StringArrayComparer : ValueComparer - { - public static readonly StringArrayComparer Instance = new StringArrayComparer(); + public static readonly StringArrayComparer Instance = new StringArrayComparer(); - public StringArrayComparer() - : base( - (c1, c2) => c1.SequenceEqual(c2), - c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())), - c => c.ToArray()) - { - } + public StringArrayComparer() + : base( + (c1, c2) => c1.SequenceEqual(c2), + c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())), + c => c.ToArray()) + { } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Entities/Converters/StringArrayToJsonConverter.cs b/src/BaGet.Core/Entities/Converters/StringArrayToJsonConverter.cs index 0991d1c3..bc95e409 100644 --- a/src/BaGet.Core/Entities/Converters/StringArrayToJsonConverter.cs +++ b/src/BaGet.Core/Entities/Converters/StringArrayToJsonConverter.cs @@ -1,17 +1,16 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Newtonsoft.Json; -namespace BaGet.Core +namespace BaGet.Core; + +public class StringArrayToJsonConverter : ValueConverter { - public class StringArrayToJsonConverter : ValueConverter - { - public static readonly StringArrayToJsonConverter Instance = new StringArrayToJsonConverter(); + public static readonly StringArrayToJsonConverter Instance = new StringArrayToJsonConverter(); - public StringArrayToJsonConverter() - : base( - v => JsonConvert.SerializeObject(v), - v => (!string.IsNullOrEmpty(v)) ? JsonConvert.DeserializeObject(v) : new string[0]) - { - } + public StringArrayToJsonConverter() + : base( + v => JsonConvert.SerializeObject(v), + v => (!string.IsNullOrEmpty(v)) ? JsonConvert.DeserializeObject(v) : new string[0]) + { } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Entities/Converters/UriToStringConverter.cs b/src/BaGet.Core/Entities/Converters/UriToStringConverter.cs index d8ace4a6..134e4ec9 100644 --- a/src/BaGet.Core/Entities/Converters/UriToStringConverter.cs +++ b/src/BaGet.Core/Entities/Converters/UriToStringConverter.cs @@ -1,17 +1,15 @@ -using System; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -namespace BaGet.Core +namespace BaGet.Core; + +public class UriToStringConverter : ValueConverter { - public class UriToStringConverter : ValueConverter - { - public static readonly UriToStringConverter Instance = new UriToStringConverter(); + public static readonly UriToStringConverter Instance = new UriToStringConverter(); - public UriToStringConverter() - : base( - v => v.AbsoluteUri, - v => new Uri(v)) - { - } + public UriToStringConverter() + : base( + v => v.AbsoluteUri, + v => new Uri(v)) + { } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Entities/IContext.cs b/src/BaGet.Core/Entities/IContext.cs index c110b59c..92f3be0d 100644 --- a/src/BaGet.Core/Entities/IContext.cs +++ b/src/BaGet.Core/Entities/IContext.cs @@ -1,36 +1,33 @@ -using System.Threading; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -namespace BaGet.Core +namespace BaGet.Core; + +public interface IContext { - public interface IContext - { - DatabaseFacade Database { get; } + DatabaseFacade Database { get; } - DbSet Packages { get; set; } + 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); + /// + /// 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); - /// - /// Whether this database engine supports LINQ "Take" in subqueries. - /// - bool SupportsLimitInSubqueries { get; } + /// + /// Whether this database engine supports LINQ "Take" in subqueries. + /// + bool SupportsLimitInSubqueries { get; } - Task SaveChangesAsync(CancellationToken cancellationToken); + Task SaveChangesAsync(CancellationToken cancellationToken); - /// - /// Applies any pending migrations for the context to the database. - /// Creates the database if it does not already exist. - /// - /// A token to cancel the task. - /// A task that completes once migrations are applied. - Task RunMigrationsAsync(CancellationToken cancellationToken); - } -} + /// + /// Applies any pending migrations for the context to the database. + /// Creates the database if it does not already exist. + /// + /// A token to cancel the task. + /// A task that completes once migrations are applied. + Task RunMigrationsAsync(CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/BaGet.Core/Entities/NullContext.cs b/src/BaGet.Core/Entities/NullContext.cs index 2f3170a8..0e88d62b 100644 --- a/src/BaGet.Core/Entities/NullContext.cs +++ b/src/BaGet.Core/Entities/NullContext.cs @@ -1,32 +1,28 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -namespace BaGet.Core +namespace BaGet.Core; + +public class NullContext : IContext { - public class NullContext : IContext - { - public DatabaseFacade Database => throw new NotImplementedException(); + public DatabaseFacade Database => throw new NotImplementedException(); - public DbSet Packages { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public DbSet Packages { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool SupportsLimitInSubqueries => throw new NotImplementedException(); + public bool SupportsLimitInSubqueries => throw new NotImplementedException(); - public bool IsUniqueConstraintViolationException(DbUpdateException exception) - { - throw new NotImplementedException(); - } + public bool IsUniqueConstraintViolationException(DbUpdateException exception) + { + throw new NotImplementedException(); + } - public Task RunMigrationsAsync(CancellationToken cancellationToken) - { - return Task.CompletedTask; - } + public Task RunMigrationsAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } - public Task SaveChangesAsync(CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } + public Task SaveChangesAsync(CancellationToken cancellationToken) + { + throw new NotImplementedException(); } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Entities/Package.cs b/src/BaGet.Core/Entities/Package.cs index f9d64cce..0f886d80 100644 --- a/src/BaGet.Core/Entities/Package.cs +++ b/src/BaGet.Core/Entities/Package.cs @@ -1,76 +1,73 @@ -using System; -using System.Collections.Generic; using NuGet.Versioning; -namespace BaGet.Core +namespace BaGet.Core; + +// See NuGetGallery's: https://github.com/NuGet/NuGetGallery/blob/master/src/NuGetGallery.Core/Entities/Package.cs +public class Package { - // 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 int Key { get; set; } - public string Id { get; set; } + public string Id { get; set; } - public NuGetVersion Version + public NuGetVersion Version + { + get { - get - { - // Favor the original version string as it contains more information. - // Packages uploaded with older versions of BaGet may not have the original version string. - return NuGetVersion.Parse( - OriginalVersionString != null - ? OriginalVersionString - : NormalizedVersionString); - } + // Favor the original version string as it contains more information. + // Packages uploaded with older versions of BaGet may not have the original version string. + return NuGetVersion.Parse( + OriginalVersionString != null + ? OriginalVersionString + : NormalizedVersionString); + } - set - { - NormalizedVersionString = value.ToNormalizedString().ToLowerInvariant(); - OriginalVersionString = value.OriginalVersion; - } + set + { + NormalizedVersionString = value.ToNormalizedString().ToLowerInvariant(); + OriginalVersionString = value.OriginalVersion; } + } - public string[] Authors { get; set; } - public string Description { get; set; } - public long Downloads { get; set; } - public bool HasReadme { get; set; } - public bool HasEmbeddedIcon { get; set; } - public bool IsPrerelease { get; set; } - public string ReleaseNotes { 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 SemVerLevel SemVerLevel { get; set; } - public string Summary { get; set; } - public string Title { get; set; } + public string[] Authors { get; set; } + public string Description { get; set; } + public long Downloads { get; set; } + public bool HasReadme { get; set; } + public bool HasEmbeddedIcon { get; set; } + public bool IsPrerelease { get; set; } + public string ReleaseNotes { 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 SemVerLevel SemVerLevel { 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 IconUrl { get; set; } + public Uri LicenseUrl { get; set; } + public Uri ProjectUrl { get; set; } - public Uri RepositoryUrl { get; set; } - public string RepositoryType { get; set; } + public Uri RepositoryUrl { get; set; } + public string RepositoryType { get; set; } - public string[] Tags { get; set; } + public string[] Tags { get; set; } - /// - /// Used for optimistic concurrency. - /// - public byte[] RowVersion { get; set; } + /// + /// Used for optimistic concurrency. + /// + public byte[] RowVersion { get; set; } - public List Dependencies { get; set; } - public List PackageTypes { get; set; } - public List TargetFrameworks { get; set; } + public List Dependencies { get; set; } + public List PackageTypes { get; set; } + public List TargetFrameworks { get; set; } - public string NormalizedVersionString { get; set; } - public string OriginalVersionString { get; set; } + public string NormalizedVersionString { get; set; } + public string OriginalVersionString { get; set; } - 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; - } -} + 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; +} \ No newline at end of file diff --git a/src/BaGet.Core/Entities/PackageDependency.cs b/src/BaGet.Core/Entities/PackageDependency.cs index 7ae6f61c..08f4794a 100644 --- a/src/BaGet.Core/Entities/PackageDependency.cs +++ b/src/BaGet.Core/Entities/PackageDependency.cs @@ -1,22 +1,21 @@ -namespace BaGet.Core +namespace BaGet.Core; + +// See NuGetGallery.Core's: https://github.com/NuGet/NuGetGallery/blob/master/src/NuGetGallery.Core/Entities/PackageDependency.cs +public class PackageDependency { - // 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 int Key { get; set; } - /// - /// The dependency's package ID. Null if this is a dependency group without any dependencies. - /// - public string Id { get; set; } + /// + /// The dependency's package ID. Null if this is a dependency group without any dependencies. + /// + public string Id { get; set; } - /// - /// The dependency's package version. Null if this is a dependency group without any dependencies. - /// - public string VersionRange { get; set; } + /// + /// The dependency's package version. Null if this is a dependency group without any dependencies. + /// + public string VersionRange { get; set; } - public string TargetFramework { get; set; } + public string TargetFramework { get; set; } - public Package Package { get; set; } - } -} + public Package Package { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Core/Entities/PackageType.cs b/src/BaGet.Core/Entities/PackageType.cs index 4e67d694..6a3b9617 100644 --- a/src/BaGet.Core/Entities/PackageType.cs +++ b/src/BaGet.Core/Entities/PackageType.cs @@ -1,13 +1,12 @@ -namespace BaGet.Core +namespace BaGet.Core; + +// See NuGetGallery.Core's: https://github.com/NuGet/NuGetGallery/blob/master/src/NuGetGallery.Core/Entities/PackageType.cs +public class PackageType { - // See NuGetGallery.Core's: https://github.com/NuGet/NuGetGallery/blob/master/src/NuGetGallery.Core/Entities/PackageType.cs - public class PackageType - { - public int Key { get; set; } + public int Key { get; set; } - public string Name { get; set; } - public string Version { get; set; } + public string Name { get; set; } + public string Version { get; set; } - public Package Package { get; set; } - } -} + public Package Package { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Core/Entities/SemVerLevel.cs b/src/BaGet.Core/Entities/SemVerLevel.cs index 29aa5ae4..50a98a20 100644 --- a/src/BaGet.Core/Entities/SemVerLevel.cs +++ b/src/BaGet.Core/Entities/SemVerLevel.cs @@ -1,15 +1,14 @@ -namespace BaGet.Core +namespace BaGet.Core; + +public enum SemVerLevel { - public enum SemVerLevel - { - /// - /// Either an invalid semantic version or a semantic version v1.0.0 - /// - Unknown = 0, + /// + /// Either an invalid semantic version or a semantic version v1.0.0 + /// + Unknown = 0, - /// - /// A valid semantic version v2.0.0 - /// - SemVer2 = 2 - } -} + /// + /// A valid semantic version v2.0.0 + /// + SemVer2 = 2 +} \ No newline at end of file diff --git a/src/BaGet.Core/Entities/TargetFramework.cs b/src/BaGet.Core/Entities/TargetFramework.cs index a8e4a9a8..9b7cfec6 100644 --- a/src/BaGet.Core/Entities/TargetFramework.cs +++ b/src/BaGet.Core/Entities/TargetFramework.cs @@ -1,11 +1,10 @@ -namespace BaGet.Core +namespace BaGet.Core; + +public class TargetFramework { - public class TargetFramework - { - public int Key { get; set; } + public int Key { get; set; } - public string Moniker { get; set; } + public string Moniker { get; set; } - public Package Package { get; set; } - } -} + public Package Package { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Core/Extensions/BaGetApplicationExtensions.cs b/src/BaGet.Core/Extensions/BaGetApplicationExtensions.cs index 7dc2967f..eed7017a 100644 --- a/src/BaGet.Core/Extensions/BaGetApplicationExtensions.cs +++ b/src/BaGet.Core/Extensions/BaGetApplicationExtensions.cs @@ -1,38 +1,36 @@ -using System; using BaGet.Core; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; -namespace BaGet +namespace BaGet; + +public static class BaGetApplicationExtensions { - public static class BaGetApplicationExtensions + public static BaGetApplication AddFileStorage(this BaGetApplication app) { - public static BaGetApplication AddFileStorage(this BaGetApplication app) - { - app.Services.TryAddTransient(provider => provider.GetRequiredService()); - return app; - } + app.Services.TryAddTransient(provider => provider.GetRequiredService()); + return app; + } - public static BaGetApplication AddFileStorage( - this BaGetApplication app, - Action configure) - { - app.AddFileStorage(); - app.Services.Configure(configure); - return app; - } + public static BaGetApplication AddFileStorage( + this BaGetApplication app, + Action configure) + { + app.AddFileStorage(); + app.Services.Configure(configure); + return app; + } - public static BaGetApplication AddNullStorage(this BaGetApplication app) - { - app.Services.TryAddTransient(provider => provider.GetRequiredService()); - return app; - } + public static BaGetApplication AddNullStorage(this BaGetApplication app) + { + app.Services.TryAddTransient(provider => provider.GetRequiredService()); + return app; + } - public static BaGetApplication AddNullSearch(this BaGetApplication app) - { - app.Services.TryAddTransient(provider => provider.GetRequiredService()); - app.Services.TryAddTransient(provider => provider.GetRequiredService()); - return app; - } + public static BaGetApplication AddNullSearch(this BaGetApplication app) + { + app.Services.TryAddTransient(provider => provider.GetRequiredService()); + app.Services.TryAddTransient(provider => provider.GetRequiredService()); + return app; } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Extensions/DependencyInjectionExtensions.Providers.cs b/src/BaGet.Core/Extensions/DependencyInjectionExtensions.Providers.cs index 7981d771..fc5a8fb2 100644 --- a/src/BaGet.Core/Extensions/DependencyInjectionExtensions.Providers.cs +++ b/src/BaGet.Core/Extensions/DependencyInjectionExtensions.Providers.cs @@ -1,138 +1,135 @@ -using System; -using System.Collections.Generic; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; -namespace BaGet.Core +namespace BaGet.Core; + +public static partial class DependencyInjectionExtensions { - public static partial class DependencyInjectionExtensions + private static readonly string DatabaseTypeKey = $"{nameof(BaGetOptions.Database)}:{nameof(DatabaseOptions.Type)}"; + private static readonly string SearchTypeKey = $"{nameof(BaGetOptions.Search)}:{nameof(SearchOptions.Type)}"; + private static readonly string StorageTypeKey = $"{nameof(BaGetOptions.Storage)}:{nameof(StorageOptions.Type)}"; + + private static readonly string DatabaseSearchType = "Database"; + + /// + /// Add a new provider to the dependency injection container. The provider may + /// provide an implementation of the service, or it may return null. + /// + /// The service that may be provided. + /// The dependency injection container. + /// A handler that provides the service, or null. + /// The dependency injection container. + public static IServiceCollection AddProvider( + this IServiceCollection services, + Func func) + where TService : class { - private static readonly string DatabaseTypeKey = $"{nameof(BaGetOptions.Database)}:{nameof(DatabaseOptions.Type)}"; - private static readonly string SearchTypeKey = $"{nameof(BaGetOptions.Search)}:{nameof(SearchOptions.Type)}"; - private static readonly string StorageTypeKey = $"{nameof(BaGetOptions.Storage)}:{nameof(StorageOptions.Type)}"; - - private static readonly string DatabaseSearchType = "Database"; - - /// - /// Add a new provider to the dependency injection container. The provider may - /// provide an implementation of the service, or it may return null. - /// - /// The service that may be provided. - /// The dependency injection container. - /// A handler that provides the service, or null. - /// The dependency injection container. - public static IServiceCollection AddProvider( - this IServiceCollection services, - Func func) - where TService : class - { - services.AddSingleton>(new DelegateProvider(func)); + services.AddSingleton>(new DelegateProvider(func)); - return services; - } + return services; + } - /// - /// Determine whether a database type is currently active. - /// - /// The application's configuration. - /// The database type that should be checked. - /// Whether the database type is active. - public static bool HasDatabaseType(this IConfiguration config, string value) - { - return config[DatabaseTypeKey].Equals(value, StringComparison.OrdinalIgnoreCase); - } + /// + /// Determine whether a database type is currently active. + /// + /// The application's configuration. + /// The database type that should be checked. + /// Whether the database type is active. + public static bool HasDatabaseType(this IConfiguration config, string value) + { + return config[DatabaseTypeKey].Equals(value, StringComparison.OrdinalIgnoreCase); + } - /// - /// Determine whether a search type is currently active. - /// - /// The application's configuration. - /// The search type that should be checked. - /// Whether the search type is active. - public static bool HasSearchType(this IConfiguration config, string value) - { - return config[SearchTypeKey].Equals(value, StringComparison.OrdinalIgnoreCase); - } + /// + /// Determine whether a search type is currently active. + /// + /// The application's configuration. + /// The search type that should be checked. + /// Whether the search type is active. + public static bool HasSearchType(this IConfiguration config, string value) + { + return config[SearchTypeKey].Equals(value, StringComparison.OrdinalIgnoreCase); + } - /// - /// Determine whether a storage type is currently active. - /// - /// The application's configuration. - /// The storage type that should be checked. - /// Whether the database type is active. - public static bool HasStorageType(this IConfiguration config, string value) - { - return config[StorageTypeKey].Equals(value, StringComparison.OrdinalIgnoreCase); - } + /// + /// Determine whether a storage type is currently active. + /// + /// The application's configuration. + /// The storage type that should be checked. + /// Whether the database type is active. + public static bool HasStorageType(this IConfiguration config, string value) + { + return config[StorageTypeKey].Equals(value, StringComparison.OrdinalIgnoreCase); + } - public static IServiceCollection AddBaGetDbContextProvider( - this IServiceCollection services, - string databaseType, - Action configureContext) - where TContext : DbContext, IContext - { - services.TryAddScoped(provider => provider.GetRequiredService()); - services.TryAddTransient(provider => provider.GetRequiredService()); + public static IServiceCollection AddBaGetDbContextProvider( + this IServiceCollection services, + string databaseType, + Action configureContext) + where TContext : DbContext, IContext + { + services.TryAddScoped(provider => provider.GetRequiredService()); + services.TryAddTransient(provider => provider.GetRequiredService()); - services.AddDbContext(configureContext); + services.AddDbContext(configureContext); - services.AddProvider((provider, config) => - { - if (!config.HasDatabaseType(databaseType)) return null; + services.AddProvider((provider, config) => + { + if (!config.HasDatabaseType(databaseType)) return null; - return provider.GetRequiredService(); - }); + return provider.GetRequiredService(); + }); - services.AddProvider((provider, config) => - { - if (!config.HasDatabaseType(databaseType)) return null; + services.AddProvider((provider, config) => + { + if (!config.HasDatabaseType(databaseType)) return null; - return provider.GetRequiredService(); - }); + return provider.GetRequiredService(); + }); - services.AddProvider((provider, config) => - { - if (!config.HasSearchType(DatabaseSearchType)) return null; - if (!config.HasDatabaseType(databaseType)) return null; + services.AddProvider((provider, config) => + { + if (!config.HasSearchType(DatabaseSearchType)) return null; + if (!config.HasDatabaseType(databaseType)) return null; - return provider.GetRequiredService(); - }); + return provider.GetRequiredService(); + }); - services.AddProvider((provider, config) => - { - if (!config.HasSearchType(DatabaseSearchType)) return null; - if (!config.HasDatabaseType(databaseType)) return null; + services.AddProvider((provider, config) => + { + if (!config.HasSearchType(DatabaseSearchType)) return null; + if (!config.HasDatabaseType(databaseType)) return null; - return provider.GetRequiredService(); - }); + return provider.GetRequiredService(); + }); - return services; - } + return services; + } - /// - /// Runs through all providers to resolve the . - /// - /// The service that will be resolved using providers. - /// The dependency injection container. - /// An instance of the service created by the providers. - public static TService GetServiceFromProviders(IServiceProvider services) - where TService : class - { - // Run through all the providers for the type. Find the first provider that results a non-null result. - var providers = services.GetRequiredService>>(); - var configuration = services.GetRequiredService(); + /// + /// Runs through all providers to resolve the . + /// + /// The service that will be resolved using providers. + /// The dependency injection container. + /// An instance of the service created by the providers. + public static TService GetServiceFromProviders(IServiceProvider services) + where TService : class + { + // Run through all the providers for the type. Find the first provider that results a non-null result. + var providers = services.GetRequiredService>>(); + var configuration = services.GetRequiredService(); - foreach (var provider in providers) + foreach (var provider in providers) + { + var result = provider.GetOrNull(services, configuration); + if (result != null) { - var result = provider.GetOrNull(services, configuration); - if (result != null) - { - return result; - } + return result; } - - return null; } + + return null; } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Extensions/DependencyInjectionExtensions.cs b/src/BaGet.Core/Extensions/DependencyInjectionExtensions.cs index a56c41ea..56780e0b 100644 --- a/src/BaGet.Core/Extensions/DependencyInjectionExtensions.cs +++ b/src/BaGet.Core/Extensions/DependencyInjectionExtensions.cs @@ -1,6 +1,4 @@ -using System; using System.Net; -using System.Net.Http; using System.Reflection; using BaGet.Protocol; using Microsoft.Extensions.Configuration; @@ -8,212 +6,211 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; -namespace BaGet.Core +namespace BaGet.Core; + +public static partial class DependencyInjectionExtensions { - public static partial class DependencyInjectionExtensions + public static IServiceCollection AddBaGetApplication( + this IServiceCollection services, + Action configureAction) { - public static IServiceCollection AddBaGetApplication( - this IServiceCollection services, - Action configureAction) - { - var app = new BaGetApplication(services); + var app = new BaGetApplication(services); - services.AddConfiguration(); - services.AddBaGetServices(); - services.AddDefaultProviders(); + services.AddConfiguration(); + services.AddBaGetServices(); + services.AddDefaultProviders(); - configureAction(app); + configureAction(app); - services.AddFallbackServices(); + services.AddFallbackServices(); - return services; - } + return services; + } - /// - /// Configures and validates options. - /// - /// The options type that should be added. - /// The dependency injection container to add options. - /// - /// The configuration key that should be used when configuring the options. - /// If null, the root configuration will be used to configure the options. - /// - /// The dependency injection container. - public static IServiceCollection AddBaGetOptions( - this IServiceCollection services, - string key = null) - where TOptions : class + /// + /// Configures and validates options. + /// + /// The options type that should be added. + /// The dependency injection container to add options. + /// + /// The configuration key that should be used when configuring the options. + /// If null, the root configuration will be used to configure the options. + /// + /// The dependency injection container. + public static IServiceCollection AddBaGetOptions( + this IServiceCollection services, + string key = null) + where TOptions : class + { + services.AddSingleton>(new ValidateBaGetOptions(key)); + services.AddSingleton>(provider => { - services.AddSingleton>(new ValidateBaGetOptions(key)); - services.AddSingleton>(provider => + var config = provider.GetRequiredService(); + if (key != null) { - var config = provider.GetRequiredService(); - if (key != null) - { - config = config.GetSection(key); - } + config = config.GetSection(key); + } - return new BindOptions(config); - }); + return new BindOptions(config); + }); - return services; - } + return services; + } + + private static void AddConfiguration(this IServiceCollection services) + { + services.AddBaGetOptions(); + services.AddBaGetOptions(nameof(BaGetOptions.Database)); + services.AddBaGetOptions(nameof(BaGetOptions.Storage)); + services.AddBaGetOptions(nameof(BaGetOptions.Mirror)); + services.AddBaGetOptions(nameof(BaGetOptions.Search)); + services.AddBaGetOptions(nameof(BaGetOptions.Storage)); + } - private static void AddConfiguration(this IServiceCollection services) + private static void AddBaGetServices(this IServiceCollection services) + { + services.TryAddSingleton(); + services.TryAddSingleton(); + + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + + services.TryAddSingleton(HttpClientFactory); + services.TryAddSingleton(NuGetClientFactoryFactory); + + services.TryAddScoped(); + + services.TryAddTransient(); + services.TryAddTransient(); + services.TryAddTransient(); + services.TryAddTransient(); + services.TryAddTransient(); + services.TryAddTransient(); + services.TryAddTransient(); + services.TryAddTransient(); + services.TryAddTransient(); + services.TryAddTransient(); + + services.TryAddTransient(); + services.TryAddTransient(); + services.TryAddTransient(); + services.TryAddTransient(); + services.TryAddTransient(); + services.TryAddTransient(); + services.TryAddSingleton(); + services.TryAddTransient(); + + services.TryAddTransient(UpstreamClientFactory); + } + + private static void AddDefaultProviders(this IServiceCollection services) + { + services.AddProvider((provider, configuration) => { - services.AddBaGetOptions(); - services.AddBaGetOptions(nameof(BaGetOptions.Database)); - services.AddBaGetOptions(nameof(BaGetOptions.Storage)); - services.AddBaGetOptions(nameof(BaGetOptions.Mirror)); - services.AddBaGetOptions(nameof(BaGetOptions.Search)); - services.AddBaGetOptions(nameof(BaGetOptions.Storage)); - } + if (!configuration.HasSearchType("null")) return null; - private static void AddBaGetServices(this IServiceCollection services) + return provider.GetRequiredService(); + }); + + services.AddProvider((provider, configuration) => { - services.TryAddSingleton(); - services.TryAddSingleton(); - - services.TryAddSingleton(); - services.TryAddSingleton(); - services.TryAddSingleton(); - services.TryAddSingleton(); - services.TryAddSingleton(); - services.TryAddSingleton(); - services.TryAddSingleton(); - - services.TryAddSingleton(HttpClientFactory); - services.TryAddSingleton(NuGetClientFactoryFactory); - - services.TryAddScoped(); - - services.TryAddTransient(); - services.TryAddTransient(); - services.TryAddTransient(); - services.TryAddTransient(); - services.TryAddTransient(); - services.TryAddTransient(); - services.TryAddTransient(); - services.TryAddTransient(); - services.TryAddTransient(); - services.TryAddTransient(); - - services.TryAddTransient(); - services.TryAddTransient(); - services.TryAddTransient(); - services.TryAddTransient(); - services.TryAddTransient(); - services.TryAddTransient(); - services.TryAddSingleton(); - services.TryAddTransient(); - - services.TryAddTransient(UpstreamClientFactory); - } + if (!configuration.HasSearchType("null")) return null; + + return provider.GetRequiredService(); + }); - private static void AddDefaultProviders(this IServiceCollection services) + services.AddProvider((provider, configuration) => { - services.AddProvider((provider, configuration) => + if (configuration.HasStorageType("filesystem")) { - if (!configuration.HasSearchType("null")) return null; - - return provider.GetRequiredService(); - }); + return provider.GetRequiredService(); + } - services.AddProvider((provider, configuration) => + if (configuration.HasStorageType("null")) { - if (!configuration.HasSearchType("null")) return null; + return provider.GetRequiredService(); + } - return provider.GetRequiredService(); - }); + return null; + }); + } - services.AddProvider((provider, configuration) => - { - if (configuration.HasStorageType("filesystem")) - { - return provider.GetRequiredService(); - } - - if (configuration.HasStorageType("null")) - { - return provider.GetRequiredService(); - } - - return null; - }); - } + private static void AddFallbackServices(this IServiceCollection services) + { + services.TryAddScoped(); + + // BaGet's services have multiple implementations that live side-by-side. + // The application will choose the implementation using one of two ways: + // + // 1. Using the first implementation that was registered in the dependency injection + // container. This is the strategy used by applications that embed BaGet. + // 2. Using "providers". The providers will examine the application's configuration to + // determine whether its service implementation is active. Thsi is the strategy used + // by the default BaGet application. + // + // BaGet has database and search services, but the database services are special + // in that they may also act as search services. If an application registers the + // database service first and the search service second, the application should + // use the search service even though it wasn't registered first. Furthermore, + // if an application registers a database service without a search service, the + // database service should be used for search. This effect is achieved by deferring + // the database search service's registration until the very end. + services.TryAddTransient(provider => provider.GetRequiredService()); + services.TryAddTransient(provider => provider.GetRequiredService()); + } - private static void AddFallbackServices(this IServiceCollection services) - { - services.TryAddScoped(); - - // BaGet's services have multiple implementations that live side-by-side. - // The application will choose the implementation using one of two ways: - // - // 1. Using the first implementation that was registered in the dependency injection - // container. This is the strategy used by applications that embed BaGet. - // 2. Using "providers". The providers will examine the application's configuration to - // determine whether its service implementation is active. Thsi is the strategy used - // by the default BaGet application. - // - // BaGet has database and search services, but the database services are special - // in that they may also act as search services. If an application registers the - // database service first and the search service second, the application should - // use the search service even though it wasn't registered first. Furthermore, - // if an application registers a database service without a search service, the - // database service should be used for search. This effect is achieved by deferring - // the database search service's registration until the very end. - services.TryAddTransient(provider => provider.GetRequiredService()); - services.TryAddTransient(provider => provider.GetRequiredService()); - } + private static HttpClient HttpClientFactory(IServiceProvider provider) + { + var options = provider.GetRequiredService>().Value; + + var assembly = Assembly.GetEntryAssembly(); + var assemblyName = assembly.GetName().Name; + var assemblyVersion = assembly.GetCustomAttribute()?.InformationalVersion ?? "0.0.0"; - private static HttpClient HttpClientFactory(IServiceProvider provider) + var client = new HttpClient(new HttpClientHandler { - var options = provider.GetRequiredService>().Value; + AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, + }); - var assembly = Assembly.GetEntryAssembly(); - var assemblyName = assembly.GetName().Name; - var assemblyVersion = assembly.GetCustomAttribute()?.InformationalVersion ?? "0.0.0"; + client.DefaultRequestHeaders.Add("User-Agent", $"{assemblyName}/{assemblyVersion}"); + client.Timeout = TimeSpan.FromSeconds(options.PackageDownloadTimeoutSeconds); - var client = new HttpClient(new HttpClientHandler - { - AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, - }); + return client; + } - client.DefaultRequestHeaders.Add("User-Agent", $"{assemblyName}/{assemblyVersion}"); - client.Timeout = TimeSpan.FromSeconds(options.PackageDownloadTimeoutSeconds); + private static NuGetClientFactory NuGetClientFactoryFactory(IServiceProvider provider) + { + var httpClient = provider.GetRequiredService(); + var options = provider.GetRequiredService>(); - return client; - } + return new NuGetClientFactory( + httpClient, + options.Value.PackageSource.ToString()); + } - private static NuGetClientFactory NuGetClientFactoryFactory(IServiceProvider provider) - { - var httpClient = provider.GetRequiredService(); - var options = provider.GetRequiredService>(); + private static IUpstreamClient UpstreamClientFactory(IServiceProvider provider) + { + var options = provider.GetRequiredService>(); - return new NuGetClientFactory( - httpClient, - options.Value.PackageSource.ToString()); + // TODO: Convert to switch expression. + if (!options.Value.Enabled) + { + return provider.GetRequiredService(); } - private static IUpstreamClient UpstreamClientFactory(IServiceProvider provider) + else if (options.Value.Legacy) { - var options = provider.GetRequiredService>(); - - // TODO: Convert to switch expression. - if (!options.Value.Enabled) - { - return provider.GetRequiredService(); - } - - else if (options.Value.Legacy) - { - return provider.GetRequiredService(); - } + return provider.GetRequiredService(); + } - else - { - return provider.GetRequiredService(); - } + else + { + return provider.GetRequiredService(); } } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Extensions/IProvider.cs b/src/BaGet.Core/Extensions/IProvider.cs index 1a7b5612..a2e4dc7e 100644 --- a/src/BaGet.Core/Extensions/IProvider.cs +++ b/src/BaGet.Core/Extensions/IProvider.cs @@ -1,47 +1,45 @@ -using System; using Microsoft.Extensions.Configuration; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// Attempts to provide the . +/// +/// The service that may be provided. +public interface IProvider { /// - /// Attempts to provide the . + /// Attempt to provide the . /// - /// The service that may be provided. - public interface IProvider - { - /// - /// Attempt to provide the . - /// - /// The dependency injection container. - /// The app's configuration. - /// - /// An instance of , or, null if the - /// provider is not currently active in the . - /// - TService GetOrNull(IServiceProvider provider, IConfiguration configuration); - } + /// The dependency injection container. + /// The app's configuration. + /// + /// An instance of , or, null if the + /// provider is not currently active in the . + /// + TService GetOrNull(IServiceProvider provider, IConfiguration configuration); +} + +/// +/// Implements as a delegate. +/// +internal class DelegateProvider : IProvider +{ + private readonly Func _func; /// - /// Implements as a delegate. + /// Create an using a delegate. /// - internal class DelegateProvider : IProvider + /// + /// A delegate that returns an instance of , or, + /// null if the provider is not currently active due to the app's configuration. + public DelegateProvider(Func func) { - private readonly Func _func; - - /// - /// Create an using a delegate. - /// - /// - /// A delegate that returns an instance of , or, - /// null if the provider is not currently active due to the app's configuration. - public DelegateProvider(Func func) - { - _func = func ?? throw new ArgumentNullException(nameof(func)); - } + _func = func ?? throw new ArgumentNullException(nameof(func)); + } - public TService GetOrNull(IServiceProvider provider, IConfiguration configuration) - { - return _func(provider, configuration); - } + public TService GetOrNull(IServiceProvider provider, IConfiguration configuration) + { + return _func(provider, configuration); } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Extensions/PackageArchiveReaderExtensions.cs b/src/BaGet.Core/Extensions/PackageArchiveReaderExtensions.cs index 3e01432b..eced52e6 100644 --- a/src/BaGet.Core/Extensions/PackageArchiveReaderExtensions.cs +++ b/src/BaGet.Core/Extensions/PackageArchiveReaderExtensions.cs @@ -1,222 +1,215 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using NuGet.Common; using NuGet.Packaging; -namespace BaGet.Core +namespace BaGet.Core; + +using NuGetPackageType = NuGet.Packaging.Core.PackageType; + +public static class PackageArchiveReaderExtensions { - using NuGetPackageType = NuGet.Packaging.Core.PackageType; + public static bool HasReadme(this PackageArchiveReader package) + => !string.IsNullOrEmpty(package.NuspecReader.GetReadme()); + + public static bool HasEmbeddedIcon(this PackageArchiveReader package) + => !string.IsNullOrEmpty(package.NuspecReader.GetIcon()); - public static class PackageArchiveReaderExtensions + public async static Task GetReadmeAsync( + this PackageArchiveReader package, + CancellationToken cancellationToken) { - public static bool HasReadme(this PackageArchiveReader package) - => !string.IsNullOrEmpty(package.NuspecReader.GetReadme()); + var readmePath = package.NuspecReader.GetReadme(); + if (readmePath == null) + { + throw new InvalidOperationException("Package does not have a readme!"); + } - public static bool HasEmbeddedIcon(this PackageArchiveReader package) - => !string.IsNullOrEmpty(package.NuspecReader.GetIcon()); + return await package.GetStreamAsync(readmePath, cancellationToken); + } - public async static Task GetReadmeAsync( - this PackageArchiveReader package, - CancellationToken cancellationToken) - { - var readmePath = package.NuspecReader.GetReadme(); - if (readmePath == null) - { - throw new InvalidOperationException("Package does not have a readme!"); - } + public async static Task GetIconAsync( + this PackageArchiveReader package, + CancellationToken cancellationToken) + { + return await package.GetStreamAsync( + PathUtility.StripLeadingDirectorySeparators(package.NuspecReader.GetIcon()), + cancellationToken); + } - return await package.GetStreamAsync(readmePath, cancellationToken); - } + public static Package GetPackageMetadata(this PackageArchiveReader packageReader) + { + var nuspec = packageReader.NuspecReader; - public async static Task GetIconAsync( - this PackageArchiveReader package, - CancellationToken cancellationToken) - { - return await package.GetStreamAsync( - PathUtility.StripLeadingDirectorySeparators(package.NuspecReader.GetIcon()), - cancellationToken); - } + (var repositoryUri, var repositoryType) = GetRepositoryMetadata(nuspec); - public static Package GetPackageMetadata(this PackageArchiveReader packageReader) + return new Package { - var nuspec = packageReader.NuspecReader; - - (var repositoryUri, var repositoryType) = GetRepositoryMetadata(nuspec); + Id = nuspec.GetId(), + Version = nuspec.GetVersion(), + Authors = ParseAuthors(nuspec.GetAuthors()), + Description = nuspec.GetDescription(), + HasReadme = packageReader.HasReadme(), + HasEmbeddedIcon = packageReader.HasEmbeddedIcon(), + IsPrerelease = nuspec.GetVersion().IsPrerelease, + Language = nuspec.GetLanguage() ?? string.Empty, + ReleaseNotes = nuspec.GetReleaseNotes() ?? string.Empty, + Listed = true, + MinClientVersion = nuspec.GetMinClientVersion()?.ToNormalizedString() ?? string.Empty, + Published = DateTime.UtcNow, + RequireLicenseAcceptance = nuspec.GetRequireLicenseAcceptance(), + SemVerLevel = GetSemVerLevel(nuspec), + Summary = nuspec.GetSummary(), + Title = nuspec.GetTitle(), + IconUrl = ParseUri(nuspec.GetIconUrl()), + LicenseUrl = ParseUri(nuspec.GetLicenseUrl()), + ProjectUrl = ParseUri(nuspec.GetProjectUrl()), + RepositoryUrl = repositoryUri, + RepositoryType = repositoryType, + Dependencies = GetDependencies(nuspec), + Tags = ParseTags(nuspec.GetTags()), + PackageTypes = GetPackageTypes(nuspec), + TargetFrameworks = GetTargetFrameworks(packageReader), + }; + } - return new Package - { - Id = nuspec.GetId(), - Version = nuspec.GetVersion(), - Authors = ParseAuthors(nuspec.GetAuthors()), - Description = nuspec.GetDescription(), - HasReadme = packageReader.HasReadme(), - HasEmbeddedIcon = packageReader.HasEmbeddedIcon(), - IsPrerelease = nuspec.GetVersion().IsPrerelease, - Language = nuspec.GetLanguage() ?? string.Empty, - ReleaseNotes = nuspec.GetReleaseNotes() ?? string.Empty, - Listed = true, - MinClientVersion = nuspec.GetMinClientVersion()?.ToNormalizedString() ?? string.Empty, - Published = DateTime.UtcNow, - RequireLicenseAcceptance = nuspec.GetRequireLicenseAcceptance(), - SemVerLevel = GetSemVerLevel(nuspec), - Summary = nuspec.GetSummary(), - Title = nuspec.GetTitle(), - IconUrl = ParseUri(nuspec.GetIconUrl()), - LicenseUrl = ParseUri(nuspec.GetLicenseUrl()), - ProjectUrl = ParseUri(nuspec.GetProjectUrl()), - RepositoryUrl = repositoryUri, - RepositoryType = repositoryType, - Dependencies = GetDependencies(nuspec), - Tags = ParseTags(nuspec.GetTags()), - PackageTypes = GetPackageTypes(nuspec), - TargetFrameworks = GetTargetFrameworks(packageReader), - }; + // Based off https://github.com/NuGet/NuGetGallery/blob/master/src/NuGetGallery.Core/SemVerLevelKey.cs + private static SemVerLevel GetSemVerLevel(NuspecReader nuspec) + { + if (nuspec.GetVersion().IsSemVer2) + { + return SemVerLevel.SemVer2; } - // Based off https://github.com/NuGet/NuGetGallery/blob/master/src/NuGetGallery.Core/SemVerLevelKey.cs - private static SemVerLevel GetSemVerLevel(NuspecReader nuspec) + foreach (var dependencyGroup in nuspec.GetDependencyGroups()) { - if (nuspec.GetVersion().IsSemVer2) + foreach (var dependency in dependencyGroup.Packages) { - return SemVerLevel.SemVer2; - } - - foreach (var dependencyGroup in nuspec.GetDependencyGroups()) - { - foreach (var dependency in dependencyGroup.Packages) + if ((dependency.VersionRange.MinVersion != null && dependency.VersionRange.MinVersion.IsSemVer2) + || (dependency.VersionRange.MaxVersion != null && dependency.VersionRange.MaxVersion.IsSemVer2)) { - if ((dependency.VersionRange.MinVersion != null && dependency.VersionRange.MinVersion.IsSemVer2) - || (dependency.VersionRange.MaxVersion != null && dependency.VersionRange.MaxVersion.IsSemVer2)) - { - return SemVerLevel.SemVer2; - } + return SemVerLevel.SemVer2; } } - - return SemVerLevel.Unknown; } - private static Uri ParseUri(string uriString) - { - if (string.IsNullOrEmpty(uriString)) return null; + return SemVerLevel.Unknown; + } - return new Uri(uriString); - } + private static Uri ParseUri(string uriString) + { + if (string.IsNullOrEmpty(uriString)) return null; - private static string[] ParseAuthors(string authors) - { - if (string.IsNullOrEmpty(authors)) return new string[0]; + return new Uri(uriString); + } - return authors.Split(new[] { ',', ';', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); - } + private static string[] ParseAuthors(string authors) + { + if (string.IsNullOrEmpty(authors)) return new string[0]; - private static string[] ParseTags(string tags) - { - if (string.IsNullOrEmpty(tags)) return new string[0]; + return authors.Split(new[] { ',', ';', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); + } - return tags.Split(new[] { ',', ';', ' ', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); - } + private static string[] ParseTags(string tags) + { + if (string.IsNullOrEmpty(tags)) return new string[0]; - private static (Uri repositoryUrl, string repositoryType) GetRepositoryMetadata(NuspecReader nuspec) - { - var repository = nuspec.GetRepositoryMetadata(); + return tags.Split(new[] { ',', ';', ' ', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); + } - if (string.IsNullOrEmpty(repository?.Url) || - !Uri.TryCreate(repository.Url, UriKind.Absolute, out var repositoryUri)) - { - return (null, null); - } + private static (Uri repositoryUrl, string repositoryType) GetRepositoryMetadata(NuspecReader nuspec) + { + var repository = nuspec.GetRepositoryMetadata(); - if (repositoryUri.Scheme != Uri.UriSchemeHttps) - { - return (null, null); - } + if (string.IsNullOrEmpty(repository?.Url) || + !Uri.TryCreate(repository.Url, UriKind.Absolute, out var repositoryUri)) + { + return (null, null); + } - if (repository.Type.Length > 100) - { - throw new InvalidOperationException("Repository type must be less than or equal 100 characters"); - } + if (repositoryUri.Scheme != Uri.UriSchemeHttps) + { + return (null, null); + } - return (repositoryUri, repository.Type); + if (repository.Type.Length > 100) + { + throw new InvalidOperationException("Repository type must be less than or equal 100 characters"); } - private static List GetDependencies(NuspecReader nuspec) + return (repositoryUri, repository.Type); + } + + private static List GetDependencies(NuspecReader nuspec) + { + var dependencies = new List(); + + foreach (var group in nuspec.GetDependencyGroups()) { - var dependencies = new List(); + var targetFramework = group.TargetFramework.GetShortFolderName(); - foreach (var group in nuspec.GetDependencyGroups()) + if (!group.Packages.Any()) { - var targetFramework = group.TargetFramework.GetShortFolderName(); - - if (!group.Packages.Any()) + dependencies.Add(new PackageDependency { - dependencies.Add(new PackageDependency - { - Id = null, - VersionRange = null, - TargetFramework = targetFramework, - }); - } + Id = null, + VersionRange = null, + TargetFramework = targetFramework, + }); + } - foreach (var dependency in group.Packages) + foreach (var dependency in group.Packages) + { + dependencies.Add(new PackageDependency { - dependencies.Add(new PackageDependency - { - Id = dependency.Id, - VersionRange = dependency.VersionRange?.ToString(), - TargetFramework = targetFramework, - }); - } + Id = dependency.Id, + VersionRange = dependency.VersionRange?.ToString(), + TargetFramework = targetFramework, + }); } - - return dependencies; } - private static List GetPackageTypes(NuspecReader nuspec) - { - var packageTypes = nuspec - .GetPackageTypes() - .Select(t => new PackageType - { - Name = t.Name, - Version = t.Version.ToString() - }) - .ToList(); + return dependencies; + } - // Default to the standard "dependency" package type if no types were found. - if (packageTypes.Count == 0) + private static List GetPackageTypes(NuspecReader nuspec) + { + var packageTypes = nuspec + .GetPackageTypes() + .Select(t => new PackageType { - packageTypes.Add(new PackageType - { - Name = NuGetPackageType.Dependency.Name, - Version = NuGetPackageType.Dependency.Version.ToString(), - }); - } + Name = t.Name, + Version = t.Version.ToString() + }) + .ToList(); - return packageTypes; + // Default to the standard "dependency" package type if no types were found. + if (packageTypes.Count == 0) + { + packageTypes.Add(new PackageType + { + Name = NuGetPackageType.Dependency.Name, + Version = NuGetPackageType.Dependency.Version.ToString(), + }); } - private static List GetTargetFrameworks(PackageArchiveReader packageReader) - { - var targetFrameworks = packageReader - .GetSupportedFrameworks() - .Select(f => new TargetFramework - { - Moniker = f.GetShortFolderName() - }) - .ToList(); + return packageTypes; + } - // Default to the "any" framework if no frameworks were found. - if (targetFrameworks.Count == 0) + private static List GetTargetFrameworks(PackageArchiveReader packageReader) + { + var targetFrameworks = packageReader + .GetSupportedFrameworks() + .Select(f => new TargetFramework { - targetFrameworks.Add(new TargetFramework { Moniker = "any" }); - } + Moniker = f.GetShortFolderName() + }) + .ToList(); - return targetFrameworks; + // Default to the "any" framework if no frameworks were found. + if (targetFrameworks.Count == 0) + { + targetFrameworks.Add(new TargetFramework { Moniker = "any" }); } + + return targetFrameworks; } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Extensions/StreamExtensions.cs b/src/BaGet.Core/Extensions/StreamExtensions.cs index bfe9d840..f2410235 100644 --- a/src/BaGet.Core/Extensions/StreamExtensions.cs +++ b/src/BaGet.Core/Extensions/StreamExtensions.cs @@ -1,59 +1,53 @@ -using System; -using System.IO; -using System.Linq; using System.Security.Cryptography; -using System.Threading; -using System.Threading.Tasks; -namespace BaGet.Core +namespace BaGet.Core; + +public static class StreamExtensions { - 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) { - // See: https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/IO/Stream.cs#L35 - private const int DefaultCopyBufferSize = 81920; + var result = new FileStream( + Path.GetTempFileName(), + FileMode.Create, + FileAccess.ReadWrite, + FileShare.None, + DefaultCopyBufferSize, + FileOptions.DeleteOnClose); - /// - /// 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) + try { - 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; + 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) + public static bool Matches(this Stream content, Stream target) + { + using (var sha256 = SHA256.Create()) { - using (var sha256 = SHA256.Create()) - { - var contentHash = sha256.ComputeHash(content); - var targetHash = sha256.ComputeHash(target); + var contentHash = sha256.ComputeHash(content); + var targetHash = sha256.ComputeHash(target); - return contentHash.SequenceEqual(targetHash); - } + return contentHash.SequenceEqual(targetHash); } } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Extensions/SystemTime.cs b/src/BaGet.Core/Extensions/SystemTime.cs index 4d8195fc..706f9293 100644 --- a/src/BaGet.Core/Extensions/SystemTime.cs +++ b/src/BaGet.Core/Extensions/SystemTime.cs @@ -1,12 +1,9 @@ -using System; +namespace BaGet.Core; -namespace BaGet.Core +/// +/// A wrapper that allows for unit tests related to system time. +/// +public class SystemTime { - /// - /// A wrapper that allows for unit tests related to system time. - /// - public class SystemTime - { - public virtual DateTime UtcNow => DateTime.UtcNow; - } -} + public virtual DateTime UtcNow => DateTime.UtcNow; +} \ No newline at end of file diff --git a/src/BaGet.Core/IPackageDatabase.cs b/src/BaGet.Core/IPackageDatabase.cs index 38ec2966..204793ae 100644 --- a/src/BaGet.Core/IPackageDatabase.cs +++ b/src/BaGet.Core/IPackageDatabase.cs @@ -1,115 +1,111 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using NuGet.Versioning; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// The "source of truth" for packages' state. Packages' content +/// are stored by the . +/// +public interface IPackageDatabase { /// - /// The "source of truth" for packages' state. Packages' content - /// are stored by the . + /// Attempt to add a new package to the database. /// - public interface IPackageDatabase - { - /// - /// Attempt to add a new package to the database. - /// - /// The package to add to the database. - /// A token to cancel the task. - /// The result of attempting to add the package to the database. - Task AddAsync(Package package, CancellationToken cancellationToken); + /// The package to add to the database. + /// A token to cancel the task. + /// The result of attempting to add the package to the database. + Task AddAsync(Package package, CancellationToken cancellationToken); - /// - /// 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. - /// A token to cancel the task. - /// The package found, or null. - Task FindOrNullAsync( - string id, - NuGetVersion version, - bool includeUnlisted, - CancellationToken cancellationToken); + /// + /// 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. + /// A token to cancel the task. + /// The package found, or null. + Task FindOrNullAsync( + string id, + NuGetVersion version, + bool includeUnlisted, + CancellationToken cancellationToken); - /// - /// Attempt to find all packages with a given id. - /// - /// The packages' id. - /// Whether unlisted results should be included. - /// A token to cancel the task. - /// The packages found. Always non-null. - Task> FindAsync(string id, bool includeUnlisted, CancellationToken cancellationToken); + /// + /// Attempt to find all packages with a given id. + /// + /// The packages' id. + /// Whether unlisted results should be included. + /// A token to cancel the task. + /// The packages found. Always non-null. + Task> FindAsync(string id, bool includeUnlisted, CancellationToken cancellationToken); - /// - /// Determine whether a package exists in the database (even if the package is unlisted). - /// - /// The package id to search. - /// A token to cancel the task. - /// Whether the package exists in the database. - Task ExistsAsync(string id, CancellationToken cancellationToken); + /// + /// Determine whether a package exists in the database (even if the package is unlisted). + /// + /// The package id to search. + /// A token to cancel the task. + /// Whether the package exists in the database. + Task ExistsAsync(string id, CancellationToken cancellationToken); - /// - /// Determine whether a package exists in the database (even if the package is unlisted). - /// - /// The package id to search. - /// The package version to search. - /// A token to cancel the task. - /// Whether the package exists in the database. - Task ExistsAsync(string id, NuGetVersion version, CancellationToken cancellationToken); + /// + /// Determine whether a package exists in the database (even if the package is unlisted). + /// + /// The package id to search. + /// The package version to search. + /// A token to cancel the task. + /// Whether the package exists in the database. + Task ExistsAsync(string id, NuGetVersion version, CancellationToken cancellationToken); - /// - /// Unlist a package, making it undiscoverable. - /// - /// The id of the package to unlist. - /// The version of the package to unlist. - /// A token to cancel the task. - /// False if the package does not exist. - Task UnlistPackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken); + /// + /// Unlist a package, making it undiscoverable. + /// + /// The id of the package to unlist. + /// The version of the package to unlist. + /// A token to cancel the task. + /// False if the package does not exist. + Task UnlistPackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken); - /// - /// Relist a package, making it discoverable. - /// - /// The id of the package to relist. - /// The version of the package to relist. - /// A token to cancel the task. - /// False if the package does not exist. - Task RelistPackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken); + /// + /// Relist a package, making it discoverable. + /// + /// The id of the package to relist. + /// The version of the package to relist. + /// A token to cancel the task. + /// False if the package does not exist. + Task RelistPackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken); - /// - /// Increment a package's download count. - /// - /// The id of the package to update. - /// The id of the package to update. - /// A token to cancel the task. - /// Task that completes when the package's download has been incremented. - Task AddDownloadAsync(string id, NuGetVersion version, CancellationToken cancellationToken); + /// + /// Increment a package's download count. + /// + /// The id of the package to update. + /// The id of the package to update. + /// A token to cancel the task. + /// Task that completes when the package's download has been incremented. + Task AddDownloadAsync(string id, NuGetVersion version, CancellationToken cancellationToken); - /// - /// Completely remove the package from the database. - /// - /// The id of the package to remove. - /// The version of the pacakge to remove. - /// A token to cancel the task. - /// False if the package doesn't exist. - Task HardDeletePackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken); - } + /// + /// Completely remove the package from the database. + /// + /// The id of the package to remove. + /// The version of the pacakge to remove. + /// A token to cancel the task. + /// False if the package doesn't exist. + Task HardDeletePackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken); +} +/// +/// The result of attempting to add the package to the database. +/// See +/// +public enum PackageAddResult +{ /// - /// The result of attempting to add the package to the database. - /// See + /// Failed to add the package as it already exists. /// - public enum PackageAddResult - { - /// - /// Failed to add the package as it already exists. - /// - PackageAlreadyExists, + PackageAlreadyExists, - /// - /// The package was added successfully. - /// - Success - } -} + /// + /// The package was added successfully. + /// + Success +} \ No newline at end of file diff --git a/src/BaGet.Core/IPackageService.cs b/src/BaGet.Core/IPackageService.cs index 768813d2..6e324695 100644 --- a/src/BaGet.Core/IPackageService.cs +++ b/src/BaGet.Core/IPackageService.cs @@ -1,70 +1,66 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using NuGet.Versioning; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// The service that combines the state of indexed packages and +/// upstream packages. +/// For upstream packages, see . +/// For indexed packages, see . +/// +public interface IPackageService { /// - /// The service that combines the state of indexed packages and - /// upstream packages. - /// For upstream packages, see . - /// For indexed packages, see . + /// Attempt to find a package's versions using mirroring. This will merge + /// results from the configured upstream source with the locally indexed packages. /// - public interface IPackageService - { - /// - /// 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 an empty list if the package cannot be found. - /// This includes unlisted versions. - /// - Task> FindPackageVersionsAsync(string id, CancellationToken cancellationToken); + /// The package's id to lookup + /// The token to cancel the lookup + /// + /// The package's versions, or an empty list if the package cannot be found. + /// This includes unlisted versions. + /// + Task> FindPackageVersionsAsync(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 metadata for all versions of a package, including unlisted versions. - /// Returns an empty list if the package cannot be found. - /// - Task> FindPackagesAsync(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 metadata for all versions of a package, including unlisted versions. + /// Returns an empty list if the package cannot be found. + /// + Task> FindPackagesAsync(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 package's version to lookup - /// The token to cancel the lookup - /// - /// The metadata for single version of a package. - /// Returns null if the package does not exist. - /// - Task FindPackageOrNullAsync(string id, NuGetVersion version, 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 package's version to lookup + /// The token to cancel the lookup + /// + /// The metadata for single version of a package. + /// Returns null if the package does not exist. + /// + Task FindPackageOrNullAsync(string id, NuGetVersion version, CancellationToken cancellationToken); - /// - /// Determine whether a package exists locally or on the upstream source. - /// - /// The package id to search. - /// The package version to search. - /// A token to cancel the task. - /// Whether the package exists in the database. - Task ExistsAsync(string id, NuGetVersion version, CancellationToken cancellationToken); + /// + /// Determine whether a package exists locally or on the upstream source. + /// + /// The package id to search. + /// The package version to search. + /// A token to cancel the task. + /// Whether the package exists in the database. + Task ExistsAsync(string id, NuGetVersion version, CancellationToken cancellationToken); - /// - /// Increment a package's download count. - /// - /// The id of the package to update. - /// The id of the package to update. - /// A token to cancel the task. - Task AddDownloadAsync(string packageId, NuGetVersion version, CancellationToken cancellationToken); - } -} + /// + /// Increment a package's download count. + /// + /// The id of the package to update. + /// The id of the package to update. + /// A token to cancel the task. + Task AddDownloadAsync(string packageId, NuGetVersion version, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/BaGet.Core/IUrlGenerator.cs b/src/BaGet.Core/IUrlGenerator.cs index 443dba79..44b61538 100644 --- a/src/BaGet.Core/IUrlGenerator.cs +++ b/src/BaGet.Core/IUrlGenerator.cs @@ -1,105 +1,104 @@ using NuGet.Versioning; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// Used to create URLs to resources in the NuGet protocol. +/// +public interface IUrlGenerator { /// - /// Used to create URLs to resources in the NuGet protocol. + /// Get the URL for the package source (also known as the "service index"). + /// See: https://docs.microsoft.com/en-us/nuget/api/service-index /// - public interface IUrlGenerator - { - /// - /// Get the URL for the package source (also known as the "service index"). - /// See: https://docs.microsoft.com/en-us/nuget/api/service-index - /// - string GetServiceIndexUrl(); + string GetServiceIndexUrl(); - /// - /// Get the URL for the root of the package content resource. - /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource - /// - string GetPackageContentResourceUrl(); + /// + /// Get the URL for the root of the package content resource. + /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource + /// + string GetPackageContentResourceUrl(); - /// - /// Get the URL for the root of the package metadata resource. - /// See: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource - /// - string GetPackageMetadataResourceUrl(); + /// + /// Get the URL for the root of the package metadata resource. + /// See: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource + /// + string GetPackageMetadataResourceUrl(); - /// - /// Get the URL to publish packages. - /// See: https://docs.microsoft.com/en-us/nuget/api/package-publish-resource - /// - string GetPackagePublishResourceUrl(); + /// + /// Get the URL to publish packages. + /// See: https://docs.microsoft.com/en-us/nuget/api/package-publish-resource + /// + string GetPackagePublishResourceUrl(); - /// - /// Get the URL to publish symbol packages. - /// See: https://docs.microsoft.com/en-us/nuget/api/symbol-package-publish-resource - /// - string GetSymbolPublishResourceUrl(); + /// + /// Get the URL to publish symbol packages. + /// See: https://docs.microsoft.com/en-us/nuget/api/symbol-package-publish-resource + /// + string GetSymbolPublishResourceUrl(); - /// - /// Get the URL to search for packages. - /// See: https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource - /// - string GetSearchResourceUrl(); + /// + /// Get the URL to search for packages. + /// See: https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource + /// + string GetSearchResourceUrl(); - /// - /// Get the URL to autocomplete package IDs. - /// See: https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource - /// - string GetAutocompleteResourceUrl(); + /// + /// Get the URL to autocomplete package IDs. + /// See: https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource + /// + string GetAutocompleteResourceUrl(); - /// - /// Get the URL for the entry point of a package's metadata. - /// See: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-index - /// - /// The package's ID. - string GetRegistrationIndexUrl(string id); + /// + /// Get the URL for the entry point of a package's metadata. + /// See: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-index + /// + /// The package's ID. + string GetRegistrationIndexUrl(string id); - /// - /// Get the URL for the metadata of several versions of a single package. - /// See: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-page - /// - /// The package's ID - /// The lowest SemVer 2.0.0 version in the page (inclusive) - /// The highest SemVer 2.0.0 version in the page (inclusive) - string GetRegistrationPageUrl(string id, NuGetVersion lower, NuGetVersion upper); + /// + /// Get the URL for the metadata of several versions of a single package. + /// See: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-page + /// + /// The package's ID + /// The lowest SemVer 2.0.0 version in the page (inclusive) + /// The highest SemVer 2.0.0 version in the page (inclusive) + string GetRegistrationPageUrl(string id, NuGetVersion lower, NuGetVersion upper); - /// - /// Get the URL for the metadata of a specific package ID and version. - /// See: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-leaf - /// - /// The package's ID - /// The package's version - string GetRegistrationLeafUrl(string id, NuGetVersion version); + /// + /// Get the URL for the metadata of a specific package ID and version. + /// See: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-leaf + /// + /// The package's ID + /// The package's version + string GetRegistrationLeafUrl(string id, NuGetVersion version); - /// - /// Get the URL that lists a package's versions. - /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#enumerate-package-versions - /// - /// The package's ID - string GetPackageVersionsUrl(string id); + /// + /// Get the URL that lists a package's versions. + /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#enumerate-package-versions + /// + /// The package's ID + string GetPackageVersionsUrl(string id); - /// - /// Get the URL to download a package (.nupkg). - /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-content-nupkg - /// - /// The package's ID - /// The package's version - string GetPackageDownloadUrl(string id, NuGetVersion version); + /// + /// Get the URL to download a package (.nupkg). + /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-content-nupkg + /// + /// The package's ID + /// The package's version + string GetPackageDownloadUrl(string id, NuGetVersion version); - /// - /// Get the URL to download a package's manifest (.nuspec). - /// - /// The package's ID - /// The package's version - string GetPackageManifestDownloadUrl(string id, NuGetVersion version); + /// + /// Get the URL to download a package's manifest (.nuspec). + /// + /// The package's ID + /// The package's version + string GetPackageManifestDownloadUrl(string id, NuGetVersion version); - /// - /// Get the URL to download a package icon. - /// - /// The package's ID - /// The package's version - string GetPackageIconDownloadUrl(string id, NuGetVersion version); - } -} + /// + /// Get the URL to download a package icon. + /// + /// The package's ID + /// The package's version + string GetPackageIconDownloadUrl(string id, NuGetVersion version); +} \ No newline at end of file diff --git a/src/BaGet.Core/Indexing/FrameworkCompatibilityService.cs b/src/BaGet.Core/Indexing/FrameworkCompatibilityService.cs index 4cf4820b..d15b514f 100644 --- a/src/BaGet.Core/Indexing/FrameworkCompatibilityService.cs +++ b/src/BaGet.Core/Indexing/FrameworkCompatibilityService.cs @@ -1,90 +1,86 @@ -using System; using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; using NuGet.Frameworks; -namespace BaGet.Core +namespace BaGet.Core; + +using static NuGet.Frameworks.FrameworkConstants; + +public class FrameworkCompatibilityService : IFrameworkCompatibilityService { - using static NuGet.Frameworks.FrameworkConstants; + private const string AnyFramework = "any"; - public class FrameworkCompatibilityService : IFrameworkCompatibilityService + private static readonly Dictionary KnownFrameworks; + private static readonly IReadOnlyList CompatibilityMapping; + private static readonly ConcurrentDictionary> CompatibleFrameworks; + + static FrameworkCompatibilityService() { - private const string AnyFramework = "any"; + var supportedFrameworks = new HashSet(); + supportedFrameworks.Add(FrameworkIdentifiers.NetStandard); + supportedFrameworks.Add(FrameworkIdentifiers.NetCoreApp); + supportedFrameworks.Add(FrameworkIdentifiers.Net); - private static readonly Dictionary KnownFrameworks; - private static readonly IReadOnlyList CompatibilityMapping; - private static readonly ConcurrentDictionary> CompatibleFrameworks; + CompatibilityMapping = DefaultFrameworkMappings.Instance.CompatibilityMappings.ToList(); + CompatibleFrameworks = new ConcurrentDictionary>(); - static FrameworkCompatibilityService() - { - var supportedFrameworks = new HashSet(); - supportedFrameworks.Add(FrameworkIdentifiers.NetStandard); - supportedFrameworks.Add(FrameworkIdentifiers.NetCoreApp); - supportedFrameworks.Add(FrameworkIdentifiers.Net); + KnownFrameworks = typeof(CommonFrameworks) + .GetFields() + .Where(f => f.IsStatic) + .Where(f => f.FieldType == typeof(NuGetFramework)) + .Select(f => (NuGetFramework)f.GetValue(null)) + .Where(f => supportedFrameworks.Contains(f.Framework)) + .ToDictionary(f => f.GetShortFolderName()); - CompatibilityMapping = DefaultFrameworkMappings.Instance.CompatibilityMappings.ToList(); - CompatibleFrameworks = new ConcurrentDictionary>(); - - KnownFrameworks = typeof(CommonFrameworks) - .GetFields() - .Where(f => f.IsStatic) - .Where(f => f.FieldType == typeof(NuGetFramework)) - .Select(f => (NuGetFramework)f.GetValue(null)) - .Where(f => supportedFrameworks.Contains(f.Framework)) - .ToDictionary(f => f.GetShortFolderName()); + // Add more frameworks missing from "CommonFrameworks" + KnownFrameworks["net472"] = new NuGetFramework(FrameworkIdentifiers.Net, new Version(4, 7, 2, 0)); + KnownFrameworks["net471"] = new NuGetFramework(FrameworkIdentifiers.Net, new Version(4, 7, 1, 0)); + } - // Add more frameworks missing from "CommonFrameworks" - KnownFrameworks["net472"] = new NuGetFramework(FrameworkIdentifiers.Net, new Version(4, 7, 2, 0)); - KnownFrameworks["net471"] = new NuGetFramework(FrameworkIdentifiers.Net, new Version(4, 7, 1, 0)); + public IReadOnlyList FindAllCompatibleFrameworks(string name) + { + if (!KnownFrameworks.TryGetValue(name, out var framework)) + { + return new List { name, AnyFramework }; } - public IReadOnlyList FindAllCompatibleFrameworks(string name) - { - if (!KnownFrameworks.TryGetValue(name, out var framework)) - { - return new List { name, AnyFramework }; - } + return CompatibleFrameworks.GetOrAdd(framework, FindAllCompatibleFrameworks); + } - return CompatibleFrameworks.GetOrAdd(framework, FindAllCompatibleFrameworks); - } + private IReadOnlyList FindAllCompatibleFrameworks(NuGetFramework targetFramework) + { + var results = new HashSet { AnyFramework }; - private IReadOnlyList FindAllCompatibleFrameworks(NuGetFramework targetFramework) + // Find all framework mappings that apply to the target framework + foreach (var mapping in CompatibilityMapping) { - var results = new HashSet { AnyFramework }; - - // Find all framework mappings that apply to the target framework - foreach (var mapping in CompatibilityMapping) + // Skip this mapping if it isn't for our target framework. + if (!mapping.TargetFrameworkRange.Satisfies(targetFramework)) { - // Skip this mapping if it isn't for our target framework. - if (!mapping.TargetFrameworkRange.Satisfies(targetFramework)) - { - continue; - } - - // Any framework that satisfies this mapping is compatible with the target framework. - foreach (var possibleFramework in KnownFrameworks.Values) - { - if (mapping.SupportedFrameworkRange.Satisfies(possibleFramework)) - { - results.Add(possibleFramework.GetShortFolderName()); - } - } + continue; } - // Find all frameworks that have the same "framework identifier" and a version - // less than equal to our target framework. For example, "net45" is compatible - // with "net20" and "net45". + // Any framework that satisfies this mapping is compatible with the target framework. foreach (var possibleFramework in KnownFrameworks.Values) { - if (possibleFramework.Framework == targetFramework.Framework && - possibleFramework.Version <= targetFramework.Version) + if (mapping.SupportedFrameworkRange.Satisfies(possibleFramework)) { results.Add(possibleFramework.GetShortFolderName()); } } + } - return results.ToList(); + // Find all frameworks that have the same "framework identifier" and a version + // less than equal to our target framework. For example, "net45" is compatible + // with "net20" and "net45". + foreach (var possibleFramework in KnownFrameworks.Values) + { + if (possibleFramework.Framework == targetFramework.Framework && + possibleFramework.Version <= targetFramework.Version) + { + results.Add(possibleFramework.GetShortFolderName()); + } } + + return results.ToList(); } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Indexing/IFrameworkCompatibilityService.cs b/src/BaGet.Core/Indexing/IFrameworkCompatibilityService.cs index 50f07256..9b139c48 100644 --- a/src/BaGet.Core/Indexing/IFrameworkCompatibilityService.cs +++ b/src/BaGet.Core/Indexing/IFrameworkCompatibilityService.cs @@ -1,17 +1,14 @@ -using System.Collections.Generic; +namespace BaGet.Core; -namespace BaGet.Core +/// +/// Used to determine the compatibility matrix between frameworks. +/// +public interface IFrameworkCompatibilityService { /// - /// Used to determine the compatibility matrix between frameworks. + /// Given a framework, find all other compatible frameworks. /// - public interface IFrameworkCompatibilityService - { - /// - /// Given a framework, find all other compatible frameworks. - /// - /// The input framework. - /// The list of compatible frameworks. - IReadOnlyList FindAllCompatibleFrameworks(string framework); - } -} + /// The input framework. + /// The list of compatible frameworks. + IReadOnlyList FindAllCompatibleFrameworks(string framework); +} \ No newline at end of file diff --git a/src/BaGet.Core/Indexing/IPackageDeletionService.cs b/src/BaGet.Core/Indexing/IPackageDeletionService.cs index 691b99e8..0bb6c168 100644 --- a/src/BaGet.Core/Indexing/IPackageDeletionService.cs +++ b/src/BaGet.Core/Indexing/IPackageDeletionService.cs @@ -1,18 +1,15 @@ -using System.Threading; -using System.Threading.Tasks; using NuGet.Versioning; -namespace BaGet.Core +namespace BaGet.Core; + +public interface IPackageDeletionService { - 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); - } -} + /// + /// 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); +} \ No newline at end of file diff --git a/src/BaGet.Core/Indexing/IPackageIndexingService.cs b/src/BaGet.Core/Indexing/IPackageIndexingService.cs index ddbf06d2..36c743e7 100644 --- a/src/BaGet.Core/Indexing/IPackageIndexingService.cs +++ b/src/BaGet.Core/Indexing/IPackageIndexingService.cs @@ -1,42 +1,37 @@ -using System.IO; -using System.Threading; -using System.Threading.Tasks; +namespace BaGet.Core; -namespace BaGet.Core +/// +/// The result of attempting to index a package. +/// See . +/// +public enum PackageIndexingResult { /// - /// The result of attempting to index a package. - /// See . + /// The package is malformed. This may also happen if BaGet is in a corrupted state. /// - public enum PackageIndexingResult - { - /// - /// The package is malformed. This may also happen if BaGet is in a corrupted state. - /// - InvalidPackage, + InvalidPackage, - /// - /// The package has already been indexed. - /// - PackageAlreadyExists, - - /// - /// The package has been indexed successfully. - /// - Success, - } + /// + /// The package has already been indexed. + /// + PackageAlreadyExists, /// - /// The service used to accept new packages. + /// The package has been indexed successfully. /// - public interface IPackageIndexingService - { - /// - /// Attempt to index a new package. - /// - /// The stream containing the package's content. - /// - /// The result of the attempted indexing operation. - Task IndexAsync(Stream stream, CancellationToken cancellationToken); - } + Success, } + +/// +/// The service used to accept new packages. +/// +public interface IPackageIndexingService +{ + /// + /// Attempt to index a new package. + /// + /// The stream containing the package's content. + /// + /// The result of the attempted indexing operation. + Task IndexAsync(Stream stream, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/BaGet.Core/Indexing/ISymbolIndexingService.cs b/src/BaGet.Core/Indexing/ISymbolIndexingService.cs index 85a671a9..7eeaafef 100644 --- a/src/BaGet.Core/Indexing/ISymbolIndexingService.cs +++ b/src/BaGet.Core/Indexing/ISymbolIndexingService.cs @@ -1,42 +1,37 @@ -using System.IO; -using System.Threading; -using System.Threading.Tasks; +namespace BaGet.Core; -namespace BaGet.Core +/// +/// The result of attempting to index a symbol package. +/// See . +/// +public enum SymbolIndexingResult { /// - /// The result of attempting to index a symbol package. - /// See . + /// The symbol package is malformed. /// - public enum SymbolIndexingResult - { - /// - /// The symbol package is malformed. - /// - InvalidSymbolPackage, + InvalidSymbolPackage, - /// - /// A corresponding package with the provided ID and version does not exist. - /// - PackageNotFound, - - /// - /// The symbol package has been indexed successfully. - /// - Success, - } + /// + /// A corresponding package with the provided ID and version does not exist. + /// + PackageNotFound, /// - /// The service used to accept new symbol packages. + /// The symbol package has been indexed successfully. /// - public interface ISymbolIndexingService - { - /// - /// Attempt to index a new symbol package. - /// - /// The stream containing the symbol package's content. - /// - /// The result of the attempted indexing operation. - Task IndexAsync(Stream stream, CancellationToken cancellationToken); - } + Success, } + +/// +/// The service used to accept new symbol packages. +/// +public interface ISymbolIndexingService +{ + /// + /// Attempt to index a new symbol package. + /// + /// The stream containing the symbol package's content. + /// + /// The result of the attempted indexing operation. + Task IndexAsync(Stream stream, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/BaGet.Core/Indexing/PackageDeletionService.cs b/src/BaGet.Core/Indexing/PackageDeletionService.cs index 41cf60a9..5ceea9d7 100644 --- a/src/BaGet.Core/Indexing/PackageDeletionService.cs +++ b/src/BaGet.Core/Indexing/PackageDeletionService.cs @@ -1,92 +1,88 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using NuGet.Versioning; -namespace BaGet.Core +namespace BaGet.Core; + +public class PackageDeletionService : IPackageDeletionService { - public class PackageDeletionService : IPackageDeletionService + private readonly IPackageDatabase _packages; + private readonly IPackageStorageService _storage; + private readonly BaGetOptions _options; + private readonly ILogger _logger; + + public PackageDeletionService( + IPackageDatabase packages, + IPackageStorageService storage, + IOptionsSnapshot options, + ILogger logger) { - private readonly IPackageDatabase _packages; - private readonly IPackageStorageService _storage; - private readonly BaGetOptions _options; - private readonly ILogger _logger; - - public PackageDeletionService( - IPackageDatabase 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)); - } + _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) + public async Task TryDeletePackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + { + switch (_options.PackageDeletionBehavior) { - switch (_options.PackageDeletionBehavior) - { - case PackageDeletionBehavior.Unlist: - return await TryUnlistPackageAsync(id, version, cancellationToken); + case PackageDeletionBehavior.Unlist: + return await TryUnlistPackageAsync(id, version, cancellationToken); - case PackageDeletionBehavior.HardDelete: - return await TryHardDeletePackageAsync(id, version, cancellationToken); + case PackageDeletionBehavior.HardDelete: + return await TryHardDeletePackageAsync(id, version, cancellationToken); - default: - throw new InvalidOperationException($"Unknown deletion behavior '{_options.PackageDeletionBehavior}'"); - } + default: + throw new InvalidOperationException($"Unknown deletion behavior '{_options.PackageDeletionBehavior}'"); } + } + + private async Task TryUnlistPackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + { + _logger.LogInformation("Unlisting package {PackageId} {PackageVersion}...", id, version); - private async Task TryUnlistPackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + if (!await _packages.UnlistPackageAsync(id, version, cancellationToken)) { - _logger.LogInformation("Unlisting package {PackageId} {PackageVersion}...", id, version); + _logger.LogWarning("Could not find package {PackageId} {PackageVersion}", id, version); - if (!await _packages.UnlistPackageAsync(id, version, cancellationToken)) - { - _logger.LogWarning("Could not find package {PackageId} {PackageVersion}", id, version); + return false; + } - return false; - } + _logger.LogInformation("Unlisted package {PackageId} {PackageVersion}", id, version); - _logger.LogInformation("Unlisted package {PackageId} {PackageVersion}", id, version); + return true; + } - return true; - } + private async Task TryHardDeletePackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + { + _logger.LogInformation( + "Hard deleting package {PackageId} {PackageVersion} from the database...", + id, + version); - private async Task TryHardDeletePackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + var found = await _packages.HardDeletePackageAsync(id, version, cancellationToken); + if (!found) { - _logger.LogInformation( - "Hard deleting package {PackageId} {PackageVersion} from the database...", + _logger.LogWarning( + "Could not find package {PackageId} {PackageVersion} in the database", id, version); + } - var found = await _packages.HardDeletePackageAsync(id, version, cancellationToken); - 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); + // 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); + await _storage.DeleteAsync(id, version, cancellationToken); - _logger.LogInformation( - "Hard deleted package {PackageId} {PackageVersion} from storage", - id, - version); + _logger.LogInformation( + "Hard deleted package {PackageId} {PackageVersion} from storage", + id, + version); - return found; - } + return found; } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Indexing/PackageIndexingService.cs b/src/BaGet.Core/Indexing/PackageIndexingService.cs index 8a3006fa..96b76d6b 100644 --- a/src/BaGet.Core/Indexing/PackageIndexingService.cs +++ b/src/BaGet.Core/Indexing/PackageIndexingService.cs @@ -1,165 +1,160 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using NuGet.Packaging; -namespace BaGet.Core +namespace BaGet.Core; + +public class PackageIndexingService : IPackageIndexingService { - public class PackageIndexingService : IPackageIndexingService + private readonly IPackageDatabase _packages; + private readonly IPackageStorageService _storage; + private readonly ISearchIndexer _search; + private readonly SystemTime _time; + private readonly IOptionsSnapshot _options; + private readonly ILogger _logger; + + public PackageIndexingService( + IPackageDatabase packages, + IPackageStorageService storage, + ISearchIndexer search, + SystemTime time, + IOptionsSnapshot options, + ILogger logger) { - private readonly IPackageDatabase _packages; - private readonly IPackageStorageService _storage; - private readonly ISearchIndexer _search; - private readonly SystemTime _time; - private readonly IOptionsSnapshot _options; - private readonly ILogger _logger; - - public PackageIndexingService( - IPackageDatabase packages, - IPackageStorageService storage, - ISearchIndexer search, - SystemTime time, - IOptionsSnapshot options, - ILogger logger) - { - _packages = packages ?? throw new ArgumentNullException(nameof(packages)); - _storage = storage ?? throw new ArgumentNullException(nameof(storage)); - _search = search ?? throw new ArgumentNullException(nameof(search)); - _time = time ?? throw new ArgumentNullException(nameof(time)); - _options = options ?? throw new ArgumentNullException(nameof(options)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + _packages = packages ?? throw new ArgumentNullException(nameof(packages)); + _storage = storage ?? throw new ArgumentNullException(nameof(storage)); + _search = search ?? throw new ArgumentNullException(nameof(search)); + _time = time ?? throw new ArgumentNullException(nameof(time)); + _options = options ?? throw new ArgumentNullException(nameof(options)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public async Task IndexAsync(Stream packageStream, CancellationToken cancellationToken) - { - // Try to extract all the necessary information from the package. - Package package; - Stream nuspecStream; - Stream readmeStream; - Stream iconStream; + public async Task IndexAsync(Stream packageStream, CancellationToken cancellationToken) + { + // Try to extract all the necessary information from the package. + Package package; + Stream nuspecStream; + Stream readmeStream; + Stream iconStream; - try - { - using (var packageReader = new PackageArchiveReader(packageStream, leaveStreamOpen: true)) - { - package = packageReader.GetPackageMetadata(); - package.Published = _time.UtcNow; - - nuspecStream = await packageReader.GetNuspecAsync(cancellationToken); - nuspecStream = await nuspecStream.AsTemporaryFileStreamAsync(cancellationToken); - - if (package.HasReadme) - { - readmeStream = await packageReader.GetReadmeAsync(cancellationToken); - readmeStream = await readmeStream.AsTemporaryFileStreamAsync(cancellationToken); - } - else - { - readmeStream = null; - } - - if (package.HasEmbeddedIcon) - { - iconStream = await packageReader.GetIconAsync(cancellationToken); - iconStream = await iconStream.AsTemporaryFileStreamAsync(cancellationToken); - } - else - { - iconStream = null; - } - } - } - catch (Exception e) + try + { + using (var packageReader = new PackageArchiveReader(packageStream, leaveStreamOpen: true)) { - _logger.LogError(e, "Uploaded package is invalid"); + package = packageReader.GetPackageMetadata(); + package.Published = _time.UtcNow; - return PackageIndexingResult.InvalidPackage; - } + nuspecStream = await packageReader.GetNuspecAsync(cancellationToken); + nuspecStream = await nuspecStream.AsTemporaryFileStreamAsync(cancellationToken); - // The package is well-formed. Ensure this is a new package. - if (await _packages.ExistsAsync(package.Id, package.Version, cancellationToken)) - { - if (!_options.Value.AllowPackageOverwrites) + if (package.HasReadme) { - return PackageIndexingResult.PackageAlreadyExists; + readmeStream = await packageReader.GetReadmeAsync(cancellationToken); + readmeStream = await readmeStream.AsTemporaryFileStreamAsync(cancellationToken); + } + else + { + readmeStream = null; } - await _packages.HardDeletePackageAsync(package.Id, package.Version, cancellationToken); - await _storage.DeleteAsync(package.Id, package.Version, cancellationToken); - } - - // TODO: Add more package validations - // TODO: Call PackageArchiveReader.ValidatePackageEntriesAsync - _logger.LogInformation( - "Validated package {PackageId} {PackageVersion}, persisting content to storage...", - package.Id, - package.NormalizedVersionString); - - try - { - packageStream.Position = 0; - - await _storage.SavePackageContentAsync( - package, - packageStream, - nuspecStream, - readmeStream, - iconStream, - cancellationToken); - } - catch (Exception e) - { - // This may happen due to concurrent pushes. - // TODO: Make IPackageStorageService.SavePackageContentAsync return a result enum so this - // can be properly handled. - _logger.LogError( - e, - "Failed to persist package {PackageId} {PackageVersion} content to storage", - package.Id, - package.NormalizedVersionString); - - throw; + if (package.HasEmbeddedIcon) + { + iconStream = await packageReader.GetIconAsync(cancellationToken); + iconStream = await iconStream.AsTemporaryFileStreamAsync(cancellationToken); + } + else + { + iconStream = null; + } } + } + catch (Exception e) + { + _logger.LogError(e, "Uploaded package is invalid"); - _logger.LogInformation( - "Persisted package {Id} {Version} content to storage, saving metadata to database...", - package.Id, - package.NormalizedVersionString); + return PackageIndexingResult.InvalidPackage; + } - var result = await _packages.AddAsync(package, cancellationToken); - if (result == PackageAddResult.PackageAlreadyExists) + // The package is well-formed. Ensure this is a new package. + if (await _packages.ExistsAsync(package.Id, package.Version, cancellationToken)) + { + if (!_options.Value.AllowPackageOverwrites) { - _logger.LogWarning( - "Package {Id} {Version} metadata already exists in database", - package.Id, - package.NormalizedVersionString); - return PackageIndexingResult.PackageAlreadyExists; } - if (result != PackageAddResult.Success) - { - _logger.LogError($"Unknown {nameof(PackageAddResult)} value: {{PackageAddResult}}", result); + await _packages.HardDeletePackageAsync(package.Id, package.Version, cancellationToken); + await _storage.DeleteAsync(package.Id, package.Version, cancellationToken); + } - throw new InvalidOperationException($"Unknown {nameof(PackageAddResult)} value: {result}"); - } + // TODO: Add more package validations + // TODO: Call PackageArchiveReader.ValidatePackageEntriesAsync + _logger.LogInformation( + "Validated package {PackageId} {PackageVersion}, persisting content to storage...", + package.Id, + package.NormalizedVersionString); - _logger.LogInformation( - "Successfully persisted package {Id} {Version} metadata to database. Indexing in search...", + try + { + packageStream.Position = 0; + + await _storage.SavePackageContentAsync( + package, + packageStream, + nuspecStream, + readmeStream, + iconStream, + cancellationToken); + } + catch (Exception e) + { + // This may happen due to concurrent pushes. + // TODO: Make IPackageStorageService.SavePackageContentAsync return a result enum so this + // can be properly handled. + _logger.LogError( + e, + "Failed to persist package {PackageId} {PackageVersion} content to storage", package.Id, package.NormalizedVersionString); - await _search.IndexAsync(package, cancellationToken); + throw; + } + + _logger.LogInformation( + "Persisted package {Id} {Version} content to storage, saving metadata to database...", + package.Id, + package.NormalizedVersionString); - _logger.LogInformation( - "Successfully indexed package {Id} {Version} in search", + var result = await _packages.AddAsync(package, cancellationToken); + if (result == PackageAddResult.PackageAlreadyExists) + { + _logger.LogWarning( + "Package {Id} {Version} metadata already exists in database", package.Id, package.NormalizedVersionString); - return PackageIndexingResult.Success; + return PackageIndexingResult.PackageAlreadyExists; + } + + if (result != PackageAddResult.Success) + { + _logger.LogError($"Unknown {nameof(PackageAddResult)} value: {{PackageAddResult}}", result); + + throw new InvalidOperationException($"Unknown {nameof(PackageAddResult)} value: {result}"); } + + _logger.LogInformation( + "Successfully persisted package {Id} {Version} metadata to database. Indexing in search...", + package.Id, + package.NormalizedVersionString); + + await _search.IndexAsync(package, cancellationToken); + + _logger.LogInformation( + "Successfully indexed package {Id} {Version} in search", + package.Id, + package.NormalizedVersionString); + + return PackageIndexingResult.Success; } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Indexing/SymbolIndexingService.cs b/src/BaGet.Core/Indexing/SymbolIndexingService.cs index a8a3e52e..ff115d5b 100644 --- a/src/BaGet.Core/Indexing/SymbolIndexingService.cs +++ b/src/BaGet.Core/Indexing/SymbolIndexingService.cs @@ -1,206 +1,199 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Reflection.Metadata; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; using NuGet.Packaging; -namespace BaGet.Core +namespace BaGet.Core; + +// Based off: https://github.com/NuGet/NuGetGallery/blob/master/src/NuGetGallery/Services/SymbolPackageUploadService.cs +// Based off: https://github.com/NuGet/NuGet.Jobs/blob/master/src/Validation.Symbols/SymbolsValidatorService.cs#L44 +public class SymbolIndexingService : ISymbolIndexingService { - // Based off: https://github.com/NuGet/NuGetGallery/blob/master/src/NuGetGallery/Services/SymbolPackageUploadService.cs - // Based off: https://github.com/NuGet/NuGet.Jobs/blob/master/src/Validation.Symbols/SymbolsValidatorService.cs#L44 - public class SymbolIndexingService : ISymbolIndexingService + private static readonly HashSet ValidSymbolPackageContentExtensions = new HashSet { - private static readonly HashSet ValidSymbolPackageContentExtensions = new HashSet - { - ".pdb", - ".nuspec", - ".xml", - ".psmdcp", - ".rels", - ".p7s" - }; - - private readonly IPackageDatabase _packages; - private readonly ISymbolStorageService _storage; - private readonly ILogger _logger; - - public SymbolIndexingService( - IPackageDatabase packages, - ISymbolStorageService storage, - ILogger logger) - { - _packages = packages ?? throw new ArgumentNullException(nameof(packages)); - _storage = storage ?? throw new ArgumentNullException(nameof(storage)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + ".pdb", + ".nuspec", + ".xml", + ".psmdcp", + ".rels", + ".p7s" + }; + + private readonly IPackageDatabase _packages; + private readonly ISymbolStorageService _storage; + private readonly ILogger _logger; + + public SymbolIndexingService( + IPackageDatabase packages, + ISymbolStorageService storage, + ILogger logger) + { + _packages = packages ?? throw new ArgumentNullException(nameof(packages)); + _storage = storage ?? throw new ArgumentNullException(nameof(storage)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public async Task IndexAsync(Stream stream, CancellationToken cancellationToken) + public async Task IndexAsync(Stream stream, CancellationToken cancellationToken) + { + try { - try + using (var symbolPackage = new PackageArchiveReader(stream, leaveStreamOpen: true)) { - using (var symbolPackage = new PackageArchiveReader(stream, leaveStreamOpen: true)) + var pdbPaths = await GetSymbolPackagePdbPathsOrNullAsync(symbolPackage, cancellationToken); + if (pdbPaths == null) { - var pdbPaths = await GetSymbolPackagePdbPathsOrNullAsync(symbolPackage, cancellationToken); - if (pdbPaths == null) - { - return SymbolIndexingResult.InvalidSymbolPackage; - } + return SymbolIndexingResult.InvalidSymbolPackage; + } - // Ensure a corresponding NuGet package exists. - var packageId = symbolPackage.NuspecReader.GetId(); - var packageVersion = symbolPackage.NuspecReader.GetVersion(); + // Ensure a corresponding NuGet package exists. + var packageId = symbolPackage.NuspecReader.GetId(); + var packageVersion = symbolPackage.NuspecReader.GetVersion(); - var package = await _packages.FindOrNullAsync(packageId, packageVersion, includeUnlisted: true, cancellationToken); - if (package == null) - { - return SymbolIndexingResult.PackageNotFound; - } + var package = await _packages.FindOrNullAsync(packageId, packageVersion, includeUnlisted: true, cancellationToken); + if (package == null) + { + return SymbolIndexingResult.PackageNotFound; + } - using (var pdbs = new PdbList()) + using (var pdbs = new PdbList()) + { + // Extract the portable PDBs from the snupkg. Nothing is persisted until after all + // PDBs have been extracted and validated sucessfully. + foreach (var pdbPath in pdbPaths) { - // Extract the portable PDBs from the snupkg. Nothing is persisted until after all - // PDBs have been extracted and validated sucessfully. - foreach (var pdbPath in pdbPaths) + var portablePdb = await ExtractPortablePdbAsync(symbolPackage, pdbPath, cancellationToken); + if (portablePdb == null) { - var portablePdb = await ExtractPortablePdbAsync(symbolPackage, pdbPath, cancellationToken); - if (portablePdb == null) - { - return SymbolIndexingResult.InvalidSymbolPackage; - } - - pdbs.Add(portablePdb); + return SymbolIndexingResult.InvalidSymbolPackage; } - // Persist the portable PDBs to storage. - foreach (var pdb in pdbs) - { - await _storage.SavePortablePdbContentAsync(pdb.Filename, pdb.Key, pdb.Content, cancellationToken); - } + pdbs.Add(portablePdb); + } - return SymbolIndexingResult.Success; + // Persist the portable PDBs to storage. + foreach (var pdb in pdbs) + { + await _storage.SavePortablePdbContentAsync(pdb.Filename, pdb.Key, pdb.Content, cancellationToken); } + + return SymbolIndexingResult.Success; } } - catch (Exception e) - { - _logger.LogError(e, "Unable to index symbol package due to exception"); - return SymbolIndexingResult.InvalidSymbolPackage; - } } - - private async Task> GetSymbolPackagePdbPathsOrNullAsync( - PackageArchiveReader symbolPackage, - CancellationToken cancellationToken) + catch (Exception e) { - try - { - await symbolPackage.ValidatePackageEntriesAsync(cancellationToken); + _logger.LogError(e, "Unable to index symbol package due to exception"); + return SymbolIndexingResult.InvalidSymbolPackage; + } + } - var files = (await symbolPackage.GetFilesAsync(cancellationToken)).ToList(); + private async Task> GetSymbolPackagePdbPathsOrNullAsync( + PackageArchiveReader symbolPackage, + CancellationToken cancellationToken) + { + try + { + await symbolPackage.ValidatePackageEntriesAsync(cancellationToken); - // Ensure there are no unexpected file extensions within the symbol package. - if (!AreSymbolFilesValid(files)) - { - return null; - } + var files = (await symbolPackage.GetFilesAsync(cancellationToken)).ToList(); - return files.Where(p => Path.GetExtension(p) == ".pdb").ToList(); - } - catch (Exception) + // Ensure there are no unexpected file extensions within the symbol package. + if (!AreSymbolFilesValid(files)) { - // TODO: ValidatePackageEntries throws PackagingException. - // Add better logging. return null; } - } - private bool AreSymbolFilesValid(IReadOnlyList entries) + return files.Where(p => Path.GetExtension(p) == ".pdb").ToList(); + } + catch (Exception) { - // TODO: Validate that all PDBs are portable. See: https://github.com/NuGet/NuGetGallery/blob/master/src/NuGetGallery/Services/SymbolPackageService.cs#L174 - bool IsValidSymbolFileInfo(FileInfo file) - { - if (string.IsNullOrEmpty(file.Name)) return false; - if (string.IsNullOrEmpty(file.Extension)) return false; - if (!ValidSymbolPackageContentExtensions.Contains(file.Extension)) return false; - - return true; - } - - return entries.Select(e => new FileInfo(e)).All(IsValidSymbolFileInfo); + // TODO: ValidatePackageEntries throws PackagingException. + // Add better logging. + return null; } + } - private async Task ExtractPortablePdbAsync( - PackageArchiveReader symbolPackage, - string pdbPath, - CancellationToken cancellationToken) + private bool AreSymbolFilesValid(IReadOnlyList entries) + { + // TODO: Validate that all PDBs are portable. See: https://github.com/NuGet/NuGetGallery/blob/master/src/NuGetGallery/Services/SymbolPackageService.cs#L174 + bool IsValidSymbolFileInfo(FileInfo file) { - // TODO: Validate that the PDB has a corresponding DLL - // See: https://github.com/NuGet/NuGet.Jobs/blob/master/src/Validation.Symbols/SymbolsValidatorService.cs#L170 - Stream pdbStream = null; - PortablePdb result = null; - - try - { - using (var rawPdbStream = await symbolPackage.GetStreamAsync(pdbPath, cancellationToken)) - { - pdbStream = await rawPdbStream.AsTemporaryFileStreamAsync(); + if (string.IsNullOrEmpty(file.Name)) return false; + if (string.IsNullOrEmpty(file.Extension)) return false; + if (!ValidSymbolPackageContentExtensions.Contains(file.Extension)) return false; - string signature; - using (var pdbReaderProvider = MetadataReaderProvider.FromPortablePdbStream(pdbStream, MetadataStreamOptions.LeaveOpen)) - { - var reader = pdbReaderProvider.GetMetadataReader(); - var id = new BlobContentId(reader.DebugMetadataHeader.Id); + return true; + } - signature = id.Guid.ToString("N").ToUpperInvariant(); - } + return entries.Select(e => new FileInfo(e)).All(IsValidSymbolFileInfo); + } - var fileName = Path.GetFileName(pdbPath).ToLowerInvariant(); - var key = $"{signature}ffffffff"; + private async Task ExtractPortablePdbAsync( + PackageArchiveReader symbolPackage, + string pdbPath, + CancellationToken cancellationToken) + { + // TODO: Validate that the PDB has a corresponding DLL + // See: https://github.com/NuGet/NuGet.Jobs/blob/master/src/Validation.Symbols/SymbolsValidatorService.cs#L170 + Stream pdbStream = null; + PortablePdb result = null; - pdbStream.Position = 0; - result = new PortablePdb(fileName, key, pdbStream); - } - } - finally + try + { + using (var rawPdbStream = await symbolPackage.GetStreamAsync(pdbPath, cancellationToken)) { - if (result == null) + pdbStream = await rawPdbStream.AsTemporaryFileStreamAsync(); + + string signature; + using (var pdbReaderProvider = MetadataReaderProvider.FromPortablePdbStream(pdbStream, MetadataStreamOptions.LeaveOpen)) { - pdbStream?.Dispose(); + var reader = pdbReaderProvider.GetMetadataReader(); + var id = new BlobContentId(reader.DebugMetadataHeader.Id); + + signature = id.Guid.ToString("N").ToUpperInvariant(); } - } - return result; - } + var fileName = Path.GetFileName(pdbPath).ToLowerInvariant(); + var key = $"{signature}ffffffff"; - private class PortablePdb : IDisposable + pdbStream.Position = 0; + result = new PortablePdb(fileName, key, pdbStream); + } + } + finally { - public PortablePdb(string filename, string key, Stream content) + if (result == null) { - Filename = filename; - Key = key; - Content = content; + pdbStream?.Dispose(); } + } - public string Filename { get; } - public string Key { get; } - public Stream Content { get; } + return result; + } - public void Dispose() => Content.Dispose(); + private class PortablePdb : IDisposable + { + public PortablePdb(string filename, string key, Stream content) + { + Filename = filename; + Key = key; + Content = content; } - private class PdbList : List, IDisposable + public string Filename { get; } + public string Key { get; } + public Stream Content { get; } + + public void Dispose() => Content.Dispose(); + } + + private class PdbList : List, IDisposable + { + public void Dispose() { - public void Dispose() + foreach (var pdb in this) { - foreach (var pdb in this) - { - pdb.Dispose(); - } + pdb.Dispose(); } } } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Metadata/BaGetPackageMetadata.cs b/src/BaGet.Core/Metadata/BaGetPackageMetadata.cs index f84747d4..01d29166 100644 --- a/src/BaGet.Core/Metadata/BaGetPackageMetadata.cs +++ b/src/BaGet.Core/Metadata/BaGetPackageMetadata.cs @@ -1,34 +1,32 @@ -using System.Collections.Generic; using System.Text.Json.Serialization; using BaGet.Protocol.Models; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// BaGet's extensions to the package metadata model. These additions +/// are not part of the official protocol. +/// +public class BaGetPackageMetadata : PackageMetadata { - /// - /// BaGet's extensions to the package metadata model. These additions - /// are not part of the official protocol. - /// - public class BaGetPackageMetadata : PackageMetadata - { - [JsonPropertyName("downloads")] - public long Downloads { get; set; } + [JsonPropertyName("downloads")] + public long Downloads { get; set; } - [JsonPropertyName("hasReadme")] - public bool HasReadme { get; set; } + [JsonPropertyName("hasReadme")] + public bool HasReadme { get; set; } - [JsonPropertyName("packageTypes")] - public IReadOnlyList PackageTypes { get; set; } + [JsonPropertyName("packageTypes")] + public IReadOnlyList PackageTypes { get; set; } - /// - /// The package's release notes. - /// - [JsonPropertyName("releaseNotes")] - public string ReleaseNotes { get; set; } + /// + /// The package's release notes. + /// + [JsonPropertyName("releaseNotes")] + public string ReleaseNotes { get; set; } - [JsonPropertyName("repositoryUrl")] - public string RepositoryUrl { get; set; } + [JsonPropertyName("repositoryUrl")] + public string RepositoryUrl { get; set; } - [JsonPropertyName("repositoryType")] - public string RepositoryType { get; set; } - } -} + [JsonPropertyName("repositoryType")] + public string RepositoryType { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Core/Metadata/BaGetRegistrationIndexPage.cs b/src/BaGet.Core/Metadata/BaGetRegistrationIndexPage.cs index 6ade52d0..95137faf 100644 --- a/src/BaGet.Core/Metadata/BaGetRegistrationIndexPage.cs +++ b/src/BaGet.Core/Metadata/BaGetRegistrationIndexPage.cs @@ -1,39 +1,37 @@ -using System.Collections.Generic; using System.Text.Json.Serialization; using BaGet.Protocol.Models; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// BaGet's extensions to a registration index page. +/// Extends . +/// +/// +/// TODO: After this project is updated to .NET 5, make +/// extend and remove identical properties. +/// Properties that are modified should be marked with the "new" modified. +/// See: https://github.com/dotnet/runtime/pull/32107 +/// +public class BaGetRegistrationIndexPage { - /// - /// BaGet's extensions to a registration index page. - /// Extends . - /// - /// - /// TODO: After this project is updated to .NET 5, make - /// extend and remove identical properties. - /// Properties that are modified should be marked with the "new" modified. - /// See: https://github.com/dotnet/runtime/pull/32107 - /// - public class BaGetRegistrationIndexPage - { -#region Original properties from RegistrationIndexPage. - [JsonPropertyName("@id")] - public string RegistrationPageUrl { get; set; } + #region Original properties from RegistrationIndexPage. + [JsonPropertyName("@id")] + public string RegistrationPageUrl { get; set; } - [JsonPropertyName("count")] - public int Count { get; set; } + [JsonPropertyName("count")] + public int Count { get; set; } - [JsonPropertyName("lower")] - public string Lower { get; set; } + [JsonPropertyName("lower")] + public string Lower { get; set; } - [JsonPropertyName("upper")] - public string Upper { get; set; } -#endregion + [JsonPropertyName("upper")] + public string Upper { get; set; } + #endregion - /// - /// This was modified to use BaGet's extended registration index page item model. - /// - [JsonPropertyName("items")] - public IReadOnlyList ItemsOrNull { get; set; } - } -} + /// + /// This was modified to use BaGet's extended registration index page item model. + /// + [JsonPropertyName("items")] + public IReadOnlyList ItemsOrNull { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Core/Metadata/BaGetRegistrationIndexPageItem.cs b/src/BaGet.Core/Metadata/BaGetRegistrationIndexPageItem.cs index 3177522e..19da43eb 100644 --- a/src/BaGet.Core/Metadata/BaGetRegistrationIndexPageItem.cs +++ b/src/BaGet.Core/Metadata/BaGetRegistrationIndexPageItem.cs @@ -1,33 +1,32 @@ using System.Text.Json.Serialization; using BaGet.Protocol.Models; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// BaGet's extensions to a registration index page. +/// Extends . +/// +/// +/// TODO: After this project is updated to .NET 5, make +/// extend and remove identical properties. +/// Properties that are modified should be marked with the "new" modified. +/// See: https://github.com/dotnet/runtime/pull/32107 +/// +public class BaGetRegistrationIndexPageItem { - /// - /// BaGet's extensions to a registration index page. - /// Extends . - /// - /// - /// TODO: After this project is updated to .NET 5, make - /// extend and remove identical properties. - /// Properties that are modified should be marked with the "new" modified. - /// See: https://github.com/dotnet/runtime/pull/32107 - /// - public class BaGetRegistrationIndexPageItem - { -#region Original properties from RegistrationIndexPageItem. - [JsonPropertyName("@id")] - public string RegistrationLeafUrl { get; set; } + #region Original properties from RegistrationIndexPageItem. + [JsonPropertyName("@id")] + public string RegistrationLeafUrl { get; set; } - [JsonPropertyName("packageContent")] - public string PackageContentUrl { get; set; } -#endregion + [JsonPropertyName("packageContent")] + public string PackageContentUrl { get; set; } + #endregion - /// - /// The catalog entry containing the package metadata. - /// This was modified to use BaGet's extended package metadata model. - /// - [JsonPropertyName("catalogEntry")] - public BaGetPackageMetadata PackageMetadata { get; set; } - } -} + /// + /// The catalog entry containing the package metadata. + /// This was modified to use BaGet's extended package metadata model. + /// + [JsonPropertyName("catalogEntry")] + public BaGetPackageMetadata PackageMetadata { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Core/Metadata/BaGetRegistrationIndexResponse.cs b/src/BaGet.Core/Metadata/BaGetRegistrationIndexResponse.cs index 9ff6ce66..6153943c 100644 --- a/src/BaGet.Core/Metadata/BaGetRegistrationIndexResponse.cs +++ b/src/BaGet.Core/Metadata/BaGetRegistrationIndexResponse.cs @@ -1,45 +1,43 @@ -using System.Collections.Generic; using System.Text.Json.Serialization; using BaGet.Protocol.Models; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// BaGet's extensions to a registration index response. +/// Extends . +/// +/// +/// TODO: After this project is updated to .NET 5, make +/// extend and remove identical properties. +/// Properties that are modified should be marked with the "new" modified. +/// See: https://github.com/dotnet/runtime/pull/32107 +/// +public class BaGetRegistrationIndexResponse { - /// - /// BaGet's extensions to a registration index response. - /// Extends . - /// - /// - /// TODO: After this project is updated to .NET 5, make - /// extend and remove identical properties. - /// Properties that are modified should be marked with the "new" modified. - /// See: https://github.com/dotnet/runtime/pull/32107 - /// - public class BaGetRegistrationIndexResponse - { -#region Original properties from RegistrationIndexResponse. - [JsonPropertyName("@id")] - public string RegistrationIndexUrl { get; set; } + #region Original properties from RegistrationIndexResponse. + [JsonPropertyName("@id")] + public string RegistrationIndexUrl { get; set; } - [JsonPropertyName("@type")] - public IReadOnlyList Type { get; set; } + [JsonPropertyName("@type")] + public IReadOnlyList Type { get; set; } - [JsonPropertyName("count")] - public int Count { get; set; } -#endregion + [JsonPropertyName("count")] + public int Count { get; set; } + #endregion - /// - /// The pages that contain all of the versions of the package, ordered - /// by the package's version. This was modified to use BaGet's extended - /// registration index page model. - /// - [JsonPropertyName("items")] - public IReadOnlyList Pages { get; set; } + /// + /// The pages that contain all of the versions of the package, ordered + /// by the package's version. This was modified to use BaGet's extended + /// registration index page model. + /// + [JsonPropertyName("items")] + public IReadOnlyList Pages { get; set; } - /// - /// The package's total downloads across all versions. - /// This is not part of the official NuGet protocol. - /// - [JsonPropertyName("totalDownloads")] - public long TotalDownloads { get; set; } - } -} + /// + /// The package's total downloads across all versions. + /// This is not part of the official NuGet protocol. + /// + [JsonPropertyName("totalDownloads")] + public long TotalDownloads { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Core/Metadata/DefaultPackageMetadataService.cs b/src/BaGet.Core/Metadata/DefaultPackageMetadataService.cs index 130e1e59..2dbeb35d 100644 --- a/src/BaGet.Core/Metadata/DefaultPackageMetadataService.cs +++ b/src/BaGet.Core/Metadata/DefaultPackageMetadataService.cs @@ -1,54 +1,49 @@ -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; using NuGet.Versioning; -namespace BaGet.Core +namespace BaGet.Core; + +/// +public class DefaultPackageMetadataService : IPackageMetadataService { - /// - public class DefaultPackageMetadataService : IPackageMetadataService + private readonly IPackageService _packages; + private readonly RegistrationBuilder _builder; + + public DefaultPackageMetadataService( + IPackageService packages, + RegistrationBuilder builder) { - private readonly IPackageService _packages; - private readonly RegistrationBuilder _builder; + _packages = packages ?? throw new ArgumentNullException(nameof(packages)); + _builder = builder ?? throw new ArgumentNullException(nameof(builder)); + } - public DefaultPackageMetadataService( - IPackageService packages, - RegistrationBuilder builder) + public async Task GetRegistrationIndexOrNullAsync( + string packageId, + CancellationToken cancellationToken = default) + { + var packages = await _packages.FindPackagesAsync(packageId, cancellationToken); + if (!packages.Any()) { - _packages = packages ?? throw new ArgumentNullException(nameof(packages)); - _builder = builder ?? throw new ArgumentNullException(nameof(builder)); + return null; } - public async Task GetRegistrationIndexOrNullAsync( - string packageId, - CancellationToken cancellationToken = default) - { - var packages = await _packages.FindPackagesAsync(packageId, cancellationToken); - if (!packages.Any()) - { - return null; - } - - return _builder.BuildIndex( - new PackageRegistration( - packageId, - packages)); - } + return _builder.BuildIndex( + new PackageRegistration( + packageId, + packages)); + } - public async Task GetRegistrationLeafOrNullAsync( - string id, - NuGetVersion version, - CancellationToken cancellationToken = default) + public async Task GetRegistrationLeafOrNullAsync( + string id, + NuGetVersion version, + CancellationToken cancellationToken = default) + { + var package = await _packages.FindPackageOrNullAsync(id, version, cancellationToken); + if (package == null) { - var package = await _packages.FindPackageOrNullAsync(id, version, cancellationToken); - if (package == null) - { - return null; - } - - return _builder.BuildLeaf(package); + return null; } + + return _builder.BuildLeaf(package); } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Metadata/IPackageMetadataService.cs b/src/BaGet.Core/Metadata/IPackageMetadataService.cs index 83bf3fab..8a6f6fd3 100644 --- a/src/BaGet.Core/Metadata/IPackageMetadataService.cs +++ b/src/BaGet.Core/Metadata/IPackageMetadataService.cs @@ -1,36 +1,33 @@ -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; using NuGet.Versioning; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// The Package Metadata client, used to fetch packages' metadata. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource +/// +public interface IPackageMetadataService { /// - /// The Package Metadata client, used to fetch packages' metadata. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource + /// Attempt to get a package's registration index, if it exists. + /// See: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-page /// - public interface IPackageMetadataService - { - /// - /// Attempt to get a package's registration index, if it exists. - /// See: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-page - /// - /// The package's ID. - /// A token to cancel the task. - /// The package's registration index, or null if the package does not exist - Task GetRegistrationIndexOrNullAsync(string packageId, CancellationToken cancellationToken = default); + /// The package's ID. + /// A token to cancel the task. + /// The package's registration index, or null if the package does not exist + Task GetRegistrationIndexOrNullAsync(string packageId, CancellationToken cancellationToken = default); - /// - /// Get the metadata for a single package version, if the package exists. - /// - /// The package's id. - /// The package's version. - /// A token to cancel the task. - /// The registration leaf, or null if the package does not exist. - Task GetRegistrationLeafOrNullAsync( - string packageId, - NuGetVersion packageVersion, - CancellationToken cancellationToken = default); - } -} + /// + /// Get the metadata for a single package version, if the package exists. + /// + /// The package's id. + /// The package's version. + /// A token to cancel the task. + /// The registration leaf, or null if the package does not exist. + Task GetRegistrationLeafOrNullAsync( + string packageId, + NuGetVersion packageVersion, + CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/BaGet.Core/Metadata/PackageRegistration.cs b/src/BaGet.Core/Metadata/PackageRegistration.cs index 6e583574..5fdd2752 100644 --- a/src/BaGet.Core/Metadata/PackageRegistration.cs +++ b/src/BaGet.Core/Metadata/PackageRegistration.cs @@ -1,34 +1,30 @@ -using System; -using System.Collections.Generic; +namespace BaGet.Core; -namespace BaGet.Core +/// +/// The information on all versions of a package. +/// +public class PackageRegistration { /// - /// The information on all versions of a package. + /// Create a new registration object. /// - public class PackageRegistration + /// + /// All versions of the package. + public PackageRegistration( + string packageId, + IReadOnlyList packages) { - /// - /// Create a new registration object. - /// - /// - /// All versions of the package. - public PackageRegistration( - string packageId, - IReadOnlyList packages) - { - PackageId = packageId ?? throw new ArgumentNullException(nameof(packageId)); - Packages = packages ?? throw new ArgumentNullException(nameof(packages)); - } + PackageId = packageId ?? throw new ArgumentNullException(nameof(packageId)); + Packages = packages ?? throw new ArgumentNullException(nameof(packages)); + } - /// - /// The package's ID. - /// - public string PackageId { get; } + /// + /// The package's ID. + /// + public string PackageId { get; } - /// - /// The information for each version of the package. - /// - public IReadOnlyList Packages { get; } - } -} + /// + /// The information for each version of the package. + /// + public IReadOnlyList Packages { get; } +} \ No newline at end of file diff --git a/src/BaGet.Core/Metadata/RegistrationBuilder.cs b/src/BaGet.Core/Metadata/RegistrationBuilder.cs index 4090e932..02ee0705 100644 --- a/src/BaGet.Core/Metadata/RegistrationBuilder.cs +++ b/src/BaGet.Core/Metadata/RegistrationBuilder.cs @@ -1,118 +1,114 @@ -using System; -using System.Collections.Generic; -using System.Linq; using BaGet.Protocol.Models; -namespace BaGet.Core +namespace BaGet.Core; + +public class RegistrationBuilder { - public class RegistrationBuilder + private readonly IUrlGenerator _url; + + public RegistrationBuilder(IUrlGenerator url) { - private readonly IUrlGenerator _url; + _url = url ?? throw new ArgumentNullException(nameof(url)); + } - public RegistrationBuilder(IUrlGenerator url) - { - _url = url ?? throw new ArgumentNullException(nameof(url)); - } + public virtual BaGetRegistrationIndexResponse BuildIndex(PackageRegistration registration) + { + var sortedPackages = registration.Packages.OrderBy(p => p.Version).ToList(); - public virtual BaGetRegistrationIndexResponse BuildIndex(PackageRegistration registration) + // TODO: Paging of registration items. + // "Un-paged" example: https://api.nuget.org/v3/registration3/newtonsoft.json/index.json + // Paged example: https://api.nuget.org/v3/registration3/fake/index.json + return new BaGetRegistrationIndexResponse { - var sortedPackages = registration.Packages.OrderBy(p => p.Version).ToList(); - - // TODO: Paging of registration items. - // "Un-paged" example: https://api.nuget.org/v3/registration3/newtonsoft.json/index.json - // Paged example: https://api.nuget.org/v3/registration3/fake/index.json - return new BaGetRegistrationIndexResponse + RegistrationIndexUrl = _url.GetRegistrationIndexUrl(registration.PackageId), + Type = RegistrationIndexResponse.DefaultType, + Count = 1, + TotalDownloads = registration.Packages.Sum(p => p.Downloads), + Pages = new[] { - RegistrationIndexUrl = _url.GetRegistrationIndexUrl(registration.PackageId), - Type = RegistrationIndexResponse.DefaultType, - Count = 1, - TotalDownloads = registration.Packages.Sum(p => p.Downloads), - Pages = new[] + new BaGetRegistrationIndexPage { - new BaGetRegistrationIndexPage - { - RegistrationPageUrl = _url.GetRegistrationIndexUrl(registration.PackageId), - Count = registration.Packages.Count(), - Lower = sortedPackages.First().Version.ToNormalizedString().ToLowerInvariant(), - Upper = sortedPackages.Last().Version.ToNormalizedString().ToLowerInvariant(), - ItemsOrNull = sortedPackages.Select(ToRegistrationIndexPageItem).ToList(), - } + RegistrationPageUrl = _url.GetRegistrationIndexUrl(registration.PackageId), + Count = registration.Packages.Count(), + Lower = sortedPackages.First().Version.ToNormalizedString().ToLowerInvariant(), + Upper = sortedPackages.Last().Version.ToNormalizedString().ToLowerInvariant(), + ItemsOrNull = sortedPackages.Select(ToRegistrationIndexPageItem).ToList(), } - }; - } + } + }; + } + + public virtual RegistrationLeafResponse BuildLeaf(Package package) + { + var id = package.Id; + var version = package.Version; - public virtual RegistrationLeafResponse BuildLeaf(Package package) + return new RegistrationLeafResponse { - var id = package.Id; - var version = package.Version; + Type = RegistrationLeafResponse.DefaultType, + Listed = package.Listed, + Published = package.Published, + RegistrationLeafUrl = _url.GetRegistrationLeafUrl(id, version), + PackageContentUrl = _url.GetPackageDownloadUrl(id, version), + RegistrationIndexUrl = _url.GetRegistrationIndexUrl(id) + }; + } - return new RegistrationLeafResponse + private BaGetRegistrationIndexPageItem ToRegistrationIndexPageItem(Package package) => + new BaGetRegistrationIndexPageItem + { + RegistrationLeafUrl = _url.GetRegistrationLeafUrl(package.Id, package.Version), + PackageContentUrl = _url.GetPackageDownloadUrl(package.Id, package.Version), + PackageMetadata = new BaGetPackageMetadata { - Type = RegistrationLeafResponse.DefaultType, + PackageId = package.Id, + Version = package.Version.ToFullString(), + Authors = string.Join(", ", package.Authors), + Description = package.Description, + Downloads = package.Downloads, + HasReadme = package.HasReadme, + IconUrl = package.HasEmbeddedIcon + ? _url.GetPackageIconDownloadUrl(package.Id, package.Version) + : package.IconUrlString, + Language = package.Language, + LicenseUrl = package.LicenseUrlString, Listed = package.Listed, + MinClientVersion = package.MinClientVersion, + ReleaseNotes = package.ReleaseNotes, + PackageContentUrl = _url.GetPackageDownloadUrl(package.Id, package.Version), + PackageTypes = package.PackageTypes.Select(t => t.Name).ToList(), + ProjectUrl = package.ProjectUrlString, + RepositoryUrl = package.RepositoryUrlString, + RepositoryType = package.RepositoryType, Published = package.Published, - RegistrationLeafUrl = _url.GetRegistrationLeafUrl(id, version), - PackageContentUrl = _url.GetPackageDownloadUrl(id, version), - RegistrationIndexUrl = _url.GetRegistrationIndexUrl(id) - }; - } + RequireLicenseAcceptance = package.RequireLicenseAcceptance, + Summary = package.Summary, + Tags = package.Tags, + Title = package.Title, + DependencyGroups = ToDependencyGroups(package) + }, + }; - private BaGetRegistrationIndexPageItem ToRegistrationIndexPageItem(Package package) => - new BaGetRegistrationIndexPageItem + private IReadOnlyList ToDependencyGroups(Package package) + { + return package.Dependencies + .GroupBy(d => d.TargetFramework) + .Select(group => new DependencyGroupItem { - RegistrationLeafUrl = _url.GetRegistrationLeafUrl(package.Id, package.Version), - PackageContentUrl = _url.GetPackageDownloadUrl(package.Id, package.Version), - PackageMetadata = new BaGetPackageMetadata - { - PackageId = package.Id, - Version = package.Version.ToFullString(), - Authors = string.Join(", ", package.Authors), - Description = package.Description, - Downloads = package.Downloads, - HasReadme = package.HasReadme, - IconUrl = package.HasEmbeddedIcon - ? _url.GetPackageIconDownloadUrl(package.Id, package.Version) - : package.IconUrlString, - Language = package.Language, - LicenseUrl = package.LicenseUrlString, - Listed = package.Listed, - MinClientVersion = package.MinClientVersion, - ReleaseNotes = package.ReleaseNotes, - PackageContentUrl = _url.GetPackageDownloadUrl(package.Id, package.Version), - PackageTypes = package.PackageTypes.Select(t => t.Name).ToList(), - ProjectUrl = package.ProjectUrlString, - RepositoryUrl = package.RepositoryUrlString, - RepositoryType = package.RepositoryType, - Published = package.Published, - RequireLicenseAcceptance = package.RequireLicenseAcceptance, - Summary = package.Summary, - Tags = package.Tags, - Title = package.Title, - DependencyGroups = ToDependencyGroups(package) - }, - }; + TargetFramework = group.Key, - private IReadOnlyList ToDependencyGroups(Package package) - { - return package.Dependencies - .GroupBy(d => d.TargetFramework) - .Select(group => new DependencyGroupItem - { - TargetFramework = group.Key, - - // A package that supports a target framework but does not have dependencies while on - // that target framework is represented by a fake dependency with a null "Id" and "VersionRange". - // This fake dependency should not be included in the output. - Dependencies = group - .Where(d => d.Id != null && d.VersionRange != null) - .Select(d => new DependencyItem - { - Id = d.Id, - Range = d.VersionRange - }) - .ToList() - }) - .ToList(); - } + // A package that supports a target framework but does not have dependencies while on + // that target framework is represented by a fake dependency with a null "Id" and "VersionRange". + // This fake dependency should not be included in the output. + Dependencies = group + .Where(d => d.Id != null && d.VersionRange != null) + .Select(d => new DependencyItem + { + Id = d.Id, + Range = d.VersionRange + }) + .ToList() + }) + .ToList(); } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/PackageDatabase.cs b/src/BaGet.Core/PackageDatabase.cs index 98f5168c..e052ce92 100644 --- a/src/BaGet.Core/PackageDatabase.cs +++ b/src/BaGet.Core/PackageDatabase.cs @@ -1,147 +1,141 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using NuGet.Versioning; -namespace BaGet.Core +namespace BaGet.Core; + +public class PackageDatabase : IPackageDatabase { - public class PackageDatabase : IPackageDatabase + private readonly IContext _context; + + public PackageDatabase(IContext context) { - private readonly IContext _context; + _context = context ?? throw new ArgumentNullException(nameof(context)); + } - public PackageDatabase(IContext context) + public async Task AddAsync(Package package, CancellationToken cancellationToken) + { + try { - _context = context ?? throw new ArgumentNullException(nameof(context)); - } + _context.Packages.Add(package); - public async Task AddAsync(Package package, CancellationToken cancellationToken) - { - try - { - _context.Packages.Add(package); - - await _context.SaveChangesAsync(cancellationToken); - - return PackageAddResult.Success; - } - catch (DbUpdateException e) - when (_context.IsUniqueConstraintViolationException(e)) - { - return PackageAddResult.PackageAlreadyExists; - } - } + await _context.SaveChangesAsync(cancellationToken); - public async Task ExistsAsync(string id, CancellationToken cancellationToken) - { - return await _context - .Packages - .Where(p => p.Id == id) - .AnyAsync(cancellationToken); + return PackageAddResult.Success; } - - public async Task ExistsAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + catch (DbUpdateException e) + when (_context.IsUniqueConstraintViolationException(e)) { - return await _context - .Packages - .Where(p => p.Id == id) - .Where(p => p.NormalizedVersionString == version.ToNormalizedString()) - .AnyAsync(cancellationToken); + return PackageAddResult.PackageAlreadyExists; } + } - public async Task> FindAsync(string id, bool includeUnlisted, CancellationToken cancellationToken) - { - var query = _context.Packages - .Include(p => p.Dependencies) - .Include(p => p.PackageTypes) - .Include(p => p.TargetFrameworks) - .Where(p => p.Id == id); - - if (!includeUnlisted) - { - query = query.Where(p => p.Listed); - } - - return (await query.ToListAsync(cancellationToken)).AsReadOnly(); - } + public async Task ExistsAsync(string id, CancellationToken cancellationToken) + { + return await _context + .Packages + .Where(p => p.Id == id) + .AnyAsync(cancellationToken); + } - public Task FindOrNullAsync( - string id, - NuGetVersion version, - bool includeUnlisted, - CancellationToken cancellationToken) - { - var query = _context.Packages - .Include(p => p.Dependencies) - .Include(p => p.TargetFrameworks) - .Where(p => p.Id == id) - .Where(p => p.NormalizedVersionString == version.ToNormalizedString()); - - if (!includeUnlisted) - { - query = query.Where(p => p.Listed); - } - - return query.FirstOrDefaultAsync(cancellationToken); - } + public async Task ExistsAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + { + return await _context + .Packages + .Where(p => p.Id == id) + .Where(p => p.NormalizedVersionString == version.ToNormalizedString()) + .AnyAsync(cancellationToken); + } - public Task UnlistPackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + public async Task> FindAsync(string id, bool includeUnlisted, CancellationToken cancellationToken) + { + var query = _context.Packages + .Include(p => p.Dependencies) + .Include(p => p.PackageTypes) + .Include(p => p.TargetFrameworks) + .Where(p => p.Id == id); + + if (!includeUnlisted) { - return TryUpdatePackageAsync(id, version, p => p.Listed = false, cancellationToken); + query = query.Where(p => p.Listed); } - public Task RelistPackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + return (await query.ToListAsync(cancellationToken)).AsReadOnly(); + } + + public Task FindOrNullAsync( + string id, + NuGetVersion version, + bool includeUnlisted, + CancellationToken cancellationToken) + { + var query = _context.Packages + .Include(p => p.Dependencies) + .Include(p => p.TargetFrameworks) + .Where(p => p.Id == id) + .Where(p => p.NormalizedVersionString == version.ToNormalizedString()); + + if (!includeUnlisted) { - return TryUpdatePackageAsync(id, version, p => p.Listed = true, cancellationToken); + query = query.Where(p => p.Listed); } - public async Task AddDownloadAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + return query.FirstOrDefaultAsync(cancellationToken); + } + + public Task UnlistPackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + { + return TryUpdatePackageAsync(id, version, p => p.Listed = false, cancellationToken); + } + + public Task RelistPackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + { + return TryUpdatePackageAsync(id, version, p => p.Listed = true, cancellationToken); + } + + public async Task AddDownloadAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + { + await TryUpdatePackageAsync(id, version, p => p.Downloads += 1, cancellationToken); + } + + public async Task HardDeletePackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + { + var package = await _context.Packages + .Where(p => p.Id == id) + .Where(p => p.NormalizedVersionString == version.ToNormalizedString()) + .Include(p => p.Dependencies) + .Include(p => p.TargetFrameworks) + .FirstOrDefaultAsync(cancellationToken); + + if (package == null) { - await TryUpdatePackageAsync(id, version, p => p.Downloads += 1, cancellationToken); + return false; } - public async Task HardDeletePackageAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + _context.Packages.Remove(package); + await _context.SaveChangesAsync(cancellationToken); + + return true; + } + + private async Task TryUpdatePackageAsync( + string id, + NuGetVersion version, + Action action, + CancellationToken cancellationToken) + { + var package = await _context.Packages + .Where(p => p.Id == id) + .Where(p => p.NormalizedVersionString == version.ToNormalizedString()) + .FirstOrDefaultAsync(); + + if (package != null) { - var package = await _context.Packages - .Where(p => p.Id == id) - .Where(p => p.NormalizedVersionString == version.ToNormalizedString()) - .Include(p => p.Dependencies) - .Include(p => p.TargetFrameworks) - .FirstOrDefaultAsync(cancellationToken); - - if (package == null) - { - return false; - } - - _context.Packages.Remove(package); + action(package); await _context.SaveChangesAsync(cancellationToken); return true; } - private async Task TryUpdatePackageAsync( - string id, - NuGetVersion version, - Action action, - CancellationToken cancellationToken) - { - var package = await _context.Packages - .Where(p => p.Id == id) - .Where(p => p.NormalizedVersionString == version.ToNormalizedString()) - .FirstOrDefaultAsync(); - - if (package != null) - { - action(package); - await _context.SaveChangesAsync(cancellationToken); - - return true; - } - - return false; - } + return false; } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/PackageService.cs b/src/BaGet.Core/PackageService.cs index 1f176346..22575c15 100644 --- a/src/BaGet.Core/PackageService.cs +++ b/src/BaGet.Core/PackageService.cs @@ -1,149 +1,143 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; using NuGet.Versioning; -namespace BaGet.Core +namespace BaGet.Core; + +public class PackageService : IPackageService { - public class PackageService : IPackageService + private readonly IPackageDatabase _db; + private readonly IUpstreamClient _upstream; + private readonly IPackageIndexingService _indexer; + private readonly ILogger _logger; + + public PackageService( + IPackageDatabase db, + IUpstreamClient upstream, + IPackageIndexingService indexer, + ILogger logger) { - private readonly IPackageDatabase _db; - private readonly IUpstreamClient _upstream; - private readonly IPackageIndexingService _indexer; - private readonly ILogger _logger; - - public PackageService( - IPackageDatabase db, - IUpstreamClient upstream, - IPackageIndexingService indexer, - ILogger logger) - { - _db = db ?? throw new ArgumentNullException(nameof(db)); - _upstream = upstream ?? throw new ArgumentNullException(nameof(upstream)); - _indexer = indexer ?? throw new ArgumentNullException(nameof(indexer)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + _db = db ?? throw new ArgumentNullException(nameof(db)); + _upstream = upstream ?? throw new ArgumentNullException(nameof(upstream)); + _indexer = indexer ?? throw new ArgumentNullException(nameof(indexer)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public async Task> FindPackageVersionsAsync( - string id, - CancellationToken cancellationToken) - { - var upstreamVersions = await _upstream.ListPackageVersionsAsync(id, cancellationToken); + public async Task> FindPackageVersionsAsync( + string id, + CancellationToken cancellationToken) + { + var upstreamVersions = await _upstream.ListPackageVersionsAsync(id, cancellationToken); - // Merge the local package versions into the upstream package versions. - var localPackages = await _db.FindAsync(id, includeUnlisted: true, cancellationToken); - var localVersions = localPackages.Select(p => p.Version); + // Merge the local package versions into the upstream package versions. + var localPackages = await _db.FindAsync(id, includeUnlisted: true, cancellationToken); + var localVersions = localPackages.Select(p => p.Version); - if (!upstreamVersions.Any()) return localVersions.ToList(); - if (!localPackages.Any()) return upstreamVersions; + if (!upstreamVersions.Any()) return localVersions.ToList(); + if (!localPackages.Any()) return upstreamVersions; - return upstreamVersions.Concat(localVersions).Distinct().ToList(); - } - - public async Task> FindPackagesAsync(string id, CancellationToken cancellationToken) - { - var upstreamPackages = await _upstream.ListPackagesAsync(id, cancellationToken); - var localPackages = await _db.FindAsync(id, includeUnlisted: true, cancellationToken); + return upstreamVersions.Concat(localVersions).Distinct().ToList(); + } - if (!upstreamPackages.Any()) return localPackages; - if (!localPackages.Any()) return upstreamPackages; + public async Task> FindPackagesAsync(string id, CancellationToken cancellationToken) + { + var upstreamPackages = await _upstream.ListPackagesAsync(id, cancellationToken); + var localPackages = await _db.FindAsync(id, includeUnlisted: true, cancellationToken); - // Merge the local packages into the upstream packages. - var result = upstreamPackages.ToDictionary(p => p.Version); - var local = localPackages.ToDictionary(p => p.Version); + if (!upstreamPackages.Any()) return localPackages; + if (!localPackages.Any()) return upstreamPackages; - foreach (var localPackage in local) - { - result[localPackage.Key] = localPackage.Value; - } + // Merge the local packages into the upstream packages. + var result = upstreamPackages.ToDictionary(p => p.Version); + var local = localPackages.ToDictionary(p => p.Version); - return result.Values.ToList(); - } - - public async Task FindPackageOrNullAsync( - string id, - NuGetVersion version, - CancellationToken cancellationToken) + foreach (var localPackage in local) { - if (!await MirrorAsync(id, version, cancellationToken)) - { - return null; - } - - return await _db.FindOrNullAsync(id, version, includeUnlisted: true, cancellationToken); + result[localPackage.Key] = localPackage.Value; } - public async Task ExistsAsync(string id, NuGetVersion version, CancellationToken cancellationToken) - { - return await MirrorAsync(id, version, cancellationToken); - } + return result.Values.ToList(); + } - public async Task AddDownloadAsync(string packageId, NuGetVersion version, CancellationToken cancellationToken) + public async Task FindPackageOrNullAsync( + string id, + NuGetVersion version, + CancellationToken cancellationToken) + { + if (!await MirrorAsync(id, version, cancellationToken)) { - await _db.AddDownloadAsync(packageId, version, cancellationToken); + return null; } - /// - /// Index the package from an upstream if it does not exist locally. - /// - /// The package ID to index from an upstream. - /// The package version to index from an upstream. - /// - /// True if the package exists locally or was indexed from an upstream source. - private async Task MirrorAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + return await _db.FindOrNullAsync(id, version, includeUnlisted: true, cancellationToken); + } + + public async Task ExistsAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + { + return await MirrorAsync(id, version, cancellationToken); + } + + public async Task AddDownloadAsync(string packageId, NuGetVersion version, CancellationToken cancellationToken) + { + await _db.AddDownloadAsync(packageId, version, cancellationToken); + } + + /// + /// Index the package from an upstream if it does not exist locally. + /// + /// The package ID to index from an upstream. + /// The package version to index from an upstream. + /// + /// True if the package exists locally or was indexed from an upstream source. + private async Task MirrorAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + { + if (await _db.ExistsAsync(id, version, cancellationToken)) { - if (await _db.ExistsAsync(id, version, cancellationToken)) - { - return true; - } + return true; + } - _logger.LogInformation( - "Package {PackageId} {PackageVersion} does not exist locally. Checking upstream feed...", - id, - version); + _logger.LogInformation( + "Package {PackageId} {PackageVersion} does not exist locally. Checking upstream feed...", + id, + version); - try + try + { + using (var packageStream = await _upstream.DownloadPackageOrNullAsync(id, version, cancellationToken)) { - using (var packageStream = await _upstream.DownloadPackageOrNullAsync(id, version, cancellationToken)) + if (packageStream == null) { - if (packageStream == null) - { - _logger.LogWarning( - "Upstream feed does not have package {PackageId} {PackageVersion}", - id, - version); - return false; - } - - _logger.LogInformation( - "Downloaded package {PackageId} {PackageVersion}, indexing...", + _logger.LogWarning( + "Upstream feed does not have package {PackageId} {PackageVersion}", id, version); + return false; + } - var result = await _indexer.IndexAsync(packageStream, cancellationToken); + _logger.LogInformation( + "Downloaded package {PackageId} {PackageVersion}, indexing...", + id, + version); - _logger.LogInformation( - "Finished indexing package {PackageId} {PackageVersion} from upstream feed with result {Result}", - id, - version, - result); + var result = await _indexer.IndexAsync(packageStream, cancellationToken); - return result == PackageIndexingResult.Success; - } - } - catch (Exception e) - { - _logger.LogError( - e, - "Failed to index package {PackageId} {PackageVersion} from upstream", + _logger.LogInformation( + "Finished indexing package {PackageId} {PackageVersion} from upstream feed with result {Result}", id, - version); + version, + result); - return false; + return result == PackageIndexingResult.Success; } } + catch (Exception e) + { + _logger.LogError( + e, + "Failed to index package {PackageId} {PackageVersion} from upstream", + id, + version); + + return false; + } } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Search/AutocompleteRequest.cs b/src/BaGet.Core/Search/AutocompleteRequest.cs index ef5b0b97..f21ecccb 100644 --- a/src/BaGet.Core/Search/AutocompleteRequest.cs +++ b/src/BaGet.Core/Search/AutocompleteRequest.cs @@ -1,39 +1,38 @@ -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// The NuGet V3 search request. +/// See: https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource#request-parameters +/// +public class AutocompleteRequest { /// - /// The NuGet V3 search request. - /// See: https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource#request-parameters + /// The number of results to skip, for pagination. /// - public class AutocompleteRequest - { - /// - /// The number of results to skip, for pagination. - /// - public int Skip { get; set; } + public int Skip { get; set; } - /// - /// The number of results to return, for pagination. - /// - public int Take { get; set; } + /// + /// The number of results to return, for pagination. + /// + public int Take { get; set; } - /// - /// Whether to include pre-release packages. - /// - public bool IncludePrerelease { get; set; } + /// + /// Whether to include pre-release packages. + /// + public bool IncludePrerelease { get; set; } - /// - /// Whether to include SemVer 2.0.0 compatible packages. - /// - public bool IncludeSemVer2 { get; set; } + /// + /// Whether to include SemVer 2.0.0 compatible packages. + /// + public bool IncludeSemVer2 { get; set; } - /// - /// Filter results to a package type. If null, no filter is applied. - /// - public string PackageType { get; set; } + /// + /// Filter results to a package type. If null, no filter is applied. + /// + public string PackageType { get; set; } - /// - /// The search query. - /// - public string Query { get; set; } - } -} + /// + /// The search query. + /// + public string Query { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Core/Search/DatabaseSearchService.cs b/src/BaGet.Core/Search/DatabaseSearchService.cs index 1ab64a41..a3d44be7 100644 --- a/src/BaGet.Core/Search/DatabaseSearchService.cs +++ b/src/BaGet.Core/Search/DatabaseSearchService.cs @@ -1,197 +1,191 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; using Microsoft.EntityFrameworkCore; -namespace BaGet.Core +namespace BaGet.Core; + +public class DatabaseSearchService : ISearchService { - public class DatabaseSearchService : ISearchService + private readonly IContext _context; + private readonly IFrameworkCompatibilityService _frameworks; + private readonly ISearchResponseBuilder _searchBuilder; + + public DatabaseSearchService( + IContext context, + IFrameworkCompatibilityService frameworks, + ISearchResponseBuilder searchBuilder) + { + _context = context ?? throw new ArgumentNullException(nameof(context)); + _frameworks = frameworks ?? throw new ArgumentNullException(nameof(frameworks)); + _searchBuilder = searchBuilder ?? throw new ArgumentNullException(nameof(searchBuilder)); + } + + public async Task SearchAsync(SearchRequest request, CancellationToken cancellationToken) { - private readonly IContext _context; - private readonly IFrameworkCompatibilityService _frameworks; - private readonly ISearchResponseBuilder _searchBuilder; - - public DatabaseSearchService( - IContext context, - IFrameworkCompatibilityService frameworks, - ISearchResponseBuilder searchBuilder) + var frameworks = GetCompatibleFrameworksOrNull(request.Framework); + + IQueryable search = _context.Packages; + search = ApplySearchQuery(search, request.Query); + search = ApplySearchFilters( + search, + request.IncludePrerelease, + request.IncludeSemVer2, + request.PackageType, + frameworks); + + var packageIds = search + .Select(p => p.Id) + .Distinct() + .OrderBy(id => id) + .Skip(request.Skip) + .Take(request.Take); + + // This query MUST fetch all versions for each package that matches the search, + // otherwise the results for a package's latest version may be incorrect. + // If possible, we'll find all these packages in a single query by matching + // the package IDs in a subquery. Otherwise, run two queries: + // 1. Find the package IDs that match the search + // 2. Find all package versions for these package IDs + if (_context.SupportsLimitInSubqueries) { - _context = context ?? throw new ArgumentNullException(nameof(context)); - _frameworks = frameworks ?? throw new ArgumentNullException(nameof(frameworks)); - _searchBuilder = searchBuilder ?? throw new ArgumentNullException(nameof(searchBuilder)); + search = _context.Packages.Where(p => packageIds.Contains(p.Id)); } - - public async Task SearchAsync(SearchRequest request, CancellationToken cancellationToken) + else { - var frameworks = GetCompatibleFrameworksOrNull(request.Framework); - - IQueryable search = _context.Packages; - search = ApplySearchQuery(search, request.Query); - search = ApplySearchFilters( - search, - request.IncludePrerelease, - request.IncludeSemVer2, - request.PackageType, - frameworks); - - var packageIds = search - .Select(p => p.Id) - .Distinct() - .OrderBy(id => id) - .Skip(request.Skip) - .Take(request.Take); - - // This query MUST fetch all versions for each package that matches the search, - // otherwise the results for a package's latest version may be incorrect. - // If possible, we'll find all these packages in a single query by matching - // the package IDs in a subquery. Otherwise, run two queries: - // 1. Find the package IDs that match the search - // 2. Find all package versions for these package IDs - if (_context.SupportsLimitInSubqueries) - { - search = _context.Packages.Where(p => packageIds.Contains(p.Id)); - } - else - { - var packageIdResults = await packageIds.ToListAsync(cancellationToken); + var packageIdResults = await packageIds.ToListAsync(cancellationToken); - search = _context.Packages.Where(p => packageIdResults.Contains(p.Id)); - } + search = _context.Packages.Where(p => packageIdResults.Contains(p.Id)); + } - search = ApplySearchFilters( - search, - request.IncludePrerelease, - request.IncludeSemVer2, - request.PackageType, - frameworks); + search = ApplySearchFilters( + search, + request.IncludePrerelease, + request.IncludeSemVer2, + request.PackageType, + frameworks); - var results = await search.ToListAsync(cancellationToken); - var groupedResults = results - .GroupBy(p => p.Id, StringComparer.OrdinalIgnoreCase) - .Select(group => new PackageRegistration(group.Key, group.ToList())) - .ToList(); + var results = await search.ToListAsync(cancellationToken); + var groupedResults = results + .GroupBy(p => p.Id, StringComparer.OrdinalIgnoreCase) + .Select(group => new PackageRegistration(group.Key, group.ToList())) + .ToList(); - return _searchBuilder.BuildSearch(groupedResults); - } + return _searchBuilder.BuildSearch(groupedResults); + } - public async Task AutocompleteAsync( - AutocompleteRequest request, - CancellationToken cancellationToken) + public async Task AutocompleteAsync( + AutocompleteRequest request, + CancellationToken cancellationToken) + { + IQueryable search = _context.Packages; + + search = ApplySearchQuery(search, request.Query); + search = ApplySearchFilters( + search, + request.IncludePrerelease, + request.IncludeSemVer2, + request.PackageType, + frameworks: null); + + var packageIds = await search + .OrderByDescending(p => p.Downloads) + .Select(p => p.Id) + .Distinct() + .Skip(request.Skip) + .Take(request.Take) + .ToListAsync(cancellationToken); + + return _searchBuilder.BuildAutocomplete(packageIds); + } + + public async Task ListPackageVersionsAsync( + VersionsRequest request, + CancellationToken cancellationToken) + { + var packageId = request.PackageId.ToLower(); + var search = _context + .Packages + .Where(p => p.Id.ToLower().Equals(packageId)); + + search = ApplySearchFilters( + search, + request.IncludePrerelease, + request.IncludeSemVer2, + packageType: null, + frameworks: null); + + var packageVersions = await search + .Select(p => p.NormalizedVersionString) + .ToListAsync(cancellationToken); + + return _searchBuilder.BuildAutocomplete(packageVersions); + } + + public async Task FindDependentsAsync(string packageId, CancellationToken cancellationToken) + { + var dependents = await _context + .Packages + .Where(p => p.Listed) + .OrderByDescending(p => p.Downloads) + .Where(p => p.Dependencies.Any(d => d.Id == packageId)) + .Take(20) + .Select(r => new PackageDependent + { + Id = r.Id, + Description = r.Description, + TotalDownloads = r.Downloads + }) + .Distinct() + .ToListAsync(cancellationToken); + + return _searchBuilder.BuildDependents(dependents); + } + + private IQueryable ApplySearchQuery(IQueryable query, string search) + { + if (string.IsNullOrEmpty(search)) { - IQueryable search = _context.Packages; - - search = ApplySearchQuery(search, request.Query); - search = ApplySearchFilters( - search, - request.IncludePrerelease, - request.IncludeSemVer2, - request.PackageType, - frameworks: null); - - var packageIds = await search - .OrderByDescending(p => p.Downloads) - .Select(p => p.Id) - .Distinct() - .Skip(request.Skip) - .Take(request.Take) - .ToListAsync(cancellationToken); - - return _searchBuilder.BuildAutocomplete(packageIds); + return query; } - public async Task ListPackageVersionsAsync( - VersionsRequest request, - CancellationToken cancellationToken) + search = search.ToLowerInvariant(); + + return query.Where(p => p.Id.ToLower().Contains(search)); + } + + private IQueryable ApplySearchFilters( + IQueryable query, + bool includePrerelease, + bool includeSemVer2, + string packageType, + IReadOnlyList frameworks) + { + if (!includePrerelease) { - var packageId = request.PackageId.ToLower(); - var search = _context - .Packages - .Where(p => p.Id.ToLower().Equals(packageId)); - - search = ApplySearchFilters( - search, - request.IncludePrerelease, - request.IncludeSemVer2, - packageType: null, - frameworks: null); - - var packageVersions = await search - .Select(p => p.NormalizedVersionString) - .ToListAsync(cancellationToken); - - return _searchBuilder.BuildAutocomplete(packageVersions); + query = query.Where(p => !p.IsPrerelease); } - public async Task FindDependentsAsync(string packageId, CancellationToken cancellationToken) + if (!includeSemVer2) { - var dependents = await _context - .Packages - .Where(p => p.Listed) - .OrderByDescending(p => p.Downloads) - .Where(p => p.Dependencies.Any(d => d.Id == packageId)) - .Take(20) - .Select(r => new PackageDependent - { - Id = r.Id, - Description = r.Description, - TotalDownloads = r.Downloads - }) - .Distinct() - .ToListAsync(cancellationToken); - - return _searchBuilder.BuildDependents(dependents); + query = query.Where(p => p.SemVerLevel != SemVerLevel.SemVer2); } - private IQueryable ApplySearchQuery(IQueryable query, string search) + if (!string.IsNullOrEmpty(packageType)) { - if (string.IsNullOrEmpty(search)) - { - return query; - } - - search = search.ToLowerInvariant(); - - return query.Where(p => p.Id.ToLower().Contains(search)); + query = query.Where(p => p.PackageTypes.Any(t => t.Name == packageType)); } - private IQueryable ApplySearchFilters( - IQueryable query, - bool includePrerelease, - bool includeSemVer2, - string packageType, - IReadOnlyList frameworks) + if (frameworks != null) { - if (!includePrerelease) - { - query = query.Where(p => !p.IsPrerelease); - } - - if (!includeSemVer2) - { - query = query.Where(p => p.SemVerLevel != SemVerLevel.SemVer2); - } - - if (!string.IsNullOrEmpty(packageType)) - { - query = query.Where(p => p.PackageTypes.Any(t => t.Name == packageType)); - } - - if (frameworks != null) - { - query = query.Where(p => p.TargetFrameworks.Any(f => frameworks.Contains(f.Moniker))); - } - - return query.Where(p => p.Listed); + query = query.Where(p => p.TargetFrameworks.Any(f => frameworks.Contains(f.Moniker))); } - private IReadOnlyList GetCompatibleFrameworksOrNull(string framework) - { - if (framework == null) return null; + return query.Where(p => p.Listed); + } - return _frameworks.FindAllCompatibleFrameworks(framework); - } + private IReadOnlyList GetCompatibleFrameworksOrNull(string framework) + { + if (framework == null) return null; + + return _frameworks.FindAllCompatibleFrameworks(framework); } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Search/DependentsResponse.cs b/src/BaGet.Core/Search/DependentsResponse.cs index 2d797045..ca4f519c 100644 --- a/src/BaGet.Core/Search/DependentsResponse.cs +++ b/src/BaGet.Core/Search/DependentsResponse.cs @@ -1,48 +1,46 @@ -using System.Collections.Generic; using System.Text.Json.Serialization; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// The package ids that depend on the queried package. +/// This is an unofficial API that isn't part of the NuGet protocol. +/// +public class DependentsResponse { /// - /// The package ids that depend on the queried package. - /// This is an unofficial API that isn't part of the NuGet protocol. + /// The total number of matches, disregarding skip and take. /// - public class DependentsResponse - { - /// - /// The total number of matches, disregarding skip and take. - /// - [JsonPropertyName("totalHits")] - public long TotalHits { get; set; } + [JsonPropertyName("totalHits")] + public long TotalHits { get; set; } - /// - /// The package IDs matched by the dependent query. - /// - [JsonPropertyName("data")] - public IReadOnlyList Data { get; set; } - } + /// + /// The package IDs matched by the dependent query. + /// + [JsonPropertyName("data")] + public IReadOnlyList Data { get; set; } +} +/// +/// A package that depends on the queried package. +/// +public class PackageDependent +{ /// - /// A package that depends on the queried package. + /// The dependent package id. /// - public class PackageDependent - { - /// - /// The dependent package id. - /// - [JsonPropertyName("id")] - public string Id { get; set; } + [JsonPropertyName("id")] + public string Id { get; set; } - /// - /// The description of the dependent package. - /// - [JsonPropertyName("description")] - public string Description { get; set; } + /// + /// The description of the dependent package. + /// + [JsonPropertyName("description")] + public string Description { get; set; } - /// - /// The total downloads for the dependent package. - /// - [JsonPropertyName("totalDownloads")] - public long TotalDownloads { get; set; } - } -} + /// + /// The total downloads for the dependent package. + /// + [JsonPropertyName("totalDownloads")] + public long TotalDownloads { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Core/Search/ISearchIndexer.cs b/src/BaGet.Core/Search/ISearchIndexer.cs index dd2ff406..d9ba4d96 100644 --- a/src/BaGet.Core/Search/ISearchIndexer.cs +++ b/src/BaGet.Core/Search/ISearchIndexer.cs @@ -1,16 +1,12 @@ -using System.Threading; -using System.Threading.Tasks; +namespace BaGet.Core; -namespace BaGet.Core +public interface ISearchIndexer { - public interface ISearchIndexer - { - /// - /// Add a package to the search index. - /// - /// The package to add. - /// A token to cancel the task. - /// A task that completes once the package has been added. - Task IndexAsync(Package package, CancellationToken cancellationToken); - } -} + /// + /// Add a package to the search index. + /// + /// The package to add. + /// A token to cancel the task. + /// A task that completes once the package has been added. + Task IndexAsync(Package package, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/BaGet.Core/Search/ISearchResponseBuilder.cs b/src/BaGet.Core/Search/ISearchResponseBuilder.cs index 734d9614..84d793a4 100644 --- a/src/BaGet.Core/Search/ISearchResponseBuilder.cs +++ b/src/BaGet.Core/Search/ISearchResponseBuilder.cs @@ -1,12 +1,10 @@ -using System.Collections.Generic; -using BaGet.Protocol.Models; +using BaGet.Protocol.Models; -namespace BaGet.Core +namespace BaGet.Core; + +public interface ISearchResponseBuilder { - public interface ISearchResponseBuilder - { - SearchResponse BuildSearch(IReadOnlyList results); - AutocompleteResponse BuildAutocomplete(IReadOnlyList data); - DependentsResponse BuildDependents(IReadOnlyList results); - } -} + SearchResponse BuildSearch(IReadOnlyList results); + AutocompleteResponse BuildAutocomplete(IReadOnlyList data); + DependentsResponse BuildDependents(IReadOnlyList results); +} \ No newline at end of file diff --git a/src/BaGet.Core/Search/ISearchService.cs b/src/BaGet.Core/Search/ISearchService.cs index 0aeb9f92..cd8ff6c8 100644 --- a/src/BaGet.Core/Search/ISearchService.cs +++ b/src/BaGet.Core/Search/ISearchService.cs @@ -1,51 +1,48 @@ -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// The service used to search for packages. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource +/// +public interface ISearchService { /// - /// The service used to search for packages. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource + /// Perform a search query. + /// See: https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-for-packages /// - public interface ISearchService - { - /// - /// Perform a search query. - /// See: https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-for-packages - /// - /// The search request. - /// A token to cancel the task. - /// The search response. - Task SearchAsync(SearchRequest request, CancellationToken cancellationToken); + /// The search request. + /// A token to cancel the task. + /// The search response. + Task SearchAsync(SearchRequest request, CancellationToken cancellationToken); - /// - /// Perform an autocomplete query. - /// See: https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource#search-for-package-ids - /// - /// The autocomplete request. - /// A token to cancel the task. - /// The autocomplete response. - Task AutocompleteAsync(AutocompleteRequest request, CancellationToken cancellationToken); + /// + /// Perform an autocomplete query. + /// See: https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource#search-for-package-ids + /// + /// The autocomplete request. + /// A token to cancel the task. + /// The autocomplete response. + Task AutocompleteAsync(AutocompleteRequest request, CancellationToken cancellationToken); - /// - /// Enumerate listed package versions. - /// See: https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource#enumerate-package-versions - /// - /// The autocomplete request. - /// A token to cancel the task. - /// The package versions that matched the request. - Task ListPackageVersionsAsync(VersionsRequest request, CancellationToken cancellationToken); + /// + /// Enumerate listed package versions. + /// See: https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource#enumerate-package-versions + /// + /// The autocomplete request. + /// A token to cancel the task. + /// The package versions that matched the request. + Task ListPackageVersionsAsync(VersionsRequest request, CancellationToken cancellationToken); - /// - /// Find the packages that depend on a given package. - /// - /// The package whose dependents should be found. - /// A token to cancel the task. - /// The dependents response. - Task FindDependentsAsync( - string packageId, - CancellationToken cancellationToken); - } -} + /// + /// Find the packages that depend on a given package. + /// + /// The package whose dependents should be found. + /// A token to cancel the task. + /// The dependents response. + Task FindDependentsAsync( + string packageId, + CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/BaGet.Core/Search/NullSearchIndexer.cs b/src/BaGet.Core/Search/NullSearchIndexer.cs index 0dd1c5ce..21ebbc8f 100644 --- a/src/BaGet.Core/Search/NullSearchIndexer.cs +++ b/src/BaGet.Core/Search/NullSearchIndexer.cs @@ -1,16 +1,12 @@ -using System.Threading; -using System.Threading.Tasks; +namespace BaGet.Core; -namespace BaGet.Core +/// +/// A no-op indexer, used when search does not need to index packages. +/// +public class NullSearchIndexer : ISearchIndexer { - /// - /// A no-op indexer, used when search does not need to index packages. - /// - public class NullSearchIndexer : ISearchIndexer + public Task IndexAsync(Package package, CancellationToken cancellationToken = default) { - public Task IndexAsync(Package package, CancellationToken cancellationToken = default) - { - return Task.CompletedTask; - } + return Task.CompletedTask; } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Search/NullSearchService.cs b/src/BaGet.Core/Search/NullSearchService.cs index 33ac06cf..b210b30e 100644 --- a/src/BaGet.Core/Search/NullSearchService.cs +++ b/src/BaGet.Core/Search/NullSearchService.cs @@ -1,63 +1,59 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// A minimal search service implementation, used for advanced scenarios. +/// +public class NullSearchService : ISearchService { - /// - /// A minimal search service implementation, used for advanced scenarios. - /// - public class NullSearchService : ISearchService - { - private static readonly IReadOnlyList EmptyStringList = new List(); - - private static readonly Task EmptyAutocompleteResponseTask = - Task.FromResult(new AutocompleteResponse - { - TotalHits = 0, - Data = EmptyStringList, - Context = AutocompleteContext.Default - }); - - private static readonly Task EmptyDependentsResponseTask = - Task.FromResult(new DependentsResponse - { - TotalHits = 0, - Data = new List() - }); - - private static readonly Task EmptySearchResponseTask = - Task.FromResult(new SearchResponse - { - TotalHits = 0, - Data = new List() - }); - - public Task AutocompleteAsync( - AutocompleteRequest request, - CancellationToken cancellationToken) - { - return EmptyAutocompleteResponseTask; - } + private static readonly IReadOnlyList EmptyStringList = new List(); - public Task ListPackageVersionsAsync( - VersionsRequest request, - CancellationToken cancellationToken) + private static readonly Task EmptyAutocompleteResponseTask = + Task.FromResult(new AutocompleteResponse { - return EmptyAutocompleteResponseTask; - } + TotalHits = 0, + Data = EmptyStringList, + Context = AutocompleteContext.Default + }); - public Task FindDependentsAsync(string packageId, CancellationToken cancellationToken) + private static readonly Task EmptyDependentsResponseTask = + Task.FromResult(new DependentsResponse { - return EmptyDependentsResponseTask; - } + TotalHits = 0, + Data = new List() + }); - public Task SearchAsync( - SearchRequest request, - CancellationToken cancellationToken) + private static readonly Task EmptySearchResponseTask = + Task.FromResult(new SearchResponse { - return EmptySearchResponseTask; - } + TotalHits = 0, + Data = new List() + }); + + public Task AutocompleteAsync( + AutocompleteRequest request, + CancellationToken cancellationToken) + { + return EmptyAutocompleteResponseTask; + } + + public Task ListPackageVersionsAsync( + VersionsRequest request, + CancellationToken cancellationToken) + { + return EmptyAutocompleteResponseTask; + } + + public Task FindDependentsAsync(string packageId, CancellationToken cancellationToken) + { + return EmptyDependentsResponseTask; + } + + public Task SearchAsync( + SearchRequest request, + CancellationToken cancellationToken) + { + return EmptySearchResponseTask; } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Search/SearchRequest.cs b/src/BaGet.Core/Search/SearchRequest.cs index fd81250b..9057ed96 100644 --- a/src/BaGet.Core/Search/SearchRequest.cs +++ b/src/BaGet.Core/Search/SearchRequest.cs @@ -1,44 +1,43 @@ -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// The NuGet V3 search request. +/// See: https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#request-parameters +/// +public class SearchRequest { /// - /// The NuGet V3 search request. - /// See: https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#request-parameters + /// The number of results to skip, for pagination. /// - public class SearchRequest - { - /// - /// The number of results to skip, for pagination. - /// - public int Skip { get; set; } + public int Skip { get; set; } - /// - /// The number of results to return, for pagination. - /// - public int Take { get; set; } + /// + /// The number of results to return, for pagination. + /// + public int Take { get; set; } - /// - /// Whether to include pre-release packages. - /// - public bool IncludePrerelease { get; set; } + /// + /// Whether to include pre-release packages. + /// + public bool IncludePrerelease { get; set; } - /// - /// Whether to include SemVer 2.0.0 compatible packages. - /// - public bool IncludeSemVer2 { get; set; } + /// + /// Whether to include SemVer 2.0.0 compatible packages. + /// + public bool IncludeSemVer2 { get; set; } - /// - /// Filter results to a package type. If null, no filter is applied. - /// - public string PackageType { get; set; } + /// + /// Filter results to a package type. If null, no filter is applied. + /// + public string PackageType { get; set; } - /// - /// Filters results to a target framework. If null, no filter is applied. - /// - public string Framework { get; set; } + /// + /// Filters results to a target framework. If null, no filter is applied. + /// + public string Framework { get; set; } - /// - /// The search query. - /// - public string Query { get; set; } - } -} + /// + /// The search query. + /// + public string Query { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Core/Search/SearchResponseBuilder.cs b/src/BaGet.Core/Search/SearchResponseBuilder.cs index 8ccd91e9..bee6265b 100644 --- a/src/BaGet.Core/Search/SearchResponseBuilder.cs +++ b/src/BaGet.Core/Search/SearchResponseBuilder.cs @@ -1,81 +1,77 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using BaGet.Protocol.Models; +using BaGet.Protocol.Models; -namespace BaGet.Core +namespace BaGet.Core; + +public class SearchResponseBuilder : ISearchResponseBuilder { - public class SearchResponseBuilder : ISearchResponseBuilder + private readonly IUrlGenerator _url; + + public SearchResponseBuilder(IUrlGenerator url) { - private readonly IUrlGenerator _url; + _url = url ?? throw new ArgumentNullException(nameof(url)); + } - public SearchResponseBuilder(IUrlGenerator url) - { - _url = url ?? throw new ArgumentNullException(nameof(url)); - } + public SearchResponse BuildSearch(IReadOnlyList packageRegistrations) + { + var result = new List(); - public SearchResponse BuildSearch(IReadOnlyList packageRegistrations) + foreach (var packageRegistration in packageRegistrations) { - var result = new List(); + var versions = packageRegistration.Packages.OrderByDescending(p => p.Version).ToList(); + var latest = versions.First(); + var iconUrl = latest.HasEmbeddedIcon + ? _url.GetPackageIconDownloadUrl(latest.Id, latest.Version) + : latest.IconUrlString; - foreach (var packageRegistration in packageRegistrations) + result.Add(new SearchResult { - var versions = packageRegistration.Packages.OrderByDescending(p => p.Version).ToList(); - var latest = versions.First(); - var iconUrl = latest.HasEmbeddedIcon - ? _url.GetPackageIconDownloadUrl(latest.Id, latest.Version) - : latest.IconUrlString; - - result.Add(new SearchResult - { - PackageId = latest.Id, - Version = latest.Version.ToFullString(), - Description = latest.Description, - Authors = latest.Authors, - IconUrl = iconUrl, - LicenseUrl = latest.LicenseUrlString, - ProjectUrl = latest.ProjectUrlString, - RegistrationIndexUrl = _url.GetRegistrationIndexUrl(latest.Id), - Summary = latest.Summary, - Tags = latest.Tags, - Title = latest.Title, - TotalDownloads = versions.Sum(p => p.Downloads), - Versions = versions - .Select(p => new SearchResultVersion - { - RegistrationLeafUrl = _url.GetRegistrationLeafUrl(p.Id, p.Version), - Version = p.Version.ToFullString(), - Downloads = p.Downloads, - }) - .ToList(), - }); - } - - return new SearchResponse - { - TotalHits = result.Count, - Data = result, - Context = SearchContext.Default(_url.GetPackageMetadataResourceUrl()), - }; + PackageId = latest.Id, + Version = latest.Version.ToFullString(), + Description = latest.Description, + Authors = latest.Authors, + IconUrl = iconUrl, + LicenseUrl = latest.LicenseUrlString, + ProjectUrl = latest.ProjectUrlString, + RegistrationIndexUrl = _url.GetRegistrationIndexUrl(latest.Id), + Summary = latest.Summary, + Tags = latest.Tags, + Title = latest.Title, + TotalDownloads = versions.Sum(p => p.Downloads), + Versions = versions + .Select(p => new SearchResultVersion + { + RegistrationLeafUrl = _url.GetRegistrationLeafUrl(p.Id, p.Version), + Version = p.Version.ToFullString(), + Downloads = p.Downloads, + }) + .ToList(), + }); } - public AutocompleteResponse BuildAutocomplete(IReadOnlyList data) + return new SearchResponse { - return new AutocompleteResponse - { - TotalHits = data.Count, - Data = data, - Context = AutocompleteContext.Default - }; - } + TotalHits = result.Count, + Data = result, + Context = SearchContext.Default(_url.GetPackageMetadataResourceUrl()), + }; + } - public DependentsResponse BuildDependents(IReadOnlyList packages) + public AutocompleteResponse BuildAutocomplete(IReadOnlyList data) + { + return new AutocompleteResponse { - return new DependentsResponse - { - TotalHits = packages.Count, - Data = packages, - }; - } + TotalHits = data.Count, + Data = data, + Context = AutocompleteContext.Default + }; + } + + public DependentsResponse BuildDependents(IReadOnlyList packages) + { + return new DependentsResponse + { + TotalHits = packages.Count, + Data = packages, + }; } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Search/VersionsRequest.cs b/src/BaGet.Core/Search/VersionsRequest.cs index 974fc5d9..5ca2f32a 100644 --- a/src/BaGet.Core/Search/VersionsRequest.cs +++ b/src/BaGet.Core/Search/VersionsRequest.cs @@ -1,24 +1,23 @@ -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// The NuGet V3 enumerate package versions request. +/// See: https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource#request-parameters-1 +/// +public class VersionsRequest { /// - /// The NuGet V3 enumerate package versions request. - /// See: https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource#request-parameters-1 + /// Whether to include pre-release packages. /// - public class VersionsRequest - { - /// - /// Whether to include pre-release packages. - /// - public bool IncludePrerelease { get; set; } + public bool IncludePrerelease { get; set; } - /// - /// Whether to include SemVer 2.0.0 compatible packages. - /// - public bool IncludeSemVer2 { get; set; } + /// + /// Whether to include SemVer 2.0.0 compatible packages. + /// + public bool IncludeSemVer2 { get; set; } - /// - /// The package ID whose versions should be fetched. - /// - public string PackageId { get; set; } - } -} + /// + /// The package ID whose versions should be fetched. + /// + public string PackageId { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Core/ServiceIndex/BaGetServiceIndex.cs b/src/BaGet.Core/ServiceIndex/BaGetServiceIndex.cs index db84b65f..17b69b6a 100644 --- a/src/BaGet.Core/ServiceIndex/BaGetServiceIndex.cs +++ b/src/BaGet.Core/ServiceIndex/BaGetServiceIndex.cs @@ -1,52 +1,47 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Core +namespace BaGet.Core; + +public class BaGetServiceIndex : IServiceIndexService { - public class BaGetServiceIndex : IServiceIndexService + private readonly IUrlGenerator _url; + + public BaGetServiceIndex(IUrlGenerator url) { - private readonly IUrlGenerator _url; + _url = url ?? throw new ArgumentNullException(nameof(url)); + } - public BaGetServiceIndex(IUrlGenerator url) + private IEnumerable BuildResource(string name, string url, params string[] versions) + { + foreach (var version in versions) { - _url = url ?? throw new ArgumentNullException(nameof(url)); - } + var type = string.IsNullOrEmpty(version) ? name : $"{name}/{version}"; - private IEnumerable BuildResource(string name, string url, params string[] versions) - { - foreach (var version in versions) + yield return new ServiceIndexItem { - var type = string.IsNullOrEmpty(version) ? name : $"{name}/{version}"; - - yield return new ServiceIndexItem - { - ResourceUrl = url, - Type = type, - }; - } + ResourceUrl = url, + Type = type, + }; } + } - public Task GetAsync(CancellationToken cancellationToken = default) - { - var resources = new List(); + public Task GetAsync(CancellationToken cancellationToken = default) + { + var resources = new List(); - resources.AddRange(BuildResource("PackagePublish", _url.GetPackagePublishResourceUrl(), "2.0.0")); - resources.AddRange(BuildResource("SymbolPackagePublish", _url.GetSymbolPublishResourceUrl(), "4.9.0")); - resources.AddRange(BuildResource("SearchQueryService", _url.GetSearchResourceUrl(), "", "3.0.0-beta", "3.0.0-rc")); - resources.AddRange(BuildResource("RegistrationsBaseUrl", _url.GetPackageMetadataResourceUrl(), "", "3.0.0-rc", "3.0.0-beta")); - resources.AddRange(BuildResource("PackageBaseAddress", _url.GetPackageContentResourceUrl(), "3.0.0")); - resources.AddRange(BuildResource("SearchAutocompleteService", _url.GetAutocompleteResourceUrl(), "", "3.0.0-rc", "3.0.0-beta")); + resources.AddRange(BuildResource("PackagePublish", _url.GetPackagePublishResourceUrl(), "2.0.0")); + resources.AddRange(BuildResource("SymbolPackagePublish", _url.GetSymbolPublishResourceUrl(), "4.9.0")); + resources.AddRange(BuildResource("SearchQueryService", _url.GetSearchResourceUrl(), "", "3.0.0-beta", "3.0.0-rc")); + resources.AddRange(BuildResource("RegistrationsBaseUrl", _url.GetPackageMetadataResourceUrl(), "", "3.0.0-rc", "3.0.0-beta")); + resources.AddRange(BuildResource("PackageBaseAddress", _url.GetPackageContentResourceUrl(), "3.0.0")); + resources.AddRange(BuildResource("SearchAutocompleteService", _url.GetAutocompleteResourceUrl(), "", "3.0.0-rc", "3.0.0-beta")); - var result = new ServiceIndexResponse - { - Version = "3.0.0", - Resources = resources, - }; + var result = new ServiceIndexResponse + { + Version = "3.0.0", + Resources = resources, + }; - return Task.FromResult(result); - } + return Task.FromResult(result); } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/ServiceIndex/IServiceIndexService.cs b/src/BaGet.Core/ServiceIndex/IServiceIndexService.cs index 67bf9a8b..b47ce3cf 100644 --- a/src/BaGet.Core/ServiceIndex/IServiceIndexService.cs +++ b/src/BaGet.Core/ServiceIndex/IServiceIndexService.cs @@ -1,21 +1,18 @@ -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// The NuGet Service Index service, used to discover other resources. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/service-index +/// +public interface IServiceIndexService { /// - /// The NuGet Service Index service, used to discover other resources. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/service-index + /// Get the resources available on this package feed. + /// See: https://docs.microsoft.com/en-us/nuget/api/service-index#resources /// - public interface IServiceIndexService - { - /// - /// Get the resources available on this package feed. - /// See: https://docs.microsoft.com/en-us/nuget/api/service-index#resources - /// - /// The resources available on this package feed. - Task GetAsync(CancellationToken cancellationToken = default); - } -} + /// The resources available on this package feed. + Task GetAsync(CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/BaGet.Core/Storage/FileStorageService.cs b/src/BaGet.Core/Storage/FileStorageService.cs index 26d6f6ec..a63302d8 100644 --- a/src/BaGet.Core/Storage/FileStorageService.cs +++ b/src/BaGet.Core/Storage/FileStorageService.cs @@ -1,118 +1,113 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Options; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// Stores content on disk. +/// +public class FileStorageService : IStorageService { - /// - /// Stores content on disk. - /// - public class FileStorageService : IStorageService - { - // See: https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/IO/Stream.cs#L35 - private const int DefaultCopyBufferSize = 81920; + // See: https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/IO/Stream.cs#L35 + private const int DefaultCopyBufferSize = 81920; - private readonly string _storePath; + private readonly string _storePath; - public FileStorageService(IOptionsSnapshot options) - { - if (options == null) throw new ArgumentNullException(nameof(options)); + public FileStorageService(IOptionsSnapshot options) + { + if (options == null) throw new ArgumentNullException(nameof(options)); - // Resolve relative path components ('.'/'..') and ensure there is a trailing slash. - _storePath = Path.GetFullPath(options.Value.Path); - if (!_storePath.EndsWith(Path.DirectorySeparatorChar.ToString())) - _storePath += Path.DirectorySeparatorChar; - } + // Resolve relative path components ('.'/'..') and ensure there is a trailing slash. + _storePath = Path.GetFullPath(options.Value.Path); + if (!_storePath.EndsWith(Path.DirectorySeparatorChar.ToString())) + _storePath += Path.DirectorySeparatorChar; + } - public Task GetAsync(string path, CancellationToken cancellationToken = default) - { - cancellationToken.ThrowIfCancellationRequested(); + public Task GetAsync(string path, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); - path = GetFullPath(path); - var content = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read); + path = GetFullPath(path); + var content = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read); - return Task.FromResult(content); - } + return Task.FromResult(content); + } - public Task GetDownloadUriAsync(string path, CancellationToken cancellationToken = default) - { - cancellationToken.ThrowIfCancellationRequested(); + public Task GetDownloadUriAsync(string path, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); - var result = new Uri(GetFullPath(path)); + var result = new Uri(GetFullPath(path)); - return Task.FromResult(result); - } + return Task.FromResult(result); + } - public async Task PutAsync( - string path, - Stream content, - string contentType, - CancellationToken cancellationToken = default) - { - if (content == null) throw new ArgumentNullException(nameof(content)); - if (string.IsNullOrEmpty(contentType)) throw new ArgumentException("Content type is required", nameof(contentType)); + public async Task PutAsync( + string path, + Stream content, + string contentType, + CancellationToken cancellationToken = default) + { + if (content == null) throw new ArgumentNullException(nameof(content)); + if (string.IsNullOrEmpty(contentType)) throw new ArgumentException("Content type is required", nameof(contentType)); - cancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); - path = GetFullPath(path); + path = GetFullPath(path); - // Ensure that the path exists. - Directory.CreateDirectory(Path.GetDirectoryName(path)); + // Ensure that the path exists. + Directory.CreateDirectory(Path.GetDirectoryName(path)); - try - { - using (var fileStream = File.Open(path, FileMode.CreateNew)) - { - await content.CopyToAsync(fileStream, DefaultCopyBufferSize, cancellationToken); - return StoragePutResult.Success; - } - } - catch (IOException) when (File.Exists(path)) + try + { + using (var fileStream = File.Open(path, FileMode.CreateNew)) { - using (var targetStream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - content.Position = 0; - return content.Matches(targetStream) - ? StoragePutResult.AlreadyExists - : StoragePutResult.Conflict; - } + await content.CopyToAsync(fileStream, DefaultCopyBufferSize, cancellationToken); + return StoragePutResult.Success; } } - - public Task DeleteAsync(string path, CancellationToken cancellationToken = default) + catch (IOException) when (File.Exists(path)) { - cancellationToken.ThrowIfCancellationRequested(); - - try + using (var targetStream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { - File.Delete(GetFullPath(path)); + content.Position = 0; + return content.Matches(targetStream) + ? StoragePutResult.AlreadyExists + : StoragePutResult.Conflict; } - catch (DirectoryNotFoundException) - { - } - - return Task.CompletedTask; } + } + + public Task DeleteAsync(string path, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); - private string GetFullPath(string path) + try { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentException("Path is required", nameof(path)); - } + File.Delete(GetFullPath(path)); + } + catch (DirectoryNotFoundException) + { + } - var fullPath = Path.GetFullPath(Path.Combine(_storePath, path)); + return Task.CompletedTask; + } - // Verify path is under the _storePath. - if (!fullPath.StartsWith(_storePath, StringComparison.Ordinal) || - fullPath.Length == _storePath.Length) - { - throw new ArgumentException("Path resolves outside store path", nameof(path)); - } + private string GetFullPath(string path) + { + if (string.IsNullOrEmpty(path)) + { + throw new ArgumentException("Path is required", nameof(path)); + } + + var fullPath = Path.GetFullPath(Path.Combine(_storePath, path)); - return fullPath; + // Verify path is under the _storePath. + if (!fullPath.StartsWith(_storePath, StringComparison.Ordinal) || + fullPath.Length == _storePath.Length) + { + throw new ArgumentException("Path resolves outside store path", nameof(path)); } + + return fullPath; } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Storage/IPackageStorageService.cs b/src/BaGet.Core/Storage/IPackageStorageService.cs index b16fbb55..d8e19e37 100644 --- a/src/BaGet.Core/Storage/IPackageStorageService.cs +++ b/src/BaGet.Core/Storage/IPackageStorageService.cs @@ -1,72 +1,68 @@ -using System.IO; -using System.Threading; -using System.Threading.Tasks; using NuGet.Versioning; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// Stores packages' content. Packages' state are stored by the +/// . +/// +public interface IPackageStorageService { /// - /// Stores packages' content. Packages' state are stored by the - /// . + /// 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. /// - 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. - /// The package's icon stream, or null if none. - /// - /// - Task SavePackageContentAsync( - Package package, - Stream packageStream, - Stream nuspecStream, - Stream readmeStream, - Stream iconStream, - CancellationToken cancellationToken); + /// The package's metadata. + /// The package's nupkg stream. + /// The package's nuspec stream. + /// The package's readme stream, or null if none. + /// The package's icon stream, or null if none. + /// + /// + Task SavePackageContentAsync( + Package package, + Stream packageStream, + Stream nuspecStream, + Stream readmeStream, + Stream iconStream, + 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 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 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); + /// + /// 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); - Task GetIconStreamAsync(string id, NuGetVersion version, CancellationToken cancellationToken); + Task GetIconStreamAsync(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); - } -} + /// + /// 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); +} \ No newline at end of file diff --git a/src/BaGet.Core/Storage/IStorageService.cs b/src/BaGet.Core/Storage/IStorageService.cs index 50e122f1..f723bf3d 100644 --- a/src/BaGet.Core/Storage/IStorageService.cs +++ b/src/BaGet.Core/Storage/IStorageService.cs @@ -1,84 +1,78 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; +namespace BaGet.Core; -namespace BaGet.Core +/// +/// A low-level storage abstraction. +/// +/// +/// This is used to store: +/// +/// * Packages, through +/// * Symbols, through +/// +/// This storage abstraction has implementations for disk, +/// Azure Blob Storage, Amazon Web Services S3, and Google Cloud Storage. +/// +public interface IStorageService { /// - /// A low-level storage abstraction. + /// Get content from storage. /// - /// - /// This is used to store: - /// - /// * Packages, through - /// * Symbols, through - /// - /// This storage abstraction has implementations for disk, - /// Azure Blob Storage, Amazon Web Services S3, and Google Cloud Storage. - /// - public interface IStorageService - { - /// - /// Get content from storage. - /// - /// The content's path. - /// A token to cancel the task. - /// The path's content or null if the content does not exist. - Task GetAsync(string path, CancellationToken cancellationToken = default); + /// The content's path. + /// A token to cancel the task. + /// The path's content or null if the content does not exist. + Task GetAsync(string path, CancellationToken cancellationToken = default); - /// - /// Get a URI that can be used to download the content. - /// - /// The content's path. - /// A token to cancel the task. - /// - /// The content's URI. This may be a local file. Returns null if the content - /// does not exist or if a URI could not be created. - /// - Task GetDownloadUriAsync(string path, CancellationToken cancellationToken = default); + /// + /// Get a URI that can be used to download the content. + /// + /// The content's path. + /// A token to cancel the task. + /// + /// The content's URI. This may be a local file. Returns null if the content + /// does not exist or if a URI could not be created. + /// + Task GetDownloadUriAsync(string path, CancellationToken cancellationToken = default); - /// - /// Store content into storage. - /// - /// The path at which to store the content. - /// The content to store at the given path. - /// The type of content that is being stored. - /// A token to cancel the task. - /// The result of the put operation. - Task PutAsync( - string path, - Stream content, - string contentType, - CancellationToken cancellationToken = default); + /// + /// Store content into storage. + /// + /// The path at which to store the content. + /// The content to store at the given path. + /// The type of content that is being stored. + /// A token to cancel the task. + /// The result of the put operation. + Task PutAsync( + string path, + Stream content, + string contentType, + CancellationToken cancellationToken = default); - /// - /// Remove content from storage. - /// - /// The path to the content to delete. - /// A token to cancel the task. - /// A task that completes when the content has been deleted. - Task DeleteAsync(string path, CancellationToken cancellationToken = default); - } + /// + /// Remove content from storage. + /// + /// The path to the content to delete. + /// A token to cancel the task. + /// A task that completes when the content has been deleted. + Task DeleteAsync(string path, CancellationToken cancellationToken = default); +} +/// +/// The result of a operation. +/// +public enum StoragePutResult +{ /// - /// The result of a operation. + /// The given path is already used to store different content. /// - public enum StoragePutResult - { - /// - /// The given path is already used to store different content. - /// - Conflict, + Conflict, - /// - /// This content is already stored at the given path. - /// - AlreadyExists, + /// + /// This content is already stored at the given path. + /// + AlreadyExists, - /// - /// The content was sucessfully stored. - /// - Success, - } -} + /// + /// The content was sucessfully stored. + /// + Success, +} \ No newline at end of file diff --git a/src/BaGet.Core/Storage/ISymbolStorageService.cs b/src/BaGet.Core/Storage/ISymbolStorageService.cs index f8681ab6..bc216c0e 100644 --- a/src/BaGet.Core/Storage/ISymbolStorageService.cs +++ b/src/BaGet.Core/Storage/ISymbolStorageService.cs @@ -1,35 +1,30 @@ -using System.IO; -using System.Threading; -using System.Threading.Tasks; +namespace BaGet.Core; -namespace BaGet.Core +/// +/// Stores the content of symbols, also known as PDBs. +/// +public interface ISymbolStorageService { /// - /// Stores the content of symbols, also known as PDBs. + /// Persist a portable PDB's content to storage. This operation MUST fail if a PDB + /// with the same key but different content has already been stored. /// - public interface ISymbolStorageService - { - /// - /// Persist a portable PDB's content to storage. This operation MUST fail if a PDB - /// with the same key but different content has already been stored. - /// - /// The portable PDB's file name. - /// The portable PDB's Signature GUID followed by its age. - /// The PDB's content stream. - /// - /// - Task SavePortablePdbContentAsync( - string file, - string key, - Stream pdbStream, - CancellationToken cancellationToken); + /// The portable PDB's file name. + /// The portable PDB's Signature GUID followed by its age. + /// The PDB's content stream. + /// + /// + Task SavePortablePdbContentAsync( + string file, + string key, + Stream pdbStream, + CancellationToken cancellationToken); - /// - /// Retrieve a portable PDB's content stream. - /// - /// The portable PDB's file name. - /// The portable PDB's Signature GUID followed by its age. - /// The portable PDB's stream, or null if it does not exist. - Task GetPortablePdbContentStreamOrNullAsync(string file, string key); - } -} + /// + /// Retrieve a portable PDB's content stream. + /// + /// The portable PDB's file name. + /// The portable PDB's Signature GUID followed by its age. + /// The portable PDB's stream, or null if it does not exist. + Task GetPortablePdbContentStreamOrNullAsync(string file, string key); +} \ No newline at end of file diff --git a/src/BaGet.Core/Storage/NullStorageService.cs b/src/BaGet.Core/Storage/NullStorageService.cs index 42aea93a..48b6cfc6 100644 --- a/src/BaGet.Core/Storage/NullStorageService.cs +++ b/src/BaGet.Core/Storage/NullStorageService.cs @@ -1,37 +1,31 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; +namespace BaGet.Core; -namespace BaGet.Core +/// +/// A minimal storage implementation, used for advanced scenarios. +/// +public class NullStorageService : IStorageService { - /// - /// A minimal storage implementation, used for advanced scenarios. - /// - public class NullStorageService : IStorageService + public Task DeleteAsync(string path, CancellationToken cancellationToken = default) { - public Task DeleteAsync(string path, CancellationToken cancellationToken = default) - { - return Task.CompletedTask; - } + return Task.CompletedTask; + } - public Task GetAsync(string path, CancellationToken cancellationToken = default) - { - return Task.FromResult(null); - } + public Task GetAsync(string path, CancellationToken cancellationToken = default) + { + return Task.FromResult(null); + } - public Task GetDownloadUriAsync(string path, CancellationToken cancellationToken = default) - { - return Task.FromResult(null); - } + public Task GetDownloadUriAsync(string path, CancellationToken cancellationToken = default) + { + return Task.FromResult(null); + } - public Task PutAsync( - string path, - Stream content, - string contentType, - CancellationToken cancellationToken = default) - { - return Task.FromResult(StoragePutResult.Success); - } + public Task PutAsync( + string path, + Stream content, + string contentType, + CancellationToken cancellationToken = default) + { + return Task.FromResult(StoragePutResult.Success); } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Storage/PackageStorageService.cs b/src/BaGet.Core/Storage/PackageStorageService.cs index 55731b00..55b1be7f 100644 --- a/src/BaGet.Core/Storage/PackageStorageService.cs +++ b/src/BaGet.Core/Storage/PackageStorageService.cs @@ -1,243 +1,238 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; using NuGet.Versioning; -namespace BaGet.Core +namespace BaGet.Core; + +public class PackageStorageService : IPackageStorageService { - public class PackageStorageService : IPackageStorageService - { - private const string PackagesPathPrefix = "packages"; + private const string PackagesPathPrefix = "packages"; + + // See: https://github.com/NuGet/NuGetGallery/blob/73a5c54629056b25b3a59960373e8fef88abff36/src/NuGetGallery.Core/CoreConstants.cs#L19 + private const string PackageContentType = "binary/octet-stream"; + private const string NuspecContentType = "text/plain"; + private const string ReadmeContentType = "text/markdown"; + private const string IconContentType = "image/xyz"; - // See: https://github.com/NuGet/NuGetGallery/blob/73a5c54629056b25b3a59960373e8fef88abff36/src/NuGetGallery.Core/CoreConstants.cs#L19 - private const string PackageContentType = "binary/octet-stream"; - private const string NuspecContentType = "text/plain"; - private const string ReadmeContentType = "text/markdown"; - private const string IconContentType = "image/xyz"; + private readonly IStorageService _storage; + private readonly ILogger _logger; - private readonly IStorageService _storage; - private readonly ILogger _logger; + public PackageStorageService( + IStorageService storage, + ILogger logger) + { + _storage = storage ?? throw new ArgumentNullException(nameof(storage)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public PackageStorageService( - IStorageService storage, - ILogger logger) + public async Task SavePackageContentAsync( + Package package, + Stream packageStream, + Stream nuspecStream, + Stream readmeStream, + Stream iconStream, + CancellationToken cancellationToken = default) + { + package = package ?? throw new ArgumentNullException(nameof(package)); + packageStream = packageStream ?? throw new ArgumentNullException(nameof(packageStream)); + nuspecStream = nuspecStream ?? throw new ArgumentNullException(nameof(nuspecStream)); + + var lowercasedId = package.Id.ToLowerInvariant(); + var lowercasedNormalizedVersion = package.NormalizedVersionString.ToLowerInvariant(); + + var packagePath = PackagePath(lowercasedId, lowercasedNormalizedVersion); + var nuspecPath = NuspecPath(lowercasedId, lowercasedNormalizedVersion); + var readmePath = ReadmePath(lowercasedId, lowercasedNormalizedVersion); + var iconPath = IconPath(lowercasedId, lowercasedNormalizedVersion); + + _logger.LogInformation( + "Storing package {PackageId} {PackageVersion} at {Path}...", + lowercasedId, + lowercasedNormalizedVersion, + packagePath); + + // Store the package. + var result = await _storage.PutAsync(packagePath, packageStream, PackageContentType, cancellationToken); + if (result == StoragePutResult.Conflict) { - _storage = storage ?? throw new ArgumentNullException(nameof(storage)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + // TODO: This should be returned gracefully with an enum. + _logger.LogInformation( + "Could not store package {PackageId} {PackageVersion} at {Path} due to conflict", + lowercasedId, + lowercasedNormalizedVersion, + packagePath); + + throw new InvalidOperationException($"Failed to store package {lowercasedId} {lowercasedNormalizedVersion} due to conflict"); } - public async Task SavePackageContentAsync( - Package package, - Stream packageStream, - Stream nuspecStream, - Stream readmeStream, - Stream iconStream, - CancellationToken cancellationToken = default) - { - package = package ?? throw new ArgumentNullException(nameof(package)); - packageStream = packageStream ?? throw new ArgumentNullException(nameof(packageStream)); - nuspecStream = nuspecStream ?? throw new ArgumentNullException(nameof(nuspecStream)); + // Store the package's nuspec. + _logger.LogInformation( + "Storing package {PackageId} {PackageVersion} nuspec at {Path}...", + lowercasedId, + lowercasedNormalizedVersion, + nuspecPath); - var lowercasedId = package.Id.ToLowerInvariant(); - var lowercasedNormalizedVersion = package.NormalizedVersionString.ToLowerInvariant(); + result = await _storage.PutAsync(nuspecPath, nuspecStream, NuspecContentType, cancellationToken); + if (result == StoragePutResult.Conflict) + { + // TODO: This should be returned gracefully with an enum. + _logger.LogInformation( + "Could not store package {PackageId} {PackageVersion} nuspec at {Path} due to conflict", + lowercasedId, + lowercasedNormalizedVersion, + nuspecPath); - var packagePath = PackagePath(lowercasedId, lowercasedNormalizedVersion); - var nuspecPath = NuspecPath(lowercasedId, lowercasedNormalizedVersion); - var readmePath = ReadmePath(lowercasedId, lowercasedNormalizedVersion); - var iconPath = IconPath(lowercasedId, lowercasedNormalizedVersion); + throw new InvalidOperationException($"Failed to store package {lowercasedId} {lowercasedNormalizedVersion} nuspec due to conflict"); + } + // Store the package's readme, if one exists. + if (readmeStream != null) + { _logger.LogInformation( - "Storing package {PackageId} {PackageVersion} at {Path}...", + "Storing package {PackageId} {PackageVersion} readme at {Path}...", lowercasedId, lowercasedNormalizedVersion, - packagePath); + readmePath); - // Store the package. - var result = await _storage.PutAsync(packagePath, packageStream, PackageContentType, cancellationToken); + result = await _storage.PutAsync(readmePath, readmeStream, ReadmeContentType, cancellationToken); if (result == StoragePutResult.Conflict) { // TODO: This should be returned gracefully with an enum. _logger.LogInformation( - "Could not store package {PackageId} {PackageVersion} at {Path} due to conflict", + "Could not store package {PackageId} {PackageVersion} readme at {Path} due to conflict", lowercasedId, lowercasedNormalizedVersion, - packagePath); + readmePath); - throw new InvalidOperationException($"Failed to store package {lowercasedId} {lowercasedNormalizedVersion} due to conflict"); + throw new InvalidOperationException($"Failed to store package {lowercasedId} {lowercasedNormalizedVersion} readme due to conflict"); } + } - // Store the package's nuspec. + // Store the package's icon, if one exists. + if (iconStream != null) + { _logger.LogInformation( - "Storing package {PackageId} {PackageVersion} nuspec at {Path}...", + "Storing package {PackageId} {PackageVersion} icon at {Path}...", lowercasedId, lowercasedNormalizedVersion, - nuspecPath); + iconPath); - result = await _storage.PutAsync(nuspecPath, nuspecStream, NuspecContentType, cancellationToken); + result = await _storage.PutAsync(iconPath, iconStream, IconContentType, cancellationToken); if (result == StoragePutResult.Conflict) { // TODO: This should be returned gracefully with an enum. _logger.LogInformation( - "Could not store package {PackageId} {PackageVersion} nuspec at {Path} due to conflict", - lowercasedId, - lowercasedNormalizedVersion, - nuspecPath); - - throw new InvalidOperationException($"Failed to store package {lowercasedId} {lowercasedNormalizedVersion} nuspec due to conflict"); - } - - // Store the package's readme, if one exists. - if (readmeStream != null) - { - _logger.LogInformation( - "Storing package {PackageId} {PackageVersion} readme at {Path}...", - lowercasedId, - lowercasedNormalizedVersion, - readmePath); - - result = await _storage.PutAsync(readmePath, readmeStream, ReadmeContentType, cancellationToken); - if (result == StoragePutResult.Conflict) - { - // TODO: This should be returned gracefully with an enum. - _logger.LogInformation( - "Could not store package {PackageId} {PackageVersion} readme at {Path} due to conflict", - lowercasedId, - lowercasedNormalizedVersion, - readmePath); - - throw new InvalidOperationException($"Failed to store package {lowercasedId} {lowercasedNormalizedVersion} readme due to conflict"); - } - } - - // Store the package's icon, if one exists. - if (iconStream != null) - { - _logger.LogInformation( - "Storing package {PackageId} {PackageVersion} icon at {Path}...", + "Could not store package {PackageId} {PackageVersion} icon at {Path} due to conflict", lowercasedId, lowercasedNormalizedVersion, iconPath); - result = await _storage.PutAsync(iconPath, iconStream, IconContentType, cancellationToken); - if (result == StoragePutResult.Conflict) - { - // TODO: This should be returned gracefully with an enum. - _logger.LogInformation( - "Could not store package {PackageId} {PackageVersion} icon at {Path} due to conflict", - lowercasedId, - lowercasedNormalizedVersion, - iconPath); - - throw new InvalidOperationException($"Failed to store package {lowercasedId} {lowercasedNormalizedVersion} icon"); - } + throw new InvalidOperationException($"Failed to store package {lowercasedId} {lowercasedNormalizedVersion} icon"); } - - _logger.LogInformation( - "Finished storing package {PackageId} {PackageVersion}", - lowercasedId, - lowercasedNormalizedVersion); } - public async Task GetPackageStreamAsync(string id, NuGetVersion version, CancellationToken cancellationToken) - { - return await GetStreamAsync(id, version, PackagePath, cancellationToken); - } + _logger.LogInformation( + "Finished storing package {PackageId} {PackageVersion}", + lowercasedId, + lowercasedNormalizedVersion); + } - public async Task GetNuspecStreamAsync(string id, NuGetVersion version, CancellationToken cancellationToken) - { - return await GetStreamAsync(id, version, NuspecPath, cancellationToken); - } + public async Task GetPackageStreamAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + { + return await GetStreamAsync(id, version, PackagePath, cancellationToken); + } - public async Task GetReadmeStreamAsync(string id, NuGetVersion version, CancellationToken cancellationToken) - { - return await GetStreamAsync(id, version, ReadmePath, cancellationToken); - } + public async Task GetNuspecStreamAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + { + return await GetStreamAsync(id, version, NuspecPath, cancellationToken); + } - public async Task GetIconStreamAsync(string id, NuGetVersion version, CancellationToken cancellationToken) - { - return await GetStreamAsync(id, version, IconPath, cancellationToken); - } + public async Task GetReadmeStreamAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + { + return await GetStreamAsync(id, version, ReadmePath, cancellationToken); + } - public async Task DeleteAsync(string id, NuGetVersion version, CancellationToken cancellationToken) - { - var lowercasedId = id.ToLowerInvariant(); - var lowercasedNormalizedVersion = version.ToNormalizedString().ToLowerInvariant(); - - var packagePath = PackagePath(lowercasedId, lowercasedNormalizedVersion); - var nuspecPath = NuspecPath(lowercasedId, lowercasedNormalizedVersion); - var readmePath = ReadmePath(lowercasedId, lowercasedNormalizedVersion); - var iconPath = IconPath(lowercasedId, lowercasedNormalizedVersion); - - await _storage.DeleteAsync(packagePath, cancellationToken); - await _storage.DeleteAsync(nuspecPath, cancellationToken); - await _storage.DeleteAsync(readmePath, cancellationToken); - await _storage.DeleteAsync(iconPath, cancellationToken); - } + public async Task GetIconStreamAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + { + return await GetStreamAsync(id, version, IconPath, cancellationToken); + } - private async Task GetStreamAsync( - string id, - NuGetVersion version, - Func pathFunc, - CancellationToken cancellationToken) - { - var lowercasedId = id.ToLowerInvariant(); - var lowercasedNormalizedVersion = version.ToNormalizedString().ToLowerInvariant(); - var path = pathFunc(lowercasedId, lowercasedNormalizedVersion); + public async Task DeleteAsync(string id, NuGetVersion version, CancellationToken cancellationToken) + { + var lowercasedId = id.ToLowerInvariant(); + var lowercasedNormalizedVersion = version.ToNormalizedString().ToLowerInvariant(); + + var packagePath = PackagePath(lowercasedId, lowercasedNormalizedVersion); + var nuspecPath = NuspecPath(lowercasedId, lowercasedNormalizedVersion); + var readmePath = ReadmePath(lowercasedId, lowercasedNormalizedVersion); + var iconPath = IconPath(lowercasedId, lowercasedNormalizedVersion); + + await _storage.DeleteAsync(packagePath, cancellationToken); + await _storage.DeleteAsync(nuspecPath, cancellationToken); + await _storage.DeleteAsync(readmePath, cancellationToken); + await _storage.DeleteAsync(iconPath, cancellationToken); + } - try - { - return await _storage.GetAsync(path, cancellationToken); - } - catch (DirectoryNotFoundException) - { - // The "packages" prefix was lowercased, which was a breaking change - // on filesystems that are case sensitive. Handle this case to help - // users migrate to the latest version of BaGet. - // See https://github.com/loic-sharma/BaGet/issues/298 - _logger.LogError( - $"Unable to find the '{PackagesPathPrefix}' folder. " + - "If you've recently upgraded BaGet, please make sure this folder starts with a lowercased letter. " + - "For more information, please see https://github.com/loic-sharma/BaGet/issues/298"); - throw; - } - } + private async Task GetStreamAsync( + string id, + NuGetVersion version, + Func pathFunc, + CancellationToken cancellationToken) + { + var lowercasedId = id.ToLowerInvariant(); + var lowercasedNormalizedVersion = version.ToNormalizedString().ToLowerInvariant(); + var path = pathFunc(lowercasedId, lowercasedNormalizedVersion); - private string PackagePath(string lowercasedId, string lowercasedNormalizedVersion) + try { - return Path.Combine( - PackagesPathPrefix, - lowercasedId, - lowercasedNormalizedVersion, - $"{lowercasedId}.{lowercasedNormalizedVersion}.nupkg"); + return await _storage.GetAsync(path, cancellationToken); } - - private string NuspecPath(string lowercasedId, string lowercasedNormalizedVersion) + catch (DirectoryNotFoundException) { - return Path.Combine( - PackagesPathPrefix, - lowercasedId, - lowercasedNormalizedVersion, - $"{lowercasedId}.nuspec"); + // The "packages" prefix was lowercased, which was a breaking change + // on filesystems that are case sensitive. Handle this case to help + // users migrate to the latest version of BaGet. + // See https://github.com/loic-sharma/BaGet/issues/298 + _logger.LogError( + $"Unable to find the '{PackagesPathPrefix}' folder. " + + "If you've recently upgraded BaGet, please make sure this folder starts with a lowercased letter. " + + "For more information, please see https://github.com/loic-sharma/BaGet/issues/298"); + throw; } + } - private string ReadmePath(string lowercasedId, string lowercasedNormalizedVersion) - { - return Path.Combine( - PackagesPathPrefix, - lowercasedId, - lowercasedNormalizedVersion, - "readme"); - } + private string PackagePath(string lowercasedId, string lowercasedNormalizedVersion) + { + return Path.Combine( + PackagesPathPrefix, + lowercasedId, + lowercasedNormalizedVersion, + $"{lowercasedId}.{lowercasedNormalizedVersion}.nupkg"); + } - private string IconPath(string lowercasedId, string lowercasedNormalizedVersion) - { - return Path.Combine( - PackagesPathPrefix, - lowercasedId, - lowercasedNormalizedVersion, - "icon"); - } + private string NuspecPath(string lowercasedId, string lowercasedNormalizedVersion) + { + return Path.Combine( + PackagesPathPrefix, + lowercasedId, + lowercasedNormalizedVersion, + $"{lowercasedId}.nuspec"); + } + + private string ReadmePath(string lowercasedId, string lowercasedNormalizedVersion) + { + return Path.Combine( + PackagesPathPrefix, + lowercasedId, + lowercasedNormalizedVersion, + "readme"); + } + + private string IconPath(string lowercasedId, string lowercasedNormalizedVersion) + { + return Path.Combine( + PackagesPathPrefix, + lowercasedId, + lowercasedNormalizedVersion, + "icon"); } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Storage/SymbolStorageService.cs b/src/BaGet.Core/Storage/SymbolStorageService.cs index 4792481f..058375e5 100644 --- a/src/BaGet.Core/Storage/SymbolStorageService.cs +++ b/src/BaGet.Core/Storage/SymbolStorageService.cs @@ -1,78 +1,71 @@ -using System; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; +namespace BaGet.Core; -namespace BaGet.Core +public class SymbolStorageService : ISymbolStorageService { - public class SymbolStorageService : ISymbolStorageService + private const string SymbolsPathPrefix = "symbols"; + private const string PdbContentType = "binary/octet-stream"; + + private readonly IStorageService _storage; + + public SymbolStorageService(IStorageService storage) { - private const string SymbolsPathPrefix = "symbols"; - private const string PdbContentType = "binary/octet-stream"; + _storage = storage ?? throw new ArgumentNullException(nameof(storage)); + } - private readonly IStorageService _storage; + public async Task SavePortablePdbContentAsync( + string filename, + string key, + Stream pdbStream, + CancellationToken cancellationToken) + { + var path = GetPathForKey(filename, key); + var result = await _storage.PutAsync(path, pdbStream, PdbContentType, cancellationToken); - public SymbolStorageService(IStorageService storage) + if (result == StoragePutResult.Conflict) { - _storage = storage ?? throw new ArgumentNullException(nameof(storage)); + throw new InvalidOperationException($"Could not save PDB {filename} {key} due to conflict"); } + } - public async Task SavePortablePdbContentAsync( - string filename, - string key, - Stream pdbStream, - CancellationToken cancellationToken) - { - var path = GetPathForKey(filename, key); - var result = await _storage.PutAsync(path, pdbStream, PdbContentType, cancellationToken); + public async Task GetPortablePdbContentStreamOrNullAsync(string filename, string key) + { + var path = GetPathForKey(filename, key); - if (result == StoragePutResult.Conflict) - { - throw new InvalidOperationException($"Could not save PDB {filename} {key} due to conflict"); - } + try + { + return await _storage.GetAsync(path); } - - public async Task GetPortablePdbContentStreamOrNullAsync(string filename, string key) + catch { - var path = GetPathForKey(filename, key); - - try - { - return await _storage.GetAsync(path); - } - catch - { - return null; - } + return null; } + } - private string GetPathForKey(string filename, string key) - { - // Ensure the filename doesn't try to escape out of the current directory. - var tempPath = Path.GetDirectoryName(Path.GetTempPath()); - var expandedPath = Path.GetDirectoryName(Path.Combine(tempPath, filename)); + private string GetPathForKey(string filename, string key) + { + // Ensure the filename doesn't try to escape out of the current directory. + var tempPath = Path.GetDirectoryName(Path.GetTempPath()); + var expandedPath = Path.GetDirectoryName(Path.Combine(tempPath, filename)); - if (expandedPath != tempPath) - { - throw new ArgumentException(nameof(filename)); - } + if (expandedPath != tempPath) + { + throw new ArgumentException(nameof(filename)); + } - if (!key.All(char.IsLetterOrDigit)) - { - throw new ArgumentException(nameof(key)); - } + if (!key.All(char.IsLetterOrDigit)) + { + throw new ArgumentException(nameof(key)); + } - // The key's first 32 characters are the GUID, the remaining characters are the age. - // See: https://github.com/dotnet/symstore/blob/98717c63ec8342acf8a07aa5c909b88bd0c664cc/docs/specs/SSQP_Key_Conventions.md#portable-pdb-signature - // Debuggers should always use the age "ffffffff", however Visual Studio 2019 - // users have reported other age values. We will ignore the age. - key = key.Substring(0, 32) + "ffffffff"; + // The key's first 32 characters are the GUID, the remaining characters are the age. + // See: https://github.com/dotnet/symstore/blob/98717c63ec8342acf8a07aa5c909b88bd0c664cc/docs/specs/SSQP_Key_Conventions.md#portable-pdb-signature + // Debuggers should always use the age "ffffffff", however Visual Studio 2019 + // users have reported other age values. We will ignore the age. + key = key.Substring(0, 32) + "ffffffff"; - return Path.Combine( - SymbolsPathPrefix, - filename.ToLowerInvariant(), - key.ToLowerInvariant()); - } + return Path.Combine( + SymbolsPathPrefix, + filename.ToLowerInvariant(), + key.ToLowerInvariant()); } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Upstream/Clients/DisabledUpstreamClient.cs b/src/BaGet.Core/Upstream/Clients/DisabledUpstreamClient.cs index 482c46a6..7e0d04f3 100644 --- a/src/BaGet.Core/Upstream/Clients/DisabledUpstreamClient.cs +++ b/src/BaGet.Core/Upstream/Clients/DisabledUpstreamClient.cs @@ -1,35 +1,30 @@ -using System.Collections.Generic; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using NuGet.Versioning; +using NuGet.Versioning; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// The client used when there are no upstream package sources. +/// +public class DisabledUpstreamClient : IUpstreamClient { - /// - /// The client used when there are no upstream package sources. - /// - public class DisabledUpstreamClient : IUpstreamClient - { - private readonly IReadOnlyList _emptyVersionList = new List(); - private readonly IReadOnlyList _emptyPackageList = new List(); + private readonly IReadOnlyList _emptyVersionList = new List(); + private readonly IReadOnlyList _emptyPackageList = new List(); - public Task> ListPackageVersionsAsync(string id, CancellationToken cancellationToken) - { - return Task.FromResult(_emptyVersionList); - } + public Task> ListPackageVersionsAsync(string id, CancellationToken cancellationToken) + { + return Task.FromResult(_emptyVersionList); + } - public Task> ListPackagesAsync(string id, CancellationToken cancellationToken) - { - return Task.FromResult(_emptyPackageList); - } + public Task> ListPackagesAsync(string id, CancellationToken cancellationToken) + { + return Task.FromResult(_emptyPackageList); + } - public Task DownloadPackageOrNullAsync( - string id, - NuGetVersion version, - CancellationToken cancellationToken) - { - return Task.FromResult(null); - } + public Task DownloadPackageOrNullAsync( + string id, + NuGetVersion version, + CancellationToken cancellationToken) + { + return Task.FromResult(null); } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Upstream/Clients/V2UpstreamClient.cs b/src/BaGet.Core/Upstream/Clients/V2UpstreamClient.cs index 763aed6c..5d40a5e6 100644 --- a/src/BaGet.Core/Upstream/Clients/V2UpstreamClient.cs +++ b/src/BaGet.Core/Upstream/Clients/V2UpstreamClient.cs @@ -1,9 +1,3 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using NuGet.Common; @@ -13,193 +7,192 @@ using NuGet.Protocol.Core.Types; using NuGet.Versioning; -namespace BaGet.Core -{ - using ILogger = Microsoft.Extensions.Logging.ILogger; - using INuGetLogger = NuGet.Common.ILogger; +namespace BaGet.Core; + +using ILogger = Microsoft.Extensions.Logging.ILogger; +using INuGetLogger = NuGet.Common.ILogger; - /// - /// The client to upstream a NuGet server that uses the V2 protocol. - /// - public class V2UpstreamClient : IUpstreamClient, IDisposable +/// +/// The client to upstream a NuGet server that uses the V2 protocol. +/// +public class V2UpstreamClient : IUpstreamClient, IDisposable +{ + private readonly SourceCacheContext _cache; + private readonly SourceRepository _repository; + private readonly INuGetLogger _ngLogger; + private readonly ILogger _logger; + + public V2UpstreamClient( + IOptionsSnapshot options, + ILogger logger) { - private readonly SourceCacheContext _cache; - private readonly SourceRepository _repository; - private readonly INuGetLogger _ngLogger; - private readonly ILogger _logger; - - public V2UpstreamClient( - IOptionsSnapshot options, - ILogger logger) + if (options is null) { - if (options is null) - { - throw new ArgumentNullException(nameof(options)); - } + throw new ArgumentNullException(nameof(options)); + } - if (options.Value?.PackageSource?.AbsolutePath == null) - { - throw new ArgumentException("No mirror package source has been set."); - } + if (options.Value?.PackageSource?.AbsolutePath == null) + { + throw new ArgumentException("No mirror package source has been set."); + } - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _ngLogger = NullLogger.Instance; - _cache = new SourceCacheContext(); - _repository = Repository.Factory.GetCoreV2(new PackageSource(options.Value.PackageSource.AbsoluteUri)); - } + _ngLogger = NullLogger.Instance; + _cache = new SourceCacheContext(); + _repository = Repository.Factory.GetCoreV2(new PackageSource(options.Value.PackageSource.AbsoluteUri)); + } - public async Task> ListPackageVersionsAsync(string id, CancellationToken cancellationToken) + public async Task> ListPackageVersionsAsync(string id, CancellationToken cancellationToken) + { + try { - try - { - var resource = await _repository.GetResourceAsync(cancellationToken); - var versions = await resource.GetAllVersionsAsync(id, _cache, _ngLogger, cancellationToken); + var resource = await _repository.GetResourceAsync(cancellationToken); + var versions = await resource.GetAllVersionsAsync(id, _cache, _ngLogger, cancellationToken); - return versions.ToList(); - } - catch (Exception e) - { - _logger.LogError(e, "Failed to mirror {PackageId}'s upstream versions", id); - return new List(); - } + return versions.ToList(); } - - public async Task> ListPackagesAsync( - string id, - CancellationToken cancellationToken) + catch (Exception e) { - try - { - var resource = await _repository.GetResourceAsync(cancellationToken); - var packages = await resource.GetMetadataAsync( - id, - includePrerelease: true, - includeUnlisted: true, - _cache, - _ngLogger, - cancellationToken); - - return packages.Select(ToPackage).ToList(); - } - catch (Exception e) - { - _logger.LogError(e, "Failed to mirror {PackageId}'s upstream versions", id); - return new List(); - } + _logger.LogError(e, "Failed to mirror {PackageId}'s upstream versions", id); + return new List(); } + } - public async Task DownloadPackageOrNullAsync( - string id, - NuGetVersion version, - CancellationToken cancellationToken) + public async Task> ListPackagesAsync( + string id, + CancellationToken cancellationToken) + { + try { - var packageStream = new MemoryStream(); - - try - { - var resource = await _repository.GetResourceAsync(cancellationToken); - var success = await resource.CopyNupkgToStreamAsync( - id, version, packageStream, _cache, _ngLogger, - cancellationToken); + var resource = await _repository.GetResourceAsync(cancellationToken); + var packages = await resource.GetMetadataAsync( + id, + includePrerelease: true, + includeUnlisted: true, + _cache, + _ngLogger, + cancellationToken); + + return packages.Select(ToPackage).ToList(); + } + catch (Exception e) + { + _logger.LogError(e, "Failed to mirror {PackageId}'s upstream versions", id); + return new List(); + } + } - if (!success) - { - packageStream.Dispose(); - return null; - } + public async Task DownloadPackageOrNullAsync( + string id, + NuGetVersion version, + CancellationToken cancellationToken) + { + var packageStream = new MemoryStream(); - packageStream.Position = 0; + try + { + var resource = await _repository.GetResourceAsync(cancellationToken); + var success = await resource.CopyNupkgToStreamAsync( + id, version, packageStream, _cache, _ngLogger, + cancellationToken); - return packageStream; - } - catch (Exception e) + if (!success) { - _logger.LogError( - e, - "Failed to index package {Id} {Version} from upstream", - id, - version); - packageStream.Dispose(); return null; } - } - public void Dispose() => _cache.Dispose(); + packageStream.Position = 0; - private Package ToPackage(IPackageSearchMetadata package) + return packageStream; + } + catch (Exception e) { - return new Package - { - Id = package.Identity.Id, - Version = package.Identity.Version, - Authors = ParseAuthors(package.Authors), - Description = package.Description, - Downloads = 0, - HasReadme = false, - Language = null, - Listed = package.IsListed, - MinClientVersion = null, - Published = package.Published?.UtcDateTime ?? DateTime.MinValue, - RequireLicenseAcceptance = package.RequireLicenseAcceptance, - Summary = package.Summary, - Title = package.Title, - IconUrl = package.IconUrl, - LicenseUrl = package.LicenseUrl, - ProjectUrl = package.ProjectUrl, - PackageTypes = new List(), - RepositoryUrl = null, - RepositoryType = null, - Tags = package.Tags?.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries), - - Dependencies = ToDependencies(package) - }; + _logger.LogError( + e, + "Failed to index package {Id} {Version} from upstream", + id, + version); + + packageStream.Dispose(); + return null; } + } - private string[] ParseAuthors(string authors) + public void Dispose() => _cache.Dispose(); + + private Package ToPackage(IPackageSearchMetadata package) + { + return new Package { - if (string.IsNullOrEmpty(authors)) return Array.Empty(); + Id = package.Identity.Id, + Version = package.Identity.Version, + Authors = ParseAuthors(package.Authors), + Description = package.Description, + Downloads = 0, + HasReadme = false, + Language = null, + Listed = package.IsListed, + MinClientVersion = null, + Published = package.Published?.UtcDateTime ?? DateTime.MinValue, + RequireLicenseAcceptance = package.RequireLicenseAcceptance, + Summary = package.Summary, + Title = package.Title, + IconUrl = package.IconUrl, + LicenseUrl = package.LicenseUrl, + ProjectUrl = package.ProjectUrl, + PackageTypes = new List(), + RepositoryUrl = null, + RepositoryType = null, + Tags = package.Tags?.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries), + + Dependencies = ToDependencies(package) + }; + } - return authors - .Split(new[] { ',', ';', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries) - .Select(a => a.Trim()) - .ToArray(); - } + private string[] ParseAuthors(string authors) + { + if (string.IsNullOrEmpty(authors)) return Array.Empty(); - private List ToDependencies(IPackageSearchMetadata package) - { - return package - .DependencySets - .SelectMany(ToDependencies) - .ToList(); - } + return authors + .Split(new[] { ',', ';', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries) + .Select(a => a.Trim()) + .ToArray(); + } - private IEnumerable ToDependencies(PackageDependencyGroup group) - { - var framework = group.TargetFramework.GetShortFolderName(); + private List ToDependencies(IPackageSearchMetadata package) + { + return package + .DependencySets + .SelectMany(ToDependencies) + .ToList(); + } - // BaGet stores a dependency group with no dependencies as a package dependency - // with no package id nor package version. - if ((group.Packages?.Count() ?? 0) == 0) - { - return new[] - { - new PackageDependency - { - Id = null, - VersionRange = null, - TargetFramework = framework, - } - }; - } + private IEnumerable ToDependencies(PackageDependencyGroup group) + { + var framework = group.TargetFramework.GetShortFolderName(); - return group.Packages.Select(d => new PackageDependency + // BaGet stores a dependency group with no dependencies as a package dependency + // with no package id nor package version. + if ((group.Packages?.Count() ?? 0) == 0) + { + return new[] { - Id = d.Id, - VersionRange = d.VersionRange?.ToString(), - TargetFramework = framework, - }); + new PackageDependency + { + Id = null, + VersionRange = null, + TargetFramework = framework, + } + }; } + + return group.Packages.Select(d => new PackageDependency + { + Id = d.Id, + VersionRange = d.VersionRange?.ToString(), + TargetFramework = framework, + }); } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Upstream/Clients/V3UpstreamClient.cs b/src/BaGet.Core/Upstream/Clients/V3UpstreamClient.cs index bb8855ff..c8b7480d 100644 --- a/src/BaGet.Core/Upstream/Clients/V3UpstreamClient.cs +++ b/src/BaGet.Core/Upstream/Clients/V3UpstreamClient.cs @@ -1,179 +1,172 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol; using BaGet.Protocol.Models; using NuGet.Versioning; using Microsoft.Extensions.Logging; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// The mirroring client for a NuGet server that uses the V3 protocol. +/// +public class V3UpstreamClient : IUpstreamClient { - /// - /// The mirroring client for a NuGet server that uses the V3 protocol. - /// - public class V3UpstreamClient : IUpstreamClient - { - private readonly NuGetClient _client; - private readonly ILogger _logger; + private readonly NuGetClient _client; + private readonly ILogger _logger; - public V3UpstreamClient(NuGetClient client, ILogger logger) - { - _client = client ?? throw new ArgumentNullException(nameof(client)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + public V3UpstreamClient(NuGetClient client, ILogger logger) + { + _client = client ?? throw new ArgumentNullException(nameof(client)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public async Task DownloadPackageOrNullAsync( - string id, - NuGetVersion version, - CancellationToken cancellationToken) + public async Task DownloadPackageOrNullAsync( + string id, + NuGetVersion version, + CancellationToken cancellationToken) + { + try { - try - { - using (var downloadStream = await _client.DownloadPackageAsync(id, version, cancellationToken)) - { - return await downloadStream.AsTemporaryFileStreamAsync(cancellationToken); - } - } - catch (PackageNotFoundException) - { - return null; - } - catch (Exception e) + using (var downloadStream = await _client.DownloadPackageAsync(id, version, cancellationToken)) { - _logger.LogError( - e, - "Failed to download {PackageId} {PackageVersion} from upstream", - id, - version); - return null; + return await downloadStream.AsTemporaryFileStreamAsync(cancellationToken); } } + catch (PackageNotFoundException) + { + return null; + } + catch (Exception e) + { + _logger.LogError( + e, + "Failed to download {PackageId} {PackageVersion} from upstream", + id, + version); + return null; + } + } - public async Task> ListPackagesAsync( - string id, - CancellationToken cancellationToken) + public async Task> ListPackagesAsync( + string id, + CancellationToken cancellationToken) + { + try { - try - { - var packages = await _client.GetPackageMetadataAsync(id, cancellationToken); + var packages = await _client.GetPackageMetadataAsync(id, cancellationToken); - return packages.Select(ToPackage).ToList(); - } - catch (Exception e) - { - _logger.LogError(e, "Failed to mirror {PackageId}'s upstream metadata", id); - return new List(); - } + return packages.Select(ToPackage).ToList(); } - - public async Task> ListPackageVersionsAsync( - string id, - CancellationToken cancellationToken) + catch (Exception e) { - try - { - return await _client.ListPackageVersionsAsync(id, includeUnlisted: true, cancellationToken); - } - catch (Exception e) - { - _logger.LogError(e, "Failed to mirror {PackageId}'s upstream versions", id); - return new List(); - } + _logger.LogError(e, "Failed to mirror {PackageId}'s upstream metadata", id); + return new List(); } + } - private Package ToPackage(PackageMetadata metadata) + public async Task> ListPackageVersionsAsync( + string id, + CancellationToken cancellationToken) + { + try { - var version = metadata.ParseVersion(); - - return new Package - { - Id = metadata.PackageId, - Version = version, - Authors = ParseAuthors(metadata.Authors), - Description = metadata.Description, - Downloads = 0, - HasReadme = false, - IsPrerelease = version.IsPrerelease, - Language = metadata.Language, - Listed = metadata.IsListed(), - MinClientVersion = metadata.MinClientVersion, - Published = metadata.Published.UtcDateTime, - RequireLicenseAcceptance = metadata.RequireLicenseAcceptance, - Summary = metadata.Summary, - Title = metadata.Title, - IconUrl = ParseUri(metadata.IconUrl), - LicenseUrl = ParseUri(metadata.LicenseUrl), - ProjectUrl = ParseUri(metadata.ProjectUrl), - PackageTypes = new List(), - RepositoryUrl = null, - RepositoryType = null, - SemVerLevel = version.IsSemVer2 ? SemVerLevel.SemVer2 : SemVerLevel.Unknown, - Tags = metadata.Tags?.ToArray() ?? Array.Empty(), - - Dependencies = ToDependencies(metadata) - }; + return await _client.ListPackageVersionsAsync(id, includeUnlisted: true, cancellationToken); + } + catch (Exception e) + { + _logger.LogError(e, "Failed to mirror {PackageId}'s upstream versions", id); + return new List(); } + } - private Uri ParseUri(string uriString) + private Package ToPackage(PackageMetadata metadata) + { + var version = metadata.ParseVersion(); + + return new Package { - if (uriString == null) return null; + Id = metadata.PackageId, + Version = version, + Authors = ParseAuthors(metadata.Authors), + Description = metadata.Description, + Downloads = 0, + HasReadme = false, + IsPrerelease = version.IsPrerelease, + Language = metadata.Language, + Listed = metadata.IsListed(), + MinClientVersion = metadata.MinClientVersion, + Published = metadata.Published.UtcDateTime, + RequireLicenseAcceptance = metadata.RequireLicenseAcceptance, + Summary = metadata.Summary, + Title = metadata.Title, + IconUrl = ParseUri(metadata.IconUrl), + LicenseUrl = ParseUri(metadata.LicenseUrl), + ProjectUrl = ParseUri(metadata.ProjectUrl), + PackageTypes = new List(), + RepositoryUrl = null, + RepositoryType = null, + SemVerLevel = version.IsSemVer2 ? SemVerLevel.SemVer2 : SemVerLevel.Unknown, + Tags = metadata.Tags?.ToArray() ?? Array.Empty(), + + Dependencies = ToDependencies(metadata) + }; + } - if (!Uri.TryCreate(uriString, UriKind.Absolute, out var uri)) - { - return null; - } + private Uri ParseUri(string uriString) + { + if (uriString == null) return null; - return uri; + if (!Uri.TryCreate(uriString, UriKind.Absolute, out var uri)) + { + return null; } - private string[] ParseAuthors(string authors) - { - if (string.IsNullOrEmpty(authors)) return Array.Empty(); + return uri; + } - return authors - .Split(new[] { ',', ';', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries) - .Select(a => a.Trim()) - .ToArray(); - } + private string[] ParseAuthors(string authors) + { + if (string.IsNullOrEmpty(authors)) return Array.Empty(); - private List ToDependencies(PackageMetadata package) - { - if ((package.DependencyGroups?.Count ?? 0) == 0) - { - return new List(); - } + return authors + .Split(new[] { ',', ';', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries) + .Select(a => a.Trim()) + .ToArray(); + } - return package.DependencyGroups - .SelectMany(ToDependencies) - .ToList(); + private List ToDependencies(PackageMetadata package) + { + if ((package.DependencyGroups?.Count ?? 0) == 0) + { + return new List(); } - private IEnumerable ToDependencies(DependencyGroupItem group) + return package.DependencyGroups + .SelectMany(ToDependencies) + .ToList(); + } + + private IEnumerable ToDependencies(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) { - // 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[] { - return new[] + new PackageDependency { - 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, - }); + Id = null, + VersionRange = null, + TargetFramework = group.TargetFramework, + } + }; } + + return group.Dependencies.Select(d => new PackageDependency + { + Id = d.Id, + VersionRange = d.Range, + TargetFramework = group.TargetFramework, + }); } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Upstream/DownloadsImporter.cs b/src/BaGet.Core/Upstream/DownloadsImporter.cs index 120b7146..837dbbfe 100644 --- a/src/BaGet.Core/Upstream/DownloadsImporter.cs +++ b/src/BaGet.Core/Upstream/DownloadsImporter.cs @@ -1,66 +1,60 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; -namespace BaGet.Core +namespace BaGet.Core; + +public class DownloadsImporter { - public class DownloadsImporter - { - private const int BatchSize = 200; + private const int BatchSize = 200; - private readonly IContext _context; - private readonly IPackageDownloadsSource _downloadsSource; - private readonly ILogger _logger; + 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 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(CancellationToken cancellationToken) + { + var packageDownloads = await _downloadsSource.GetPackageDownloadsAsync(); + var packages = await _context.Packages.CountAsync(); + var batches = (packages / BatchSize) + 1; - public async Task ImportAsync(CancellationToken cancellationToken) + for (var batch = 0; batch < batches; batch++) { - var packageDownloads = await _downloadsSource.GetPackageDownloadsAsync(); - var packages = await _context.Packages.CountAsync(); - var batches = (packages / BatchSize) + 1; + _logger.LogInformation("Importing batch {Batch}...", batch); - for (var batch = 0; batch < batches; batch++) + foreach (var package in await GetBatchAsync(batch, cancellationToken)) { - _logger.LogInformation("Importing batch {Batch}...", batch); + var packageId = package.Id.ToLowerInvariant(); + var packageVersion = package.NormalizedVersionString.ToLowerInvariant(); - foreach (var package in await GetBatchAsync(batch, cancellationToken)) + if (!packageDownloads.ContainsKey(packageId) || + !packageDownloads[packageId].ContainsKey(packageVersion)) { - var packageId = package.Id.ToLowerInvariant(); - var packageVersion = package.NormalizedVersionString.ToLowerInvariant(); - - if (!packageDownloads.ContainsKey(packageId) || - !packageDownloads[packageId].ContainsKey(packageVersion)) - { - continue; - } - - package.Downloads = packageDownloads[packageId][packageVersion]; + continue; } - await _context.SaveChangesAsync(cancellationToken); - - _logger.LogInformation("Imported batch {Batch}", batch); + package.Downloads = packageDownloads[packageId][packageVersion]; } - } - private Task> GetBatchAsync(int batch, CancellationToken cancellationToken) - => _context.Packages - .OrderBy(p => p.Key) - .Skip(batch * BatchSize) - .Take(BatchSize) - .ToListAsync(cancellationToken); + await _context.SaveChangesAsync(cancellationToken); + + _logger.LogInformation("Imported batch {Batch}", batch); + } } -} + + private Task> GetBatchAsync(int batch, CancellationToken cancellationToken) + => _context.Packages + .OrderBy(p => p.Key) + .Skip(batch * BatchSize) + .Take(BatchSize) + .ToListAsync(cancellationToken); +} \ No newline at end of file diff --git a/src/BaGet.Core/Upstream/IPackageDownloadsSource.cs b/src/BaGet.Core/Upstream/IPackageDownloadsSource.cs index 706e9638..9287468b 100644 --- a/src/BaGet.Core/Upstream/IPackageDownloadsSource.cs +++ b/src/BaGet.Core/Upstream/IPackageDownloadsSource.cs @@ -1,10 +1,6 @@ -using System.Collections.Generic; -using System.Threading.Tasks; +namespace BaGet.Core; -namespace BaGet.Core +public interface IPackageDownloadsSource { - public interface IPackageDownloadsSource - { - Task>> GetPackageDownloadsAsync(); - } -} + Task>> GetPackageDownloadsAsync(); +} \ No newline at end of file diff --git a/src/BaGet.Core/Upstream/IUpstreamClient.cs b/src/BaGet.Core/Upstream/IUpstreamClient.cs index 648d0c42..93a01dfb 100644 --- a/src/BaGet.Core/Upstream/IUpstreamClient.cs +++ b/src/BaGet.Core/Upstream/IUpstreamClient.cs @@ -1,51 +1,45 @@ -using BaGet.Protocol.Models; -using NuGet.Versioning; -using System.Collections.Generic; -using System.IO; -using System.Threading; -using System.Threading.Tasks; +using NuGet.Versioning; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// A client to interact with an upstream package source. +/// +public interface IUpstreamClient { /// - /// A client to interact with an upstream package source. + /// Try to get all versions of a package from the upstream package source. Returns empty + /// if the package could not be found. /// - public interface IUpstreamClient - { - /// - /// Try to get all versions of a package from the upstream package source. Returns empty - /// if the package could not be found. - /// - /// The package ID to lookup - /// - /// - /// All versions of a package, including unlisted packages. - /// Returns an empty list if the package could not be found. - /// - Task> ListPackageVersionsAsync(string id, CancellationToken cancellationToken); + /// The package ID to lookup + /// + /// + /// All versions of a package, including unlisted packages. + /// Returns an empty list if the package could not be found. + /// + Task> ListPackageVersionsAsync(string id, CancellationToken cancellationToken); - /// - /// Try to get the metadata for all versions of a package from the upstream package source. Returns empty - /// if the package could not be found. - /// - /// The package ID to lookup - /// - /// - /// The metadata for all versions of a package, including unlisted versions. - /// Returns an empty list if the package could not be found. - /// - Task> ListPackagesAsync(string id, CancellationToken cancellationToken); + /// + /// Try to get the metadata for all versions of a package from the upstream package source. Returns empty + /// if the package could not be found. + /// + /// The package ID to lookup + /// + /// + /// The metadata for all versions of a package, including unlisted versions. + /// Returns an empty list if the package could not be found. + /// + Task> ListPackagesAsync(string id, CancellationToken cancellationToken); - /// - /// Download a package from the upstream package source. Returns null if the package does not exist. - /// - /// The package ID to download. - /// The package version to download. - /// - /// - /// The package stream or null if the package cannot be found. - /// The stream is guaranteed to be seekable if not not null. - /// - Task DownloadPackageOrNullAsync(string id, NuGetVersion version, CancellationToken cancellationToken); - } -} + /// + /// Download a package from the upstream package source. Returns null if the package does not exist. + /// + /// The package ID to download. + /// The package version to download. + /// + /// + /// The package stream or null if the package cannot be found. + /// The stream is guaranteed to be seekable if not not null. + /// + Task DownloadPackageOrNullAsync(string id, NuGetVersion version, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/BaGet.Core/Upstream/PackageDownloadsJsonSource.cs b/src/BaGet.Core/Upstream/PackageDownloadsJsonSource.cs index 53c98628..d075f060 100644 --- a/src/BaGet.Core/Upstream/PackageDownloadsJsonSource.cs +++ b/src/BaGet.Core/Upstream/PackageDownloadsJsonSource.cs @@ -1,109 +1,102 @@ -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 +namespace BaGet.Core; + +// See https://github.com/NuGet/NuGet.Services.Metadata/blob/master/src/NuGet.Indexing/Downloads.cs +public class PackageDownloadsJsonSource : IPackageDownloadsSource { - // 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) { - public const string PackageDownloadsV1Url = "https://nugetprod0.blob.core.windows.net/ng-search-data/downloads.v1.json"; + _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - private readonly HttpClient _httpClient; - private readonly ILogger _logger; + public async Task>> GetPackageDownloadsAsync() + { + _logger.LogInformation("Fetching package downloads..."); - public PackageDownloadsJsonSource(HttpClient httpClient, ILogger logger) - { - _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + var results = new Dictionary>(); - public async Task>> GetPackageDownloadsAsync() + using (var downloadsStream = await GetDownloadsStreamAsync()) + using (var downloadStreamReader = new StreamReader(downloadsStream)) + using (var jsonReader = new JsonTextReader(downloadStreamReader)) { - _logger.LogInformation("Fetching package downloads..."); + _logger.LogInformation("Parsing package downloads..."); - var results = new Dictionary>(); + jsonReader.Read(); - using (var downloadsStream = await GetDownloadsStreamAsync()) - using (var downloadStreamReader = new StreamReader(downloadsStream)) - using (var jsonReader = new JsonTextReader(downloadStreamReader)) + while (jsonReader.Read()) { - _logger.LogInformation("Parsing package downloads..."); - - jsonReader.Read(); - - while (jsonReader.Read()) + try { - try + if (jsonReader.TokenType == JsonToken.StartArray) { - 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()); + // 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; - } + // 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()); - } + if (!results.ContainsKey(id)) + { + results.Add(id, new Dictionary()); + } - foreach (var token in record) + foreach (var token in record) + { + if (token != null && token.Count() == 2) { - if (token != null && token.Count() == 2) - { - var version = string.Intern(NuGetVersion.Parse(token[0].ToString()).ToNormalizedString().ToLowerInvariant()); - var downloads = token[1].ToObject(); + var version = string.Intern(NuGetVersion.Parse(token[0].ToString()).ToNormalizedString().ToLowerInvariant()); + var downloads = token[1].ToObject(); - results[id][version] = downloads; - } + results[id][version] = downloads; } } } - catch (JsonReaderException e) - { - _logger.LogError(e, "Invalid entry in downloads.v1.json"); - } } - - _logger.LogInformation("Parsed package downloads"); + catch (JsonReaderException e) + { + _logger.LogError(e, "Invalid entry in downloads.v1.json"); + } } - return results; + _logger.LogInformation("Parsed package downloads"); } - private async Task GetDownloadsStreamAsync() - { - _logger.LogInformation("Downloading downloads.v1.json..."); + return results; + } - var fileStream = File.Open(Path.GetTempFileName(), FileMode.Create); - var response = await _httpClient.GetAsync(PackageDownloadsV1Url, HttpCompletionOption.ResponseHeadersRead); + private async Task GetDownloadsStreamAsync() + { + _logger.LogInformation("Downloading downloads.v1.json..."); - response.EnsureSuccessStatusCode(); + var fileStream = File.Open(Path.GetTempFileName(), FileMode.Create); + var response = await _httpClient.GetAsync(PackageDownloadsV1Url, HttpCompletionOption.ResponseHeadersRead); - using (var networkStream = await response.Content.ReadAsStreamAsync()) - { - await networkStream.CopyToAsync(fileStream); - } + response.EnsureSuccessStatusCode(); - fileStream.Seek(0, SeekOrigin.Begin); + using (var networkStream = await response.Content.ReadAsStreamAsync()) + { + await networkStream.CopyToAsync(fileStream); + } - _logger.LogInformation("Downloaded downloads.v1.json"); + fileStream.Seek(0, SeekOrigin.Begin); - return fileStream; - } + _logger.LogInformation("Downloaded downloads.v1.json"); + + return fileStream; } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Validation/RequiredIfAttribute.cs b/src/BaGet.Core/Validation/RequiredIfAttribute.cs index 56571a3d..6ab44aee 100644 --- a/src/BaGet.Core/Validation/RequiredIfAttribute.cs +++ b/src/BaGet.Core/Validation/RequiredIfAttribute.cs @@ -1,134 +1,132 @@ -using System; using System.ComponentModel.DataAnnotations; using System.Globalization; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// Provides conditional validation based on related property value. +/// +/// Inspiration: https://stackoverflow.com/a/27666044 +/// +[AttributeUsage(AttributeTargets.Property)] +public sealed class RequiredIfAttribute : ValidationAttribute { + #region Properties + + /// + /// Gets or sets the other property name that will be used during validation. + /// + /// + /// The other property name. + /// + public string OtherProperty { get; } + + /// + /// Gets or sets the display name of the other property. + /// + /// + /// The display name of the other property. + /// + public string OtherPropertyDisplayName { get; set; } + + /// + /// Gets or sets the other property value that will be relevant for validation. + /// + /// + /// The other property value. + /// + public object OtherPropertyValue { get; } + + /// + /// Gets or sets a value indicating whether other property's value should match or differ from provided other property's value (default is false). + /// + /// + /// true if other property's value validation should be inverted; otherwise, false. + /// + /// + /// How this works + /// - true: validated property is required when other property doesn't equal provided value + /// - false: validated property is required when other property matches provided value + /// + public bool IsInverted { get; set; } + + /// + /// Gets a value that indicates whether the attribute requires validation context. + /// + /// true if the attribute requires validation context; otherwise, false. + public override bool RequiresValidationContext => true; + + #endregion + + #region Constructor + /// - /// Provides conditional validation based on related property value. - /// - /// Inspiration: https://stackoverflow.com/a/27666044 + /// Initializes a new instance of the class. /// - [AttributeUsage(AttributeTargets.Property)] - public sealed class RequiredIfAttribute : ValidationAttribute + /// The other property. + /// The other property value. + public RequiredIfAttribute(string otherProperty, object otherPropertyValue) + : base("'{0}' is required because '{1}' has a value {3}'{2}'.") { - #region Properties - - /// - /// Gets or sets the other property name that will be used during validation. - /// - /// - /// The other property name. - /// - public string OtherProperty { get; } - - /// - /// Gets or sets the display name of the other property. - /// - /// - /// The display name of the other property. - /// - public string OtherPropertyDisplayName { get; set; } - - /// - /// Gets or sets the other property value that will be relevant for validation. - /// - /// - /// The other property value. - /// - public object OtherPropertyValue { get; } - - /// - /// Gets or sets a value indicating whether other property's value should match or differ from provided other property's value (default is false). - /// - /// - /// true if other property's value validation should be inverted; otherwise, false. - /// - /// - /// How this works - /// - true: validated property is required when other property doesn't equal provided value - /// - false: validated property is required when other property matches provided value - /// - public bool IsInverted { get; set; } - - /// - /// Gets a value that indicates whether the attribute requires validation context. - /// - /// true if the attribute requires validation context; otherwise, false. - public override bool RequiresValidationContext => true; - - #endregion - - #region Constructor - - /// - /// Initializes a new instance of the class. - /// - /// The other property. - /// The other property value. - public RequiredIfAttribute(string otherProperty, object otherPropertyValue) - : base("'{0}' is required because '{1}' has a value {3}'{2}'.") - { - OtherProperty = otherProperty; - OtherPropertyValue = otherPropertyValue; - IsInverted = false; - } + OtherProperty = otherProperty; + OtherPropertyValue = otherPropertyValue; + IsInverted = false; + } - #endregion + #endregion - /// - /// Applies formatting to an error message, based on the data field where the error occurred. - /// - /// The name to include in the formatted message. - /// - /// An instance of the formatted error message. - /// - public override string FormatErrorMessage(string name) + /// + /// Applies formatting to an error message, based on the data field where the error occurred. + /// + /// The name to include in the formatted message. + /// + /// An instance of the formatted error message. + /// + public override string FormatErrorMessage(string name) + { + return string.Format( + CultureInfo.CurrentCulture, + ErrorMessageString, + name, + OtherPropertyDisplayName ?? OtherProperty, + OtherPropertyValue, + IsInverted ? "other than " : "of "); + } + + /// + /// Validates the specified value with respect to the current validation attribute. + /// + /// The value to validate. + /// The context information about the validation operation. + /// + /// An instance of the class. + /// + protected override ValidationResult IsValid(object value, ValidationContext validationContext) + { + if (validationContext == null) + throw new ArgumentNullException(nameof(validationContext)); + + var otherProperty = validationContext.ObjectType.GetProperty(OtherProperty); + if (otherProperty == null) { - return string.Format( - CultureInfo.CurrentCulture, - ErrorMessageString, - name, - OtherPropertyDisplayName ?? OtherProperty, - OtherPropertyValue, - IsInverted ? "other than " : "of "); + return new ValidationResult( + string.Format(CultureInfo.CurrentCulture, "Could not find a property named '{0}'.", OtherProperty)); } - /// - /// Validates the specified value with respect to the current validation attribute. - /// - /// The value to validate. - /// The context information about the validation operation. - /// - /// An instance of the class. - /// - protected override ValidationResult IsValid(object value, ValidationContext validationContext) + var otherValue = otherProperty.GetValue(validationContext.ObjectInstance); + + // Check if this value is actually required and validate it. + if (!IsInverted && Equals(otherValue, OtherPropertyValue) || + IsInverted && !Equals(otherValue, OtherPropertyValue)) { - if (validationContext == null) - throw new ArgumentNullException(nameof(validationContext)); - - var otherProperty = validationContext.ObjectType.GetProperty(OtherProperty); - if (otherProperty == null) - { - return new ValidationResult( - string.Format(CultureInfo.CurrentCulture, "Could not find a property named '{0}'.", OtherProperty)); - } - - var otherValue = otherProperty.GetValue(validationContext.ObjectInstance); - - // Check if this value is actually required and validate it. - if (!IsInverted && Equals(otherValue, OtherPropertyValue) || - IsInverted && !Equals(otherValue, OtherPropertyValue)) - { - if (value == null) - return new ValidationResult(FormatErrorMessage(validationContext.DisplayName)); - - // Additional check for strings so they're not empty - if (value is string val && val.Trim().Length == 0) - return new ValidationResult(FormatErrorMessage(validationContext.DisplayName)); - } - - return ValidationResult.Success; + if (value == null) + return new ValidationResult(FormatErrorMessage(validationContext.DisplayName)); + + // Additional check for strings so they're not empty + if (value is string val && val.Trim().Length == 0) + return new ValidationResult(FormatErrorMessage(validationContext.DisplayName)); } + + return ValidationResult.Success; } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Validation/ValidateBaGetOptions.cs b/src/BaGet.Core/Validation/ValidateBaGetOptions.cs index 3c20189d..bfde8dc7 100644 --- a/src/BaGet.Core/Validation/ValidateBaGetOptions.cs +++ b/src/BaGet.Core/Validation/ValidateBaGetOptions.cs @@ -1,50 +1,48 @@ -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using Microsoft.Extensions.Options; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// A configuration that validates options using data annotations. +/// +/// The type of options to validate. +public class ValidateBaGetOptions : IValidateOptions where TOptions : class { + private readonly string _optionsName; + /// - /// A configuration that validates options using data annotations. + /// Create a new validator. /// - /// The type of options to validate. - public class ValidateBaGetOptions : IValidateOptions where TOptions : class + /// + /// The option's key in the configuration or appsettings.json file, + /// or null if the options was created from the root configuration. + /// + public ValidateBaGetOptions(string optionsName) { - private readonly string _optionsName; + _optionsName = optionsName; + } - /// - /// Create a new validator. - /// - /// - /// The option's key in the configuration or appsettings.json file, - /// or null if the options was created from the root configuration. - /// - public ValidateBaGetOptions(string optionsName) + public ValidateOptionsResult Validate(string name, TOptions options) + { + // See: https://github.com/dotnet/runtime/blob/e3ffd343ad5bd3a999cb9515f59e6e7a777b2c34/src/libraries/Microsoft.Extensions.Options.DataAnnotations/src/DataAnnotationValidateOptions.cs + var context = new ValidationContext(options); + var validationResults = new List(); + if (Validator.TryValidateObject(options, context, validationResults, validateAllProperties: true)) { - _optionsName = optionsName; + return ValidateOptionsResult.Success; } - public ValidateOptionsResult Validate(string name, TOptions options) - { - // See: https://github.com/dotnet/runtime/blob/e3ffd343ad5bd3a999cb9515f59e6e7a777b2c34/src/libraries/Microsoft.Extensions.Options.DataAnnotations/src/DataAnnotationValidateOptions.cs - var context = new ValidationContext(options); - var validationResults = new List(); - if (Validator.TryValidateObject(options, context, validationResults, validateAllProperties: true)) - { - return ValidateOptionsResult.Success; - } + var errors = new List(); + var message = (_optionsName == null) + ? $"Invalid configs" + : $"Invalid '{_optionsName}' configs"; - var errors = new List(); - var message = (_optionsName == null) - ? $"Invalid configs" - : $"Invalid '{_optionsName}' configs"; - - foreach (var result in validationResults) - { - errors.Add($"{message}: {result}"); - } - - return ValidateOptionsResult.Fail(errors); + foreach (var result in validationResults) + { + errors.Add($"{message}: {result}"); } + + return ValidateOptionsResult.Fail(errors); } -} +} \ No newline at end of file diff --git a/src/BaGet.Core/Validation/ValidateStartupOptions.cs b/src/BaGet.Core/Validation/ValidateStartupOptions.cs index c662253a..5ad54df7 100644 --- a/src/BaGet.Core/Validation/ValidateStartupOptions.cs +++ b/src/BaGet.Core/Validation/ValidateStartupOptions.cs @@ -1,57 +1,55 @@ -using System; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace BaGet.Core +namespace BaGet.Core; + +/// +/// Validates BaGet's options, used at startup. +/// +public class ValidateStartupOptions { - /// - /// Validates BaGet's options, used at startup. - /// - public class ValidateStartupOptions + private readonly IOptions _root; + private readonly IOptions _database; + private readonly IOptions _storage; + private readonly IOptions _mirror; + private readonly ILogger _logger; + + public ValidateStartupOptions( + IOptions root, + IOptions database, + IOptions storage, + IOptions mirror, + ILogger logger) { - private readonly IOptions _root; - private readonly IOptions _database; - private readonly IOptions _storage; - private readonly IOptions _mirror; - private readonly ILogger _logger; + _root = root ?? throw new ArgumentNullException(nameof(root)); + _database = database ?? throw new ArgumentNullException(nameof(database)); + _storage = storage ?? throw new ArgumentNullException(nameof(storage)); + _mirror = mirror ?? throw new ArgumentNullException(nameof(mirror)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public ValidateStartupOptions( - IOptions root, - IOptions database, - IOptions storage, - IOptions mirror, - ILogger logger) + public bool Validate() + { + try { - _root = root ?? throw new ArgumentNullException(nameof(root)); - _database = database ?? throw new ArgumentNullException(nameof(database)); - _storage = storage ?? throw new ArgumentNullException(nameof(storage)); - _mirror = mirror ?? throw new ArgumentNullException(nameof(mirror)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + // Access each option to force validations to run. + // Invalid options will trigger an "OptionsValidationException" exception. + _ = _root.Value; + _ = _database.Value; + _ = _storage.Value; + _ = _mirror.Value; - public bool Validate() + return true; + } + catch (OptionsValidationException e) { - try + foreach (var failure in e.Failures) { - // Access each option to force validations to run. - // Invalid options will trigger an "OptionsValidationException" exception. - _ = _root.Value; - _ = _database.Value; - _ = _storage.Value; - _ = _mirror.Value; - - return true; + _logger.LogError("{OptionsFailure}", failure); } - catch (OptionsValidationException e) - { - foreach (var failure in e.Failures) - { - _logger.LogError("{OptionsFailure}", failure); - } - _logger.LogError(e, "BaGet configuration is invalid."); - return false; - } + _logger.LogError(e, "BaGet configuration is invalid."); + return false; } } -} +} \ No newline at end of file diff --git a/src/BaGet.Database.MySql/BaGet.Database.MySql.csproj b/src/BaGet.Database.MySql/BaGet.Database.MySql.csproj index adb58c0c..c6c2dca7 100644 --- a/src/BaGet.Database.MySql/BaGet.Database.MySql.csproj +++ b/src/BaGet.Database.MySql/BaGet.Database.MySql.csproj @@ -1,19 +1,18 @@ - netstandard2.0 + net6.0 NuGet The libraries to host BaGet on MySQL. - + - - + \ No newline at end of file diff --git a/src/BaGet.Database.MySql/Migrations/20181212113156_Initial.cs b/src/BaGet.Database.MySql/Migrations/20181212113156_Initial.cs index 90101ee1..db339aa4 100644 --- a/src/BaGet.Database.MySql/Migrations/20181212113156_Initial.cs +++ b/src/BaGet.Database.MySql/Migrations/20181212113156_Initial.cs @@ -1,4 +1,3 @@ -using System; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; diff --git a/src/BaGet.Database.MySql/Migrations/20190303071640_AddSearchDimensions.cs b/src/BaGet.Database.MySql/Migrations/20190303071640_AddSearchDimensions.cs index 02fb3c07..f727bc9a 100644 --- a/src/BaGet.Database.MySql/Migrations/20190303071640_AddSearchDimensions.cs +++ b/src/BaGet.Database.MySql/Migrations/20190303071640_AddSearchDimensions.cs @@ -1,4 +1,3 @@ -using System; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; diff --git a/src/BaGet.Database.MySql/Migrations/20190914215810_AddOriginalVersionStringColumn.cs b/src/BaGet.Database.MySql/Migrations/20190914215810_AddOriginalVersionStringColumn.cs index df0217fe..7334fd0b 100644 --- a/src/BaGet.Database.MySql/Migrations/20190914215810_AddOriginalVersionStringColumn.cs +++ b/src/BaGet.Database.MySql/Migrations/20190914215810_AddOriginalVersionStringColumn.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; namespace BaGet.Database.MySql.Migrations { diff --git a/src/BaGet.Database.MySql/Migrations/20200109085155_AddReleaseNotesStringColumn.cs b/src/BaGet.Database.MySql/Migrations/20200109085155_AddReleaseNotesStringColumn.cs index e098cdf8..6f7b57da 100644 --- a/src/BaGet.Database.MySql/Migrations/20200109085155_AddReleaseNotesStringColumn.cs +++ b/src/BaGet.Database.MySql/Migrations/20200109085155_AddReleaseNotesStringColumn.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; namespace BaGet.Database.MySql.Migrations { diff --git a/src/BaGet.Database.MySql/Migrations/20200210004047_AddHasEmbeddedIconColumn.cs b/src/BaGet.Database.MySql/Migrations/20200210004047_AddHasEmbeddedIconColumn.cs index d9b12c93..f3a31d9f 100644 --- a/src/BaGet.Database.MySql/Migrations/20200210004047_AddHasEmbeddedIconColumn.cs +++ b/src/BaGet.Database.MySql/Migrations/20200210004047_AddHasEmbeddedIconColumn.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; namespace BaGet.Database.MySql.Migrations diff --git a/src/BaGet.Database.MySql/Migrations/20210919191554_RemoveReleaseNotesMaxLength.cs b/src/BaGet.Database.MySql/Migrations/20210919191554_RemoveReleaseNotesMaxLength.cs index de331311..34e62b1a 100644 --- a/src/BaGet.Database.MySql/Migrations/20210919191554_RemoveReleaseNotesMaxLength.cs +++ b/src/BaGet.Database.MySql/Migrations/20210919191554_RemoveReleaseNotesMaxLength.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; namespace BaGet.Database.MySql.Migrations diff --git a/src/BaGet.Database.MySql/MySqlApplicationExtensions.cs b/src/BaGet.Database.MySql/MySqlApplicationExtensions.cs index 2156d90c..9a60bfd1 100644 --- a/src/BaGet.Database.MySql/MySqlApplicationExtensions.cs +++ b/src/BaGet.Database.MySql/MySqlApplicationExtensions.cs @@ -1,33 +1,31 @@ -using System; using BaGet.Core; using BaGet.Database.MySql; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -namespace BaGet +namespace BaGet; + +public static class MySqlApplicationExtensions { - public static class MySqlApplicationExtensions + public static BaGetApplication AddMySqlDatabase(this BaGetApplication app) { - public static BaGetApplication AddMySqlDatabase(this BaGetApplication app) + app.Services.AddBaGetDbContextProvider("MySql", (provider, options) => { - app.Services.AddBaGetDbContextProvider("MySql", (provider, options) => - { - var databaseOptions = provider.GetRequiredService>(); + var databaseOptions = provider.GetRequiredService>(); - options.UseMySql(databaseOptions.Value.ConnectionString); - }); + var serverVersion = new MySqlServerVersion(new Version(8, 0, 29)); - return app; - } + options.UseMySql(databaseOptions.Value.ConnectionString, serverVersion); + }); - public static BaGetApplication AddMySqlDatabase( - this BaGetApplication app, - Action configure) - { - app.AddMySqlDatabase(); - app.Services.Configure(configure); - return app; - } + return app; + } + + public static BaGetApplication AddMySqlDatabase(this BaGetApplication app, Action configure) + { + app.AddMySqlDatabase(); + app.Services.Configure(configure); + return app; } } diff --git a/src/BaGet.Database.MySql/MySqlContext.cs b/src/BaGet.Database.MySql/MySqlContext.cs index 49bbb65f..0895d062 100644 --- a/src/BaGet.Database.MySql/MySqlContext.cs +++ b/src/BaGet.Database.MySql/MySqlContext.cs @@ -1,30 +1,29 @@ using BaGet.Core; using Microsoft.EntityFrameworkCore; -using MySql.Data.MySqlClient; +using MySqlConnector; -namespace BaGet.Database.MySql -{ - public class MySqlContext : AbstractContext - { - /// - /// The MySQL Server error code for when a unique constraint is violated. - /// - private const int UniqueConstraintViolationErrorCode = 1062; +namespace BaGet.Database.MySql; - public MySqlContext(DbContextOptions options) : base(options) - { - } +public class MySqlContext : AbstractContext +{ + /// + /// The MySQL Server error code for when a unique constraint is violated. + /// + private const int UniqueConstraintViolationErrorCode = 1062; - public override bool IsUniqueConstraintViolationException(DbUpdateException exception) - { - return exception.InnerException is MySqlException mysqlException && - mysqlException.Number == UniqueConstraintViolationErrorCode; - } + public MySqlContext(DbContextOptions options) : base(options) + { + } - /// - /// MySQL does not support LIMIT clauses in subqueries for certain subquery operators. - /// See: https://dev.mysql.com/doc/refman/8.0/en/subquery-restrictions.html - /// - public override bool SupportsLimitInSubqueries => false; + public override bool IsUniqueConstraintViolationException(DbUpdateException exception) + { + return exception.InnerException is MySqlException mysqlException && + mysqlException.Number == UniqueConstraintViolationErrorCode; } + + /// + /// MySQL does not support LIMIT clauses in subqueries for certain subquery operators. + /// See: https://dev.mysql.com/doc/refman/8.0/en/subquery-restrictions.html + /// + public override bool SupportsLimitInSubqueries => false; } diff --git a/src/BaGet.Database.PostgreSql/BaGet.Database.PostgreSql.csproj b/src/BaGet.Database.PostgreSql/BaGet.Database.PostgreSql.csproj index afc7a121..34f3ae89 100644 --- a/src/BaGet.Database.PostgreSql/BaGet.Database.PostgreSql.csproj +++ b/src/BaGet.Database.PostgreSql/BaGet.Database.PostgreSql.csproj @@ -1,18 +1,18 @@ - netstandard2.0 + net6.0 NuGet The libraries to host BaGet on PostgreSQL. - + - + \ No newline at end of file diff --git a/src/BaGet.Database.PostgreSql/Migrations/20190311172227_Initial.cs b/src/BaGet.Database.PostgreSql/Migrations/20190311172227_Initial.cs index a8d2058f..f308e0e8 100644 --- a/src/BaGet.Database.PostgreSql/Migrations/20190311172227_Initial.cs +++ b/src/BaGet.Database.PostgreSql/Migrations/20190311172227_Initial.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace BaGet.Database.PostgreSql.Migrations diff --git a/src/BaGet.Database.PostgreSql/PostgreSqlApplicationExtensions.cs b/src/BaGet.Database.PostgreSql/PostgreSqlApplicationExtensions.cs index 9268d3af..d6771901 100644 --- a/src/BaGet.Database.PostgreSql/PostgreSqlApplicationExtensions.cs +++ b/src/BaGet.Database.PostgreSql/PostgreSqlApplicationExtensions.cs @@ -1,33 +1,31 @@ -using System; using BaGet.Core; using BaGet.Database.PostgreSql; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -namespace BaGet +namespace BaGet; + +public static class PostgreSqlApplicationExtensions { - public static class PostgreSqlApplicationExtensions + public static BaGetApplication AddPostgreSqlDatabase(this BaGetApplication app) { - public static BaGetApplication AddPostgreSqlDatabase(this BaGetApplication app) + app.Services.AddBaGetDbContextProvider("PostgreSql", (provider, options) => { - app.Services.AddBaGetDbContextProvider("PostgreSql", (provider, options) => - { - var databaseOptions = provider.GetRequiredService>(); + var databaseOptions = provider.GetRequiredService>(); - options.UseNpgsql(databaseOptions.Value.ConnectionString); - }); + options.UseNpgsql(databaseOptions.Value.ConnectionString); + }); - return app; - } + return app; + } - public static BaGetApplication AddPostgreSqlDatabase( - this BaGetApplication app, - Action configure) - { - app.AddPostgreSqlDatabase(); - app.Services.Configure(configure); - return app; - } + public static BaGetApplication AddPostgreSqlDatabase( + this BaGetApplication app, + Action configure) + { + app.AddPostgreSqlDatabase(); + app.Services.Configure(configure); + return app; } -} +} \ No newline at end of file diff --git a/src/BaGet.Database.PostgreSql/PostgreSqlContext.cs b/src/BaGet.Database.PostgreSql/PostgreSqlContext.cs index c008e6cc..d1988115 100644 --- a/src/BaGet.Database.PostgreSql/PostgreSqlContext.cs +++ b/src/BaGet.Database.PostgreSql/PostgreSqlContext.cs @@ -1,72 +1,69 @@ -using System.Threading; -using System.Threading.Tasks; using BaGet.Core; using Microsoft.EntityFrameworkCore; using Npgsql; -namespace BaGet.Database.PostgreSql +namespace BaGet.Database.PostgreSql; + +public class PostgreSqlContext : AbstractContext { - public class PostgreSqlContext : AbstractContext + /// + /// The PostgreSql error code for when a unique constraint is violated. + /// See: https://www.postgresql.org/docs/9.6/errcodes-appendix.html + /// + private const int UniqueConstraintViolationErrorCode = 23505; + + public PostgreSqlContext(DbContextOptions options) + : base(options) { - /// - /// The PostgreSql error code for when a unique constraint is violated. - /// See: https://www.postgresql.org/docs/9.6/errcodes-appendix.html - /// - private const int UniqueConstraintViolationErrorCode = 23505; + } - public PostgreSqlContext(DbContextOptions options) - : base(options) - { - } + public override bool IsUniqueConstraintViolationException(DbUpdateException exception) + { + return exception.InnerException is PostgresException postgresException && + int.TryParse(postgresException.SqlState, out var code) && + code == UniqueConstraintViolationErrorCode; + } - public override bool IsUniqueConstraintViolationException(DbUpdateException exception) - { - return exception.InnerException is PostgresException postgresException && - int.TryParse(postgresException.SqlState, out var code) && - code == UniqueConstraintViolationErrorCode; - } + public override async Task RunMigrationsAsync(CancellationToken cancellationToken) + { + await base.RunMigrationsAsync(cancellationToken); - public override async Task RunMigrationsAsync(CancellationToken cancellationToken) + // Npgsql caches the database's type information on the initial connection. + // This causes issues when BaGet creates the database as it may add the citext + // extension to support case insensitive columns. + // See: https://github.com/loic-sharma/BaGet/issues/442 + // See: https://github.com/npgsql/efcore.pg/issues/170#issuecomment-303417225 + if (Database.GetDbConnection() is NpgsqlConnection connection) { - await base.RunMigrationsAsync(cancellationToken); - - // Npgsql caches the database's type information on the initial connection. - // This causes issues when BaGet creates the database as it may add the citext - // extension to support case insensitive columns. - // See: https://github.com/loic-sharma/BaGet/issues/442 - // See: https://github.com/npgsql/efcore.pg/issues/170#issuecomment-303417225 - if (Database.GetDbConnection() is NpgsqlConnection connection) - { - await connection.OpenAsync(cancellationToken); - connection.ReloadTypes(); - } + await connection.OpenAsync(cancellationToken); + connection.ReloadTypes(); } + } - protected override void OnModelCreating(ModelBuilder builder) - { - base.OnModelCreating(builder); + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); - builder.HasPostgresExtension("citext"); + builder.HasPostgresExtension("citext"); - builder.Entity() - .Property(p => p.Id) - .HasColumnType("citext"); + builder.Entity() + .Property(p => p.Id) + .HasColumnType("citext"); - builder.Entity() - .Property(p => p.NormalizedVersionString) - .HasColumnType("citext"); + builder.Entity() + .Property(p => p.NormalizedVersionString) + .HasColumnType("citext"); - builder.Entity() - .Property(p => p.Id) - .HasColumnType("citext"); + builder.Entity() + .Property(p => p.Id) + .HasColumnType("citext"); - builder.Entity() - .Property(p => p.Name) - .HasColumnType("citext"); + builder.Entity() + .Property(p => p.Name) + .HasColumnType("citext"); - builder.Entity() - .Property(p => p.Moniker) - .HasColumnType("citext"); - } + builder.Entity() + .Property(p => p.Moniker) + .HasColumnType("citext"); } -} +} \ No newline at end of file diff --git a/src/BaGet.Database.SqlServer/BaGet.Database.SqlServer.csproj b/src/BaGet.Database.SqlServer/BaGet.Database.SqlServer.csproj index c6720e41..e75fbc53 100644 --- a/src/BaGet.Database.SqlServer/BaGet.Database.SqlServer.csproj +++ b/src/BaGet.Database.SqlServer/BaGet.Database.SqlServer.csproj @@ -1,18 +1,18 @@ - netstandard2.0 + net6.0 NuGet The libraries to host BaGet on SQL Server. - + - + \ No newline at end of file diff --git a/src/BaGet.Database.SqlServer/Migrations/20180804082816_Initial.cs b/src/BaGet.Database.SqlServer/Migrations/20180804082816_Initial.cs index a9a34764..dacf55d9 100644 --- a/src/BaGet.Database.SqlServer/Migrations/20180804082816_Initial.cs +++ b/src/BaGet.Database.SqlServer/Migrations/20180804082816_Initial.cs @@ -1,4 +1,3 @@ -using System; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; diff --git a/src/BaGet.Database.SqlServer/SqlServerApplicationExtensions.cs b/src/BaGet.Database.SqlServer/SqlServerApplicationExtensions.cs index b2292e08..dcecc168 100644 --- a/src/BaGet.Database.SqlServer/SqlServerApplicationExtensions.cs +++ b/src/BaGet.Database.SqlServer/SqlServerApplicationExtensions.cs @@ -1,33 +1,31 @@ -using System; using BaGet.Core; using BaGet.Database.SqlServer; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -namespace BaGet +namespace BaGet; + +public static class SqlServerApplicationExtensions { - public static class SqlServerApplicationExtensions + public static BaGetApplication AddSqlServerDatabase(this BaGetApplication app) { - public static BaGetApplication AddSqlServerDatabase(this BaGetApplication app) + app.Services.AddBaGetDbContextProvider("SqlServer", (provider, options) => { - app.Services.AddBaGetDbContextProvider("SqlServer", (provider, options) => - { - var databaseOptions = provider.GetRequiredService>(); + var databaseOptions = provider.GetRequiredService>(); - options.UseSqlServer(databaseOptions.Value.ConnectionString); - }); + options.UseSqlServer(databaseOptions.Value.ConnectionString); + }); - return app; - } + return app; + } - public static BaGetApplication AddSqlServerDatabase( - this BaGetApplication app, - Action configure) - { - app.AddSqlServerDatabase(); - app.Services.Configure(configure); - return app; - } + public static BaGetApplication AddSqlServerDatabase( + this BaGetApplication app, + Action configure) + { + app.AddSqlServerDatabase(); + app.Services.Configure(configure); + return app; } -} +} \ No newline at end of file diff --git a/src/BaGet.Database.SqlServer/SqlServerContext.cs b/src/BaGet.Database.SqlServer/SqlServerContext.cs index 3639b8ca..15219af4 100644 --- a/src/BaGet.Database.SqlServer/SqlServerContext.cs +++ b/src/BaGet.Database.SqlServer/SqlServerContext.cs @@ -1,36 +1,34 @@ -using System.Linq; using BaGet.Core; using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore; -namespace BaGet.Database.SqlServer +namespace BaGet.Database.SqlServer; + +public class SqlServerContext : AbstractContext { - public class SqlServerContext : AbstractContext - { - /// - /// The SQL Server error code for when a unique contraint is violated. - /// - private const int UniqueConstraintViolationErrorCode = 2627; + /// + /// The SQL Server error code for when a unique contraint is violated. + /// + private const int UniqueConstraintViolationErrorCode = 2627; - public SqlServerContext(DbContextOptions options) - : base(options) - { } + public SqlServerContext(DbContextOptions options) + : base(options) + { } - /// - /// 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. - public override bool IsUniqueConstraintViolationException(DbUpdateException exception) + /// + /// 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. + public override bool IsUniqueConstraintViolationException(DbUpdateException exception) + { + if (exception.GetBaseException() is SqlException sqlException) { - if (exception.GetBaseException() is SqlException sqlException) - { - return sqlException.Errors - .OfType() - .Any(error => error.Number == UniqueConstraintViolationErrorCode); - } - - return false; + return sqlException.Errors + .OfType() + .Any(error => error.Number == UniqueConstraintViolationErrorCode); } + + return false; } -} +} \ No newline at end of file diff --git a/src/BaGet.Database.Sqlite/BaGet.Database.Sqlite.csproj b/src/BaGet.Database.Sqlite/BaGet.Database.Sqlite.csproj index c7515daa..88988ffd 100644 --- a/src/BaGet.Database.Sqlite/BaGet.Database.Sqlite.csproj +++ b/src/BaGet.Database.Sqlite/BaGet.Database.Sqlite.csproj @@ -1,18 +1,18 @@ - netstandard2.0 + net6.0 NuGet The libraries to host BaGet on SQLite. - + - + \ No newline at end of file diff --git a/src/BaGet.Database.Sqlite/Migrations/20180804082808_Initial.cs b/src/BaGet.Database.Sqlite/Migrations/20180804082808_Initial.cs index e1cf5c96..cde1429f 100644 --- a/src/BaGet.Database.Sqlite/Migrations/20180804082808_Initial.cs +++ b/src/BaGet.Database.Sqlite/Migrations/20180804082808_Initial.cs @@ -1,4 +1,3 @@ -using System; using Microsoft.EntityFrameworkCore.Migrations; namespace BaGet.Database.Sqlite.Migrations diff --git a/src/BaGet.Database.Sqlite/SqliteApplicationExtensions.cs b/src/BaGet.Database.Sqlite/SqliteApplicationExtensions.cs index 34e3a91e..b5af3012 100644 --- a/src/BaGet.Database.Sqlite/SqliteApplicationExtensions.cs +++ b/src/BaGet.Database.Sqlite/SqliteApplicationExtensions.cs @@ -1,33 +1,31 @@ -using System; using BaGet.Core; using BaGet.Database.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -namespace BaGet +namespace BaGet; + +public static class SqliteApplicationExtensions { - public static class SqliteApplicationExtensions + public static BaGetApplication AddSqliteDatabase(this BaGetApplication app) { - public static BaGetApplication AddSqliteDatabase(this BaGetApplication app) + app.Services.AddBaGetDbContextProvider("Sqlite", (provider, options) => { - app.Services.AddBaGetDbContextProvider("Sqlite", (provider, options) => - { - var databaseOptions = provider.GetRequiredService>(); + var databaseOptions = provider.GetRequiredService>(); - options.UseSqlite(databaseOptions.Value.ConnectionString); - }); + options.UseSqlite(databaseOptions.Value.ConnectionString); + }); - return app; - } + return app; + } - public static BaGetApplication AddSqliteDatabase( - this BaGetApplication app, - Action configure) - { - app.AddSqliteDatabase(); - app.Services.Configure(configure); - return app; - } + public static BaGetApplication AddSqliteDatabase( + this BaGetApplication app, + Action configure) + { + app.AddSqliteDatabase(); + app.Services.Configure(configure); + return app; } -} +} \ No newline at end of file diff --git a/src/BaGet.Database.Sqlite/SqliteContext.cs b/src/BaGet.Database.Sqlite/SqliteContext.cs index 974ae587..bbc4a00d 100644 --- a/src/BaGet.Database.Sqlite/SqliteContext.cs +++ b/src/BaGet.Database.Sqlite/SqliteContext.cs @@ -2,48 +2,47 @@ using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; -namespace BaGet.Database.Sqlite +namespace BaGet.Database.Sqlite; + +public class SqliteContext : AbstractContext { - public class SqliteContext : AbstractContext + /// + /// The Sqlite error code for when a unique constraint is violated. + /// + private const int SqliteUniqueConstraintViolationErrorCode = 19; + + public SqliteContext(DbContextOptions options) + : base(options) + { } + + public override bool IsUniqueConstraintViolationException(DbUpdateException exception) + { + return exception.InnerException is SqliteException sqliteException && + sqliteException.SqliteErrorCode == SqliteUniqueConstraintViolationErrorCode; + } + + protected override void OnModelCreating(ModelBuilder builder) { - /// - /// The Sqlite error code for when a unique constraint is violated. - /// - private const int SqliteUniqueConstraintViolationErrorCode = 19; - - public SqliteContext(DbContextOptions options) - : base(options) - { } - - public override bool IsUniqueConstraintViolationException(DbUpdateException exception) - { - return exception.InnerException is SqliteException sqliteException && - sqliteException.SqliteErrorCode == SqliteUniqueConstraintViolationErrorCode; - } - - protected override void OnModelCreating(ModelBuilder builder) - { - base.OnModelCreating(builder); - - builder.Entity() - .Property(p => p.Id) - .HasColumnType("TEXT COLLATE NOCASE"); - - builder.Entity() - .Property(p => p.NormalizedVersionString) - .HasColumnType("TEXT COLLATE NOCASE"); - - builder.Entity() - .Property(d => d.Id) - .HasColumnType("TEXT COLLATE NOCASE"); - - builder.Entity() - .Property(t => t.Name) - .HasColumnType("TEXT COLLATE NOCASE"); - - builder.Entity() - .Property(f => f.Moniker) - .HasColumnType("TEXT COLLATE NOCASE"); - } + base.OnModelCreating(builder); + + builder.Entity() + .Property(p => p.Id) + .HasColumnType("TEXT COLLATE NOCASE"); + + builder.Entity() + .Property(p => p.NormalizedVersionString) + .HasColumnType("TEXT COLLATE NOCASE"); + + builder.Entity() + .Property(d => d.Id) + .HasColumnType("TEXT COLLATE NOCASE"); + + builder.Entity() + .Property(t => t.Name) + .HasColumnType("TEXT COLLATE NOCASE"); + + builder.Entity() + .Property(f => f.Moniker) + .HasColumnType("TEXT COLLATE NOCASE"); } -} +} \ No newline at end of file diff --git a/src/BaGet.Gcp/BaGet.Gcp.csproj b/src/BaGet.Gcp/BaGet.Gcp.csproj index 3dc33ae6..2049869d 100644 --- a/src/BaGet.Gcp/BaGet.Gcp.csproj +++ b/src/BaGet.Gcp/BaGet.Gcp.csproj @@ -1,18 +1,18 @@ - netstandard2.0 + net6.0 NuGet;Google;Cloud The libraries to host BaGet on the Google Cloud Platform. - + - + \ No newline at end of file diff --git a/src/BaGet.Gcp/GoogleCloudApplicationExtensions.cs b/src/BaGet.Gcp/GoogleCloudApplicationExtensions.cs index 5dcb1bb8..ec069114 100644 --- a/src/BaGet.Gcp/GoogleCloudApplicationExtensions.cs +++ b/src/BaGet.Gcp/GoogleCloudApplicationExtensions.cs @@ -1,37 +1,35 @@ -using System; using BaGet.Core; using BaGet.Gcp; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; -namespace BaGet +namespace BaGet; + +public static class GoogleCloudApplicationExtensions { - public static class GoogleCloudApplicationExtensions + public static BaGetApplication AddGoogleCloudStorage(this BaGetApplication app) { - public static BaGetApplication AddGoogleCloudStorage(this BaGetApplication app) - { - app.Services.AddBaGetOptions(nameof(BaGetOptions.Storage)); - app.Services.AddTransient(); + app.Services.AddBaGetOptions(nameof(BaGetOptions.Storage)); + app.Services.AddTransient(); - app.Services.TryAddTransient(provider => provider.GetRequiredService()); + app.Services.TryAddTransient(provider => provider.GetRequiredService()); - app.Services.AddProvider((provider, config) => - { - if (!config.HasStorageType("GoogleCloud")) return null; + app.Services.AddProvider((provider, config) => + { + if (!config.HasStorageType("GoogleCloud")) return null; - return provider.GetRequiredService(); - }); + return provider.GetRequiredService(); + }); - return app; - } + return app; + } - public static BaGetApplication AddGoogleCloudStorage( - this BaGetApplication app, - Action configure) - { - app.AddGoogleCloudStorage(); - app.Services.Configure(configure); - return app; - } + public static BaGetApplication AddGoogleCloudStorage( + this BaGetApplication app, + Action configure) + { + app.AddGoogleCloudStorage(); + app.Services.Configure(configure); + return app; } -} +} \ No newline at end of file diff --git a/src/BaGet.Gcp/GoogleCloudStorageOptions.cs b/src/BaGet.Gcp/GoogleCloudStorageOptions.cs index 6b9a2ff4..9c847e23 100644 --- a/src/BaGet.Gcp/GoogleCloudStorageOptions.cs +++ b/src/BaGet.Gcp/GoogleCloudStorageOptions.cs @@ -1,11 +1,10 @@ using System.ComponentModel.DataAnnotations; using BaGet.Core; -namespace BaGet.Gcp +namespace BaGet.Gcp; + +public class GoogleCloudStorageOptions : StorageOptions { - public class GoogleCloudStorageOptions : StorageOptions - { - [Required] - public string BucketName { get; set; } - } -} + [Required] + public string BucketName { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Gcp/GoogleCloudStorageService.cs b/src/BaGet.Gcp/GoogleCloudStorageService.cs index 6a01f406..39761f48 100644 --- a/src/BaGet.Gcp/GoogleCloudStorageService.cs +++ b/src/BaGet.Gcp/GoogleCloudStorageService.cs @@ -1,100 +1,94 @@ -using System; -using System.IO; -using System.Linq; using System.Net; using System.Security.Cryptography; -using System.Threading; -using System.Threading.Tasks; using BaGet.Core; using Google; using Google.Cloud.Storage.V1; using Microsoft.Extensions.Options; -namespace BaGet.Gcp +namespace BaGet.Gcp; + +public class GoogleCloudStorageService : IStorageService { - public class GoogleCloudStorageService : IStorageService - { - private readonly string _bucketName; + private readonly string _bucketName; - public GoogleCloudStorageService(IOptionsSnapshot options) - { - if (options == null) - throw new ArgumentNullException(nameof(options)); + public GoogleCloudStorageService(IOptionsSnapshot options) + { + if (options == null) + throw new ArgumentNullException(nameof(options)); - _bucketName = options.Value.BucketName; - } + _bucketName = options.Value.BucketName; + } - public async Task GetAsync(string path, CancellationToken cancellationToken = default) + public async Task GetAsync(string path, CancellationToken cancellationToken = default) + { + using (var storage = await StorageClient.CreateAsync()) { - using (var storage = await StorageClient.CreateAsync()) - { - var stream = new MemoryStream(); - await storage.DownloadObjectAsync(_bucketName, CoercePath(path), stream, cancellationToken: cancellationToken); - stream.Position = 0; - return stream; - } + var stream = new MemoryStream(); + await storage.DownloadObjectAsync(_bucketName, CoercePath(path), stream, cancellationToken: cancellationToken); + stream.Position = 0; + return stream; } + } - public Task GetDownloadUriAsync(string path, CancellationToken cancellationToken = default) - { - // returns an Authenticated Browser Download URL: https://cloud.google.com/storage/docs/request-endpoints#cookieauth - return Task.FromResult(new Uri($"https://storage.googleapis.com/{_bucketName}/{CoercePath(path).TrimStart('/')}")); - } + public Task GetDownloadUriAsync(string path, CancellationToken cancellationToken = default) + { + // returns an Authenticated Browser Download URL: https://cloud.google.com/storage/docs/request-endpoints#cookieauth + return Task.FromResult(new Uri($"https://storage.googleapis.com/{_bucketName}/{CoercePath(path).TrimStart('/')}")); + } - public async Task PutAsync(string path, Stream content, string contentType, CancellationToken cancellationToken = default) + public async Task PutAsync(string path, Stream content, string contentType, CancellationToken cancellationToken = default) + { + using (var storage = await StorageClient.CreateAsync()) + using (var seekableContent = new MemoryStream()) { - using (var storage = await StorageClient.CreateAsync()) - using (var seekableContent = new MemoryStream()) - { - await content.CopyToAsync(seekableContent, 65536, cancellationToken); - seekableContent.Position = 0; + await content.CopyToAsync(seekableContent, 65536, cancellationToken); + seekableContent.Position = 0; - var objectName = CoercePath(path); + var objectName = CoercePath(path); - try - { - // attempt to upload, succeeding only if the object doesn't exist - await storage.UploadObjectAsync(_bucketName, objectName, contentType, seekableContent, new UploadObjectOptions { IfGenerationMatch = 0 }, cancellationToken); - return StoragePutResult.Success; - } - catch (GoogleApiException e) when (e.HttpStatusCode == HttpStatusCode.PreconditionFailed) - { - // the object already exists; get the hash of its content from its metadata - var existingObject = await storage.GetObjectAsync(_bucketName, objectName, cancellationToken: cancellationToken); - var existingHash = Convert.FromBase64String(existingObject.Md5Hash); + try + { + // attempt to upload, succeeding only if the object doesn't exist + await storage.UploadObjectAsync(_bucketName, objectName, contentType, seekableContent, new UploadObjectOptions { IfGenerationMatch = 0 }, cancellationToken); + return StoragePutResult.Success; + } + catch (GoogleApiException e) when (e.HttpStatusCode == HttpStatusCode.PreconditionFailed) + { + // the object already exists; get the hash of its content from its metadata + var existingObject = await storage.GetObjectAsync(_bucketName, objectName, cancellationToken: cancellationToken); + var existingHash = Convert.FromBase64String(existingObject.Md5Hash); - // hash the content that was uploaded - seekableContent.Position = 0; - byte[] contentHash; - using (var md5 = MD5.Create()) - contentHash = md5.ComputeHash(seekableContent); + // hash the content that was uploaded + seekableContent.Position = 0; + byte[] contentHash; + using (var md5 = MD5.Create()) + contentHash = md5.ComputeHash(seekableContent); - // conflict if the two hashes are different - return existingHash.SequenceEqual(contentHash) ? StoragePutResult.AlreadyExists : StoragePutResult.Conflict; - } + // conflict if the two hashes are different + return existingHash.SequenceEqual(contentHash) ? StoragePutResult.AlreadyExists : StoragePutResult.Conflict; } } + } - public async Task DeleteAsync(string path, CancellationToken cancellationToken = default) + public async Task DeleteAsync(string path, CancellationToken cancellationToken = default) + { + using (var storage = await StorageClient.CreateAsync()) { - using (var storage = await StorageClient.CreateAsync()) + try + { + var obj = await storage.GetObjectAsync(_bucketName, CoercePath(path), cancellationToken: cancellationToken); + await storage.DeleteObjectAsync(obj, cancellationToken: cancellationToken); + } + catch (GoogleApiException e) when (e.HttpStatusCode == HttpStatusCode.NotFound) { - try - { - var obj = await storage.GetObjectAsync(_bucketName, CoercePath(path), cancellationToken: cancellationToken); - await storage.DeleteObjectAsync(obj, cancellationToken: cancellationToken); - } - catch (GoogleApiException e) when (e.HttpStatusCode == HttpStatusCode.NotFound) - { - } } } + } - private static string CoercePath(string path) - { - // although Google Cloud Storage objects exist in a flat namespace, using forward slashes allows the objects to - // be exposed as nested subdirectories, e.g., when browsing via Google Cloud Console - return path.Replace('\\', '/'); - } + private static string CoercePath(string path) + { + // although Google Cloud Storage objects exist in a flat namespace, using forward slashes allows the objects to + // be exposed as nested subdirectories, e.g., when browsing via Google Cloud Console + return path.Replace('\\', '/'); } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/BaGet.Protocol.csproj b/src/BaGet.Protocol/BaGet.Protocol.csproj index 5711a54b..8c1053ee 100644 --- a/src/BaGet.Protocol/BaGet.Protocol.csproj +++ b/src/BaGet.Protocol/BaGet.Protocol.csproj @@ -1,17 +1,17 @@ - netstandard2.0 + net6.0 NuGet;Protocol Libraries to interact with NuGet server APIs. - - - - + + + + - + \ No newline at end of file diff --git a/src/BaGet.Protocol/Catalog/CatalogClient.cs b/src/BaGet.Protocol/Catalog/CatalogClient.cs index f87089d9..31a350b6 100644 --- a/src/BaGet.Protocol/Catalog/CatalogClient.cs +++ b/src/BaGet.Protocol/Catalog/CatalogClient.cs @@ -1,48 +1,44 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +public partial class NuGetClientFactory { - public partial class NuGetClientFactory + private class CatalogClient : ICatalogClient { - private class CatalogClient : ICatalogClient - { - private readonly NuGetClientFactory _clientfactory; + private readonly NuGetClientFactory _clientfactory; - public CatalogClient(NuGetClientFactory clientFactory) - { - _clientfactory = clientFactory ?? throw new ArgumentNullException(nameof(clientFactory)); - } + public CatalogClient(NuGetClientFactory clientFactory) + { + _clientfactory = clientFactory ?? throw new ArgumentNullException(nameof(clientFactory)); + } - public async Task GetIndexAsync(CancellationToken cancellationToken = default) - { - var client = await _clientfactory.GetCatalogClientAsync(cancellationToken); + public async Task GetIndexAsync(CancellationToken cancellationToken = default) + { + var client = await _clientfactory.GetCatalogClientAsync(cancellationToken); - return await client.GetIndexAsync(cancellationToken); - } + return await client.GetIndexAsync(cancellationToken); + } - public async Task GetPageAsync(string pageUrl, CancellationToken cancellationToken = default) - { - var client = await _clientfactory.GetCatalogClientAsync(cancellationToken); + public async Task GetPageAsync(string pageUrl, CancellationToken cancellationToken = default) + { + var client = await _clientfactory.GetCatalogClientAsync(cancellationToken); - return await client.GetPageAsync(pageUrl, cancellationToken); - } + return await client.GetPageAsync(pageUrl, cancellationToken); + } - public async Task GetPackageDetailsLeafAsync(string leafUrl, CancellationToken cancellationToken = default) - { - var client = await _clientfactory.GetCatalogClientAsync(cancellationToken); + public async Task GetPackageDetailsLeafAsync(string leafUrl, CancellationToken cancellationToken = default) + { + var client = await _clientfactory.GetCatalogClientAsync(cancellationToken); - return await client.GetPackageDetailsLeafAsync(leafUrl, cancellationToken); - } + return await client.GetPackageDetailsLeafAsync(leafUrl, cancellationToken); + } - public async Task GetPackageDeleteLeafAsync(string leafUrl, CancellationToken cancellationToken = default) - { - var client = await _clientfactory.GetCatalogClientAsync(cancellationToken); + public async Task GetPackageDeleteLeafAsync(string leafUrl, CancellationToken cancellationToken = default) + { + var client = await _clientfactory.GetCatalogClientAsync(cancellationToken); - return await client.GetPackageDeleteLeafAsync(leafUrl, cancellationToken); - } + return await client.GetPackageDeleteLeafAsync(leafUrl, cancellationToken); } } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Catalog/CatalogProcessor.cs b/src/BaGet.Protocol/Catalog/CatalogProcessor.cs index 13c5cc27..98e20428 100644 --- a/src/BaGet.Protocol/Catalog/CatalogProcessor.cs +++ b/src/BaGet.Protocol/Catalog/CatalogProcessor.cs @@ -1,201 +1,197 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; using Microsoft.Extensions.Logging; -namespace BaGet.Protocol.Catalog +namespace BaGet.Protocol.Catalog; + +/// +/// Processes catalog leafs in chronological order. +/// See: https://docs.microsoft.com/en-us/nuget/api/catalog-resource +/// Based off: https://github.com/NuGet/NuGet.Services.Metadata/blob/3a468fe534a03dcced897eb5992209fdd3c4b6c9/src/NuGet.Protocol.Catalog/CatalogProcessor.cs +/// +public class CatalogProcessor { + private readonly ICatalogLeafProcessor _leafProcessor; + private readonly ICatalogClient _client; + private readonly ICursor _cursor; + private readonly CatalogProcessorOptions _options; + private readonly ILogger _logger; + /// - /// Processes catalog leafs in chronological order. - /// See: https://docs.microsoft.com/en-us/nuget/api/catalog-resource - /// Based off: https://github.com/NuGet/NuGet.Services.Metadata/blob/3a468fe534a03dcced897eb5992209fdd3c4b6c9/src/NuGet.Protocol.Catalog/CatalogProcessor.cs + /// Create a processor to discover and download catalog leafs. Leafs are processed + /// by the . /// - public class CatalogProcessor + /// Cursor to track succesfully processed leafs. Leafs before the cursor are skipped. + /// The client to interact with the catalog resource. + /// The leaf processor. + /// The options to configure catalog processing. + /// The logger used for telemetry. + public CatalogProcessor( + ICursor cursor, + ICatalogClient client, + ICatalogLeafProcessor leafProcessor, + CatalogProcessorOptions options, + ILogger logger) { - private readonly ICatalogLeafProcessor _leafProcessor; - private readonly ICatalogClient _client; - private readonly ICursor _cursor; - private readonly CatalogProcessorOptions _options; - private readonly ILogger _logger; - - /// - /// Create a processor to discover and download catalog leafs. Leafs are processed - /// by the . - /// - /// Cursor to track succesfully processed leafs. Leafs before the cursor are skipped. - /// The client to interact with the catalog resource. - /// The leaf processor. - /// The options to configure catalog processing. - /// The logger used for telemetry. - public CatalogProcessor( - ICursor cursor, - ICatalogClient client, - ICatalogLeafProcessor leafProcessor, - CatalogProcessorOptions options, - ILogger logger) - { - _leafProcessor = leafProcessor ?? throw new ArgumentNullException(nameof(leafProcessor)); - _client = client ?? throw new ArgumentNullException(nameof(client)); - _cursor = cursor ?? throw new ArgumentNullException(nameof(cursor)); - _options = options ?? throw new ArgumentNullException(nameof(options)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + _leafProcessor = leafProcessor ?? throw new ArgumentNullException(nameof(leafProcessor)); + _client = client ?? throw new ArgumentNullException(nameof(client)); + _cursor = cursor ?? throw new ArgumentNullException(nameof(cursor)); + _options = options ?? throw new ArgumentNullException(nameof(options)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - /// - /// Discovers and downloads all of the catalog leafs after the current cursor value and before the maximum - /// commit timestamp found in the settings. Each catalog leaf is passed to the catalog leaf processor in - /// chronological order. After a commit is completed, its commit timestamp is written to the cursor, i.e. when - /// transitioning from commit timestamp A to B, A is written to the cursor so that it never is processed again. - /// - /// A token to cancel the task. - /// True if all of the catalog leaves found were processed successfully. - public async Task ProcessAsync(CancellationToken cancellationToken = default) - { - var minCommitTimestamp = await GetMinCommitTimestamp(cancellationToken); - _logger.LogInformation( - "Using time bounds {min:O} (exclusive) to {max:O} (inclusive).", - minCommitTimestamp, - _options.MaxCommitTimestamp); + /// + /// Discovers and downloads all of the catalog leafs after the current cursor value and before the maximum + /// commit timestamp found in the settings. Each catalog leaf is passed to the catalog leaf processor in + /// chronological order. After a commit is completed, its commit timestamp is written to the cursor, i.e. when + /// transitioning from commit timestamp A to B, A is written to the cursor so that it never is processed again. + /// + /// A token to cancel the task. + /// True if all of the catalog leaves found were processed successfully. + public async Task ProcessAsync(CancellationToken cancellationToken = default) + { + var minCommitTimestamp = await GetMinCommitTimestamp(cancellationToken); + _logger.LogInformation( + "Using time bounds {min:O} (exclusive) to {max:O} (inclusive).", + minCommitTimestamp, + _options.MaxCommitTimestamp); - return await ProcessIndexAsync(minCommitTimestamp, cancellationToken); - } + return await ProcessIndexAsync(minCommitTimestamp, cancellationToken); + } - private async Task ProcessIndexAsync(DateTimeOffset minCommitTimestamp, CancellationToken cancellationToken) + private async Task ProcessIndexAsync(DateTimeOffset minCommitTimestamp, CancellationToken cancellationToken) + { + var index = await _client.GetIndexAsync(cancellationToken); + + var pageItems = index.GetPagesInBounds( + minCommitTimestamp, + _options.MaxCommitTimestamp); + _logger.LogInformation( + "{pages} pages were in the time bounds, out of {totalPages}.", + pageItems.Count, + index.Items.Count); + + var success = true; + for (var i = 0; i < pageItems.Count; i++) { - var index = await _client.GetIndexAsync(cancellationToken); - - var pageItems = index.GetPagesInBounds( - minCommitTimestamp, - _options.MaxCommitTimestamp); - _logger.LogInformation( - "{pages} pages were in the time bounds, out of {totalPages}.", - pageItems.Count, - index.Items.Count); - - var success = true; - for (var i = 0; i < pageItems.Count; i++) + success = await ProcessPageAsync(minCommitTimestamp, pageItems[i], cancellationToken); + if (!success) { - success = await ProcessPageAsync(minCommitTimestamp, pageItems[i], cancellationToken); - if (!success) - { - _logger.LogWarning( - "{unprocessedPages} out of {pages} pages were left incomplete due to a processing failure.", - pageItems.Count - i, - pageItems.Count); - break; - } + _logger.LogWarning( + "{unprocessedPages} out of {pages} pages were left incomplete due to a processing failure.", + pageItems.Count - i, + pageItems.Count); + break; } - - return success; } - private async Task ProcessPageAsync( - DateTimeOffset minCommitTimestamp, - CatalogPageItem pageItem, - CancellationToken cancellationToken) + return success; + } + + private async Task ProcessPageAsync( + DateTimeOffset minCommitTimestamp, + CatalogPageItem pageItem, + CancellationToken cancellationToken) + { + var page = await _client.GetPageAsync(pageItem.CatalogPageUrl, cancellationToken); + + var leafItems = page.GetLeavesInBounds( + minCommitTimestamp, + _options.MaxCommitTimestamp, + _options.ExcludeRedundantLeaves); + _logger.LogInformation( + "On page {page}, {leaves} out of {totalLeaves} were in the time bounds.", + pageItem.CatalogPageUrl, + leafItems.Count, + page.Items.Count); + + DateTimeOffset? newCursor = null; + var success = true; + for (var i = 0; i < leafItems.Count; i++) { - var page = await _client.GetPageAsync(pageItem.CatalogPageUrl, cancellationToken); - - var leafItems = page.GetLeavesInBounds( - minCommitTimestamp, - _options.MaxCommitTimestamp, - _options.ExcludeRedundantLeaves); - _logger.LogInformation( - "On page {page}, {leaves} out of {totalLeaves} were in the time bounds.", - pageItem.CatalogPageUrl, - leafItems.Count, - page.Items.Count); - - DateTimeOffset? newCursor = null; - var success = true; - for (var i = 0; i < leafItems.Count; i++) + var leafItem = leafItems[i]; + + if (newCursor.HasValue && newCursor.Value != leafItem.CommitTimestamp) { - var leafItem = leafItems[i]; - - if (newCursor.HasValue && newCursor.Value != leafItem.CommitTimestamp) - { - await _cursor.SetAsync(newCursor.Value, cancellationToken); - } - - newCursor = leafItem.CommitTimestamp; - - success = await ProcessLeafAsync(leafItem, cancellationToken); - if (!success) - { - _logger.LogWarning( - "{unprocessedLeaves} out of {leaves} leaves were left incomplete due to a processing failure.", - leafItems.Count - i, - leafItems.Count); - break; - } + await _cursor.SetAsync(newCursor.Value, cancellationToken); } - if (newCursor.HasValue && success) + newCursor = leafItem.CommitTimestamp; + + success = await ProcessLeafAsync(leafItem, cancellationToken); + if (!success) { - await _cursor.SetAsync(newCursor.Value); + _logger.LogWarning( + "{unprocessedLeaves} out of {leaves} leaves were left incomplete due to a processing failure.", + leafItems.Count - i, + leafItems.Count); + break; } + } - return success; + if (newCursor.HasValue && success) + { + await _cursor.SetAsync(newCursor.Value); } - private async Task ProcessLeafAsync(CatalogLeafItem leafItem, CancellationToken cancellationToken) + return success; + } + + private async Task ProcessLeafAsync(CatalogLeafItem leafItem, CancellationToken cancellationToken) + { + bool success; + try { - bool success; - try + if (leafItem.IsPackageDelete()) { - if (leafItem.IsPackageDelete()) - { - var packageDelete = await _client.GetPackageDeleteLeafAsync(leafItem.CatalogLeafUrl); - success = await _leafProcessor.ProcessPackageDeleteAsync(packageDelete, cancellationToken); - } - else if (leafItem.IsPackageDetails()) - { - var packageDetails = await _client.GetPackageDetailsLeafAsync(leafItem.CatalogLeafUrl); - success = await _leafProcessor.ProcessPackageDetailsAsync(packageDetails, cancellationToken); - } - else - { - throw new NotSupportedException($"The catalog leaf type '{leafItem.Type}' is not supported."); - } + var packageDelete = await _client.GetPackageDeleteLeafAsync(leafItem.CatalogLeafUrl); + success = await _leafProcessor.ProcessPackageDeleteAsync(packageDelete, cancellationToken); } - catch (Exception exception) + else if (leafItem.IsPackageDetails()) { - _logger.LogError( - 0, - exception, - "An exception was thrown while processing leaf {leafUrl}.", - leafItem.CatalogLeafUrl); - success = false; + var packageDetails = await _client.GetPackageDetailsLeafAsync(leafItem.CatalogLeafUrl); + success = await _leafProcessor.ProcessPackageDetailsAsync(packageDetails, cancellationToken); } - - if (!success) + else { - _logger.LogWarning( - "Failed to process leaf {leafUrl} ({packageId} {packageVersion}, {leafType}).", - leafItem.CatalogLeafUrl, - leafItem.PackageId, - leafItem.PackageVersion, - leafItem.Type); + throw new NotSupportedException($"The catalog leaf type '{leafItem.Type}' is not supported."); } - - return success; + } + catch (Exception exception) + { + _logger.LogError( + 0, + exception, + "An exception was thrown while processing leaf {leafUrl}.", + leafItem.CatalogLeafUrl); + success = false; } - private async Task GetMinCommitTimestamp(CancellationToken cancellationToken) + if (!success) { - var minCommitTimestamp = await _cursor.GetAsync(cancellationToken); + _logger.LogWarning( + "Failed to process leaf {leafUrl} ({packageId} {packageVersion}, {leafType}).", + leafItem.CatalogLeafUrl, + leafItem.PackageId, + leafItem.PackageVersion, + leafItem.Type); + } - minCommitTimestamp = minCommitTimestamp - ?? _options.DefaultMinCommitTimestamp - ?? _options.MinCommitTimestamp; + return success; + } - if (minCommitTimestamp.Value < _options.MinCommitTimestamp) - { - minCommitTimestamp = _options.MinCommitTimestamp; - } + private async Task GetMinCommitTimestamp(CancellationToken cancellationToken) + { + var minCommitTimestamp = await _cursor.GetAsync(cancellationToken); + + minCommitTimestamp = minCommitTimestamp + ?? _options.DefaultMinCommitTimestamp + ?? _options.MinCommitTimestamp; - return minCommitTimestamp.Value; + if (minCommitTimestamp.Value < _options.MinCommitTimestamp) + { + minCommitTimestamp = _options.MinCommitTimestamp; } + + return minCommitTimestamp.Value; } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Catalog/CatalogProcessorOptions.cs b/src/BaGet.Protocol/Catalog/CatalogProcessorOptions.cs index 1b9bd5cc..23a02722 100644 --- a/src/BaGet.Protocol/Catalog/CatalogProcessorOptions.cs +++ b/src/BaGet.Protocol/Catalog/CatalogProcessorOptions.cs @@ -1,36 +1,33 @@ -using System; +namespace BaGet.Protocol.Catalog; -namespace BaGet.Protocol.Catalog +/// +/// The options to configure . +/// Based off: https://github.com/NuGet/NuGet.Services.Metadata/blob/3a468fe534a03dcced897eb5992209fdd3c4b6c9/src/NuGet.Protocol.Catalog/CatalogProcessorSettings.cs +/// +public class CatalogProcessorOptions { /// - /// The options to configure . - /// Based off: https://github.com/NuGet/NuGet.Services.Metadata/blob/3a468fe534a03dcced897eb5992209fdd3c4b6c9/src/NuGet.Protocol.Catalog/CatalogProcessorSettings.cs + /// The minimum commit timestamp to use when no cursor value has been saved. /// - public class CatalogProcessorOptions - { - /// - /// The minimum commit timestamp to use when no cursor value has been saved. - /// - public DateTimeOffset? DefaultMinCommitTimestamp { get; set; } + public DateTimeOffset? DefaultMinCommitTimestamp { get; set; } - /// - /// The absolute minimum (exclusive) commit timestamp to process in the catalog. - /// Use this to filter out catalog items that are "too old". - /// Set this to to process all catalog items. - /// - public DateTimeOffset MinCommitTimestamp { get; set; } + /// + /// The absolute minimum (exclusive) commit timestamp to process in the catalog. + /// Use this to filter out catalog items that are "too old". + /// Set this to to process all catalog items. + /// + public DateTimeOffset MinCommitTimestamp { get; set; } - /// - /// The absolute maximum (inclusive) commit timestamp to process in the catalog. - /// Use this to filter out catalog items that are "too new". - /// Set this to to process all catalog items. - /// - public DateTimeOffset MaxCommitTimestamp { get; set; } + /// + /// The absolute maximum (inclusive) commit timestamp to process in the catalog. + /// Use this to filter out catalog items that are "too new". + /// Set this to to process all catalog items. + /// + public DateTimeOffset MaxCommitTimestamp { get; set; } - /// - /// If multiple catalog leaves are found in a page concerning the same package ID and version, only the latest - /// is processed. - /// - public bool ExcludeRedundantLeaves { get; set; } - } -} + /// + /// If multiple catalog leaves are found in a page concerning the same package ID and version, only the latest + /// is processed. + /// + public bool ExcludeRedundantLeaves { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Catalog/FileCursor.cs b/src/BaGet.Protocol/Catalog/FileCursor.cs index 9be09c2c..e8f6aa05 100644 --- a/src/BaGet.Protocol/Catalog/FileCursor.cs +++ b/src/BaGet.Protocol/Catalog/FileCursor.cs @@ -1,59 +1,54 @@ -using System; -using System.IO; using System.Text.Json; using System.Text.Json.Serialization; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; -namespace BaGet.Protocol.Catalog +namespace BaGet.Protocol.Catalog; + +/// +/// A cursor implementation which stores the cursor in local file. The cursor value is written to the file as +/// a JSON object. +/// Based off: https://github.com/NuGet/NuGet.Services.Metadata/blob/3a468fe534a03dcced897eb5992209fdd3c4b6c9/src/NuGet.Protocol.Catalog/FileCursor.cs +/// +public class FileCursor : ICursor { - /// - /// A cursor implementation which stores the cursor in local file. The cursor value is written to the file as - /// a JSON object. - /// Based off: https://github.com/NuGet/NuGet.Services.Metadata/blob/3a468fe534a03dcced897eb5992209fdd3c4b6c9/src/NuGet.Protocol.Catalog/FileCursor.cs - /// - public class FileCursor : ICursor - { - private readonly string _path; - private readonly ILogger _logger; + private readonly string _path; + private readonly ILogger _logger; - public FileCursor(string path, ILogger logger) - { - _path = path ?? throw new ArgumentNullException(nameof(path)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + public FileCursor(string path, ILogger logger) + { + _path = path ?? throw new ArgumentNullException(nameof(path)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public async Task GetAsync(CancellationToken cancellationToken) + public async Task GetAsync(CancellationToken cancellationToken) + { + try { - try - { - using (var file = File.OpenRead(_path)) - { - var data = await JsonSerializer.DeserializeAsync(file, options: null, cancellationToken); - _logger.LogDebug("Read cursor value {cursor:O} from {path}.", data.Value, _path); - return data.Value; - } - } - catch (Exception e) when (e is FileNotFoundException || e is JsonException) + using (var file = File.OpenRead(_path)) { - return null; + var data = await JsonSerializer.DeserializeAsync(file, options: null, cancellationToken); + _logger.LogDebug("Read cursor value {cursor:O} from {path}.", data.Value, _path); + return data.Value; } } - - public Task SetAsync(DateTimeOffset value, CancellationToken cancellationToken) + catch (Exception e) when (e is FileNotFoundException || e is JsonException) { - var data = new Data { Value = value }; - var jsonString = JsonSerializer.Serialize(data); - File.WriteAllText(_path, jsonString); - _logger.LogDebug("Wrote cursor value {cursor:O} to {path}.", data.Value, _path); - return Task.CompletedTask; + return null; } + } - private class Data - { - [JsonPropertyName("value")] - public DateTimeOffset Value { get; set; } - } + public Task SetAsync(DateTimeOffset value, CancellationToken cancellationToken) + { + var data = new Data { Value = value }; + var jsonString = JsonSerializer.Serialize(data); + File.WriteAllText(_path, jsonString); + _logger.LogDebug("Wrote cursor value {cursor:O} to {path}.", data.Value, _path); + return Task.CompletedTask; + } + + private class Data + { + [JsonPropertyName("value")] + public DateTimeOffset Value { get; set; } } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Catalog/ICatalogClient.cs b/src/BaGet.Protocol/Catalog/ICatalogClient.cs index 65dbb460..25298941 100644 --- a/src/BaGet.Protocol/Catalog/ICatalogClient.cs +++ b/src/BaGet.Protocol/Catalog/ICatalogClient.cs @@ -1,56 +1,53 @@ -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +/// +/// The Catalog client, used to discover package events. +/// You can use this resource to query for all published packages. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/catalog-resource +/// +public interface ICatalogClient { /// - /// The Catalog client, used to discover package events. - /// You can use this resource to query for all published packages. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/catalog-resource + /// Get the entry point for the catalog resource. + /// See: https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-index /// - public interface ICatalogClient - { - /// - /// Get the entry point for the catalog resource. - /// See: https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-index - /// - /// A token to cancel the task. - /// The catalog index. - Task GetIndexAsync(CancellationToken cancellationToken = default); + /// A token to cancel the task. + /// The catalog index. + Task GetIndexAsync(CancellationToken cancellationToken = default); - /// - /// Get a single catalog page, used to discover catalog leafs. - /// See: https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-page - /// - /// The URL of the page, from the . - /// A token to cancel the task. - /// A catalog page. - Task GetPageAsync( - string pageUrl, - CancellationToken cancellationToken = default); + /// + /// Get a single catalog page, used to discover catalog leafs. + /// See: https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-page + /// + /// The URL of the page, from the . + /// A token to cancel the task. + /// A catalog page. + Task GetPageAsync( + string pageUrl, + CancellationToken cancellationToken = default); - /// - /// Get a single catalog leaf, representing a package deletion event. - /// See: https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-leaf - /// - /// The URL of the leaf, from a . - /// A token to cancel the task. - /// A catalog leaf. - Task GetPackageDeleteLeafAsync( - string leafUrl, - CancellationToken cancellationToken = default); + /// + /// Get a single catalog leaf, representing a package deletion event. + /// See: https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-leaf + /// + /// The URL of the leaf, from a . + /// A token to cancel the task. + /// A catalog leaf. + Task GetPackageDeleteLeafAsync( + string leafUrl, + CancellationToken cancellationToken = default); - /// - /// Get a single catalog leaf, representing a package creation or update event. - /// See: https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-leaf - /// - /// The URL of the leaf, from a . - /// A token to cancel the task. - /// A catalog leaf. - Task GetPackageDetailsLeafAsync( - string leafUrl, - CancellationToken cancellationToken = default); - } -} + /// + /// Get a single catalog leaf, representing a package creation or update event. + /// See: https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-leaf + /// + /// The URL of the leaf, from a . + /// A token to cancel the task. + /// A catalog leaf. + Task GetPackageDetailsLeafAsync( + string leafUrl, + CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Catalog/ICatalogLeafProcessor.cs b/src/BaGet.Protocol/Catalog/ICatalogLeafProcessor.cs index c45641a7..856569b8 100644 --- a/src/BaGet.Protocol/Catalog/ICatalogLeafProcessor.cs +++ b/src/BaGet.Protocol/Catalog/ICatalogLeafProcessor.cs @@ -1,38 +1,35 @@ -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Protocol.Catalog +namespace BaGet.Protocol.Catalog; + +/// +/// An interface which allows custom processing of catalog leaves. This interface should be implemented when the +/// catalog leaf documents need to be downloaded and processed in chronological order. +/// Based off: https://github.com/NuGet/NuGet.Services.Metadata/blob/master/src/NuGet.Protocol.Catalog/ICatalogLeafProcessor.cs +/// +public interface ICatalogLeafProcessor { /// - /// An interface which allows custom processing of catalog leaves. This interface should be implemented when the - /// catalog leaf documents need to be downloaded and processed in chronological order. - /// Based off: https://github.com/NuGet/NuGet.Services.Metadata/blob/master/src/NuGet.Protocol.Catalog/ICatalogLeafProcessor.cs + /// Process a catalog leaf containing package details. This method should return false or throw an exception + /// if the catalog leaf cannot be processed. In this case, the will stop + /// processing items. Note that the same package ID/version combination can be passed to this multiple times, + /// for example due to an edit in the package metadata or due to a transient error and retry on the part of the + /// . /// - public interface ICatalogLeafProcessor - { - /// - /// Process a catalog leaf containing package details. This method should return false or throw an exception - /// if the catalog leaf cannot be processed. In this case, the will stop - /// processing items. Note that the same package ID/version combination can be passed to this multiple times, - /// for example due to an edit in the package metadata or due to a transient error and retry on the part of the - /// . - /// - /// The leaf document. - /// A token to cancel the task. - /// True, if the leaf was successfully processed. False, otherwise. - Task ProcessPackageDetailsAsync(PackageDetailsCatalogLeaf leaf, CancellationToken cancellationToken = default); + /// The leaf document. + /// A token to cancel the task. + /// True, if the leaf was successfully processed. False, otherwise. + Task ProcessPackageDetailsAsync(PackageDetailsCatalogLeaf leaf, CancellationToken cancellationToken = default); - /// - /// Process a catalog leaf containing a package delete. This method should return false or throw an exception - /// if the catalog leaf cannot be processed. In this case, the will stop - /// processing items. Note that the same package ID/version combination can be passed to this multiple times, - /// for example due to a package being deleted again due to a transient error and retry on the part of the - /// . - /// - /// The leaf document. - /// A token to cancel the task. - /// True, if the leaf was successfully processed. False, otherwise. - Task ProcessPackageDeleteAsync(PackageDeleteCatalogLeaf leaf, CancellationToken cancellationToken = default); - } -} + /// + /// Process a catalog leaf containing a package delete. This method should return false or throw an exception + /// if the catalog leaf cannot be processed. In this case, the will stop + /// processing items. Note that the same package ID/version combination can be passed to this multiple times, + /// for example due to a package being deleted again due to a transient error and retry on the part of the + /// . + /// + /// The leaf document. + /// A token to cancel the task. + /// True, if the leaf was successfully processed. False, otherwise. + Task ProcessPackageDeleteAsync(PackageDeleteCatalogLeaf leaf, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Catalog/ICursor.cs b/src/BaGet.Protocol/Catalog/ICursor.cs index a564f8c5..2e774e74 100644 --- a/src/BaGet.Protocol/Catalog/ICursor.cs +++ b/src/BaGet.Protocol/Catalog/ICursor.cs @@ -1,30 +1,25 @@ -using System; -using System.Threading; -using System.Threading.Tasks; +namespace BaGet.Protocol.Catalog; -namespace BaGet.Protocol.Catalog +/// +/// The NuGet Catalog resource is an append-only data structure indexed by time. +/// The tracks up to what point in the catalog has been successfully +/// processed. The value is a catalog commit timestamp. +/// See: https://docs.microsoft.com/en-us/nuget/api/catalog-resource#cursor +/// Based off: https://github.com/NuGet/NuGet.Services.Metadata/blob/3a468fe534a03dcced897eb5992209fdd3c4b6c9/src/NuGet.Protocol.Catalog/ICursor.cs +/// +public interface ICursor { /// - /// The NuGet Catalog resource is an append-only data structure indexed by time. - /// The tracks up to what point in the catalog has been successfully - /// processed. The value is a catalog commit timestamp. - /// See: https://docs.microsoft.com/en-us/nuget/api/catalog-resource#cursor - /// Based off: https://github.com/NuGet/NuGet.Services.Metadata/blob/3a468fe534a03dcced897eb5992209fdd3c4b6c9/src/NuGet.Protocol.Catalog/ICursor.cs + /// Get the value of the cursor. /// - public interface ICursor - { - /// - /// Get the value of the cursor. - /// - /// A token to cancel the task. - /// The cursor value. Null if the cursor has no value yet. - Task GetAsync(CancellationToken cancellationToken = default); + /// A token to cancel the task. + /// The cursor value. Null if the cursor has no value yet. + Task GetAsync(CancellationToken cancellationToken = default); - /// - /// Set the value of the cursor. - /// - /// The new cursor value. - /// A token to cancel the task. - Task SetAsync(DateTimeOffset value, CancellationToken cancellationToken = default); - } -} + /// + /// Set the value of the cursor. + /// + /// The new cursor value. + /// A token to cancel the task. + Task SetAsync(DateTimeOffset value, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Catalog/NullCatalogClient.cs b/src/BaGet.Protocol/Catalog/NullCatalogClient.cs index b77dc15b..9987776f 100644 --- a/src/BaGet.Protocol/Catalog/NullCatalogClient.cs +++ b/src/BaGet.Protocol/Catalog/NullCatalogClient.cs @@ -1,36 +1,31 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Protocol.Internal +namespace BaGet.Protocol.Internal; + +public class NullCatalogClient : ICatalogClient { - public class NullCatalogClient : ICatalogClient + public Task GetIndexAsync(CancellationToken cancellationToken = default) { - public Task GetIndexAsync(CancellationToken cancellationToken = default) + return Task.FromResult(new CatalogIndex { - return Task.FromResult(new CatalogIndex - { - CommitTimestamp = DateTimeOffset.MinValue, - Count = 0, - Items = new List() - }); - } + CommitTimestamp = DateTimeOffset.MinValue, + Count = 0, + Items = new List() + }); + } - public Task GetPageAsync(string pageUrl, CancellationToken cancellationToken = default) - { - throw new NotSupportedException($"{nameof(NullCatalogClient)} does not support loading catalog pages."); - } + public Task GetPageAsync(string pageUrl, CancellationToken cancellationToken = default) + { + throw new NotSupportedException($"{nameof(NullCatalogClient)} does not support loading catalog pages."); + } - public Task GetPackageDeleteLeafAsync(string leafUrl, CancellationToken cancellationToken = default) - { - throw new NotSupportedException($"{nameof(NullCatalogClient)} does not support loading catalog leaves."); - } + public Task GetPackageDeleteLeafAsync(string leafUrl, CancellationToken cancellationToken = default) + { + throw new NotSupportedException($"{nameof(NullCatalogClient)} does not support loading catalog leaves."); + } - public Task GetPackageDetailsLeafAsync(string leafUrl, CancellationToken cancellationToken = default) - { - throw new NotSupportedException($"{nameof(NullCatalogClient)} does not support loading catalog leaves."); - } + public Task GetPackageDetailsLeafAsync(string leafUrl, CancellationToken cancellationToken = default) + { + throw new NotSupportedException($"{nameof(NullCatalogClient)} does not support loading catalog leaves."); } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Catalog/NullCursor.cs b/src/BaGet.Protocol/Catalog/NullCursor.cs index 8af2f2f5..f85c12e3 100644 --- a/src/BaGet.Protocol/Catalog/NullCursor.cs +++ b/src/BaGet.Protocol/Catalog/NullCursor.cs @@ -1,24 +1,19 @@ -using System; -using System.Threading; -using System.Threading.Tasks; +namespace BaGet.Protocol.Catalog; -namespace BaGet.Protocol.Catalog +/// +/// A cursor that does not persist any state. Use this with a +/// to process all leafs each time +/// is called. +/// +public class NullCursor : ICursor { - /// - /// A cursor that does not persist any state. Use this with a - /// to process all leafs each time - /// is called. - /// - public class NullCursor : ICursor + public Task GetAsync(CancellationToken cancellationToken = default) { - public Task GetAsync(CancellationToken cancellationToken = default) - { - return Task.FromResult(null); - } + return Task.FromResult(null); + } - public Task SetAsync(DateTimeOffset value, CancellationToken cancellationToken = default) - { - return Task.CompletedTask; - } + public Task SetAsync(DateTimeOffset value, CancellationToken cancellationToken = default) + { + return Task.CompletedTask; } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Catalog/RawCatalogClient.cs b/src/BaGet.Protocol/Catalog/RawCatalogClient.cs index d773e365..b730fa39 100644 --- a/src/BaGet.Protocol/Catalog/RawCatalogClient.cs +++ b/src/BaGet.Protocol/Catalog/RawCatalogClient.cs @@ -1,64 +1,58 @@ -using System; -using System.Linq; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Protocol.Internal +namespace BaGet.Protocol.Internal; + +public class RawCatalogClient : ICatalogClient { - public class RawCatalogClient : ICatalogClient + private readonly HttpClient _httpClient; + private readonly string _catalogUrl; + + public RawCatalogClient(HttpClient httpClient, string catalogUrl) { - private readonly HttpClient _httpClient; - private readonly string _catalogUrl; + _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); + _catalogUrl = catalogUrl ?? throw new ArgumentNullException(nameof(catalogUrl)); + } - public RawCatalogClient(HttpClient httpClient, string catalogUrl) - { - _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); - _catalogUrl = catalogUrl ?? throw new ArgumentNullException(nameof(catalogUrl)); - } + public async Task GetIndexAsync(CancellationToken cancellationToken = default) + { + return await _httpClient.GetFromJsonAsync(_catalogUrl, cancellationToken); + } - public async Task GetIndexAsync(CancellationToken cancellationToken = default) - { - return await _httpClient.GetFromJsonAsync(_catalogUrl, cancellationToken); - } + public async Task GetPageAsync(string pageUrl, CancellationToken cancellationToken = default) + { + return await _httpClient.GetFromJsonAsync(pageUrl, cancellationToken); + } - public async Task GetPageAsync(string pageUrl, CancellationToken cancellationToken = default) - { - return await _httpClient.GetFromJsonAsync(pageUrl, cancellationToken); - } + public async Task GetPackageDeleteLeafAsync(string leafUrl, CancellationToken cancellationToken = default) + { + return await GetAndValidateLeafAsync( + "PackageDelete", + leafUrl, + cancellationToken); + } - public async Task GetPackageDeleteLeafAsync(string leafUrl, CancellationToken cancellationToken = default) - { - return await GetAndValidateLeafAsync( - "PackageDelete", - leafUrl, - cancellationToken); - } + public async Task GetPackageDetailsLeafAsync(string leafUrl, CancellationToken cancellationToken = default) + { + return await GetAndValidateLeafAsync( + "PackageDetails", + leafUrl, + cancellationToken); + } - public async Task GetPackageDetailsLeafAsync(string leafUrl, CancellationToken cancellationToken = default) - { - return await GetAndValidateLeafAsync( - "PackageDetails", - leafUrl, - cancellationToken); - } + private async Task GetAndValidateLeafAsync( + string leafType, + string leafUrl, + CancellationToken cancellationToken) where TCatalogLeaf : CatalogLeaf + { + var leaf = await _httpClient.GetFromJsonAsync(leafUrl, cancellationToken); - private async Task GetAndValidateLeafAsync( - string leafType, - string leafUrl, - CancellationToken cancellationToken) where TCatalogLeaf : CatalogLeaf + if (leaf.Type.FirstOrDefault() != leafType) { - var leaf = await _httpClient.GetFromJsonAsync(leafUrl, cancellationToken); - - if (leaf.Type.FirstOrDefault() != leafType) - { - throw new ArgumentException( - $"The leaf type found in the document does not match the expected '{leafType}' type.", - nameof(leafType)); - } - - return leaf; + throw new ArgumentException( + $"The leaf type found in the document does not match the expected '{leafType}' type.", + nameof(leafType)); } + + return leaf; } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Converters/PackageDependencyRangeJsonConverter.cs b/src/BaGet.Protocol/Converters/PackageDependencyRangeJsonConverter.cs index 9514cb5d..ac5a1568 100644 --- a/src/BaGet.Protocol/Converters/PackageDependencyRangeJsonConverter.cs +++ b/src/BaGet.Protocol/Converters/PackageDependencyRangeJsonConverter.cs @@ -1,57 +1,55 @@ -using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace BaGet.Protocol.Internal +namespace BaGet.Protocol.Internal; + +/// +/// This is an internal API that may be changed or removed without notice in any release. +/// +public class PackageDependencyRangeJsonConverter : JsonConverter { - /// - /// This is an internal API that may be changed or removed without notice in any release. - /// - public class PackageDependencyRangeJsonConverter : JsonConverter + public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + if (reader.TokenType == JsonTokenType.String) { - if (reader.TokenType == JsonTokenType.String) - { - return reader.GetString(); - } + return reader.GetString(); + } + + // There are some quirky packages with arrays of dependency version ranges. + // In this case, we take the first element. + // Example: https://api.nuget.org/v3/catalog0/data/2016.02.21.11.06.01/dingu.generic.repo.ef7.1.0.0-beta2.json + if (reader.TokenType != JsonTokenType.StartArray) + { + throw new JsonException(); + } + + reader.Read(); + if (reader.TokenType != JsonTokenType.String) + { + throw new JsonException(); + } + + var result = reader.GetString(); - // There are some quirky packages with arrays of dependency version ranges. - // In this case, we take the first element. - // Example: https://api.nuget.org/v3/catalog0/data/2016.02.21.11.06.01/dingu.generic.repo.ef7.1.0.0-beta2.json - if (reader.TokenType != JsonTokenType.StartArray) + // Ignore all other strings until we reach the end of the array. + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndArray) { - throw new JsonException(); + break; } - reader.Read(); if (reader.TokenType != JsonTokenType.String) { throw new JsonException(); } - - var result = reader.GetString(); - - // Ignore all other strings until we reach the end of the array. - while (reader.Read()) - { - if (reader.TokenType == JsonTokenType.EndArray) - { - break; - } - - if (reader.TokenType != JsonTokenType.String) - { - throw new JsonException(); - } - } - - return result; } - public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) - { - writer.WriteStringValue(value); - } + return result; + } + + public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) + { + writer.WriteStringValue(value); } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Converters/StringOrStringArrayJsonConverter.cs b/src/BaGet.Protocol/Converters/StringOrStringArrayJsonConverter.cs index d4ea42e0..56ddfe5a 100644 --- a/src/BaGet.Protocol/Converters/StringOrStringArrayJsonConverter.cs +++ b/src/BaGet.Protocol/Converters/StringOrStringArrayJsonConverter.cs @@ -1,60 +1,57 @@ -using System; -using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; -namespace BaGet.Protocol.Internal +namespace BaGet.Protocol.Internal; + +/// +/// This is an internal API that may be changed or removed without notice in any release. +/// +public class StringOrStringArrayJsonConverter : JsonConverter> { - /// - /// This is an internal API that may be changed or removed without notice in any release. - /// - public class StringOrStringArrayJsonConverter : JsonConverter> + public override IReadOnlyList Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - public override IReadOnlyList Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + // Try to read a single string first. + if (reader.TokenType == JsonTokenType.String) + { + return new List { reader.GetString() }; + } + + // Try to read an array of strings. + if (reader.TokenType != JsonTokenType.StartArray) + { + throw new JsonException(); + } + + var result = new List(); + + while (reader.Read()) { - // Try to read a single string first. if (reader.TokenType == JsonTokenType.String) { - return new List { reader.GetString() }; + result.Add(reader.GetString()); } - - // Try to read an array of strings. - if (reader.TokenType != JsonTokenType.StartArray) + else if (reader.TokenType == JsonTokenType.EndArray) { - throw new JsonException(); + return result; } - - var result = new List(); - - while (reader.Read()) + else { - if (reader.TokenType == JsonTokenType.String) - { - result.Add(reader.GetString()); - } - else if (reader.TokenType == JsonTokenType.EndArray) - { - return result; - } - else - { - break; - } + break; } - - throw new JsonException(); } - public override void Write(Utf8JsonWriter writer, IReadOnlyList values, JsonSerializerOptions options) - { - writer.WriteStartArray(); + throw new JsonException(); + } - foreach (var value in values) - { - writer.WriteStringValue(value); - } + public override void Write(Utf8JsonWriter writer, IReadOnlyList values, JsonSerializerOptions options) + { + writer.WriteStartArray(); - writer.WriteEndArray(); + foreach (var value in values) + { + writer.WriteStringValue(value); } + + writer.WriteEndArray(); } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Extensions/CatalogModelExtensions.cs b/src/BaGet.Protocol/Extensions/CatalogModelExtensions.cs index 66f18ffd..e4585af1 100644 --- a/src/BaGet.Protocol/Extensions/CatalogModelExtensions.cs +++ b/src/BaGet.Protocol/Extensions/CatalogModelExtensions.cs @@ -1,246 +1,242 @@ -using System; -using System.Collections.Generic; -using System.Linq; using BaGet.Protocol.Models; using NuGet.Frameworks; using NuGet.Versioning; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +/// +/// These are documented interpretations of values returned by the catalog API. +/// Based off: https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/ModelExtensions.cs +/// +public static class CatalogModelExtensions { /// - /// These are documented interpretations of values returned by the catalog API. - /// Based off: https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/ModelExtensions.cs + /// Gets the leaves that lie within the provided commit timestamp bounds. The result is sorted by commit + /// timestamp, then package ID, then package version (SemVer order). /// - public static class CatalogModelExtensions + /// + /// The exclusive lower time bound on . + /// The inclusive upper time bound on . + /// Only show the latest leaf concerning each package. + public static List GetLeavesInBounds( + this CatalogPage catalogPage, + DateTimeOffset minCommitTimestamp, + DateTimeOffset maxCommitTimestamp, + bool excludeRedundantLeaves) { - /// - /// Gets the leaves that lie within the provided commit timestamp bounds. The result is sorted by commit - /// timestamp, then package ID, then package version (SemVer order). - /// - /// - /// The exclusive lower time bound on . - /// The inclusive upper time bound on . - /// Only show the latest leaf concerning each package. - public static List GetLeavesInBounds( - this CatalogPage catalogPage, - DateTimeOffset minCommitTimestamp, - DateTimeOffset maxCommitTimestamp, - bool excludeRedundantLeaves) + var leaves = catalogPage + .Items + .Where(x => x.CommitTimestamp > minCommitTimestamp && x.CommitTimestamp <= maxCommitTimestamp) + .OrderBy(x => x.CommitTimestamp); + + if (excludeRedundantLeaves) { - var leaves = catalogPage - .Items - .Where(x => x.CommitTimestamp > minCommitTimestamp && x.CommitTimestamp <= maxCommitTimestamp) + leaves = leaves + .GroupBy(x => new + { + PackageId = x.PackageId.ToLowerInvariant(), + PackageVersion = x.ParsePackageVersion() + }) + .Select(x => x.Last()) .OrderBy(x => x.CommitTimestamp); - - if (excludeRedundantLeaves) - { - leaves = leaves - .GroupBy(x => new - { - PackageId = x.PackageId.ToLowerInvariant(), - PackageVersion = x.ParsePackageVersion() - }) - .Select(x => x.Last()) - .OrderBy(x => x.CommitTimestamp); - } - - return leaves - .ThenBy(x => x.PackageId, StringComparer.OrdinalIgnoreCase) - .ThenBy(x => x.ParsePackageVersion()) - .ToList(); } - /// - /// Gets the pages that may have catalog leaves within the provided commit timestamp bounds. The result is - /// sorted by commit timestamp. - /// - /// The catalog index to fetch pages from. - /// The exclusive lower time bound on . - /// The inclusive upper time bound on . - public static List GetPagesInBounds( - this CatalogIndex catalogIndex, - DateTimeOffset minCommitTimestamp, - DateTimeOffset maxCommitTimestamp) - { - return catalogIndex - .GetPagesInBoundsLazy(minCommitTimestamp, maxCommitTimestamp) - .ToList(); - } + return leaves + .ThenBy(x => x.PackageId, StringComparer.OrdinalIgnoreCase) + .ThenBy(x => x.ParsePackageVersion()) + .ToList(); + } - private static IEnumerable GetPagesInBoundsLazy( - this CatalogIndex catalogIndex, - DateTimeOffset minCommitTimestamp, - DateTimeOffset maxCommitTimestamp) + /// + /// Gets the pages that may have catalog leaves within the provided commit timestamp bounds. The result is + /// sorted by commit timestamp. + /// + /// The catalog index to fetch pages from. + /// The exclusive lower time bound on . + /// The inclusive upper time bound on . + public static List GetPagesInBounds( + this CatalogIndex catalogIndex, + DateTimeOffset minCommitTimestamp, + DateTimeOffset maxCommitTimestamp) + { + return catalogIndex + .GetPagesInBoundsLazy(minCommitTimestamp, maxCommitTimestamp) + .ToList(); + } + + private static IEnumerable GetPagesInBoundsLazy( + this CatalogIndex catalogIndex, + DateTimeOffset minCommitTimestamp, + DateTimeOffset maxCommitTimestamp) + { + // Filter out pages that fall entirely before the minimum commit timestamp and sort the remaining pages by + // commit timestamp. + var upperRange = catalogIndex + .Items + .Where(x => x.CommitTimestamp > minCommitTimestamp) + .OrderBy(x => x.CommitTimestamp); + + // Take pages from the sorted list until the commit timestamp goes past the maximum commit timestamp. This + // essentially LINQ's TakeWhile plus one more element. + foreach (var page in upperRange) { - // Filter out pages that fall entirely before the minimum commit timestamp and sort the remaining pages by - // commit timestamp. - var upperRange = catalogIndex - .Items - .Where(x => x.CommitTimestamp > minCommitTimestamp) - .OrderBy(x => x.CommitTimestamp); + yield return page; - // Take pages from the sorted list until the commit timestamp goes past the maximum commit timestamp. This - // essentially LINQ's TakeWhile plus one more element. - foreach (var page in upperRange) + if (page.CommitTimestamp > maxCommitTimestamp) { - yield return page; - - if (page.CommitTimestamp > maxCommitTimestamp) - { - break; - } + break; } } + } - /// - /// Parse the package version as a . - /// - /// The catalog leaf. - /// The package version. - public static NuGetVersion ParsePackageVersion(this ICatalogLeafItem leaf) + /// + /// Parse the package version as a . + /// + /// The catalog leaf. + /// The package version. + public static NuGetVersion ParsePackageVersion(this ICatalogLeafItem leaf) + { + return NuGetVersion.Parse(leaf.PackageVersion); + } + + /// + /// Parse the target framework as a . + /// + /// The package dependency group. + /// The framework. + public static NuGetFramework ParseTargetFramework(this DependencyGroupItem packageDependencyGroup) + { + if (string.IsNullOrEmpty(packageDependencyGroup.TargetFramework)) { - return NuGetVersion.Parse(leaf.PackageVersion); + return NuGetFramework.AnyFramework; } - /// - /// Parse the target framework as a . - /// - /// The package dependency group. - /// The framework. - public static NuGetFramework ParseTargetFramework(this DependencyGroupItem packageDependencyGroup) - { - if (string.IsNullOrEmpty(packageDependencyGroup.TargetFramework)) - { - return NuGetFramework.AnyFramework; - } + return NuGetFramework.Parse(packageDependencyGroup.TargetFramework); + } - return NuGetFramework.Parse(packageDependencyGroup.TargetFramework); + /// + /// Parse the version range as a . + /// + /// The package dependency. + /// The version range. + public static VersionRange ParseRange(this DependencyItem packageDependency) + { + // Server side treats invalid version ranges as empty strings. + // Source: https://github.com/NuGet/NuGet.Services.Metadata/blob/382c214c60993edfd7158bc6d223fafeebbc920c/src/Catalog/Helpers/NuGetVersionUtility.cs#L25-L34 + // Client side treats empty string version ranges as the "all" range. + // Source: https://github.com/NuGet/NuGet.Client/blob/849063018d8ee08625774a2dcd07ab84224dabb9/src/NuGet.Core/NuGet.Protocol/DependencyInfo/RegistrationUtility.cs#L20-L30 + // Example: https://api.nuget.org/v3/catalog0/data/2016.03.14.21.19.28/servicestack.extras.serilog.2.0.1.json + if (!VersionRange.TryParse(packageDependency.Range, out var parsed)) + { + return VersionRange.All; } - /// - /// Parse the version range as a . - /// - /// The package dependency. - /// The version range. - public static VersionRange ParseRange(this DependencyItem packageDependency) - { - // Server side treats invalid version ranges as empty strings. - // Source: https://github.com/NuGet/NuGet.Services.Metadata/blob/382c214c60993edfd7158bc6d223fafeebbc920c/src/Catalog/Helpers/NuGetVersionUtility.cs#L25-L34 - // Client side treats empty string version ranges as the "all" range. - // Source: https://github.com/NuGet/NuGet.Client/blob/849063018d8ee08625774a2dcd07ab84224dabb9/src/NuGet.Core/NuGet.Protocol/DependencyInfo/RegistrationUtility.cs#L20-L30 - // Example: https://api.nuget.org/v3/catalog0/data/2016.03.14.21.19.28/servicestack.extras.serilog.2.0.1.json - if (!VersionRange.TryParse(packageDependency.Range, out var parsed)) - { - return VersionRange.All; - } + return parsed; + } - return parsed; - } + /// + /// Determines if the provided catalog leaf is a package delete. + /// + /// The catalog leaf. + /// True if the catalog leaf represents a package delete. + public static bool IsPackageDelete(this CatalogLeafItem leaf) + { + return leaf.Type == "nuget:PackageDelete"; + } - /// - /// Determines if the provided catalog leaf is a package delete. - /// - /// The catalog leaf. - /// True if the catalog leaf represents a package delete. - public static bool IsPackageDelete(this CatalogLeafItem leaf) - { - return leaf.Type == "nuget:PackageDelete"; - } + /// + /// Determines if the provided catalog leaf is a package delete. + /// + /// The catalog leaf. + /// True if the catalog leaf represents a package delete. + public static bool IsPackageDelete(this CatalogLeaf leaf) + { + return leaf.Type.FirstOrDefault() == "PackageDelete"; + } - /// - /// Determines if the provided catalog leaf is a package delete. - /// - /// The catalog leaf. - /// True if the catalog leaf represents a package delete. - public static bool IsPackageDelete(this CatalogLeaf leaf) - { - return leaf.Type.FirstOrDefault() == "PackageDelete"; - } + /// + /// Determines if the provided catalog leaf is contains package details. + /// + /// The catalog leaf. + /// True if the catalog leaf contains package details. + public static bool IsPackageDetails(this CatalogLeafItem leaf) + { + return leaf.Type == "nuget:PackageDetails"; + } - /// - /// Determines if the provided catalog leaf is contains package details. - /// - /// The catalog leaf. - /// True if the catalog leaf contains package details. - public static bool IsPackageDetails(this CatalogLeafItem leaf) - { - return leaf.Type == "nuget:PackageDetails"; - } + /// + /// Determines if the provided catalog leaf is contains package details. + /// + /// The catalog leaf. + /// True if the catalog leaf contains package details. + public static bool IsPackageDetails(this CatalogLeaf leaf) + { + return leaf.Type.FirstOrDefault() == "PackageDetails"; + } - /// - /// Determines if the provided catalog leaf is contains package details. - /// - /// The catalog leaf. - /// True if the catalog leaf contains package details. - public static bool IsPackageDetails(this CatalogLeaf leaf) + /// + /// Determines if the provided package details leaf represents a listed package. + /// + /// The catalog leaf. + /// True if the package is listed. + public static bool IsListed(this PackageDetailsCatalogLeaf leaf) + { + if (leaf.Listed.HasValue) { - return leaf.Type.FirstOrDefault() == "PackageDetails"; + return leaf.Listed.Value; } - /// - /// Determines if the provided package details leaf represents a listed package. - /// - /// The catalog leaf. - /// True if the package is listed. - public static bool IsListed(this PackageDetailsCatalogLeaf leaf) - { - if (leaf.Listed.HasValue) - { - return leaf.Listed.Value; - } + // A published year of 1900 indicates that this package is unlisted, when the listed property itself is + // not present (legacy behavior). + // Example: https://api.nuget.org/v3/catalog0/data/2015.02.01.06.22.45/antixss.4.0.1.json + return leaf.Published.Year != 1900; + } - // A published year of 1900 indicates that this package is unlisted, when the listed property itself is - // not present (legacy behavior). - // Example: https://api.nuget.org/v3/catalog0/data/2015.02.01.06.22.45/antixss.4.0.1.json - return leaf.Published.Year != 1900; + /// + /// Determines if the provied package details leaf represents a SemVer 2.0.0 package. A package is considered + /// SemVer 2.0.0 if it's version is SemVer 2.0.0 or one of its dependency version ranges is SemVer 2.0.0. + /// + /// The catalog leaf. + /// True if the package is SemVer 2.0.0. + public static bool IsSemVer2(this PackageDetailsCatalogLeaf leaf) + { + var parsedPackageVersion = leaf.ParsePackageVersion(); + if (parsedPackageVersion.IsSemVer2) + { + return true; } - /// - /// Determines if the provied package details leaf represents a SemVer 2.0.0 package. A package is considered - /// SemVer 2.0.0 if it's version is SemVer 2.0.0 or one of its dependency version ranges is SemVer 2.0.0. - /// - /// The catalog leaf. - /// True if the package is SemVer 2.0.0. - public static bool IsSemVer2(this PackageDetailsCatalogLeaf leaf) + if (leaf.VerbatimVersion != null) { - var parsedPackageVersion = leaf.ParsePackageVersion(); - if (parsedPackageVersion.IsSemVer2) + var parsedVerbatimVersion = NuGetVersion.Parse(leaf.VerbatimVersion); + if (parsedVerbatimVersion.IsSemVer2) { return true; } + } - if (leaf.VerbatimVersion != null) + if (leaf.DependencyGroups != null) + { + foreach (var dependencyGroup in leaf.DependencyGroups) { - var parsedVerbatimVersion = NuGetVersion.Parse(leaf.VerbatimVersion); - if (parsedVerbatimVersion.IsSemVer2) + // Example: https://api.nuget.org/v3/catalog0/data/2018.10.28.07.42.42/mvcsitemapprovider.3.3.0-pre1.json + if (dependencyGroup.Dependencies == null) { - return true; + continue; } - } - if (leaf.DependencyGroups != null) - { - foreach (var dependencyGroup in leaf.DependencyGroups) + foreach (var dependency in dependencyGroup.Dependencies) { - // Example: https://api.nuget.org/v3/catalog0/data/2018.10.28.07.42.42/mvcsitemapprovider.3.3.0-pre1.json - if (dependencyGroup.Dependencies == null) + var versionRange = dependency.ParseRange(); + if ((versionRange.MaxVersion != null && versionRange.MaxVersion.IsSemVer2) + || (versionRange.MinVersion != null && versionRange.MinVersion.IsSemVer2)) { - continue; - } - - foreach (var dependency in dependencyGroup.Dependencies) - { - var versionRange = dependency.ParseRange(); - if ((versionRange.MaxVersion != null && versionRange.MaxVersion.IsSemVer2) - || (versionRange.MinVersion != null && versionRange.MinVersion.IsSemVer2)) - { - return true; - } + return true; } } } - - return false; } + + return false; } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Extensions/HttpClientExtensions.cs b/src/BaGet.Protocol/Extensions/HttpClientExtensions.cs index 95db48fd..74c0d6de 100644 --- a/src/BaGet.Protocol/Extensions/HttpClientExtensions.cs +++ b/src/BaGet.Protocol/Extensions/HttpClientExtensions.cs @@ -1,73 +1,69 @@ using System.Net; -using System.Net.Http; using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +internal static class HttpClientExtensions { - internal static class HttpClientExtensions + /// + /// Deserialize JSON content. + /// + /// The JSON type to deserialize. + /// The HTTP client that will perform the request. + /// The request URI. + /// A token to cancel the task. + /// The deserialized JSON content + public static async Task GetFromJsonAsync( + this HttpClient httpClient, + string requestUri, + CancellationToken cancellationToken = default) { - /// - /// Deserialize JSON content. - /// - /// The JSON type to deserialize. - /// The HTTP client that will perform the request. - /// The request URI. - /// A token to cancel the task. - /// The deserialized JSON content - public static async Task GetFromJsonAsync( - this HttpClient httpClient, - string requestUri, - CancellationToken cancellationToken = default) + using (var response = await httpClient.GetAsync( + requestUri, + HttpCompletionOption.ResponseHeadersRead, + cancellationToken)) { - using (var response = await httpClient.GetAsync( - requestUri, - HttpCompletionOption.ResponseHeadersRead, - cancellationToken)) - { - // This is similar to System.Net.Http.Json's implementation, however, - // this does not validate that the response's content type indicates JSON content. - response.EnsureSuccessStatusCode(); + // This is similar to System.Net.Http.Json's implementation, however, + // this does not validate that the response's content type indicates JSON content. + response.EnsureSuccessStatusCode(); - using (var stream = await response.Content.ReadAsStreamAsync()) - { - return await JsonSerializer.DeserializeAsync(stream, cancellationToken: cancellationToken); - } + using (var stream = await response.Content.ReadAsStreamAsync()) + { + return await JsonSerializer.DeserializeAsync(stream, cancellationToken: cancellationToken); } } + } - /// - /// Deserialize JSON content. If the HTTP response status code is 404, - /// returns the default value. - /// - /// The JSON type to deserialize. - /// The HTTP client that will perform the request. - /// The request URI. - /// A token to cancel the task. - /// The JSON content, or, the default value if the HTTP response status code is 404. - public static async Task GetFromJsonOrDefaultAsync( - this HttpClient httpClient, - string requestUri, - CancellationToken cancellationToken = default) + /// + /// Deserialize JSON content. If the HTTP response status code is 404, + /// returns the default value. + /// + /// The JSON type to deserialize. + /// The HTTP client that will perform the request. + /// The request URI. + /// A token to cancel the task. + /// The JSON content, or, the default value if the HTTP response status code is 404. + public static async Task GetFromJsonOrDefaultAsync( + this HttpClient httpClient, + string requestUri, + CancellationToken cancellationToken = default) + { + using (var response = await httpClient.GetAsync( + requestUri, + HttpCompletionOption.ResponseHeadersRead, + cancellationToken)) { - using (var response = await httpClient.GetAsync( - requestUri, - HttpCompletionOption.ResponseHeadersRead, - cancellationToken)) + if (response.StatusCode == HttpStatusCode.NotFound) { - if (response.StatusCode == HttpStatusCode.NotFound) - { - return default; - } + return default; + } - response.EnsureSuccessStatusCode(); + response.EnsureSuccessStatusCode(); - using (var stream = await response.Content.ReadAsStreamAsync()) - { - return await JsonSerializer.DeserializeAsync(stream, cancellationToken: cancellationToken); - } + using (var stream = await response.Content.ReadAsStreamAsync()) + { + return await JsonSerializer.DeserializeAsync(stream, cancellationToken: cancellationToken); } } } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Extensions/NuGetClientFactoryExtensions.cs b/src/BaGet.Protocol/Extensions/NuGetClientFactoryExtensions.cs index 5a17073d..0efd3876 100644 --- a/src/BaGet.Protocol/Extensions/NuGetClientFactoryExtensions.cs +++ b/src/BaGet.Protocol/Extensions/NuGetClientFactoryExtensions.cs @@ -1,35 +1,34 @@ using BaGet.Protocol.Catalog; using Microsoft.Extensions.Logging; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +public static class NuGetClientFactoryExtensions { - public static class NuGetClientFactoryExtensions + /// + /// Create a new to discover and download catalog leafs. + /// Leafs are processed by the . + /// + /// The factory used to create NuGet clients. + /// Cursor to track succesfully processed leafs. Leafs before the cursor are skipped. + /// The leaf processor. + /// The options to configure catalog processing. + /// The logger used for telemetry. + /// The catalog processor. + public static CatalogProcessor CreateCatalogProcessor( + this NuGetClientFactory clientFactory, + ICursor cursor, + ICatalogLeafProcessor leafProcessor, + CatalogProcessorOptions options, + ILogger logger) { - /// - /// Create a new to discover and download catalog leafs. - /// Leafs are processed by the . - /// - /// The factory used to create NuGet clients. - /// Cursor to track succesfully processed leafs. Leafs before the cursor are skipped. - /// The leaf processor. - /// The options to configure catalog processing. - /// The logger used for telemetry. - /// The catalog processor. - public static CatalogProcessor CreateCatalogProcessor( - this NuGetClientFactory clientFactory, - ICursor cursor, - ICatalogLeafProcessor leafProcessor, - CatalogProcessorOptions options, - ILogger logger) - { - var catalogClient = clientFactory.CreateCatalogClient(); + var catalogClient = clientFactory.CreateCatalogClient(); - return new CatalogProcessor( - cursor, - catalogClient, - leafProcessor, - options, - logger); - } + return new CatalogProcessor( + cursor, + catalogClient, + leafProcessor, + options, + logger); } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Extensions/PackageContentModelExtensions.cs b/src/BaGet.Protocol/Extensions/PackageContentModelExtensions.cs index 0a026cdf..8727b8c9 100644 --- a/src/BaGet.Protocol/Extensions/PackageContentModelExtensions.cs +++ b/src/BaGet.Protocol/Extensions/PackageContentModelExtensions.cs @@ -1,26 +1,23 @@ -using System.Collections.Generic; -using System.Linq; using BaGet.Protocol.Models; using NuGet.Versioning; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +/// +/// These are documented interpretations of values returned by the Package Content resource. +/// +public static class PackageContentModelExtensions { /// - /// These are documented interpretations of values returned by the Package Content resource. + /// Parse the package versions as s. /// - public static class PackageContentModelExtensions + /// The package versions response. + /// The package versions. + public static IReadOnlyList ParseVersions(this PackageVersionsResponse response) { - /// - /// Parse the package versions as s. - /// - /// The package versions response. - /// The package versions. - public static IReadOnlyList ParseVersions(this PackageVersionsResponse response) - { - return response - .Versions - .Select(NuGetVersion.Parse) - .ToList(); - } + return response + .Versions + .Select(NuGetVersion.Parse) + .ToList(); } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Extensions/PackageMetadataModelExtensions.cs b/src/BaGet.Protocol/Extensions/PackageMetadataModelExtensions.cs index 65947846..5fc909de 100644 --- a/src/BaGet.Protocol/Extensions/PackageMetadataModelExtensions.cs +++ b/src/BaGet.Protocol/Extensions/PackageMetadataModelExtensions.cs @@ -1,59 +1,58 @@ using BaGet.Protocol.Models; using NuGet.Versioning; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +/// +/// These are documented interpretations of values returned by the +/// Package Metadata resource API. +/// +public static class RegistrationModelExtensions { /// - /// These are documented interpretations of values returned by the - /// Package Metadata resource API. + /// Parse the package version as a . /// - public static class RegistrationModelExtensions + /// The package metadata. + /// The package version. + public static NuGetVersion ParseVersion(this PackageMetadata package) { - /// - /// Parse the package version as a . - /// - /// The package metadata. - /// The package version. - public static NuGetVersion ParseVersion(this PackageMetadata package) - { - return NuGetVersion.Parse(package.Version); - } + return NuGetVersion.Parse(package.Version); + } - /// - /// Determines if the provided package metadata represents a listed package. - /// - /// The package metadata. - /// True if the package is listed. - public static bool IsListed(this PackageMetadata package) + /// + /// Determines if the provided package metadata represents a listed package. + /// + /// The package metadata. + /// True if the package is listed. + public static bool IsListed(this PackageMetadata package) + { + if (package.Listed.HasValue) { - if (package.Listed.HasValue) - { - return package.Listed.Value; - } - - // A published year of 1900 indicates that this package is unlisted, when the listed property itself is - // not present (legacy behavior). - return package.Published.Year != 1900; + return package.Listed.Value; } - /// - /// Parse the registration page's lower version as a . - /// - /// The registration page. - /// The page's lower version. - public static NuGetVersion ParseLower(this RegistrationIndexPage page) - { - return NuGetVersion.Parse(page.Lower); - } + // A published year of 1900 indicates that this package is unlisted, when the listed property itself is + // not present (legacy behavior). + return package.Published.Year != 1900; + } - /// - /// Parse the registration page's upper version as a . - /// - /// The registration page. - /// The page's upper version. - public static NuGetVersion ParseUpper(this RegistrationIndexPage page) - { - return NuGetVersion.Parse(page.Upper); - } + /// + /// Parse the registration page's lower version as a . + /// + /// The registration page. + /// The page's lower version. + public static NuGetVersion ParseLower(this RegistrationIndexPage page) + { + return NuGetVersion.Parse(page.Lower); + } + + /// + /// Parse the registration page's upper version as a . + /// + /// The registration page. + /// The page's upper version. + public static NuGetVersion ParseUpper(this RegistrationIndexPage page) + { + return NuGetVersion.Parse(page.Upper); } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Extensions/SearchModelExtensions.cs b/src/BaGet.Protocol/Extensions/SearchModelExtensions.cs index e849ea77..299afe9f 100644 --- a/src/BaGet.Protocol/Extensions/SearchModelExtensions.cs +++ b/src/BaGet.Protocol/Extensions/SearchModelExtensions.cs @@ -1,31 +1,30 @@ using BaGet.Protocol.Models; using NuGet.Versioning; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +/// +/// These are documented interpretations of values returned by the Search resource. +/// +public static class SearchModelExtensions { /// - /// These are documented interpretations of values returned by the Search resource. + /// Parse the search result's version as a . /// - public static class SearchModelExtensions + /// The search result. + /// The search result's version. + public static NuGetVersion ParseVersion(this SearchResult result) { - /// - /// Parse the search result's version as a . - /// - /// The search result. - /// The search result's version. - public static NuGetVersion ParseVersion(this SearchResult result) - { - return NuGetVersion.Parse(result.Version); - } + return NuGetVersion.Parse(result.Version); + } - /// - /// Parse the search result's version as a . - /// - /// The search result. - /// The search result's version. - public static NuGetVersion ParseVersion(this SearchResultVersion result) - { - return NuGetVersion.Parse(result.Version); - } + /// + /// Parse the search result's version as a . + /// + /// The search result. + /// The search result's version. + public static NuGetVersion ParseVersion(this SearchResultVersion result) + { + return NuGetVersion.Parse(result.Version); } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Extensions/ServiceIndexModelExtensions.cs b/src/BaGet.Protocol/Extensions/ServiceIndexModelExtensions.cs index 7d0afea2..14fc82e7 100644 --- a/src/BaGet.Protocol/Extensions/ServiceIndexModelExtensions.cs +++ b/src/BaGet.Protocol/Extensions/ServiceIndexModelExtensions.cs @@ -1,81 +1,78 @@ -using System; -using System.Linq; using BaGet.Protocol.Models; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +/// +/// These are documented interpretations of values returned by the Service Index resource. +/// +public static class ServiceIndexModelExtensions { - /// - /// These are documented interpretations of values returned by the Service Index resource. - /// - public static class ServiceIndexModelExtensions - { - // See: https://github.com/NuGet/NuGet.Client/blob/e08358296db5bfa6f7f32d6f4ec8de288f3b0388/src/NuGet.Core/NuGet.Protocol/ServiceTypes.cs - private static readonly string Version200 = "/2.0.0"; - private static readonly string Version300beta = "/3.0.0-beta"; - private static readonly string Version300 = "/3.0.0"; - private static readonly string Version340 = "/3.4.0"; - private static readonly string Version360 = "/3.6.0"; - private static readonly string Version470 = "/4.7.0"; - private static readonly string Version490 = "/4.9.0"; - private static readonly string Version500 = "/5.0.0"; - private static readonly string Version510 = "/5.1.0"; + // See: https://github.com/NuGet/NuGet.Client/blob/e08358296db5bfa6f7f32d6f4ec8de288f3b0388/src/NuGet.Core/NuGet.Protocol/ServiceTypes.cs + private static readonly string Version200 = "/2.0.0"; + private static readonly string Version300beta = "/3.0.0-beta"; + private static readonly string Version300 = "/3.0.0"; + private static readonly string Version340 = "/3.4.0"; + private static readonly string Version360 = "/3.6.0"; + private static readonly string Version470 = "/4.7.0"; + private static readonly string Version490 = "/4.9.0"; + private static readonly string Version500 = "/5.0.0"; + private static readonly string Version510 = "/5.1.0"; - private static readonly string[] Catalog = { "Catalog" + Version300 }; - private static readonly string[] SearchQueryService = { "SearchQueryService" + Version340, "SearchQueryService" + Version300beta, "SearchQueryService" }; - private static readonly string[] RegistrationsBaseUrl = { "RegistrationsBaseUrl" + Version360, "RegistrationsBaseUrl" + Version340, "RegistrationsBaseUrl" + Version300beta, "RegistrationsBaseUrl" }; - private static readonly string[] SearchAutocompleteService = { "SearchAutocompleteService", "SearchAutocompleteService" + Version300beta }; - private static readonly string[] ReportAbuse = { "ReportAbuseUriTemplate", "ReportAbuseUriTemplate" + Version300 }; - private static readonly string[] PackageDetailsUriTemplate = { "PackageDetailsUriTemplate" + Version510 }; - private static readonly string[] LegacyGallery = { "LegacyGallery" + Version200 }; - private static readonly string[] PackagePublish = { "PackagePublish" + Version200 }; - private static readonly string[] PackageBaseAddress = { "PackageBaseAddress" + Version300 }; - private static readonly string[] RepositorySignatures = { "RepositorySignatures" + Version500, "RepositorySignatures" + Version490, "RepositorySignatures" + Version470 }; - private static readonly string[] SymbolPackagePublish = { "SymbolPackagePublish" + Version490 }; + private static readonly string[] Catalog = { "Catalog" + Version300 }; + private static readonly string[] SearchQueryService = { "SearchQueryService" + Version340, "SearchQueryService" + Version300beta, "SearchQueryService" }; + private static readonly string[] RegistrationsBaseUrl = { "RegistrationsBaseUrl" + Version360, "RegistrationsBaseUrl" + Version340, "RegistrationsBaseUrl" + Version300beta, "RegistrationsBaseUrl" }; + private static readonly string[] SearchAutocompleteService = { "SearchAutocompleteService", "SearchAutocompleteService" + Version300beta }; + private static readonly string[] ReportAbuse = { "ReportAbuseUriTemplate", "ReportAbuseUriTemplate" + Version300 }; + private static readonly string[] PackageDetailsUriTemplate = { "PackageDetailsUriTemplate" + Version510 }; + private static readonly string[] LegacyGallery = { "LegacyGallery" + Version200 }; + private static readonly string[] PackagePublish = { "PackagePublish" + Version200 }; + private static readonly string[] PackageBaseAddress = { "PackageBaseAddress" + Version300 }; + private static readonly string[] RepositorySignatures = { "RepositorySignatures" + Version500, "RepositorySignatures" + Version490, "RepositorySignatures" + Version470 }; + private static readonly string[] SymbolPackagePublish = { "SymbolPackagePublish" + Version490 }; - public static string GetPackageContentResourceUrl(this ServiceIndexResponse serviceIndex) - { - return serviceIndex.GetRequiredResourceUrl(PackageBaseAddress, nameof(PackageBaseAddress)); - } + public static string GetPackageContentResourceUrl(this ServiceIndexResponse serviceIndex) + { + return serviceIndex.GetRequiredResourceUrl(PackageBaseAddress, nameof(PackageBaseAddress)); + } - public static string GetPackageMetadataResourceUrl(this ServiceIndexResponse serviceIndex) - { - return serviceIndex.GetRequiredResourceUrl(RegistrationsBaseUrl, nameof(RegistrationsBaseUrl)); - } + public static string GetPackageMetadataResourceUrl(this ServiceIndexResponse serviceIndex) + { + return serviceIndex.GetRequiredResourceUrl(RegistrationsBaseUrl, nameof(RegistrationsBaseUrl)); + } - public static string GetSearchQueryResourceUrl(this ServiceIndexResponse serviceIndex) - { - return serviceIndex.GetRequiredResourceUrl(SearchQueryService, nameof(SearchQueryService)); - } + public static string GetSearchQueryResourceUrl(this ServiceIndexResponse serviceIndex) + { + return serviceIndex.GetRequiredResourceUrl(SearchQueryService, nameof(SearchQueryService)); + } - public static string GetCatalogResourceUrl(this ServiceIndexResponse serviceIndex) - { - return serviceIndex.GetResourceUrl(Catalog); - } + public static string GetCatalogResourceUrl(this ServiceIndexResponse serviceIndex) + { + return serviceIndex.GetResourceUrl(Catalog); + } - public static string GetSearchAutocompleteResourceUrl(this ServiceIndexResponse serviceIndex) - { - return serviceIndex.GetResourceUrl(SearchAutocompleteService); - } + public static string GetSearchAutocompleteResourceUrl(this ServiceIndexResponse serviceIndex) + { + return serviceIndex.GetResourceUrl(SearchAutocompleteService); + } - public static string GetResourceUrl(this ServiceIndexResponse serviceIndex, string[] types) - { - var resource = types.SelectMany(t => serviceIndex.Resources.Where(r => r.Type == t)).FirstOrDefault(); + public static string GetResourceUrl(this ServiceIndexResponse serviceIndex, string[] types) + { + var resource = types.SelectMany(t => serviceIndex.Resources.Where(r => r.Type == t)).FirstOrDefault(); - return resource?.ResourceUrl.Trim('/'); - } + return resource?.ResourceUrl.Trim('/'); + } - public static string GetRequiredResourceUrl(this ServiceIndexResponse serviceIndex, string[] types, string resourceName) + public static string GetRequiredResourceUrl(this ServiceIndexResponse serviceIndex, string[] types, string resourceName) + { + // For more information on required resources, + // see: https://docs.microsoft.com/en-us/nuget/api/overview#resources-and-schema + var resourceUrl = serviceIndex.GetResourceUrl(types); + if (string.IsNullOrEmpty(resourceUrl)) { - // For more information on required resources, - // see: https://docs.microsoft.com/en-us/nuget/api/overview#resources-and-schema - var resourceUrl = serviceIndex.GetResourceUrl(types); - if (string.IsNullOrEmpty(resourceUrl)) - { - throw new InvalidOperationException( - $"The service index does not have a resource named '{resourceName}'"); - } - - return resourceUrl; + throw new InvalidOperationException( + $"The service index does not have a resource named '{resourceName}'"); } + + return resourceUrl; } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/AlternatePackage.cs b/src/BaGet.Protocol/Models/AlternatePackage.cs index a5e272ad..63dbc383 100644 --- a/src/BaGet.Protocol/Models/AlternatePackage.cs +++ b/src/BaGet.Protocol/Models/AlternatePackage.cs @@ -1,30 +1,29 @@ using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// +/// The alternate package that should be used instead of a deprecated package. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#package-deprecation +/// +public class AlternatePackage { - /// - /// The alternate package that should be used instead of a deprecated package. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#package-deprecation - /// - public class AlternatePackage - { - [JsonPropertyName("@id")] - public string Url { get; set; } + [JsonPropertyName("@id")] + public string Url { get; set; } - [JsonPropertyName("@type")] - public string Type { get; set; } + [JsonPropertyName("@type")] + public string Type { get; set; } - /// - /// The ID of the alternate package. - /// - [JsonPropertyName("id")] - public string Id { get; set; } + /// + /// The ID of the alternate package. + /// + [JsonPropertyName("id")] + public string Id { get; set; } - /// - /// The allowed version range, or * if any version is allowed. - /// - [JsonPropertyName("range")] - public string Range { get; set; } - } -} + /// + /// The allowed version range, or * if any version is allowed. + /// + [JsonPropertyName("range")] + public string Range { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/AutocompleteContext.cs b/src/BaGet.Protocol/Models/AutocompleteContext.cs index 96b711c2..5862759a 100644 --- a/src/BaGet.Protocol/Models/AutocompleteContext.cs +++ b/src/BaGet.Protocol/Models/AutocompleteContext.cs @@ -1,15 +1,14 @@ using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +public class AutocompleteContext { - public class AutocompleteContext + public static readonly AutocompleteContext Default = new AutocompleteContext { - public static readonly AutocompleteContext Default = new AutocompleteContext - { - Vocab = "http://schema.nuget.org/schema#" - }; + Vocab = "http://schema.nuget.org/schema#" + }; - [JsonPropertyName("@vocab")] - public string Vocab { get; set; } - } -} + [JsonPropertyName("@vocab")] + public string Vocab { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/AutocompleteResponse.cs b/src/BaGet.Protocol/Models/AutocompleteResponse.cs index 5522656f..e4f69026 100644 --- a/src/BaGet.Protocol/Models/AutocompleteResponse.cs +++ b/src/BaGet.Protocol/Models/AutocompleteResponse.cs @@ -1,28 +1,26 @@ -using System.Collections.Generic; using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// +/// The package ids that matched the autocomplete query. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource#search-for-package-ids +/// +public class AutocompleteResponse { + [JsonPropertyName("@context")] + public AutocompleteContext Context { get; set; } + /// - /// The package ids that matched the autocomplete query. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource#search-for-package-ids + /// The total number of matches, disregarding skip and take. /// - public class AutocompleteResponse - { - [JsonPropertyName("@context")] - public AutocompleteContext Context { get; set; } - - /// - /// The total number of matches, disregarding skip and take. - /// - [JsonPropertyName("totalHits")] - public long TotalHits { get; set; } + [JsonPropertyName("totalHits")] + public long TotalHits { get; set; } - /// - /// The package IDs matched by the autocomplete query. - /// - [JsonPropertyName("data")] - public IReadOnlyList Data { get; set; } - } -} + /// + /// The package IDs matched by the autocomplete query. + /// + [JsonPropertyName("data")] + public IReadOnlyList Data { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/CatalogIndex.cs b/src/BaGet.Protocol/Models/CatalogIndex.cs index beeea373..12e296e1 100644 --- a/src/BaGet.Protocol/Models/CatalogIndex.cs +++ b/src/BaGet.Protocol/Models/CatalogIndex.cs @@ -1,35 +1,31 @@ -using System; -using System.Collections.Generic; using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models -{ - // This class is based off: https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/CatalogIndex.cs +namespace BaGet.Protocol.Models; +// This class is based off: https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/CatalogIndex.cs +/// +/// The catalog index is the entry point for the catalog resource. +/// Use this to discover catalog pages, which in turn can be used to discover catalog leafs. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-index +/// +public class CatalogIndex +{ /// - /// The catalog index is the entry point for the catalog resource. - /// Use this to discover catalog pages, which in turn can be used to discover catalog leafs. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-index + /// A timestamp of the most recent commit. /// - public class CatalogIndex - { - /// - /// A timestamp of the most recent commit. - /// - [JsonPropertyName("commitTimeStamp")] - public DateTimeOffset CommitTimestamp { get; set; } + [JsonPropertyName("commitTimeStamp")] + public DateTimeOffset CommitTimestamp { get; set; } - /// - /// The number of catalog pages in the catalog index. - /// - [JsonPropertyName("count")] - public int Count { get; set; } + /// + /// The number of catalog pages in the catalog index. + /// + [JsonPropertyName("count")] + public int Count { get; set; } - /// - /// The items used to discover s. - /// - [JsonPropertyName("items")] - public List Items { get; set; } - } -} + /// + /// The items used to discover s. + /// + [JsonPropertyName("items")] + public List Items { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/CatalogLeaf.cs b/src/BaGet.Protocol/Models/CatalogLeaf.cs index f41ce62b..dd0ae8a4 100644 --- a/src/BaGet.Protocol/Models/CatalogLeaf.cs +++ b/src/BaGet.Protocol/Models/CatalogLeaf.cs @@ -1,59 +1,55 @@ -using System; -using System.Collections.Generic; using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; +// This classed is based off: https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/CatalogLeaf.cs + +/// +/// A catalog leaf. Represents a single package event. +/// Leafs can be discovered from a . +/// +/// See https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-leaf +/// +public class CatalogLeaf : ICatalogLeafItem { - // This classed is based off: https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/CatalogLeaf.cs + /// + /// The URL to the current catalog leaf. + /// + [JsonPropertyName("@id")] + public string CatalogLeafUrl { get; set; } + + /// + /// The type of the current catalog leaf. + /// + [JsonPropertyName("@type")] + public IReadOnlyList Type { get; set; } + + /// + /// The catalog commit ID associated with this catalog item. + /// + [JsonPropertyName("catalog:commitId")] + public string CommitId { get; set; } + + /// + /// The commit timestamp of this catalog item. + /// + [JsonPropertyName("catalog:commitTimeStamp")] + public DateTimeOffset CommitTimestamp { get; set; } + + /// + /// The package ID of the catalog item. + /// + [JsonPropertyName("id")] + public string PackageId { get; set; } + + /// + /// The published date of the package catalog item. + /// + [JsonPropertyName("published")] + public DateTimeOffset Published { get; set; } /// - /// A catalog leaf. Represents a single package event. - /// Leafs can be discovered from a . - /// - /// See https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-leaf + /// The package version of the catalog item. /// - public class CatalogLeaf : ICatalogLeafItem - { - /// - /// The URL to the current catalog leaf. - /// - [JsonPropertyName("@id")] - public string CatalogLeafUrl { get; set; } - - /// - /// The type of the current catalog leaf. - /// - [JsonPropertyName("@type")] - public IReadOnlyList Type { get; set; } - - /// - /// The catalog commit ID associated with this catalog item. - /// - [JsonPropertyName("catalog:commitId")] - public string CommitId { get; set; } - - /// - /// The commit timestamp of this catalog item. - /// - [JsonPropertyName("catalog:commitTimeStamp")] - public DateTimeOffset CommitTimestamp { get; set; } - - /// - /// The package ID of the catalog item. - /// - [JsonPropertyName("id")] - public string PackageId { get; set; } - - /// - /// The published date of the package catalog item. - /// - [JsonPropertyName("published")] - public DateTimeOffset Published { get; set; } - - /// - /// The package version of the catalog item. - /// - [JsonPropertyName("version")] - public string PackageVersion { get; set; } - } -} + [JsonPropertyName("version")] + public string PackageVersion { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/CatalogLeafItem.cs b/src/BaGet.Protocol/Models/CatalogLeafItem.cs index 36dcb940..315c8e60 100644 --- a/src/BaGet.Protocol/Models/CatalogLeafItem.cs +++ b/src/BaGet.Protocol/Models/CatalogLeafItem.cs @@ -1,45 +1,42 @@ -using System; using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models -{ - // This class is based off https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/CatalogLeafItem.cs +namespace BaGet.Protocol.Models; +// This class is based off https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/CatalogLeafItem.cs +/// +/// An item in a that references a . +/// +/// See https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-item-object-in-a-page +/// +public class CatalogLeafItem : ICatalogLeafItem +{ /// - /// An item in a that references a . - /// - /// See https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-item-object-in-a-page + /// The URL to the current catalog leaf. /// - public class CatalogLeafItem : ICatalogLeafItem - { - /// - /// The URL to the current catalog leaf. - /// - [JsonPropertyName("@id")] - public string CatalogLeafUrl { get; set; } + [JsonPropertyName("@id")] + public string CatalogLeafUrl { get; set; } - /// - /// The type of the current catalog leaf. - /// - [JsonPropertyName("@type")] - public string Type { get; set; } + /// + /// The type of the current catalog leaf. + /// + [JsonPropertyName("@type")] + public string Type { get; set; } - /// - /// The commit timestamp of this catalog item. - /// - [JsonPropertyName("commitTimeStamp")] - public DateTimeOffset CommitTimestamp { get; set; } + /// + /// The commit timestamp of this catalog item. + /// + [JsonPropertyName("commitTimeStamp")] + public DateTimeOffset CommitTimestamp { get; set; } - /// - /// The package ID of the catalog item. - /// - [JsonPropertyName("nuget:id")] - public string PackageId { get; set; } + /// + /// The package ID of the catalog item. + /// + [JsonPropertyName("nuget:id")] + public string PackageId { get; set; } - /// - /// The package version of the catalog item. - /// - [JsonPropertyName("nuget:version")] - public string PackageVersion { get; set; } - } -} + /// + /// The package version of the catalog item. + /// + [JsonPropertyName("nuget:version")] + public string PackageVersion { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/CatalogPage.cs b/src/BaGet.Protocol/Models/CatalogPage.cs index 65f9e6d3..9fc12719 100644 --- a/src/BaGet.Protocol/Models/CatalogPage.cs +++ b/src/BaGet.Protocol/Models/CatalogPage.cs @@ -1,41 +1,37 @@ -using System; -using System.Collections.Generic; using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models -{ - // This class is based off https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/CatalogPage.cs +namespace BaGet.Protocol.Models; +// This class is based off https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/CatalogPage.cs +/// +/// A catalog page, used to discover catalog leafs. +/// Pages can be discovered from a . +/// +/// See https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-page +/// +public class CatalogPage +{ /// - /// A catalog page, used to discover catalog leafs. - /// Pages can be discovered from a . - /// - /// See https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-page + /// A unique ID associated with the most recent commit in this page. /// - public class CatalogPage - { - /// - /// A unique ID associated with the most recent commit in this page. - /// - [JsonPropertyName("commitTimeStamp")] - public DateTimeOffset CommitTimestamp { get; set; } + [JsonPropertyName("commitTimeStamp")] + public DateTimeOffset CommitTimestamp { get; set; } - /// - /// The number of items in the page. - /// - [JsonPropertyName("count")] - public int Count { get; set; } + /// + /// The number of items in the page. + /// + [JsonPropertyName("count")] + public int Count { get; set; } - /// - /// The items used to discover s. - /// - [JsonPropertyName("items")] - public List Items { get; set; } + /// + /// The items used to discover s. + /// + [JsonPropertyName("items")] + public List Items { get; set; } - /// - /// The URL to the Catalog Index. - /// - [JsonPropertyName("parent")] - public string CatalogIndexUrl { get; set; } - } -} + /// + /// The URL to the Catalog Index. + /// + [JsonPropertyName("parent")] + public string CatalogIndexUrl { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/CatalogPageItem.cs b/src/BaGet.Protocol/Models/CatalogPageItem.cs index 0240d9e2..13783d93 100644 --- a/src/BaGet.Protocol/Models/CatalogPageItem.cs +++ b/src/BaGet.Protocol/Models/CatalogPageItem.cs @@ -1,33 +1,30 @@ -using System; using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models -{ - // This class is based off https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/CatalogPageItem.cs +namespace BaGet.Protocol.Models; +// This class is based off https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/CatalogPageItem.cs +/// +/// An item in the that references a . +/// +/// See https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-page-object-in-the-index +/// +public class CatalogPageItem +{ /// - /// An item in the that references a . - /// - /// See https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-page-object-in-the-index + /// The URL to this item's corresponding . /// - public class CatalogPageItem - { - /// - /// The URL to this item's corresponding . - /// - [JsonPropertyName("@id")] - public string CatalogPageUrl { get; set; } + [JsonPropertyName("@id")] + public string CatalogPageUrl { get; set; } - /// - /// A timestamp of the most recent commit in this page. - /// - [JsonPropertyName("commitTimeStamp")] - public DateTimeOffset CommitTimestamp { get; set; } + /// + /// A timestamp of the most recent commit in this page. + /// + [JsonPropertyName("commitTimeStamp")] + public DateTimeOffset CommitTimestamp { get; set; } - /// - /// The number of items in the page. - /// - [JsonPropertyName("count")] - public int Count { get; set; } - } -} + /// + /// The number of items in the page. + /// + [JsonPropertyName("count")] + public int Count { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/DependencyGroupItem.cs b/src/BaGet.Protocol/Models/DependencyGroupItem.cs index e9626c05..1f111515 100644 --- a/src/BaGet.Protocol/Models/DependencyGroupItem.cs +++ b/src/BaGet.Protocol/Models/DependencyGroupItem.cs @@ -1,25 +1,23 @@ -using System.Collections.Generic; using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// +/// The dependencies of the package for a specific target framework. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#package-dependency-group +/// +public class DependencyGroupItem { /// - /// The dependencies of the package for a specific target framework. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#package-dependency-group + /// The target framework that these dependencies are applicable to. /// - public class DependencyGroupItem - { - /// - /// The target framework that these dependencies are applicable to. - /// - [JsonPropertyName("targetFramework")] - public string TargetFramework { get; set; } + [JsonPropertyName("targetFramework")] + public string TargetFramework { get; set; } - /// - /// A list of dependencies. - /// - [JsonPropertyName("dependencies")] - public List Dependencies { get; set; } - } -} + /// + /// A list of dependencies. + /// + [JsonPropertyName("dependencies")] + public List Dependencies { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/DependencyItem.cs b/src/BaGet.Protocol/Models/DependencyItem.cs index 95af1301..555e84bc 100644 --- a/src/BaGet.Protocol/Models/DependencyItem.cs +++ b/src/BaGet.Protocol/Models/DependencyItem.cs @@ -1,26 +1,25 @@ using System.Text.Json.Serialization; using BaGet.Protocol.Internal; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// +/// Represents a package dependency. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#package-dependency +/// +public class DependencyItem { /// - /// Represents a package dependency. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#package-dependency + /// The ID of the package dependency. /// - public class DependencyItem - { - /// - /// The ID of the package dependency. - /// - [JsonPropertyName("id")] - public string Id { get; set; } + [JsonPropertyName("id")] + public string Id { get; set; } - /// - /// The allowed version range of the dependency. - /// - [JsonPropertyName("range")] - [JsonConverter(typeof(PackageDependencyRangeJsonConverter))] - public string Range { get; set; } - } -} + /// + /// The allowed version range of the dependency. + /// + [JsonPropertyName("range")] + [JsonConverter(typeof(PackageDependencyRangeJsonConverter))] + public string Range { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/ICatalogLeafItem.cs b/src/BaGet.Protocol/Models/ICatalogLeafItem.cs index 2cd59a37..6aa1cdc2 100644 --- a/src/BaGet.Protocol/Models/ICatalogLeafItem.cs +++ b/src/BaGet.Protocol/Models/ICatalogLeafItem.cs @@ -1,30 +1,26 @@ -using System; +namespace BaGet.Protocol.Models; +// This class is based off https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/ICatalogLeafItem.cs -namespace BaGet.Protocol.Models +/// +/// A catalog leaf. Represents a single package event. +/// Leafs can be discovered from a . +/// +/// See https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-leaf +/// +public interface ICatalogLeafItem { - // This class is based off https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/ICatalogLeafItem.cs - /// - /// A catalog leaf. Represents a single package event. - /// Leafs can be discovered from a . - /// - /// See https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-leaf + /// The commit timestamp of this catalog item. /// - public interface ICatalogLeafItem - { - /// - /// The commit timestamp of this catalog item. - /// - DateTimeOffset CommitTimestamp { get; } + DateTimeOffset CommitTimestamp { get; } - /// - /// The package ID of the catalog item. - /// - string PackageId { get; } + /// + /// The package ID of the catalog item. + /// + string PackageId { get; } - /// - /// The package version of the catalog item. - /// - string PackageVersion { get; } - } -} + /// + /// The package version of the catalog item. + /// + string PackageVersion { get; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/PackageDeleteCatalogLeaf.cs b/src/BaGet.Protocol/Models/PackageDeleteCatalogLeaf.cs index da53173a..90444cac 100644 --- a/src/BaGet.Protocol/Models/PackageDeleteCatalogLeaf.cs +++ b/src/BaGet.Protocol/Models/PackageDeleteCatalogLeaf.cs @@ -1,14 +1,12 @@ -namespace BaGet.Protocol.Models -{ - // This class is based off https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/PackageDeleteCatalogLeaf.cs +namespace BaGet.Protocol.Models; +// This class is based off https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/PackageDeleteCatalogLeaf.cs - /// - /// A "package delete" catalog leaf. Represents a single package deletion event. - /// Leafs can be discovered from a . - /// - /// See https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-leaf - /// - public class PackageDeleteCatalogLeaf : CatalogLeaf - { - } -} +/// +/// A "package delete" catalog leaf. Represents a single package deletion event. +/// Leafs can be discovered from a . +/// +/// See https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-leaf +/// +public class PackageDeleteCatalogLeaf : CatalogLeaf +{ +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/PackageDeprecation.cs b/src/BaGet.Protocol/Models/PackageDeprecation.cs index 9445ade2..7a965d75 100644 --- a/src/BaGet.Protocol/Models/PackageDeprecation.cs +++ b/src/BaGet.Protocol/Models/PackageDeprecation.cs @@ -1,38 +1,36 @@ -using System.Collections.Generic; using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// +/// A package's metadata. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#package-deprecation +/// +public class PackageDeprecation { /// - /// A package's metadata. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#package-deprecation + /// The URL to the document used to produce this object. /// - public class PackageDeprecation - { - /// - /// The URL to the document used to produce this object. - /// - [JsonPropertyName("@id")] - public string CatalogLeafUrl { get; set; } + [JsonPropertyName("@id")] + public string CatalogLeafUrl { get; set; } - /// - /// The reasons why the package was deprecated. - /// Deprecation reasons include: "Legacy", "CriticalBugs", and "Other". - /// - [JsonPropertyName("reasons")] - public IReadOnlyList Reasons { get; set; } + /// + /// The reasons why the package was deprecated. + /// Deprecation reasons include: "Legacy", "CriticalBugs", and "Other". + /// + [JsonPropertyName("reasons")] + public IReadOnlyList Reasons { get; set; } - /// - /// The additional details about this deprecation. - /// - [JsonPropertyName("message")] - public string Message { get; set; } + /// + /// The additional details about this deprecation. + /// + [JsonPropertyName("message")] + public string Message { get; set; } - /// - /// The alternate package that should be used instead. - /// - [JsonPropertyName("alternatePackage")] - public AlternatePackage AlternatePackage { get; set; } - } -} + /// + /// The alternate package that should be used instead. + /// + [JsonPropertyName("alternatePackage")] + public AlternatePackage AlternatePackage { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/PackageDetailsCatalogLeaf.cs b/src/BaGet.Protocol/Models/PackageDetailsCatalogLeaf.cs index 5e8cf257..319f85d9 100644 --- a/src/BaGet.Protocol/Models/PackageDetailsCatalogLeaf.cs +++ b/src/BaGet.Protocol/Models/PackageDetailsCatalogLeaf.cs @@ -1,170 +1,166 @@ -using System; -using System.Collections.Generic; using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// This class is based off https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/PackageDetailsCatalogLeaf.cs +/// +/// A "package details" catalog leaf. Represents a single package create or update event. +/// s can be discovered from a . +/// +/// See: https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-leaf +/// +public class PackageDetailsCatalogLeaf : CatalogLeaf { - /// This class is based off https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/PackageDetailsCatalogLeaf.cs - - /// - /// A "package details" catalog leaf. Represents a single package create or update event. - /// s can be discovered from a . - /// - /// See: https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-leaf - /// - public class PackageDetailsCatalogLeaf : CatalogLeaf - { - /// - /// The package's authors. - /// - [JsonPropertyName("authors")] - public string Authors { get; set; } - - /// - /// The package's copyright. - /// - [JsonPropertyName("copyright")] - public string Copyright { get; set; } - - /// - /// A timestamp of when the package was first created. Fallback property: . - /// - [JsonPropertyName("created")] - public DateTimeOffset Created { get; set; } - - /// - /// A timestamp of when the package was last edited. - /// - [JsonPropertyName("lastEdited")] - public DateTimeOffset LastEdited { get; set; } - - /// - /// The dependencies of the package, grouped by target framework. - /// - [JsonPropertyName("dependencyGroups")] - public List DependencyGroups { get; set; } - - /// - /// The package's description. - /// - [JsonPropertyName("description")] - public string Description { get; set; } - - /// - /// The URL to the package's icon. - /// - [JsonPropertyName("iconUrl")] - public string IconUrl { get; set; } - - /// - /// Whether or not the package is prerelease. Can be detected from . - /// Note that the NuGet.org catalog had this wrong in some cases. - /// Example: https://api.nuget.org/v3/catalog0/data/2016.03.11.21.02.55/mvid.fody.2.json - /// - [JsonPropertyName("isPrerelease")] - public bool IsPrerelease { get; set; } - - /// - /// The package's language. - /// - [JsonPropertyName("language")] - public string Language { get; set; } - - /// - /// THe URL to the package's license. - /// - [JsonPropertyName("licenseUrl")] - public string LicenseUrl { get; set; } - - /// - /// Whether the pacakge is listed. - /// - [JsonPropertyName("listed")] - public bool? Listed { get; set; } - - /// - /// The minimum NuGet client version needed to use this package. - /// - [JsonPropertyName("minClientVersion")] - public string MinClientVersion { get; set; } - - /// - /// The hash of the package encoded using Base64. - /// Hash algorithm can be detected using . - /// - [JsonPropertyName("packageHash")] - public string PackageHash { get; set; } - - /// - /// The algorithm used to hash . - /// - [JsonPropertyName("packageHashAlgorithm")] - public string PackageHashAlgorithm { get; set; } - - /// - /// The size of the package .nupkg in bytes. - /// - [JsonPropertyName("packageSize")] - public long PackageSize { get; set; } - - /// - /// The URL for the package's home page. - /// - [JsonPropertyName("projectUrl")] - public string ProjectUrl { get; set; } - - /// - /// The package's release notes. - /// - [JsonPropertyName("releaseNotes")] - public string ReleaseNotes { get; set; } - - /// - /// If true, the package requires its license to be accepted. - /// - [JsonPropertyName("requireLicenseAcceptance")] - public bool? RequireLicenseAcceptance { get; set; } - - /// - /// The package's summary. - /// - [JsonPropertyName("summary")] - public string Summary { get; set; } - - /// - /// The package's tags. - /// - [JsonPropertyName("tags")] - public List Tags { get; set; } - - /// - /// The package's title. - /// - [JsonPropertyName("title")] - public string Title { get; set; } - - /// - /// The version string as it's originally found in the .nuspec. - /// - [JsonPropertyName("verbatimVersion")] - public string VerbatimVersion { get; set; } - - /// - /// The package's License Expression. - /// - [JsonPropertyName("licenseExpression")] - public string LicenseExpression { get; set; } - - /// - /// The package's license file. - /// - [JsonPropertyName("licenseFile")] - public string LicenseFile { get; set; } - - /// - /// The package's icon file. - /// - [JsonPropertyName("iconFile")] - public string IconFile { get; set; } - } -} + /// + /// The package's authors. + /// + [JsonPropertyName("authors")] + public string Authors { get; set; } + + /// + /// The package's copyright. + /// + [JsonPropertyName("copyright")] + public string Copyright { get; set; } + + /// + /// A timestamp of when the package was first created. Fallback property: . + /// + [JsonPropertyName("created")] + public DateTimeOffset Created { get; set; } + + /// + /// A timestamp of when the package was last edited. + /// + [JsonPropertyName("lastEdited")] + public DateTimeOffset LastEdited { get; set; } + + /// + /// The dependencies of the package, grouped by target framework. + /// + [JsonPropertyName("dependencyGroups")] + public List DependencyGroups { get; set; } + + /// + /// The package's description. + /// + [JsonPropertyName("description")] + public string Description { get; set; } + + /// + /// The URL to the package's icon. + /// + [JsonPropertyName("iconUrl")] + public string IconUrl { get; set; } + + /// + /// Whether or not the package is prerelease. Can be detected from . + /// Note that the NuGet.org catalog had this wrong in some cases. + /// Example: https://api.nuget.org/v3/catalog0/data/2016.03.11.21.02.55/mvid.fody.2.json + /// + [JsonPropertyName("isPrerelease")] + public bool IsPrerelease { get; set; } + + /// + /// The package's language. + /// + [JsonPropertyName("language")] + public string Language { get; set; } + + /// + /// THe URL to the package's license. + /// + [JsonPropertyName("licenseUrl")] + public string LicenseUrl { get; set; } + + /// + /// Whether the pacakge is listed. + /// + [JsonPropertyName("listed")] + public bool? Listed { get; set; } + + /// + /// The minimum NuGet client version needed to use this package. + /// + [JsonPropertyName("minClientVersion")] + public string MinClientVersion { get; set; } + + /// + /// The hash of the package encoded using Base64. + /// Hash algorithm can be detected using . + /// + [JsonPropertyName("packageHash")] + public string PackageHash { get; set; } + + /// + /// The algorithm used to hash . + /// + [JsonPropertyName("packageHashAlgorithm")] + public string PackageHashAlgorithm { get; set; } + + /// + /// The size of the package .nupkg in bytes. + /// + [JsonPropertyName("packageSize")] + public long PackageSize { get; set; } + + /// + /// The URL for the package's home page. + /// + [JsonPropertyName("projectUrl")] + public string ProjectUrl { get; set; } + + /// + /// The package's release notes. + /// + [JsonPropertyName("releaseNotes")] + public string ReleaseNotes { get; set; } + + /// + /// If true, the package requires its license to be accepted. + /// + [JsonPropertyName("requireLicenseAcceptance")] + public bool? RequireLicenseAcceptance { get; set; } + + /// + /// The package's summary. + /// + [JsonPropertyName("summary")] + public string Summary { get; set; } + + /// + /// The package's tags. + /// + [JsonPropertyName("tags")] + public List Tags { get; set; } + + /// + /// The package's title. + /// + [JsonPropertyName("title")] + public string Title { get; set; } + + /// + /// The version string as it's originally found in the .nuspec. + /// + [JsonPropertyName("verbatimVersion")] + public string VerbatimVersion { get; set; } + + /// + /// The package's License Expression. + /// + [JsonPropertyName("licenseExpression")] + public string LicenseExpression { get; set; } + + /// + /// The package's license file. + /// + [JsonPropertyName("licenseFile")] + public string LicenseFile { get; set; } + + /// + /// The package's icon file. + /// + [JsonPropertyName("iconFile")] + public string IconFile { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/PackageMetadata.cs b/src/BaGet.Protocol/Models/PackageMetadata.cs index 2e9563d1..86f8f8e5 100644 --- a/src/BaGet.Protocol/Models/PackageMetadata.cs +++ b/src/BaGet.Protocol/Models/PackageMetadata.cs @@ -1,129 +1,126 @@ -using System; -using System.Collections.Generic; using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// +/// A package's metadata. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#catalog-entry +/// +public class PackageMetadata { /// - /// A package's metadata. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#catalog-entry - /// - public class PackageMetadata - { - /// - /// The URL to the document used to produce this object. - /// - [JsonPropertyName("@id")] - public string CatalogLeafUrl { get; set; } - - /// - /// The ID of the package. - /// - [JsonPropertyName("id")] - public string PackageId { get; set; } - - /// - /// The full NuGet version after normalization, including any SemVer 2.0.0 build metadata. - /// - [JsonPropertyName("version")] - public string Version { get; set; } - - /// - /// The package's authors. - /// - [JsonPropertyName("authors")] - public string Authors { get; set; } - - /// - /// The dependencies of the package, grouped by target framework. - /// - [JsonPropertyName("dependencyGroups")] - public IReadOnlyList DependencyGroups { get; set; } - - /// - /// The deprecation associated with the package, if any. - /// - [JsonPropertyName("deprecation")] - public PackageDeprecation Deprecation { get; set; } - - /// - /// The package's description. - /// - [JsonPropertyName("description")] - public string Description { get; set; } - - /// - /// The URL to the package's icon. - /// - [JsonPropertyName("iconUrl")] - public string IconUrl { get; set; } - - /// - /// The package's language. - /// - [JsonPropertyName("language")] - public string Language { get; set; } - - /// - /// The URL to the package's license. - /// - [JsonPropertyName("licenseUrl")] - public string LicenseUrl { get; set; } - - /// - /// Whether the package is listed in search results. - /// If , the package should be considered as listed. - /// - [JsonPropertyName("listed")] - public bool? Listed { get; set; } - - /// - /// The minimum NuGet client version needed to use this package. - /// - [JsonPropertyName("minClientVersion")] - public string MinClientVersion { get; set; } - - /// - /// The URL to download the package's content. - /// - [JsonPropertyName("packageContent")] - public string PackageContentUrl { get; set; } - - /// - /// The URL for the package's home page. - /// - [JsonPropertyName("projectUrl")] - public string ProjectUrl { get; set; } - - /// - /// The package's publish date. - /// - [JsonPropertyName("published")] - public DateTimeOffset Published { get; set; } - - /// - /// If true, the package requires its license to be accepted. - /// - [JsonPropertyName("requireLicenseAcceptance")] - public bool RequireLicenseAcceptance { get; set; } - - /// - /// The package's summary. - /// - [JsonPropertyName("summary")] - public string Summary { get; set; } - - /// - /// The package's tags. - /// - [JsonPropertyName("tags")] - public IReadOnlyList Tags { get; set; } - - /// - /// The package's title. - /// - [JsonPropertyName("title")] - public string Title { get; set; } - } -} + /// The URL to the document used to produce this object. + /// + [JsonPropertyName("@id")] + public string CatalogLeafUrl { get; set; } + + /// + /// The ID of the package. + /// + [JsonPropertyName("id")] + public string PackageId { get; set; } + + /// + /// The full NuGet version after normalization, including any SemVer 2.0.0 build metadata. + /// + [JsonPropertyName("version")] + public string Version { get; set; } + + /// + /// The package's authors. + /// + [JsonPropertyName("authors")] + public string Authors { get; set; } + + /// + /// The dependencies of the package, grouped by target framework. + /// + [JsonPropertyName("dependencyGroups")] + public IReadOnlyList DependencyGroups { get; set; } + + /// + /// The deprecation associated with the package, if any. + /// + [JsonPropertyName("deprecation")] + public PackageDeprecation Deprecation { get; set; } + + /// + /// The package's description. + /// + [JsonPropertyName("description")] + public string Description { get; set; } + + /// + /// The URL to the package's icon. + /// + [JsonPropertyName("iconUrl")] + public string IconUrl { get; set; } + + /// + /// The package's language. + /// + [JsonPropertyName("language")] + public string Language { get; set; } + + /// + /// The URL to the package's license. + /// + [JsonPropertyName("licenseUrl")] + public string LicenseUrl { get; set; } + + /// + /// Whether the package is listed in search results. + /// If , the package should be considered as listed. + /// + [JsonPropertyName("listed")] + public bool? Listed { get; set; } + + /// + /// The minimum NuGet client version needed to use this package. + /// + [JsonPropertyName("minClientVersion")] + public string MinClientVersion { get; set; } + + /// + /// The URL to download the package's content. + /// + [JsonPropertyName("packageContent")] + public string PackageContentUrl { get; set; } + + /// + /// The URL for the package's home page. + /// + [JsonPropertyName("projectUrl")] + public string ProjectUrl { get; set; } + + /// + /// The package's publish date. + /// + [JsonPropertyName("published")] + public DateTimeOffset Published { get; set; } + + /// + /// If true, the package requires its license to be accepted. + /// + [JsonPropertyName("requireLicenseAcceptance")] + public bool RequireLicenseAcceptance { get; set; } + + /// + /// The package's summary. + /// + [JsonPropertyName("summary")] + public string Summary { get; set; } + + /// + /// The package's tags. + /// + [JsonPropertyName("tags")] + public IReadOnlyList Tags { get; set; } + + /// + /// The package's title. + /// + [JsonPropertyName("title")] + public string Title { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/PackageNotFoundException.cs b/src/BaGet.Protocol/Models/PackageNotFoundException.cs index 880c5e71..4def0f69 100644 --- a/src/BaGet.Protocol/Models/PackageNotFoundException.cs +++ b/src/BaGet.Protocol/Models/PackageNotFoundException.cs @@ -1,33 +1,31 @@ -using System; using NuGet.Versioning; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// +/// An exception thrown when a package could not be found on the NuGet server. +/// +public class PackageNotFoundException : Exception { /// - /// An exception thrown when a package could not be found on the NuGet server. + /// Create a new instance of the . /// - public class PackageNotFoundException : Exception + /// The ID of the package that could not be found. + /// The version of the package that could not be found. + public PackageNotFoundException(string packageId, NuGetVersion packageVersion) + : base($"Could not find package {packageId} {packageVersion}") { - /// - /// Create a new instance of the . - /// - /// The ID of the package that could not be found. - /// The version of the package that could not be found. - public PackageNotFoundException(string packageId, NuGetVersion packageVersion) - : base($"Could not find package {packageId} {packageVersion}") - { - PackageId = packageId ?? throw new ArgumentNullException(nameof(packageId)); - PackageVersion = packageVersion ?? throw new ArgumentNullException(nameof(packageVersion)); - } + PackageId = packageId ?? throw new ArgumentNullException(nameof(packageId)); + PackageVersion = packageVersion ?? throw new ArgumentNullException(nameof(packageVersion)); + } - /// - /// The package ID that could not be found. - /// - public string PackageId { get; } + /// + /// The package ID that could not be found. + /// + public string PackageId { get; } - /// - /// The package version that could not be found. - /// - public NuGetVersion PackageVersion { get; } - } -} + /// + /// The package version that could not be found. + /// + public NuGetVersion PackageVersion { get; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/PackageVersionsResponse.cs b/src/BaGet.Protocol/Models/PackageVersionsResponse.cs index 4a8a64f9..cf062542 100644 --- a/src/BaGet.Protocol/Models/PackageVersionsResponse.cs +++ b/src/BaGet.Protocol/Models/PackageVersionsResponse.cs @@ -1,19 +1,17 @@ -using System.Collections.Generic; using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// +/// The full list of versions for a single package. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#enumerate-package-versions +/// +public class PackageVersionsResponse { /// - /// The full list of versions for a single package. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#enumerate-package-versions + /// The versions, lowercased and normalized. /// - public class PackageVersionsResponse - { - /// - /// The versions, lowercased and normalized. - /// - [JsonPropertyName("versions")] - public IReadOnlyList Versions { get; set; } - } -} + [JsonPropertyName("versions")] + public IReadOnlyList Versions { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/ProtocolException.cs b/src/BaGet.Protocol/Models/ProtocolException.cs index 893c8902..dec539bf 100644 --- a/src/BaGet.Protocol/Models/ProtocolException.cs +++ b/src/BaGet.Protocol/Models/ProtocolException.cs @@ -1,53 +1,50 @@ -using System; using System.Net; -using System.Net.Http; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// +/// An exception that is thrown when an API has returned an unexpected result. +/// +public class ProtocolException : Exception { /// - /// An exception that is thrown when an API has returned an unexpected result. + /// Create a new . /// - public class ProtocolException : Exception + /// The HTTP response message. + /// The HTTP request method. + /// The URI that was requested. + /// The response status code. + /// The HTTP reason phrase. + public ProtocolException( + string message, + HttpMethod method, + string requestUri, + HttpStatusCode statusCode, + string reasonPhrase) : base(message) { - /// - /// Create a new . - /// - /// The HTTP response message. - /// The HTTP request method. - /// The URI that was requested. - /// The response status code. - /// The HTTP reason phrase. - public ProtocolException( - string message, - HttpMethod method, - string requestUri, - HttpStatusCode statusCode, - string reasonPhrase) : base(message) - { - Method = method ?? throw new ArgumentNullException(nameof(method)); - RequestUri = requestUri ?? throw new ArgumentNullException(nameof(requestUri)); - StatusCode = statusCode; - ReasonPhrase = reasonPhrase ?? throw new ArgumentNullException(nameof(reasonPhrase)); - } + Method = method ?? throw new ArgumentNullException(nameof(method)); + RequestUri = requestUri ?? throw new ArgumentNullException(nameof(requestUri)); + StatusCode = statusCode; + ReasonPhrase = reasonPhrase ?? throw new ArgumentNullException(nameof(reasonPhrase)); + } - /// - /// The HTTP response message. - /// - public HttpMethod Method { get; } + /// + /// The HTTP response message. + /// + public HttpMethod Method { get; } - /// - /// The URI that was requested. - /// - public string RequestUri { get; } + /// + /// The URI that was requested. + /// + public string RequestUri { get; } - /// - /// The response status code. - /// - public HttpStatusCode StatusCode { get; } + /// + /// The response status code. + /// + public HttpStatusCode StatusCode { get; } - /// - /// The response reason phrase. - /// - public string ReasonPhrase { get; } - } -} + /// + /// The response reason phrase. + /// + public string ReasonPhrase { get; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/RegistrationIndexPage.cs b/src/BaGet.Protocol/Models/RegistrationIndexPage.cs index e7190af5..5c4b9d3e 100644 --- a/src/BaGet.Protocol/Models/RegistrationIndexPage.cs +++ b/src/BaGet.Protocol/Models/RegistrationIndexPage.cs @@ -1,46 +1,44 @@ -using System.Collections.Generic; using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// +/// The registration page object found in the registration index. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-page-object +/// +public class RegistrationIndexPage { /// - /// The registration page object found in the registration index. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-page-object + /// The URL to the registration page. /// - public class RegistrationIndexPage - { - /// - /// The URL to the registration page. - /// - [JsonPropertyName("@id")] - public string RegistrationPageUrl { get; set; } + [JsonPropertyName("@id")] + public string RegistrationPageUrl { get; set; } - /// - /// The number of registration leafs in the page. - /// - [JsonPropertyName("count")] - public int Count { get; set; } + /// + /// The number of registration leafs in the page. + /// + [JsonPropertyName("count")] + public int Count { get; set; } - /// - /// if this package's registration is paged. The items can be found - /// by following the page's . - /// - [JsonPropertyName("items")] - public IReadOnlyList ItemsOrNull { get; set; } + /// + /// if this package's registration is paged. The items can be found + /// by following the page's . + /// + [JsonPropertyName("items")] + public IReadOnlyList ItemsOrNull { get; set; } - /// - /// This page's lowest package version. The version should be lowercased, normalized, - /// and the SemVer 2.0.0 build metadata removed, if any. - /// - [JsonPropertyName("lower")] - public string Lower { get; set; } + /// + /// This page's lowest package version. The version should be lowercased, normalized, + /// and the SemVer 2.0.0 build metadata removed, if any. + /// + [JsonPropertyName("lower")] + public string Lower { get; set; } - /// - /// This page's highest package version. The version should be lowercased, normalized, - /// and the SemVer 2.0.0 build metadata removed, if any. - /// - [JsonPropertyName("upper")] - public string Upper { get; set; } - } -} + /// + /// This page's highest package version. The version should be lowercased, normalized, + /// and the SemVer 2.0.0 build metadata removed, if any. + /// + [JsonPropertyName("upper")] + public string Upper { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/RegistrationIndexPageItem.cs b/src/BaGet.Protocol/Models/RegistrationIndexPageItem.cs index 91668366..4a34353e 100644 --- a/src/BaGet.Protocol/Models/RegistrationIndexPageItem.cs +++ b/src/BaGet.Protocol/Models/RegistrationIndexPageItem.cs @@ -1,30 +1,29 @@ using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// +/// An item in the that references a . +/// +/// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-leaf-object-in-a-page +/// +public class RegistrationIndexPageItem { /// - /// An item in the that references a . - /// - /// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-leaf-object-in-a-page + /// The URL to the registration leaf. /// - public class RegistrationIndexPageItem - { - /// - /// The URL to the registration leaf. - /// - [JsonPropertyName("@id")] - public string RegistrationLeafUrl { get; set; } + [JsonPropertyName("@id")] + public string RegistrationLeafUrl { get; set; } - /// - /// The catalog entry containing the package metadata. - /// - [JsonPropertyName("catalogEntry")] - public PackageMetadata PackageMetadata { get; set; } + /// + /// The catalog entry containing the package metadata. + /// + [JsonPropertyName("catalogEntry")] + public PackageMetadata PackageMetadata { get; set; } - /// - /// The URL to the package content (.nupkg) - /// - [JsonPropertyName("packageContent")] - public string PackageContentUrl { get; set; } - } -} + /// + /// The URL to the package content (.nupkg) + /// + [JsonPropertyName("packageContent")] + public string PackageContentUrl { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/RegistrationIndexResponse.cs b/src/BaGet.Protocol/Models/RegistrationIndexResponse.cs index 79cf01ba..e6308d23 100644 --- a/src/BaGet.Protocol/Models/RegistrationIndexResponse.cs +++ b/src/BaGet.Protocol/Models/RegistrationIndexResponse.cs @@ -1,45 +1,43 @@ -using System.Collections.Generic; using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// +/// The metadata for a package and all of its versions. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-index +/// +public class RegistrationIndexResponse { - /// - /// The metadata for a package and all of its versions. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-index - /// - public class RegistrationIndexResponse + public static readonly IReadOnlyList DefaultType = new List { - public static readonly IReadOnlyList DefaultType = new List - { - "catalog:CatalogRoot", - "PackageRegistration", - "catalog:Permalink" - }; + "catalog:CatalogRoot", + "PackageRegistration", + "catalog:Permalink" + }; - /// - /// The URL to the registration index. - /// - [JsonPropertyName("@id")] - public string RegistrationIndexUrl { get; set; } + /// + /// The URL to the registration index. + /// + [JsonPropertyName("@id")] + public string RegistrationIndexUrl { get; set; } - /// - /// The registration index's type. - /// - [JsonPropertyName("@type")] - public IReadOnlyList Type { get; set; } + /// + /// The registration index's type. + /// + [JsonPropertyName("@type")] + public IReadOnlyList Type { get; set; } - /// - /// The number of registration pages. See . - /// - [JsonPropertyName("count")] - public int Count { get; set; } + /// + /// The number of registration pages. See . + /// + [JsonPropertyName("count")] + public int Count { get; set; } - /// - /// The pages that contain all of the versions of the package, ordered - /// by the package's version. - /// - [JsonPropertyName("items")] - public IReadOnlyList Pages { get; set; } - } -} + /// + /// The pages that contain all of the versions of the package, ordered + /// by the package's version. + /// + [JsonPropertyName("items")] + public IReadOnlyList Pages { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/RegistrationLeafResponse.cs b/src/BaGet.Protocol/Models/RegistrationLeafResponse.cs index c95ca7ed..c5269633 100644 --- a/src/BaGet.Protocol/Models/RegistrationLeafResponse.cs +++ b/src/BaGet.Protocol/Models/RegistrationLeafResponse.cs @@ -1,57 +1,54 @@ -using System; -using System.Collections.Generic; using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// +/// The metadata for a single version of a package. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-leaf +/// +public class RegistrationLeafResponse { + public static readonly IReadOnlyList DefaultType = new List + { + "Package", + "http://schema.nuget.org/catalog#Permalink" + }; + /// - /// The metadata for a single version of a package. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-leaf + /// The URL to the registration leaf. /// - public class RegistrationLeafResponse - { - public static readonly IReadOnlyList DefaultType = new List - { - "Package", - "http://schema.nuget.org/catalog#Permalink" - }; - - /// - /// The URL to the registration leaf. - /// - [JsonPropertyName("@id")] - public string RegistrationLeafUrl { get; set; } - - /// - /// The registration leaf's type. - /// - [JsonPropertyName("@type")] - public IReadOnlyList Type { get; set; } - - /// - /// Whether the package is listed. - /// - [JsonPropertyName("listed")] - public bool Listed { get; set; } - - /// - /// The URL to the package content (.nupkg). - /// - [JsonPropertyName("packageContent")] - public string PackageContentUrl { get; set; } - - /// - /// The date the package was published. On NuGet.org, - /// is set to the year 1900 if the package is unlisted. - /// - [JsonPropertyName("published")] - public DateTimeOffset Published { get; set; } - - /// - /// The URL to the package's registration index. - /// - [JsonPropertyName("registration")] - public string RegistrationIndexUrl { get; set; } - } -} + [JsonPropertyName("@id")] + public string RegistrationLeafUrl { get; set; } + + /// + /// The registration leaf's type. + /// + [JsonPropertyName("@type")] + public IReadOnlyList Type { get; set; } + + /// + /// Whether the package is listed. + /// + [JsonPropertyName("listed")] + public bool Listed { get; set; } + + /// + /// The URL to the package content (.nupkg). + /// + [JsonPropertyName("packageContent")] + public string PackageContentUrl { get; set; } + + /// + /// The date the package was published. On NuGet.org, + /// is set to the year 1900 if the package is unlisted. + /// + [JsonPropertyName("published")] + public DateTimeOffset Published { get; set; } + + /// + /// The URL to the package's registration index. + /// + [JsonPropertyName("registration")] + public string RegistrationIndexUrl { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/RegistrationPageResponse.cs b/src/BaGet.Protocol/Models/RegistrationPageResponse.cs index 60ee23d7..c7a4e819 100644 --- a/src/BaGet.Protocol/Models/RegistrationPageResponse.cs +++ b/src/BaGet.Protocol/Models/RegistrationPageResponse.cs @@ -1,11 +1,10 @@ -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// +/// A page of package metadata entries. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-page +/// +public class RegistrationPageResponse : RegistrationIndexPage { - /// - /// A page of package metadata entries. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-page - /// - public class RegistrationPageResponse : RegistrationIndexPage - { - } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/SearchContext.cs b/src/BaGet.Protocol/Models/SearchContext.cs index 6abfb92e..19e231b2 100644 --- a/src/BaGet.Protocol/Models/SearchContext.cs +++ b/src/BaGet.Protocol/Models/SearchContext.cs @@ -1,22 +1,21 @@ using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +public class SearchContext { - public class SearchContext + public static SearchContext Default(string registrationBaseUrl) { - public static SearchContext Default(string registrationBaseUrl) + return new SearchContext { - return new SearchContext - { - Vocab = "http://schema.nuget.org/schema#", - Base = registrationBaseUrl - }; - } + Vocab = "http://schema.nuget.org/schema#", + Base = registrationBaseUrl + }; + } - [JsonPropertyName("@vocab")] - public string Vocab { get; set; } + [JsonPropertyName("@vocab")] + public string Vocab { get; set; } - [JsonPropertyName("@base")] - public string Base { get; set; } - } -} + [JsonPropertyName("@base")] + public string Base { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/SearchResponse.cs b/src/BaGet.Protocol/Models/SearchResponse.cs index 70fce739..67b417d1 100644 --- a/src/BaGet.Protocol/Models/SearchResponse.cs +++ b/src/BaGet.Protocol/Models/SearchResponse.cs @@ -1,28 +1,26 @@ -using System.Collections.Generic; using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// +/// The response to a search query. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#response +/// +public class SearchResponse { + [JsonPropertyName("@context")] + public SearchContext Context { get; set; } + /// - /// The response to a search query. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#response + /// The total number of matches, disregarding skip and take. /// - public class SearchResponse - { - [JsonPropertyName("@context")] - public SearchContext Context { get; set; } - - /// - /// The total number of matches, disregarding skip and take. - /// - [JsonPropertyName("totalHits")] - public long TotalHits { get; set; } + [JsonPropertyName("totalHits")] + public long TotalHits { get; set; } - /// - /// The packages that matched the search query. - /// - [JsonPropertyName("data")] - public IReadOnlyList Data { get; set; } - } -} + /// + /// The packages that matched the search query. + /// + [JsonPropertyName("data")] + public IReadOnlyList Data { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/SearchResult.cs b/src/BaGet.Protocol/Models/SearchResult.cs index fecd9d6b..3502a8ec 100644 --- a/src/BaGet.Protocol/Models/SearchResult.cs +++ b/src/BaGet.Protocol/Models/SearchResult.cs @@ -1,100 +1,98 @@ -using System.Collections.Generic; using System.Text.Json.Serialization; using BaGet.Protocol.Internal; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// +/// A package that matched a search query. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-result +/// +public class SearchResult { /// - /// A package that matched a search query. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-result + /// The ID of the matched package. /// - public class SearchResult - { - /// - /// The ID of the matched package. - /// - [JsonPropertyName("id")] - public string PackageId { get; set; } + [JsonPropertyName("id")] + public string PackageId { get; set; } - /// - /// The latest version of the matched pacakge. This is the full NuGet version after normalization, - /// including any SemVer 2.0.0 build metadata. - /// - [JsonPropertyName("version")] - public string Version { get; set; } + /// + /// The latest version of the matched pacakge. This is the full NuGet version after normalization, + /// including any SemVer 2.0.0 build metadata. + /// + [JsonPropertyName("version")] + public string Version { get; set; } - /// - /// The description of the matched package. - /// - [JsonPropertyName("description")] - public string Description { get; set; } + /// + /// The description of the matched package. + /// + [JsonPropertyName("description")] + public string Description { get; set; } - /// - /// The authors of the matched package. - /// - [JsonPropertyName("authors")] - [JsonConverter(typeof(StringOrStringArrayJsonConverter))] - public IReadOnlyList Authors { get; set; } + /// + /// The authors of the matched package. + /// + [JsonPropertyName("authors")] + [JsonConverter(typeof(StringOrStringArrayJsonConverter))] + public IReadOnlyList Authors { get; set; } - /// - /// The URL of the matched package's icon. - /// - [JsonPropertyName("iconUrl")] - public string IconUrl { get; set; } + /// + /// The URL of the matched package's icon. + /// + [JsonPropertyName("iconUrl")] + public string IconUrl { get; set; } - /// - /// The URL of the matched package's license. - /// - [JsonPropertyName("licenseUrl")] - public string LicenseUrl { get; set; } + /// + /// The URL of the matched package's license. + /// + [JsonPropertyName("licenseUrl")] + public string LicenseUrl { get; set; } - /// - /// The package types defined by the package author. - /// - [JsonPropertyName("packageTypes")] - public IReadOnlyList PackageTypes { get; set; } + /// + /// The package types defined by the package author. + /// + [JsonPropertyName("packageTypes")] + public IReadOnlyList PackageTypes { get; set; } - /// - /// The URL of the matched package's homepage. - /// - [JsonPropertyName("projectUrl")] - public string ProjectUrl { get; set; } + /// + /// The URL of the matched package's homepage. + /// + [JsonPropertyName("projectUrl")] + public string ProjectUrl { get; set; } - /// - /// The URL for the matched package's registration index. - /// - [JsonPropertyName("registration")] - public string RegistrationIndexUrl { get; set; } + /// + /// The URL for the matched package's registration index. + /// + [JsonPropertyName("registration")] + public string RegistrationIndexUrl { get; set; } - /// - /// The summary of the matched package. - /// - [JsonPropertyName("summary")] - public string Summary { get; set; } + /// + /// The summary of the matched package. + /// + [JsonPropertyName("summary")] + public string Summary { get; set; } - /// - /// The tags of the matched package. - /// - [JsonPropertyName("tags")] - public IReadOnlyList Tags { get; set; } + /// + /// The tags of the matched package. + /// + [JsonPropertyName("tags")] + public IReadOnlyList Tags { get; set; } - /// - /// The title of the matched package. - /// - [JsonPropertyName("title")] - public string Title { get; set; } + /// + /// The title of the matched package. + /// + [JsonPropertyName("title")] + public string Title { get; set; } - /// - /// The total downloads for all versions of the matched package. - /// - [JsonPropertyName("totalDownloads")] - public long TotalDownloads { get; set; } + /// + /// The total downloads for all versions of the matched package. + /// + [JsonPropertyName("totalDownloads")] + public long TotalDownloads { get; set; } - /// - /// The versions of the matched package. - /// - [JsonPropertyName("versions")] - public IReadOnlyList Versions { get; set; } - } -} + /// + /// The versions of the matched package. + /// + [JsonPropertyName("versions")] + public IReadOnlyList Versions { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/SearchResultPackageType.cs b/src/BaGet.Protocol/Models/SearchResultPackageType.cs index aa353b7c..7bddbdf8 100644 --- a/src/BaGet.Protocol/Models/SearchResultPackageType.cs +++ b/src/BaGet.Protocol/Models/SearchResultPackageType.cs @@ -1,18 +1,17 @@ using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// +/// A single package type from a . +/// +/// See https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-result +/// +public class SearchResultPackageType { /// - /// A single package type from a . - /// - /// See https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-result + /// The name of the package type. /// - public class SearchResultPackageType - { - /// - /// The name of the package type. - /// - [JsonPropertyName("name")] - public string Name { get; set; } - } -} + [JsonPropertyName("name")] + public string Name { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/SearchResultVersion.cs b/src/BaGet.Protocol/Models/SearchResultVersion.cs index a3a2cb23..9145fbb8 100644 --- a/src/BaGet.Protocol/Models/SearchResultVersion.cs +++ b/src/BaGet.Protocol/Models/SearchResultVersion.cs @@ -1,30 +1,29 @@ using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// +/// A single version from a . +/// +/// See https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-result +/// +public class SearchResultVersion { /// - /// A single version from a . - /// - /// See https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-result + /// The registration leaf URL for this single version of the matched package. /// - public class SearchResultVersion - { - /// - /// The registration leaf URL for this single version of the matched package. - /// - [JsonPropertyName("@id")] - public string RegistrationLeafUrl { get; set; } + [JsonPropertyName("@id")] + public string RegistrationLeafUrl { get; set; } - /// - /// The package's full NuGet version after normalization, including any SemVer 2.0.0 build metadata. - /// - [JsonPropertyName("version")] - public string Version { get; set; } + /// + /// The package's full NuGet version after normalization, including any SemVer 2.0.0 build metadata. + /// + [JsonPropertyName("version")] + public string Version { get; set; } - /// - /// The downloads for this single version of the matched package. - /// - [JsonPropertyName("downloads")] - public long Downloads { get; set; } - } -} + /// + /// The downloads for this single version of the matched package. + /// + [JsonPropertyName("downloads")] + public long Downloads { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/ServiceIndexItem.cs b/src/BaGet.Protocol/Models/ServiceIndexItem.cs index 36ba4388..f984117a 100644 --- a/src/BaGet.Protocol/Models/ServiceIndexItem.cs +++ b/src/BaGet.Protocol/Models/ServiceIndexItem.cs @@ -1,30 +1,29 @@ using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// +/// A resource in the . +/// +/// See https://docs.microsoft.com/en-us/nuget/api/service-index#resources +/// +public class ServiceIndexItem { /// - /// A resource in the . - /// - /// See https://docs.microsoft.com/en-us/nuget/api/service-index#resources + /// The resource's base URL. /// - public class ServiceIndexItem - { - /// - /// The resource's base URL. - /// - [JsonPropertyName("@id")] - public string ResourceUrl { get; set; } + [JsonPropertyName("@id")] + public string ResourceUrl { get; set; } - /// - /// The resource's type. - /// - [JsonPropertyName("@type")] - public string Type { get; set; } + /// + /// The resource's type. + /// + [JsonPropertyName("@type")] + public string Type { get; set; } - /// - /// Human readable comments about the resource. - /// - [JsonPropertyName("comment")] - public string Comment { get; set; } - } -} + /// + /// Human readable comments about the resource. + /// + [JsonPropertyName("comment")] + public string Comment { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/ServiceIndexResponse.cs b/src/BaGet.Protocol/Models/ServiceIndexResponse.cs index 4b032712..4eca6890 100644 --- a/src/BaGet.Protocol/Models/ServiceIndexResponse.cs +++ b/src/BaGet.Protocol/Models/ServiceIndexResponse.cs @@ -1,25 +1,23 @@ -using System.Collections.Generic; using System.Text.Json.Serialization; -namespace BaGet.Protocol.Models +namespace BaGet.Protocol.Models; + +/// +/// The entry point for a NuGet package source used by the client to discover NuGet APIs. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/overview +/// +public class ServiceIndexResponse { /// - /// The entry point for a NuGet package source used by the client to discover NuGet APIs. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/overview + /// The service index's version. /// - public class ServiceIndexResponse - { - /// - /// The service index's version. - /// - [JsonPropertyName("version")] - public string Version { get; set; } + [JsonPropertyName("version")] + public string Version { get; set; } - /// - /// The resources declared by this service index. - /// - [JsonPropertyName("resources")] - public IReadOnlyList Resources { get; set; } - } -} + /// + /// The resources declared by this service index. + /// + [JsonPropertyName("resources")] + public IReadOnlyList Resources { get; set; } +} \ No newline at end of file diff --git a/src/BaGet.Protocol/NuGetClient.cs b/src/BaGet.Protocol/NuGetClient.cs index ebecab97..482ea6f2 100644 --- a/src/BaGet.Protocol/NuGetClient.cs +++ b/src/BaGet.Protocol/NuGetClient.cs @@ -1,495 +1,487 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Net; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; using NuGet.Versioning; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +/// +/// The client to interact with a NuGet server. +/// +public class NuGetClient { + private readonly IPackageContentClient _contentClient; + private readonly IPackageMetadataClient _metadataClient; + private readonly ISearchClient _searchClient; + private readonly IAutocompleteClient _autocompleteClient; + /// - /// The client to interact with a NuGet server. + /// Initializes a new instance of the class + /// for mocking. /// - public class NuGetClient + protected NuGetClient() { - private readonly IPackageContentClient _contentClient; - private readonly IPackageMetadataClient _metadataClient; - private readonly ISearchClient _searchClient; - private readonly IAutocompleteClient _autocompleteClient; - - /// - /// Initializes a new instance of the class - /// for mocking. - /// - protected NuGetClient() - { - } + } - /// - /// Initializes a new instance of the class. - /// - /// - /// The NuGet Service Index resource URL. - /// - /// For NuGet.org, use https://api.nuget.org/v3/index.json - /// - public NuGetClient(string serviceIndexUrl) + /// + /// Initializes a new instance of the class. + /// + /// + /// The NuGet Service Index resource URL. + /// + /// For NuGet.org, use https://api.nuget.org/v3/index.json + /// + public NuGetClient(string serviceIndexUrl) + { + var httpClient = new HttpClient(new HttpClientHandler { - var httpClient = new HttpClient(new HttpClientHandler - { - AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, - }); + AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, + }); - var clientFactory = new NuGetClientFactory(httpClient, serviceIndexUrl); + var clientFactory = new NuGetClientFactory(httpClient, serviceIndexUrl); - _contentClient = clientFactory.CreatePackageContentClient(); - _metadataClient = clientFactory.CreatePackageMetadataClient(); - _searchClient = clientFactory.CreateSearchClient(); - _autocompleteClient = clientFactory.CreateAutocompleteClient(); - } + _contentClient = clientFactory.CreatePackageContentClient(); + _metadataClient = clientFactory.CreatePackageMetadataClient(); + _searchClient = clientFactory.CreateSearchClient(); + _autocompleteClient = clientFactory.CreateAutocompleteClient(); + } - /// - /// Initializes a new instance of the class. - /// - /// The factory used to create NuGet clients. - public NuGetClient(NuGetClientFactory clientFactory) - { - if (clientFactory == null) throw new ArgumentNullException(nameof(clientFactory)); + /// + /// Initializes a new instance of the class. + /// + /// The factory used to create NuGet clients. + public NuGetClient(NuGetClientFactory clientFactory) + { + if (clientFactory == null) throw new ArgumentNullException(nameof(clientFactory)); - _contentClient = clientFactory.CreatePackageContentClient(); - _metadataClient = clientFactory.CreatePackageMetadataClient(); - _searchClient = clientFactory.CreateSearchClient(); - _autocompleteClient = clientFactory.CreateAutocompleteClient(); - } + _contentClient = clientFactory.CreatePackageContentClient(); + _metadataClient = clientFactory.CreatePackageMetadataClient(); + _searchClient = clientFactory.CreateSearchClient(); + _autocompleteClient = clientFactory.CreateAutocompleteClient(); + } - /// - /// Check if a package exists. - /// - /// The package ID. - /// A token to cancel the task. - /// Whether the package exists. - public virtual async Task ExistsAsync( - string packageId, - CancellationToken cancellationToken = default) - { - var versions = await _contentClient.GetPackageVersionsOrNullAsync(packageId, cancellationToken); + /// + /// Check if a package exists. + /// + /// The package ID. + /// A token to cancel the task. + /// Whether the package exists. + public virtual async Task ExistsAsync( + string packageId, + CancellationToken cancellationToken = default) + { + var versions = await _contentClient.GetPackageVersionsOrNullAsync(packageId, cancellationToken); - return (versions != null && versions.Versions.Any()); - } + return (versions != null && versions.Versions.Any()); + } + + /// + /// Check if a package exists. + /// + /// The package ID. + /// The package version. + /// A token to cancel the task. + /// Whether the package exists. + public virtual async Task ExistsAsync( + string packageId, + NuGetVersion packageVersion, + CancellationToken cancellationToken = default) + { + var versions = await _contentClient.GetPackageVersionsOrNullAsync(packageId, cancellationToken); - /// - /// Check if a package exists. - /// - /// The package ID. - /// The package version. - /// A token to cancel the task. - /// Whether the package exists. - public virtual async Task ExistsAsync( - string packageId, - NuGetVersion packageVersion, - CancellationToken cancellationToken = default) + if (versions == null) { - var versions = await _contentClient.GetPackageVersionsOrNullAsync(packageId, cancellationToken); + return false; + } - if (versions == null) - { - return false; - } + return versions + .ParseVersions() + .Any(v => v == packageVersion); + } - return versions - .ParseVersions() - .Any(v => v == packageVersion); - } + /// + /// Download a package (.nupkg), or throws if the package does not exist. + /// + /// The package ID. + /// The package version. + /// A token to cancel the task. + /// The package's content stream. The stream may be unseekable and may be unbuffered. + /// + /// The package could not be found. + /// + public virtual async Task DownloadPackageAsync(string packageId, NuGetVersion packageVersion, CancellationToken cancellationToken = default) + { + var stream = await _contentClient.DownloadPackageOrNullAsync(packageId, packageVersion, cancellationToken); - /// - /// Download a package (.nupkg), or throws if the package does not exist. - /// - /// The package ID. - /// The package version. - /// A token to cancel the task. - /// The package's content stream. The stream may be unseekable and may be unbuffered. - /// - /// The package could not be found. - /// - public virtual async Task DownloadPackageAsync(string packageId, NuGetVersion packageVersion, CancellationToken cancellationToken = default) + if (stream == null) { - var stream = await _contentClient.DownloadPackageOrNullAsync(packageId, packageVersion, cancellationToken); + throw new PackageNotFoundException(packageId, packageVersion); + } - if (stream == null) - { - throw new PackageNotFoundException(packageId, packageVersion); - } + return stream; + } - return stream; - } + /// + /// Download a package's manifest (.nuspec), or throws if the package does not exist. + /// + /// The package ID. + /// The package version. + /// A token to cancel the task. + /// The package's manifest stream. The stream may not be seekable. + /// + /// The package could not be found. + /// + public virtual async Task DownloadPackageManifestAsync( + string packageId, + NuGetVersion packageVersion, + CancellationToken cancellationToken = default) + { + var stream = await _contentClient.DownloadPackageManifestOrNullAsync(packageId, packageVersion, cancellationToken); - /// - /// Download a package's manifest (.nuspec), or throws if the package does not exist. - /// - /// The package ID. - /// The package version. - /// A token to cancel the task. - /// The package's manifest stream. The stream may not be seekable. - /// - /// The package could not be found. - /// - public virtual async Task DownloadPackageManifestAsync( - string packageId, - NuGetVersion packageVersion, - CancellationToken cancellationToken = default) + if (stream == null) { - var stream = await _contentClient.DownloadPackageManifestOrNullAsync(packageId, packageVersion, cancellationToken); + throw new PackageNotFoundException(packageId, packageVersion); + } - if (stream == null) - { - throw new PackageNotFoundException(packageId, packageVersion); - } + return stream; + } - return stream; - } + /// + /// Find all versions of a package, excluding unlisted versions. + /// + /// The package ID. + /// A token to cancel the task. + /// The package's listed versions, if any. + public virtual async Task> ListPackageVersionsAsync( + string packageId, + CancellationToken cancellationToken = default) + { + // TODO: Use the Autocomplete's enumerate versions endpoint if this is not Sleet. + var packages = await GetPackageMetadataAsync(packageId, cancellationToken); - /// - /// Find all versions of a package, excluding unlisted versions. - /// - /// The package ID. - /// A token to cancel the task. - /// The package's listed versions, if any. - public virtual async Task> ListPackageVersionsAsync( - string packageId, - CancellationToken cancellationToken = default) - { - // TODO: Use the Autocomplete's enumerate versions endpoint if this is not Sleet. - var packages = await GetPackageMetadataAsync(packageId, cancellationToken); + return packages + .Where(p => p.IsListed()) + .Select(p => p.ParseVersion()) + .ToList(); + } - return packages - .Where(p => p.IsListed()) - .Select(p => p.ParseVersion()) - .ToList(); + /// + /// Find all versions of a package. + /// + /// The package ID. + /// Whether to include unlisted versions. + /// A token to cancel the task. + /// The package's versions, or an empty list if the package does not exist. + public virtual async Task> ListPackageVersionsAsync( + string packageId, + bool includeUnlisted, + CancellationToken cancellationToken = default) + { + if (!includeUnlisted) + { + return await ListPackageVersionsAsync(packageId, cancellationToken); } - /// - /// Find all versions of a package. - /// - /// The package ID. - /// Whether to include unlisted versions. - /// A token to cancel the task. - /// The package's versions, or an empty list if the package does not exist. - public virtual async Task> ListPackageVersionsAsync( - string packageId, - bool includeUnlisted, - CancellationToken cancellationToken = default) + var response = await _contentClient.GetPackageVersionsOrNullAsync(packageId, cancellationToken); + + if (response == null) { - if (!includeUnlisted) - { - return await ListPackageVersionsAsync(packageId, cancellationToken); - } + return new List(); + } - var response = await _contentClient.GetPackageVersionsOrNullAsync(packageId, cancellationToken); + return response.ParseVersions(); + } - if (response == null) - { - return new List(); - } + /// + /// Find the metadata for all versions of a package, including unlisted versions. + /// + /// The package ID. + /// A token to cancel the task. + /// The package's metadata, or an empty list if the package does not exist. + public virtual async Task> GetPackageMetadataAsync( + string packageId, + CancellationToken cancellationToken = default) + { + var result = new List(); - return response.ParseVersions(); - } + var registrationIndex = await _metadataClient.GetRegistrationIndexOrNullAsync(packageId, cancellationToken); - /// - /// Find the metadata for all versions of a package, including unlisted versions. - /// - /// The package ID. - /// A token to cancel the task. - /// The package's metadata, or an empty list if the package does not exist. - public virtual async Task> GetPackageMetadataAsync( - string packageId, - CancellationToken cancellationToken = default) + if (registrationIndex == null) { - var result = new List(); - - var registrationIndex = await _metadataClient.GetRegistrationIndexOrNullAsync(packageId, cancellationToken); + return result; + } - if (registrationIndex == null) + foreach (var registrationIndexPage in registrationIndex.Pages) + { + // If the package's registration index is too big, it will be split into registration + // pages stored at different URLs. We will need to fetch each page's items individually. + // We can detect this case as the registration index will have "null" items. + var items = registrationIndexPage.ItemsOrNull; + if (items == null) { - return result; - } + var externalRegistrationPage = await _metadataClient.GetRegistrationPageAsync( + registrationIndexPage.RegistrationPageUrl, + cancellationToken); - foreach (var registrationIndexPage in registrationIndex.Pages) - { - // If the package's registration index is too big, it will be split into registration - // pages stored at different URLs. We will need to fetch each page's items individually. - // We can detect this case as the registration index will have "null" items. - var items = registrationIndexPage.ItemsOrNull; - if (items == null) - { - var externalRegistrationPage = await _metadataClient.GetRegistrationPageAsync( - registrationIndexPage.RegistrationPageUrl, - cancellationToken); - - // Skip malformed external pages. - if (externalRegistrationPage?.ItemsOrNull == null) continue; - - items = externalRegistrationPage.ItemsOrNull; - } - - result.AddRange(items.Select(i => i.PackageMetadata)); + // Skip malformed external pages. + if (externalRegistrationPage?.ItemsOrNull == null) continue; + + items = externalRegistrationPage.ItemsOrNull; } - return result; + result.AddRange(items.Select(i => i.PackageMetadata)); } - /// - /// Find the metadata for a single version of a package, or throws if the package does not exist. - /// - /// The package ID. - /// The package version. - /// A token to cancel the task. - /// The package's metadata. - /// - /// The package could not be found. - /// - public virtual async Task GetPackageMetadataAsync( - string packageId, - NuGetVersion packageVersion, - CancellationToken cancellationToken = default) + return result; + } + + /// + /// Find the metadata for a single version of a package, or throws if the package does not exist. + /// + /// The package ID. + /// The package version. + /// A token to cancel the task. + /// The package's metadata. + /// + /// The package could not be found. + /// + public virtual async Task GetPackageMetadataAsync( + string packageId, + NuGetVersion packageVersion, + CancellationToken cancellationToken = default) + { + var registrationIndex = await _metadataClient.GetRegistrationIndexOrNullAsync(packageId, cancellationToken); + + if (registrationIndex == null) { - var registrationIndex = await _metadataClient.GetRegistrationIndexOrNullAsync(packageId, cancellationToken); + throw new PackageNotFoundException(packageId, packageVersion); + } - if (registrationIndex == null) + foreach (var registrationIndexPage in registrationIndex.Pages) + { + // Skip pages that do not contain the desired package version. + var pageLowerVersion = registrationIndexPage.ParseLower(); + var pageUpperVersion = registrationIndexPage.ParseUpper(); + + if (pageLowerVersion > packageVersion) continue; + if (pageUpperVersion < packageVersion) continue; + + // If the package's registration index is too big, it will be split into registration + // pages stored at different URLs. We will need to fetch each page's items individually. + // We can detect this case as the registration index will have "null" items. + var items = registrationIndexPage.ItemsOrNull; + if (items == null) { - throw new PackageNotFoundException(packageId, packageVersion); + var externalRegistrationPage = await _metadataClient.GetRegistrationPageAsync( + registrationIndexPage.RegistrationPageUrl, + cancellationToken); + + // Skip malformed external pages. + if (externalRegistrationPage?.ItemsOrNull == null) continue; + + items = externalRegistrationPage.ItemsOrNull; } - foreach (var registrationIndexPage in registrationIndex.Pages) + // We've found the registration items that should cover the desired package. + var result = items.SingleOrDefault(i => i.PackageMetadata.ParseVersion() == packageVersion); + if (result == null) { - // Skip pages that do not contain the desired package version. - var pageLowerVersion = registrationIndexPage.ParseLower(); - var pageUpperVersion = registrationIndexPage.ParseUpper(); - - if (pageLowerVersion > packageVersion) continue; - if (pageUpperVersion < packageVersion) continue; - - // If the package's registration index is too big, it will be split into registration - // pages stored at different URLs. We will need to fetch each page's items individually. - // We can detect this case as the registration index will have "null" items. - var items = registrationIndexPage.ItemsOrNull; - if (items == null) - { - var externalRegistrationPage = await _metadataClient.GetRegistrationPageAsync( - registrationIndexPage.RegistrationPageUrl, - cancellationToken); - - // Skip malformed external pages. - if (externalRegistrationPage?.ItemsOrNull == null) continue; - - items = externalRegistrationPage.ItemsOrNull; - } - - // We've found the registration items that should cover the desired package. - var result = items.SingleOrDefault(i => i.PackageMetadata.ParseVersion() == packageVersion); - if (result == null) - { - break; - } - - return result.PackageMetadata; + break; } - // No registration pages contained the desired version. - throw new PackageNotFoundException(packageId, packageVersion); + return result.PackageMetadata; } - /// - /// Search for packages. Includes prerelease packages. - /// - /// - /// The search query. If , gets default search results. - /// - /// A token to cancel the task. - /// The search results, including prerelease packages. - public virtual async Task> SearchAsync( - string query = null, - CancellationToken cancellationToken = default) - { - var response = await _searchClient.SearchAsync(query, cancellationToken: cancellationToken); + // No registration pages contained the desired version. + throw new PackageNotFoundException(packageId, packageVersion); + } - return response.Data; - } + /// + /// Search for packages. Includes prerelease packages. + /// + /// + /// The search query. If , gets default search results. + /// + /// A token to cancel the task. + /// The search results, including prerelease packages. + public virtual async Task> SearchAsync( + string query = null, + CancellationToken cancellationToken = default) + { + var response = await _searchClient.SearchAsync(query, cancellationToken: cancellationToken); - /// - /// Search for packages. Includes prerelease packages. - /// - /// - /// The search query. If , gets default search results. - /// - /// The number of results to skip. - /// The number of results to include. - /// A token to cancel the task. - /// The search results, including prerelease packages. - public virtual async Task> SearchAsync( - string query, - int skip, - int take, - CancellationToken cancellationToken = default) - { - var response = await _searchClient.SearchAsync( - query, - skip, - take, - includePrerelease: true, - includeSemVer2: true, - cancellationToken: cancellationToken); - - return response.Data; - } + return response.Data; + } - /// - /// Search for packages. - /// - /// - /// The search query. If , gets default search results. - /// - /// Whether to include prerelease packages. - /// A token to cancel the task. - /// The search results. - public virtual async Task> SearchAsync( - string query, - bool includePrerelease, - CancellationToken cancellationToken = default) - { - var response = await _searchClient.SearchAsync( - query, - includePrerelease: includePrerelease, - cancellationToken: cancellationToken); + /// + /// Search for packages. Includes prerelease packages. + /// + /// + /// The search query. If , gets default search results. + /// + /// The number of results to skip. + /// The number of results to include. + /// A token to cancel the task. + /// The search results, including prerelease packages. + public virtual async Task> SearchAsync( + string query, + int skip, + int take, + CancellationToken cancellationToken = default) + { + var response = await _searchClient.SearchAsync( + query, + skip, + take, + includePrerelease: true, + includeSemVer2: true, + cancellationToken: cancellationToken); + + return response.Data; + } - return response.Data; - } + /// + /// Search for packages. + /// + /// + /// The search query. If , gets default search results. + /// + /// Whether to include prerelease packages. + /// A token to cancel the task. + /// The search results. + public virtual async Task> SearchAsync( + string query, + bool includePrerelease, + CancellationToken cancellationToken = default) + { + var response = await _searchClient.SearchAsync( + query, + includePrerelease: includePrerelease, + cancellationToken: cancellationToken); - /// - /// Search for packages. - /// - /// - /// The search query. If , gets default search results. - /// - /// The number of results to skip. - /// The number of results to include. - /// Whether to include prerelease packages. - /// A token to cancel the task. - /// The search results, including prerelease packages. - public virtual async Task> SearchAsync( - string query, - int skip, - int take, - bool includePrerelease, - CancellationToken cancellationToken = default) - { - var response = await _searchClient.SearchAsync( - query, - skip, - take, - includePrerelease, - includeSemVer2: true, - cancellationToken); - - return response.Data; - } + return response.Data; + } - /// - /// Search for package IDs. Includes prerelease packages. - /// - /// - /// The search query. If , gets default autocomplete results. - /// - /// A token to cancel the task. - /// The package IDs that matched the query. - public virtual async Task> AutocompleteAsync( - string query = null, - CancellationToken cancellationToken = default) - { - var response = await _autocompleteClient.AutocompleteAsync(query, cancellationToken: cancellationToken); + /// + /// Search for packages. + /// + /// + /// The search query. If , gets default search results. + /// + /// The number of results to skip. + /// The number of results to include. + /// Whether to include prerelease packages. + /// A token to cancel the task. + /// The search results, including prerelease packages. + public virtual async Task> SearchAsync( + string query, + int skip, + int take, + bool includePrerelease, + CancellationToken cancellationToken = default) + { + var response = await _searchClient.SearchAsync( + query, + skip, + take, + includePrerelease, + includeSemVer2: true, + cancellationToken); + + return response.Data; + } - return response.Data; - } + /// + /// Search for package IDs. Includes prerelease packages. + /// + /// + /// The search query. If , gets default autocomplete results. + /// + /// A token to cancel the task. + /// The package IDs that matched the query. + public virtual async Task> AutocompleteAsync( + string query = null, + CancellationToken cancellationToken = default) + { + var response = await _autocompleteClient.AutocompleteAsync(query, cancellationToken: cancellationToken); - /// - /// Search for package IDs. Includes prerelease packages. - /// - /// - /// The search query. If , gets default autocomplete results. - /// - /// The number of results to skip. - /// The number of results to include. - /// A token to cancel the task. - /// The package IDs that matched the query. - public virtual async Task> AutocompleteAsync( - string query, - int skip, - int take, - CancellationToken cancellationToken = default) - { - var response = await _autocompleteClient.AutocompleteAsync( - query, - skip, - take, - includePrerelease: true, - includeSemVer2: true, - cancellationToken); - - return response.Data; - } + return response.Data; + } - /// - /// Search for package IDs. - /// - /// - /// The search query. If , gets default autocomplete results. - /// - /// Whether to include prerelease packages. - /// A token to cancel the task. - /// The package IDs that matched the query. - public virtual async Task> AutocompleteAsync( - string query, - bool includePrerelease, - CancellationToken cancellationToken = default) - { - var response = await _autocompleteClient.AutocompleteAsync( - query, - includePrerelease: includePrerelease, - cancellationToken: cancellationToken); + /// + /// Search for package IDs. Includes prerelease packages. + /// + /// + /// The search query. If , gets default autocomplete results. + /// + /// The number of results to skip. + /// The number of results to include. + /// A token to cancel the task. + /// The package IDs that matched the query. + public virtual async Task> AutocompleteAsync( + string query, + int skip, + int take, + CancellationToken cancellationToken = default) + { + var response = await _autocompleteClient.AutocompleteAsync( + query, + skip, + take, + includePrerelease: true, + includeSemVer2: true, + cancellationToken); + + return response.Data; + } - return response.Data; - } + /// + /// Search for package IDs. + /// + /// + /// The search query. If , gets default autocomplete results. + /// + /// Whether to include prerelease packages. + /// A token to cancel the task. + /// The package IDs that matched the query. + public virtual async Task> AutocompleteAsync( + string query, + bool includePrerelease, + CancellationToken cancellationToken = default) + { + var response = await _autocompleteClient.AutocompleteAsync( + query, + includePrerelease: includePrerelease, + cancellationToken: cancellationToken); - /// - /// Search for package IDs. - /// - /// - /// The search query. If , gets default autocomplete results. - /// - /// The number of results to skip. - /// The number of results to include. - /// Whether to include prerelease packages. - /// A token to cancel the task. - /// The package IDs that matched the query. - public virtual async Task> AutocompleteAsync( - string query, - int skip, - int take, - bool includePrerelease, - CancellationToken cancellationToken = default) - { - var response = await _autocompleteClient.AutocompleteAsync( - query, - skip, - take, - includePrerelease, - includeSemVer2: true, - cancellationToken); - - return response.Data; - } + return response.Data; + } + + /// + /// Search for package IDs. + /// + /// + /// The search query. If , gets default autocomplete results. + /// + /// The number of results to skip. + /// The number of results to include. + /// Whether to include prerelease packages. + /// A token to cancel the task. + /// The package IDs that matched the query. + public virtual async Task> AutocompleteAsync( + string query, + int skip, + int take, + bool includePrerelease, + CancellationToken cancellationToken = default) + { + var response = await _autocompleteClient.AutocompleteAsync( + query, + skip, + take, + includePrerelease, + includeSemVer2: true, + cancellationToken); + + return response.Data; } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/NuGetClientFactory.cs b/src/BaGet.Protocol/NuGetClientFactory.cs index 452c322f..b0c376aa 100644 --- a/src/BaGet.Protocol/NuGetClientFactory.cs +++ b/src/BaGet.Protocol/NuGetClientFactory.cs @@ -1,211 +1,206 @@ -using System; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Internal; using BaGet.Protocol.Models; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +/// +/// The creates clients to interact with a NuGet server. +/// Use this for advanced scenarios. For most scenarios, consider using instead. +/// +public partial class NuGetClientFactory { + private readonly HttpClient _httpClient; + private readonly string _serviceIndexUrl; + + private readonly SemaphoreSlim _mutex; + private NuGetClients _clients; + /// - /// The creates clients to interact with a NuGet server. - /// Use this for advanced scenarios. For most scenarios, consider using instead. + /// Initializes a new instance of the class + /// for mocking. /// - public partial class NuGetClientFactory + protected NuGetClientFactory() { - private readonly HttpClient _httpClient; - private readonly string _serviceIndexUrl; - - private readonly SemaphoreSlim _mutex; - private NuGetClients _clients; + } - /// - /// Initializes a new instance of the class - /// for mocking. - /// - protected NuGetClientFactory() - { - } + /// + /// Initializes a new instance of the class. + /// + /// The client used for HTTP requests. + /// + /// The NuGet Service Index resource URL. + /// + /// For NuGet.org, use https://api.nuget.org/v3/index.json + /// + public NuGetClientFactory(HttpClient httpClient, string serviceIndexUrl) + { + _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); + _serviceIndexUrl = serviceIndexUrl ?? throw new ArgumentNullException(nameof(serviceIndexUrl)); - /// - /// Initializes a new instance of the class. - /// - /// The client used for HTTP requests. - /// - /// The NuGet Service Index resource URL. - /// - /// For NuGet.org, use https://api.nuget.org/v3/index.json - /// - public NuGetClientFactory(HttpClient httpClient, string serviceIndexUrl) - { - _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); - _serviceIndexUrl = serviceIndexUrl ?? throw new ArgumentNullException(nameof(serviceIndexUrl)); + _mutex = new SemaphoreSlim(1, 1); + _clients = null; + } - _mutex = new SemaphoreSlim(1, 1); - _clients = null; - } + /// + /// Create a client to interact with the NuGet Service Index resource. + /// + /// See https://docs.microsoft.com/en-us/nuget/api/service-index + /// + /// A client to interact with the NuGet Service Index resource. + public virtual IServiceIndexClient CreateServiceIndexClient() + { + return new ServiceIndexClient(this); + } - /// - /// Create a client to interact with the NuGet Service Index resource. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/service-index - /// - /// A client to interact with the NuGet Service Index resource. - public virtual IServiceIndexClient CreateServiceIndexClient() - { - return new ServiceIndexClient(this); - } + /// + /// Create a client to interact with the NuGet Package Content resource. + /// + /// See https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource + /// + /// A client to interact with the NuGet Package Content resource. + public virtual IPackageContentClient CreatePackageContentClient() + { + return new PackageContentClient(this); + } - /// - /// Create a client to interact with the NuGet Package Content resource. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource - /// - /// A client to interact with the NuGet Package Content resource. - public virtual IPackageContentClient CreatePackageContentClient() - { - return new PackageContentClient(this); - } + /// + /// Create a client to interact with the NuGet Package Metadata resource. + /// + /// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource + /// + /// A client to interact with the NuGet Package Metadata resource. + public virtual IPackageMetadataClient CreatePackageMetadataClient() + { + return new PackageMetadataClient(this); + } - /// - /// Create a client to interact with the NuGet Package Metadata resource. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource - /// - /// A client to interact with the NuGet Package Metadata resource. - public virtual IPackageMetadataClient CreatePackageMetadataClient() - { - return new PackageMetadataClient(this); - } + /// + /// Create a client to interact with the NuGet Search resource. + /// + /// See https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource + /// + /// A client to interact with the NuGet Search resource. + public virtual ISearchClient CreateSearchClient() + { + return new SearchClient(this); + } - /// - /// Create a client to interact with the NuGet Search resource. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource - /// - /// A client to interact with the NuGet Search resource. - public virtual ISearchClient CreateSearchClient() - { - return new SearchClient(this); - } + /// + /// Create a client to interact with the NuGet Autocomplete resource. + /// + /// See https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource + /// + /// A client to interact with the NuGet Autocomplete resource. + public virtual IAutocompleteClient CreateAutocompleteClient() + { + return new AutocompleteClient(this); + } - /// - /// Create a client to interact with the NuGet Autocomplete resource. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource - /// - /// A client to interact with the NuGet Autocomplete resource. - public virtual IAutocompleteClient CreateAutocompleteClient() - { - return new AutocompleteClient(this); - } + /// + /// Create a client to interact with the NuGet catalog resource. + /// + /// See https://docs.microsoft.com/en-us/nuget/api/catalog-resource + /// + /// A client to interact with the Catalog resource. + public virtual ICatalogClient CreateCatalogClient() + { + return new CatalogClient(this); + } - /// - /// Create a client to interact with the NuGet catalog resource. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/catalog-resource - /// - /// A client to interact with the Catalog resource. - public virtual ICatalogClient CreateCatalogClient() - { - return new CatalogClient(this); - } + private Task GetServiceIndexAsync(CancellationToken cancellationToken = default) + { + return GetAsync(c => c.ServiceIndex, cancellationToken); + } - private Task GetServiceIndexAsync(CancellationToken cancellationToken = default) - { - return GetAsync(c => c.ServiceIndex, cancellationToken); - } + private Task GetPackageContentClientAsync(CancellationToken cancellationToken = default) + { + return GetAsync(c => c.PackageContentClient, cancellationToken); + } - private Task GetPackageContentClientAsync(CancellationToken cancellationToken = default) - { - return GetAsync(c => c.PackageContentClient, cancellationToken); - } + private Task GetPackageMetadataClientAsync(CancellationToken cancellationToken = default) + { + return GetAsync(c => c.PackageMetadataClient, cancellationToken); + } - private Task GetPackageMetadataClientAsync(CancellationToken cancellationToken = default) - { - return GetAsync(c => c.PackageMetadataClient, cancellationToken); - } + private Task GetSearchClientAsync(CancellationToken cancellationToken = default) + { + return GetAsync(c => c.SearchClient, cancellationToken); + } - private Task GetSearchClientAsync(CancellationToken cancellationToken = default) - { - return GetAsync(c => c.SearchClient, cancellationToken); - } + private Task GetAutocompleteClientAsync(CancellationToken cancellationToken = default) + { + return GetAsync(c => c.AutocompleteClient, cancellationToken); + } - private Task GetAutocompleteClientAsync(CancellationToken cancellationToken = default) - { - return GetAsync(c => c.AutocompleteClient, cancellationToken); - } + private Task GetCatalogClientAsync(CancellationToken cancellationToken = default) + { + return GetAsync(c => c.CatalogClient, cancellationToken); + } - private Task GetCatalogClientAsync(CancellationToken cancellationToken = default) + private async Task GetAsync(Func clientFactory, CancellationToken cancellationToken) + { + if (_clients == null) { - return GetAsync(c => c.CatalogClient, cancellationToken); - } + await _mutex.WaitAsync(cancellationToken); - private async Task GetAsync(Func clientFactory, CancellationToken cancellationToken) - { - if (_clients == null) + try { - await _mutex.WaitAsync(cancellationToken); - - try + if (_clients == null) { - if (_clients == null) + var serviceIndexClient = new RawServiceIndexClient(_httpClient, _serviceIndexUrl); + + var serviceIndex = await serviceIndexClient.GetAsync(cancellationToken); + + var contentResourceUrl = serviceIndex.GetPackageContentResourceUrl(); + var metadataResourceUrl = serviceIndex.GetPackageMetadataResourceUrl(); + var catalogResourceUrl = serviceIndex.GetCatalogResourceUrl(); + var searchResourceUrl = serviceIndex.GetSearchQueryResourceUrl(); + var autocompleteResourceUrl = serviceIndex.GetSearchAutocompleteResourceUrl(); + + // Create clients for required resources. + var contentClient = new RawPackageContentClient(_httpClient, contentResourceUrl); + var metadataClient = new RawPackageMetadataClient(_httpClient, metadataResourceUrl); + var searchClient = new RawSearchClient(_httpClient, searchResourceUrl); + + // Create clients for optional resources. + var catalogClient = catalogResourceUrl == null + ? new NullCatalogClient() as ICatalogClient + : new RawCatalogClient(_httpClient, catalogResourceUrl); + var autocompleteClient = autocompleteResourceUrl == null + ? new NullAutocompleteClient() as IAutocompleteClient + : new RawAutocompleteClient(_httpClient, autocompleteResourceUrl); + + _clients = new NuGetClients { - var serviceIndexClient = new RawServiceIndexClient(_httpClient, _serviceIndexUrl); - - var serviceIndex = await serviceIndexClient.GetAsync(cancellationToken); - - var contentResourceUrl = serviceIndex.GetPackageContentResourceUrl(); - var metadataResourceUrl = serviceIndex.GetPackageMetadataResourceUrl(); - var catalogResourceUrl = serviceIndex.GetCatalogResourceUrl(); - var searchResourceUrl = serviceIndex.GetSearchQueryResourceUrl(); - var autocompleteResourceUrl = serviceIndex.GetSearchAutocompleteResourceUrl(); - - // Create clients for required resources. - var contentClient = new RawPackageContentClient(_httpClient, contentResourceUrl); - var metadataClient = new RawPackageMetadataClient(_httpClient, metadataResourceUrl); - var searchClient = new RawSearchClient(_httpClient, searchResourceUrl); - - // Create clients for optional resources. - var catalogClient = catalogResourceUrl == null - ? new NullCatalogClient() as ICatalogClient - : new RawCatalogClient(_httpClient, catalogResourceUrl); - var autocompleteClient = autocompleteResourceUrl == null - ? new NullAutocompleteClient() as IAutocompleteClient - : new RawAutocompleteClient(_httpClient, autocompleteResourceUrl); - - _clients = new NuGetClients - { - ServiceIndex = serviceIndex, - - PackageContentClient = contentClient, - PackageMetadataClient = metadataClient, - SearchClient = searchClient, - AutocompleteClient = autocompleteClient, - CatalogClient = catalogClient, - }; - } - } - finally - { - _mutex.Release(); + ServiceIndex = serviceIndex, + + PackageContentClient = contentClient, + PackageMetadataClient = metadataClient, + SearchClient = searchClient, + AutocompleteClient = autocompleteClient, + CatalogClient = catalogClient, + }; } } - - // TODO: This should periodically refresh the service index response. - return clientFactory(_clients); + finally + { + _mutex.Release(); + } } - private class NuGetClients - { - public ServiceIndexResponse ServiceIndex { get; set; } + // TODO: This should periodically refresh the service index response. + return clientFactory(_clients); + } - public IPackageContentClient PackageContentClient { get; set; } - public IPackageMetadataClient PackageMetadataClient { get; set; } - public ISearchClient SearchClient { get; set; } - public IAutocompleteClient AutocompleteClient { get; set; } - public ICatalogClient CatalogClient { get; set; } - } + private class NuGetClients + { + public ServiceIndexResponse ServiceIndex { get; set; } + + public IPackageContentClient PackageContentClient { get; set; } + public IPackageMetadataClient PackageMetadataClient { get; set; } + public ISearchClient SearchClient { get; set; } + public IAutocompleteClient AutocompleteClient { get; set; } + public ICatalogClient CatalogClient { get; set; } } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/PackageContent/IPackageContentClient.cs b/src/BaGet.Protocol/PackageContent/IPackageContentClient.cs index 33422254..4b40c334 100644 --- a/src/BaGet.Protocol/PackageContent/IPackageContentClient.cs +++ b/src/BaGet.Protocol/PackageContent/IPackageContentClient.cs @@ -1,57 +1,53 @@ -using System.IO; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; using NuGet.Versioning; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +/// +/// The Package Content resource, used to download NuGet packages and to fetch other metadata. +/// +/// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource +/// +public interface IPackageContentClient { /// - /// The Package Content resource, used to download NuGet packages and to fetch other metadata. - /// - /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource + /// Get a package's versions, or null if the package does not exist. + /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#enumerate-package-versions /// - public interface IPackageContentClient - { - /// - /// Get a package's versions, or null if the package does not exist. - /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#enumerate-package-versions - /// - /// The package ID. - /// A token to cancel the task. - /// The package's versions, or null if the package does not exist. - Task GetPackageVersionsOrNullAsync( - string packageId, - CancellationToken cancellationToken = default); + /// The package ID. + /// A token to cancel the task. + /// The package's versions, or null if the package does not exist. + Task GetPackageVersionsOrNullAsync( + string packageId, + CancellationToken cancellationToken = default); - /// - /// Download a package, or null if the package does not exist. - /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-content-nupkg - /// - /// The package ID. - /// The package's version. - /// A token to cancel the task. - /// - /// The package's content stream, or null if the package does not exist. The stream may not be seekable. - /// - Task DownloadPackageOrNullAsync( - string packageId, - NuGetVersion packageVersion, - CancellationToken cancellationToken = default); + /// + /// Download a package, or null if the package does not exist. + /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-content-nupkg + /// + /// The package ID. + /// The package's version. + /// A token to cancel the task. + /// + /// The package's content stream, or null if the package does not exist. The stream may not be seekable. + /// + Task DownloadPackageOrNullAsync( + string packageId, + NuGetVersion packageVersion, + CancellationToken cancellationToken = default); - /// - /// Download a package's manifest (nuspec), or null if the package does not exist. - /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-manifest-nuspec - /// - /// The package id. - /// The package's version. - /// A token to cancel the task. - /// - /// The package's manifest stream, or null if the package does not exist. The stream may not be seekable. - /// - Task DownloadPackageManifestOrNullAsync( - string packageId, - NuGetVersion packageVersion, - CancellationToken cancellationToken = default); - } -} + /// + /// Download a package's manifest (nuspec), or null if the package does not exist. + /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-manifest-nuspec + /// + /// The package id. + /// The package's version. + /// A token to cancel the task. + /// + /// The package's manifest stream, or null if the package does not exist. The stream may not be seekable. + /// + Task DownloadPackageManifestOrNullAsync( + string packageId, + NuGetVersion packageVersion, + CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/BaGet.Protocol/PackageContent/PackageContentClient.cs b/src/BaGet.Protocol/PackageContent/PackageContentClient.cs index a42c00fd..df012a44 100644 --- a/src/BaGet.Protocol/PackageContent/PackageContentClient.cs +++ b/src/BaGet.Protocol/PackageContent/PackageContentClient.cs @@ -1,51 +1,46 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; using NuGet.Versioning; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +public partial class NuGetClientFactory { - public partial class NuGetClientFactory + private class PackageContentClient : IPackageContentClient { - private class PackageContentClient : IPackageContentClient + private readonly NuGetClientFactory _clientfactory; + + public PackageContentClient(NuGetClientFactory clientFactory) + { + _clientfactory = clientFactory ?? throw new ArgumentNullException(nameof(clientFactory)); + } + + public async Task DownloadPackageOrNullAsync( + string packageId, + NuGetVersion packageVersion, + CancellationToken cancellationToken = default) + { + var client = await _clientfactory.GetPackageContentClientAsync(cancellationToken); + + return await client.DownloadPackageOrNullAsync(packageId, packageVersion, cancellationToken); + } + + public async Task DownloadPackageManifestOrNullAsync( + string packageId, + NuGetVersion packageVersion, + CancellationToken cancellationToken = default) { - private readonly NuGetClientFactory _clientfactory; - - public PackageContentClient(NuGetClientFactory clientFactory) - { - _clientfactory = clientFactory ?? throw new ArgumentNullException(nameof(clientFactory)); - } - - public async Task DownloadPackageOrNullAsync( - string packageId, - NuGetVersion packageVersion, - CancellationToken cancellationToken = default) - { - var client = await _clientfactory.GetPackageContentClientAsync(cancellationToken); - - return await client.DownloadPackageOrNullAsync(packageId, packageVersion, cancellationToken); - } - - public async Task DownloadPackageManifestOrNullAsync( - string packageId, - NuGetVersion packageVersion, - CancellationToken cancellationToken = default) - { - var client = await _clientfactory.GetPackageContentClientAsync(cancellationToken); - - return await client.DownloadPackageManifestOrNullAsync(packageId, packageVersion, cancellationToken); - } - - public async Task GetPackageVersionsOrNullAsync( - string packageId, - CancellationToken cancellationToken = default) - { - var client = await _clientfactory.GetPackageContentClientAsync(cancellationToken); - - return await client.GetPackageVersionsOrNullAsync(packageId, cancellationToken); - } + var client = await _clientfactory.GetPackageContentClientAsync(cancellationToken); + + return await client.DownloadPackageManifestOrNullAsync(packageId, packageVersion, cancellationToken); + } + + public async Task GetPackageVersionsOrNullAsync( + string packageId, + CancellationToken cancellationToken = default) + { + var client = await _clientfactory.GetPackageContentClientAsync(cancellationToken); + + return await client.GetPackageVersionsOrNullAsync(packageId, cancellationToken); } } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/PackageContent/RawPackageContentClient.cs b/src/BaGet.Protocol/PackageContent/RawPackageContentClient.cs index 1d172844..c65c8cd4 100644 --- a/src/BaGet.Protocol/PackageContent/RawPackageContentClient.cs +++ b/src/BaGet.Protocol/PackageContent/RawPackageContentClient.cs @@ -1,85 +1,79 @@ -using System; -using System.IO; using System.Net; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; using NuGet.Versioning; -namespace BaGet.Protocol.Internal +namespace BaGet.Protocol.Internal; + +/// +/// The client to interact with an upstream source's Package Content resource. +/// +public class RawPackageContentClient : IPackageContentClient { + private readonly HttpClient _httpClient; + private readonly string _packageContentUrl; + /// - /// The client to interact with an upstream source's Package Content resource. + /// Create a new Package Content client. /// - public class RawPackageContentClient : IPackageContentClient + /// The HTTP client used to send requests. + /// The NuGet Server's package content URL. + public RawPackageContentClient(HttpClient httpClient, string packageContentUrl) { - private readonly HttpClient _httpClient; - private readonly string _packageContentUrl; - - /// - /// Create a new Package Content client. - /// - /// The HTTP client used to send requests. - /// The NuGet Server's package content URL. - public RawPackageContentClient(HttpClient httpClient, string packageContentUrl) - { - _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); - _packageContentUrl = packageContentUrl?.TrimEnd('/') - ?? throw new ArgumentNullException(nameof(packageContentUrl)); - } - - /// - public async Task GetPackageVersionsOrNullAsync( - string packageId, - CancellationToken cancellationToken = default) - { - var id = packageId.ToLowerInvariant(); - var url = $"{_packageContentUrl}/{id}/index.json"; + _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); + _packageContentUrl = packageContentUrl?.TrimEnd('/') + ?? throw new ArgumentNullException(nameof(packageContentUrl)); + } - return await _httpClient.GetFromJsonOrDefaultAsync(url, cancellationToken); - } + /// + public async Task GetPackageVersionsOrNullAsync( + string packageId, + CancellationToken cancellationToken = default) + { + var id = packageId.ToLowerInvariant(); + var url = $"{_packageContentUrl}/{id}/index.json"; - /// - public async Task DownloadPackageOrNullAsync( - string packageId, - NuGetVersion packageVersion, - CancellationToken cancellationToken = default) - { - var id = packageId.ToLowerInvariant(); - var version = packageVersion.ToNormalizedString().ToLowerInvariant(); + return await _httpClient.GetFromJsonOrDefaultAsync(url, cancellationToken); + } - // The response will be disposed when the returned content stream is disposed. - var url = $"{_packageContentUrl}/{id}/{version}/{id}.{version}.nupkg"; - var response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken); + /// + public async Task DownloadPackageOrNullAsync( + string packageId, + NuGetVersion packageVersion, + CancellationToken cancellationToken = default) + { + var id = packageId.ToLowerInvariant(); + var version = packageVersion.ToNormalizedString().ToLowerInvariant(); - if (response.StatusCode == HttpStatusCode.NotFound) - { - return null; - } + // The response will be disposed when the returned content stream is disposed. + var url = $"{_packageContentUrl}/{id}/{version}/{id}.{version}.nupkg"; + var response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken); - return await response.Content.ReadAsStreamAsync(); + if (response.StatusCode == HttpStatusCode.NotFound) + { + return null; } - /// - public async Task DownloadPackageManifestOrNullAsync( - string packageId, - NuGetVersion packageVersion, - CancellationToken cancellationToken = default) - { - var id = packageId.ToLowerInvariant(); - var version = packageVersion.ToNormalizedString().ToLowerInvariant(); + return await response.Content.ReadAsStreamAsync(); + } - // The response will be disposed when the returned content stream is disposed. - var url = $"{_packageContentUrl}/{id}/{version}/{id}.nuspec"; - var response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken); + /// + public async Task DownloadPackageManifestOrNullAsync( + string packageId, + NuGetVersion packageVersion, + CancellationToken cancellationToken = default) + { + var id = packageId.ToLowerInvariant(); + var version = packageVersion.ToNormalizedString().ToLowerInvariant(); - if (response.StatusCode == HttpStatusCode.NotFound) - { - return null; - } + // The response will be disposed when the returned content stream is disposed. + var url = $"{_packageContentUrl}/{id}/{version}/{id}.nuspec"; + var response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken); - return await response.Content.ReadAsStreamAsync(); + if (response.StatusCode == HttpStatusCode.NotFound) + { + return null; } + + return await response.Content.ReadAsStreamAsync(); } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/PackageMetadata/IPackageMetadataClient.cs b/src/BaGet.Protocol/PackageMetadata/IPackageMetadataClient.cs index fb3e06f8..d24eec1d 100644 --- a/src/BaGet.Protocol/PackageMetadata/IPackageMetadataClient.cs +++ b/src/BaGet.Protocol/PackageMetadata/IPackageMetadataClient.cs @@ -1,44 +1,41 @@ -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +/// +/// The Package Metadata client, used to fetch packages' metadata. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource +/// +public interface IPackageMetadataClient { /// - /// The Package Metadata client, used to fetch packages' metadata. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource + /// Attempt to get a package's registration index, if it exists. + /// See: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-page /// - public interface IPackageMetadataClient - { - /// - /// Attempt to get a package's registration index, if it exists. - /// See: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-page - /// - /// The package's ID. - /// A token to cancel the task. - /// The package's registration index, or null if the package does not exist - Task GetRegistrationIndexOrNullAsync(string packageId, CancellationToken cancellationToken = default); + /// The package's ID. + /// A token to cancel the task. + /// The package's registration index, or null if the package does not exist + Task GetRegistrationIndexOrNullAsync(string packageId, CancellationToken cancellationToken = default); - /// - /// Get a page that was linked from the package's registration index. - /// See: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-page - /// - /// The URL of the page, from the . - /// A token to cancel the task. - /// The registration index page. - Task GetRegistrationPageAsync( - string pageUrl, - CancellationToken cancellationToken = default); + /// + /// Get a page that was linked from the package's registration index. + /// See: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-page + /// + /// The URL of the page, from the . + /// A token to cancel the task. + /// The registration index page. + Task GetRegistrationPageAsync( + string pageUrl, + CancellationToken cancellationToken = default); - /// - /// Get the metadata for a single package version. - /// - /// The URL of the leaf, from the . - /// A token to cancel the task. - /// The registration leaf. - Task GetRegistrationLeafAsync( - string leafUrl, - CancellationToken cancellationToken = default); - } -} + /// + /// Get the metadata for a single package version. + /// + /// The URL of the leaf, from the . + /// A token to cancel the task. + /// The registration leaf. + Task GetRegistrationLeafAsync( + string leafUrl, + CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/BaGet.Protocol/PackageMetadata/PackageMetadataClient.cs b/src/BaGet.Protocol/PackageMetadata/PackageMetadataClient.cs index 9d338bc2..a0bd04da 100644 --- a/src/BaGet.Protocol/PackageMetadata/PackageMetadataClient.cs +++ b/src/BaGet.Protocol/PackageMetadata/PackageMetadataClient.cs @@ -1,47 +1,43 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +public partial class NuGetClientFactory { - public partial class NuGetClientFactory + private class PackageMetadataClient : IPackageMetadataClient { - private class PackageMetadataClient : IPackageMetadataClient + private readonly NuGetClientFactory _clientfactory; + + public PackageMetadataClient(NuGetClientFactory clientFactory) + { + _clientfactory = clientFactory ?? throw new ArgumentNullException(nameof(clientFactory)); + } + + public async Task GetRegistrationIndexOrNullAsync( + string packageId, + CancellationToken cancellationToken = default) + { + var client = await _clientfactory.GetPackageMetadataClientAsync(cancellationToken); + + return await client.GetRegistrationIndexOrNullAsync(packageId, cancellationToken); + } + + public async Task GetRegistrationPageAsync( + string pageUrl, + CancellationToken cancellationToken = default) { - private readonly NuGetClientFactory _clientfactory; - - public PackageMetadataClient(NuGetClientFactory clientFactory) - { - _clientfactory = clientFactory ?? throw new ArgumentNullException(nameof(clientFactory)); - } - - public async Task GetRegistrationIndexOrNullAsync( - string packageId, - CancellationToken cancellationToken = default) - { - var client = await _clientfactory.GetPackageMetadataClientAsync(cancellationToken); - - return await client.GetRegistrationIndexOrNullAsync(packageId, cancellationToken); - } - - public async Task GetRegistrationPageAsync( - string pageUrl, - CancellationToken cancellationToken = default) - { - var client = await _clientfactory.GetPackageMetadataClientAsync(cancellationToken); - - return await client.GetRegistrationPageAsync(pageUrl, cancellationToken); - } - - public async Task GetRegistrationLeafAsync( - string leafUrl, - CancellationToken cancellationToken = default) - { - var client = await _clientfactory.GetPackageMetadataClientAsync(cancellationToken); - - return await client.GetRegistrationLeafAsync(leafUrl, cancellationToken); - } + var client = await _clientfactory.GetPackageMetadataClientAsync(cancellationToken); + + return await client.GetRegistrationPageAsync(pageUrl, cancellationToken); + } + + public async Task GetRegistrationLeafAsync( + string leafUrl, + CancellationToken cancellationToken = default) + { + var client = await _clientfactory.GetPackageMetadataClientAsync(cancellationToken); + + return await client.GetRegistrationLeafAsync(leafUrl, cancellationToken); } } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/PackageMetadata/RawPackageMetadataClient.cs b/src/BaGet.Protocol/PackageMetadata/RawPackageMetadataClient.cs index 75f7071f..35e4c2f3 100644 --- a/src/BaGet.Protocol/PackageMetadata/RawPackageMetadataClient.cs +++ b/src/BaGet.Protocol/PackageMetadata/RawPackageMetadataClient.cs @@ -1,54 +1,49 @@ -using System; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Protocol.Internal +namespace BaGet.Protocol.Internal; + +/// +/// The client to interact with an upstream source's Package Metadata resource. +/// +public class RawPackageMetadataClient : IPackageMetadataClient { + private readonly HttpClient _httpClient; + private readonly string _packageMetadataUrl; + /// - /// The client to interact with an upstream source's Package Metadata resource. + /// Create a new Package Metadata client. /// - public class RawPackageMetadataClient : IPackageMetadataClient + /// The HTTP client used to send requests. + /// The NuGet server's registration resource URL. + public RawPackageMetadataClient(HttpClient httpClient, string registrationBaseUrl) { - private readonly HttpClient _httpClient; - private readonly string _packageMetadataUrl; - - /// - /// Create a new Package Metadata client. - /// - /// The HTTP client used to send requests. - /// The NuGet server's registration resource URL. - public RawPackageMetadataClient(HttpClient httpClient, string registrationBaseUrl) - { - _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); - _packageMetadataUrl = registrationBaseUrl ?? throw new ArgumentNullException(nameof(registrationBaseUrl)); - } + _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); + _packageMetadataUrl = registrationBaseUrl ?? throw new ArgumentNullException(nameof(registrationBaseUrl)); + } - /// - public async Task GetRegistrationIndexOrNullAsync( - string packageId, - CancellationToken cancellationToken = default) - { - var url = $"{_packageMetadataUrl}/{packageId.ToLowerInvariant()}/index.json"; + /// + public async Task GetRegistrationIndexOrNullAsync( + string packageId, + CancellationToken cancellationToken = default) + { + var url = $"{_packageMetadataUrl}/{packageId.ToLowerInvariant()}/index.json"; - return await _httpClient.GetFromJsonOrDefaultAsync(url, cancellationToken); - } + return await _httpClient.GetFromJsonOrDefaultAsync(url, cancellationToken); + } - /// - public async Task GetRegistrationPageAsync( - string pageUrl, - CancellationToken cancellationToken = default) - { - return await _httpClient.GetFromJsonAsync(pageUrl, cancellationToken); - } + /// + public async Task GetRegistrationPageAsync( + string pageUrl, + CancellationToken cancellationToken = default) + { + return await _httpClient.GetFromJsonAsync(pageUrl, cancellationToken); + } - /// - public async Task GetRegistrationLeafAsync( - string leafUrl, - CancellationToken cancellationToken = default) - { - return await _httpClient.GetFromJsonAsync(leafUrl, cancellationToken); - } + /// + public async Task GetRegistrationLeafAsync( + string leafUrl, + CancellationToken cancellationToken = default) + { + return await _httpClient.GetFromJsonAsync(leafUrl, cancellationToken); } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Search/AutocompleteClient.cs b/src/BaGet.Protocol/Search/AutocompleteClient.cs index de2c1c43..ce961de0 100644 --- a/src/BaGet.Protocol/Search/AutocompleteClient.cs +++ b/src/BaGet.Protocol/Search/AutocompleteClient.cs @@ -1,48 +1,44 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +public partial class NuGetClientFactory { - public partial class NuGetClientFactory + private class AutocompleteClient : IAutocompleteClient { - private class AutocompleteClient : IAutocompleteClient - { - private readonly NuGetClientFactory _clientfactory; + private readonly NuGetClientFactory _clientfactory; - public AutocompleteClient(NuGetClientFactory clientFactory) - { - _clientfactory = clientFactory ?? throw new ArgumentNullException(nameof(clientFactory)); - } + public AutocompleteClient(NuGetClientFactory clientFactory) + { + _clientfactory = clientFactory ?? throw new ArgumentNullException(nameof(clientFactory)); + } - public async Task AutocompleteAsync( - string query = null, - int skip = 0, - int take = 20, - bool includePrerelease = true, - bool includeSemVer2 = true, - CancellationToken cancellationToken = default) - { - // TODO: Support search failover. - // See: https://github.com/loic-sharma/BaGet/issues/314 - var client = await _clientfactory.GetAutocompleteClientAsync(cancellationToken); + public async Task AutocompleteAsync( + string query = null, + int skip = 0, + int take = 20, + bool includePrerelease = true, + bool includeSemVer2 = true, + CancellationToken cancellationToken = default) + { + // TODO: Support search failover. + // See: https://github.com/loic-sharma/BaGet/issues/314 + var client = await _clientfactory.GetAutocompleteClientAsync(cancellationToken); - return await client.AutocompleteAsync(query, skip, take, includePrerelease, includeSemVer2, cancellationToken); - } + return await client.AutocompleteAsync(query, skip, take, includePrerelease, includeSemVer2, cancellationToken); + } - public async Task ListPackageVersionsAsync( - string packageId, - bool includePrerelease = true, - bool includeSemVer2 = true, - CancellationToken cancellationToken = default) - { - // TODO: Support search failover. - // See: https://github.com/loic-sharma/BaGet/issues/314 - var client = await _clientfactory.GetAutocompleteClientAsync(cancellationToken); + public async Task ListPackageVersionsAsync( + string packageId, + bool includePrerelease = true, + bool includeSemVer2 = true, + CancellationToken cancellationToken = default) + { + // TODO: Support search failover. + // See: https://github.com/loic-sharma/BaGet/issues/314 + var client = await _clientfactory.GetAutocompleteClientAsync(cancellationToken); - return await client.ListPackageVersionsAsync(packageId, includePrerelease, includeSemVer2, cancellationToken); - } + return await client.ListPackageVersionsAsync(packageId, includePrerelease, includeSemVer2, cancellationToken); } } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Search/IAutocompleteClient.cs b/src/BaGet.Protocol/Search/IAutocompleteClient.cs index 5afbb4a5..b8f31540 100644 --- a/src/BaGet.Protocol/Search/IAutocompleteClient.cs +++ b/src/BaGet.Protocol/Search/IAutocompleteClient.cs @@ -1,48 +1,45 @@ -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +/// +/// The client used to search for packages. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource +/// +public interface IAutocompleteClient { /// - /// The client used to search for packages. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource + /// Perform an autocomplete query on package IDs. + /// See: https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource#search-for-package-ids /// - public interface IAutocompleteClient - { - /// - /// Perform an autocomplete query on package IDs. - /// See: https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource#search-for-package-ids - /// - /// The autocomplete query. - /// How many results to skip. - /// How many results to return. - /// Whether pre-release packages should be returned. - /// Whether packages that require SemVer 2.0.0 compatibility should be returned. - /// A token to cancel the task. - /// The autocomplete response. - Task AutocompleteAsync( - string query = null, - int skip = 0, - int take = 20, - bool includePrerelease = true, - bool includeSemVer2 = true, - CancellationToken cancellationToken = default); + /// The autocomplete query. + /// How many results to skip. + /// How many results to return. + /// Whether pre-release packages should be returned. + /// Whether packages that require SemVer 2.0.0 compatibility should be returned. + /// A token to cancel the task. + /// The autocomplete response. + Task AutocompleteAsync( + string query = null, + int skip = 0, + int take = 20, + bool includePrerelease = true, + bool includeSemVer2 = true, + CancellationToken cancellationToken = default); - /// - /// Enumerate listed package versions. - /// See: https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource#enumerate-package-versions - /// - /// The package ID. - /// Whether pre-release packages should be returned. - /// Whether packages that require SemVer 2.0.0 compatibility should be returned. - /// A token to cancel the task. - /// The package versions that matched the request. - Task ListPackageVersionsAsync( - string packageId, - bool includePrerelease = true, - bool includeSemVer2 = true, - CancellationToken cancellationToken = default); - } -} + /// + /// Enumerate listed package versions. + /// See: https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource#enumerate-package-versions + /// + /// The package ID. + /// Whether pre-release packages should be returned. + /// Whether packages that require SemVer 2.0.0 compatibility should be returned. + /// A token to cancel the task. + /// The package versions that matched the request. + Task ListPackageVersionsAsync( + string packageId, + bool includePrerelease = true, + bool includeSemVer2 = true, + CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Search/ISearchClient.cs b/src/BaGet.Protocol/Search/ISearchClient.cs index 6b21029b..d5e45f9d 100644 --- a/src/BaGet.Protocol/Search/ISearchClient.cs +++ b/src/BaGet.Protocol/Search/ISearchClient.cs @@ -1,33 +1,30 @@ -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +/// +/// The client used to search for packages. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource +/// +public interface ISearchClient { /// - /// The client used to search for packages. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource + /// Perform a search query. + /// See: https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-for-packages /// - public interface ISearchClient - { - /// - /// Perform a search query. - /// See: https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-for-packages - /// - /// The search query. - /// How many results to skip. - /// How many results to return. - /// Whether pre-release packages should be returned. - /// Whether packages that require SemVer 2.0.0 compatibility should be returned. - /// A token to cancel the task. - /// The search response. - Task SearchAsync( - string query = null, - int skip = 0, - int take = 20, - bool includePrerelease = true, - bool includeSemVer2 = true, - CancellationToken cancellationToken = default); - } -} + /// The search query. + /// How many results to skip. + /// How many results to return. + /// Whether pre-release packages should be returned. + /// Whether packages that require SemVer 2.0.0 compatibility should be returned. + /// A token to cancel the task. + /// The search response. + Task SearchAsync( + string query = null, + int skip = 0, + int take = 20, + bool includePrerelease = true, + bool includeSemVer2 = true, + CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Search/NullAutocompleteClient.cs b/src/BaGet.Protocol/Search/NullAutocompleteClient.cs index 32619388..81a0719d 100644 --- a/src/BaGet.Protocol/Search/NullAutocompleteClient.cs +++ b/src/BaGet.Protocol/Search/NullAutocompleteClient.cs @@ -1,28 +1,24 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Protocol.Internal +namespace BaGet.Protocol.Internal; + +public class NullAutocompleteClient : IAutocompleteClient { - public class NullAutocompleteClient : IAutocompleteClient + public Task AutocompleteAsync(string query = null, int skip = 0, int take = 20, bool includePrerelease = true, bool includeSemVer2 = true, CancellationToken cancellationToken = default) { - public Task AutocompleteAsync(string query = null, int skip = 0, int take = 20, bool includePrerelease = true, bool includeSemVer2 = true, CancellationToken cancellationToken = default) + return Task.FromResult(new AutocompleteResponse { - return Task.FromResult(new AutocompleteResponse - { - TotalHits = 0, - Data = new List() - }); - } + TotalHits = 0, + Data = new List() + }); + } - public Task ListPackageVersionsAsync(string packageId, bool includePrerelease = true, bool includeSemVer2 = true, CancellationToken cancellationToken = default) + public Task ListPackageVersionsAsync(string packageId, bool includePrerelease = true, bool includeSemVer2 = true, CancellationToken cancellationToken = default) + { + return Task.FromResult(new AutocompleteResponse { - return Task.FromResult(new AutocompleteResponse - { - TotalHits = 0, - Data = new List() - }); - } + TotalHits = 0, + Data = new List() + }); } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Search/RawAutocompleteClient.cs b/src/BaGet.Protocol/Search/RawAutocompleteClient.cs index 26f63be0..d9d66170 100644 --- a/src/BaGet.Protocol/Search/RawAutocompleteClient.cs +++ b/src/BaGet.Protocol/Search/RawAutocompleteClient.cs @@ -1,68 +1,63 @@ -using System; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Protocol.Internal +namespace BaGet.Protocol.Internal; + +/// +/// The client used to search for packages. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource +/// +public class RawAutocompleteClient : IAutocompleteClient { + private readonly HttpClient _httpClient; + private readonly string _autocompleteUrl; + /// - /// The client used to search for packages. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/search-autocomplete-service-resource + /// Create a new Search client. /// - public class RawAutocompleteClient : IAutocompleteClient + /// The HTTP client used to send requests. + /// The NuGet server's autocomplete URL. + public RawAutocompleteClient(HttpClient httpClient, string autocompleteUrl) { - private readonly HttpClient _httpClient; - private readonly string _autocompleteUrl; - - /// - /// Create a new Search client. - /// - /// The HTTP client used to send requests. - /// The NuGet server's autocomplete URL. - public RawAutocompleteClient(HttpClient httpClient, string autocompleteUrl) - { - _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); - _autocompleteUrl = autocompleteUrl ?? throw new ArgumentNullException(nameof(autocompleteUrl)); - } + _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); + _autocompleteUrl = autocompleteUrl ?? throw new ArgumentNullException(nameof(autocompleteUrl)); + } - public async Task AutocompleteAsync( - string query = null, - int skip = 0, - int take = 20, - bool includePrerelease = true, - bool includeSemVer2 = true, - CancellationToken cancellationToken = default) - { - var url = RawSearchClient.AddSearchQueryString( - _autocompleteUrl, - query, - skip, - take, - includePrerelease, - includeSemVer2, - "q"); + public async Task AutocompleteAsync( + string query = null, + int skip = 0, + int take = 20, + bool includePrerelease = true, + bool includeSemVer2 = true, + CancellationToken cancellationToken = default) + { + var url = RawSearchClient.AddSearchQueryString( + _autocompleteUrl, + query, + skip, + take, + includePrerelease, + includeSemVer2, + "q"); - return await _httpClient.GetFromJsonAsync(url, cancellationToken); - } + return await _httpClient.GetFromJsonAsync(url, cancellationToken); + } - public async Task ListPackageVersionsAsync( - string packageId, - bool includePrerelease = true, - bool includeSemVer2 = true, - CancellationToken cancellationToken = default) - { - var url = RawSearchClient.AddSearchQueryString( - _autocompleteUrl, - packageId, - skip: null, - take: null, - includePrerelease, - includeSemVer2, - "id"); + public async Task ListPackageVersionsAsync( + string packageId, + bool includePrerelease = true, + bool includeSemVer2 = true, + CancellationToken cancellationToken = default) + { + var url = RawSearchClient.AddSearchQueryString( + _autocompleteUrl, + packageId, + skip: null, + take: null, + includePrerelease, + includeSemVer2, + "id"); - return await _httpClient.GetFromJsonAsync(url, cancellationToken); - } + return await _httpClient.GetFromJsonAsync(url, cancellationToken); } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Search/RawSearchClient.cs b/src/BaGet.Protocol/Search/RawSearchClient.cs index 914b08b4..ab68fc68 100644 --- a/src/BaGet.Protocol/Search/RawSearchClient.cs +++ b/src/BaGet.Protocol/Search/RawSearchClient.cs @@ -1,90 +1,84 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; using System.Text; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Protocol.Internal +namespace BaGet.Protocol.Internal; + +/// +/// The client used to search for packages. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource +/// +public class RawSearchClient : ISearchClient { + private readonly HttpClient _httpClient; + private readonly string _searchUrl; + /// - /// The client used to search for packages. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource + /// Create a new Search client. /// - public class RawSearchClient : ISearchClient + /// The HTTP client used to send requests. + /// The NuGet server's search URL. + public RawSearchClient(HttpClient httpClient, string searchUrl) { - private readonly HttpClient _httpClient; - private readonly string _searchUrl; - - /// - /// Create a new Search client. - /// - /// The HTTP client used to send requests. - /// The NuGet server's search URL. - public RawSearchClient(HttpClient httpClient, string searchUrl) - { - _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); - _searchUrl = searchUrl ?? throw new ArgumentNullException(nameof(searchUrl)); - } - - public async Task SearchAsync( - string query = null, - int skip = 0, - int take = 20, - bool includePrerelease = true, - bool includeSemVer2 = true, - CancellationToken cancellationToken = default) - { - var url = AddSearchQueryString(_searchUrl, query, skip, take, includePrerelease, includeSemVer2, "q"); + _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); + _searchUrl = searchUrl ?? throw new ArgumentNullException(nameof(searchUrl)); + } - return await _httpClient.GetFromJsonAsync(url, cancellationToken); - } + public async Task SearchAsync( + string query = null, + int skip = 0, + int take = 20, + bool includePrerelease = true, + bool includeSemVer2 = true, + CancellationToken cancellationToken = default) + { + var url = AddSearchQueryString(_searchUrl, query, skip, take, includePrerelease, includeSemVer2, "q"); - internal static string AddSearchQueryString( - string uri, - string query, - int? skip, - int? take, - bool includePrerelease, - bool includeSemVer2, - string queryParamName) - { - var queryString = new Dictionary(); + return await _httpClient.GetFromJsonAsync(url, cancellationToken); + } - if (skip.HasValue && skip.Value > 0) queryString["skip"] = skip.ToString(); - if (take.HasValue) queryString["take"] = take.ToString(); - if (includePrerelease) queryString["prerelease"] = true.ToString(); - if (includeSemVer2) queryString["semVerLevel"] = "2.0.0"; + internal static string AddSearchQueryString( + string uri, + string query, + int? skip, + int? take, + bool includePrerelease, + bool includeSemVer2, + string queryParamName) + { + var queryString = new Dictionary(); - if (!string.IsNullOrEmpty(query)) - { - queryString[queryParamName] = query; - } + if (skip.HasValue && skip.Value > 0) queryString["skip"] = skip.ToString(); + if (take.HasValue) queryString["take"] = take.ToString(); + if (includePrerelease) queryString["prerelease"] = true.ToString(); + if (includeSemVer2) queryString["semVerLevel"] = "2.0.0"; - return AddQueryString(uri, queryString); + if (!string.IsNullOrEmpty(query)) + { + queryString[queryParamName] = query; } - // See: https://github.com/aspnet/AspNetCore/blob/8c02467b4a218df3b1b0a69bceb50f5b64f482b1/src/Http/WebUtilities/src/QueryHelpers.cs#L63 - private static string AddQueryString(string uri, Dictionary queryString) - { - if (uri.IndexOf('#') != -1) throw new InvalidOperationException("URL anchors are not supported"); - if (uri.IndexOf('?') != -1) throw new InvalidOperationException("Adding query strings to URL with query strings is not supported"); + return AddQueryString(uri, queryString); + } - var builder = new StringBuilder(uri); - var hasQuery = false; + // See: https://github.com/aspnet/AspNetCore/blob/8c02467b4a218df3b1b0a69bceb50f5b64f482b1/src/Http/WebUtilities/src/QueryHelpers.cs#L63 + private static string AddQueryString(string uri, Dictionary queryString) + { + if (uri.IndexOf('#') != -1) throw new InvalidOperationException("URL anchors are not supported"); + if (uri.IndexOf('?') != -1) throw new InvalidOperationException("Adding query strings to URL with query strings is not supported"); - foreach (var parameter in queryString) - { - builder.Append(hasQuery ? '&' : '?'); - builder.Append(Uri.EscapeDataString(parameter.Key)); - builder.Append('='); - builder.Append(Uri.EscapeDataString(parameter.Value)); - hasQuery = true; - } + var builder = new StringBuilder(uri); + var hasQuery = false; - return builder.ToString(); + foreach (var parameter in queryString) + { + builder.Append(hasQuery ? '&' : '?'); + builder.Append(Uri.EscapeDataString(parameter.Key)); + builder.Append('='); + builder.Append(Uri.EscapeDataString(parameter.Value)); + hasQuery = true; } + + return builder.ToString(); } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/Search/SearchClient.cs b/src/BaGet.Protocol/Search/SearchClient.cs index b6bca0d9..63a28a58 100644 --- a/src/BaGet.Protocol/Search/SearchClient.cs +++ b/src/BaGet.Protocol/Search/SearchClient.cs @@ -1,35 +1,31 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +public partial class NuGetClientFactory { - public partial class NuGetClientFactory + private class SearchClient : ISearchClient { - private class SearchClient : ISearchClient - { - private readonly NuGetClientFactory _clientfactory; + private readonly NuGetClientFactory _clientfactory; - public SearchClient(NuGetClientFactory clientFactory) - { - _clientfactory = clientFactory ?? throw new ArgumentNullException(nameof(clientFactory)); - } + public SearchClient(NuGetClientFactory clientFactory) + { + _clientfactory = clientFactory ?? throw new ArgumentNullException(nameof(clientFactory)); + } - public async Task SearchAsync( - string query = null, - int skip = 0, - int take = 20, - bool includePrerelease = true, - bool includeSemVer2 = true, - CancellationToken cancellationToken = default) - { - // TODO: Support search failover. - // See: https://github.com/loic-sharma/BaGet/issues/314 - var client = await _clientfactory.GetSearchClientAsync(cancellationToken); + public async Task SearchAsync( + string query = null, + int skip = 0, + int take = 20, + bool includePrerelease = true, + bool includeSemVer2 = true, + CancellationToken cancellationToken = default) + { + // TODO: Support search failover. + // See: https://github.com/loic-sharma/BaGet/issues/314 + var client = await _clientfactory.GetSearchClientAsync(cancellationToken); - return await client.SearchAsync(query, skip, take, includePrerelease, includeSemVer2); - } + return await client.SearchAsync(query, skip, take, includePrerelease, includeSemVer2); } } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/ServiceIndex/IServiceIndexClient.cs b/src/BaGet.Protocol/ServiceIndex/IServiceIndexClient.cs index 73fc2cfb..f46aa555 100644 --- a/src/BaGet.Protocol/ServiceIndex/IServiceIndexClient.cs +++ b/src/BaGet.Protocol/ServiceIndex/IServiceIndexClient.cs @@ -1,21 +1,18 @@ -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +/// +/// The NuGet Service Index client, used to discover other resources. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/service-index +/// +public interface IServiceIndexClient { /// - /// The NuGet Service Index client, used to discover other resources. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/service-index + /// Get the resources available on this package feed. + /// See: https://docs.microsoft.com/en-us/nuget/api/service-index#resources /// - public interface IServiceIndexClient - { - /// - /// Get the resources available on this package feed. - /// See: https://docs.microsoft.com/en-us/nuget/api/service-index#resources - /// - /// The resources available on this package feed. - Task GetAsync(CancellationToken cancellationToken = default); - } -} + /// The resources available on this package feed. + Task GetAsync(CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/BaGet.Protocol/ServiceIndex/RawServiceIndexClient.cs b/src/BaGet.Protocol/ServiceIndex/RawServiceIndexClient.cs index 70d85669..d249cfff 100644 --- a/src/BaGet.Protocol/ServiceIndex/RawServiceIndexClient.cs +++ b/src/BaGet.Protocol/ServiceIndex/RawServiceIndexClient.cs @@ -1,38 +1,33 @@ -using System; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Protocol.Internal +namespace BaGet.Protocol.Internal; + +/// +/// The NuGet Service Index client, used to discover other resources. +/// +/// See https://docs.microsoft.com/en-us/nuget/api/service-index +/// +public class RawServiceIndexClient : IServiceIndexClient { + private readonly HttpClient _httpClient; + private readonly string _serviceIndexUrl; + /// - /// The NuGet Service Index client, used to discover other resources. - /// - /// See https://docs.microsoft.com/en-us/nuget/api/service-index + /// Create a service index for the upstream source. /// - public class RawServiceIndexClient : IServiceIndexClient + /// The HTTP client used to send requests. + /// The NuGet server's service index URL. + public RawServiceIndexClient(HttpClient httpClient, string serviceIndexUrl) { - private readonly HttpClient _httpClient; - private readonly string _serviceIndexUrl; - - /// - /// Create a service index for the upstream source. - /// - /// The HTTP client used to send requests. - /// The NuGet server's service index URL. - public RawServiceIndexClient(HttpClient httpClient, string serviceIndexUrl) - { - _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); - _serviceIndexUrl = serviceIndexUrl ?? throw new ArgumentNullException(nameof(serviceIndexUrl)); - } + _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); + _serviceIndexUrl = serviceIndexUrl ?? throw new ArgumentNullException(nameof(serviceIndexUrl)); + } - /// - public async Task GetAsync(CancellationToken cancellationToken = default) - { - return await _httpClient.GetFromJsonAsync( - _serviceIndexUrl, - cancellationToken); - } + /// + public async Task GetAsync(CancellationToken cancellationToken = default) + { + return await _httpClient.GetFromJsonAsync( + _serviceIndexUrl, + cancellationToken); } -} +} \ No newline at end of file diff --git a/src/BaGet.Protocol/ServiceIndex/ServiceIndexClient.cs b/src/BaGet.Protocol/ServiceIndex/ServiceIndexClient.cs index a429b60f..7d5050e2 100644 --- a/src/BaGet.Protocol/ServiceIndex/ServiceIndexClient.cs +++ b/src/BaGet.Protocol/ServiceIndex/ServiceIndexClient.cs @@ -1,25 +1,21 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using BaGet.Protocol.Models; -namespace BaGet.Protocol +namespace BaGet.Protocol; + +public partial class NuGetClientFactory { - public partial class NuGetClientFactory + private class ServiceIndexClient : IServiceIndexClient { - private class ServiceIndexClient : IServiceIndexClient - { - private readonly NuGetClientFactory _clientFactory; + private readonly NuGetClientFactory _clientFactory; - public ServiceIndexClient(NuGetClientFactory clientFactory) - { - _clientFactory = clientFactory ?? throw new ArgumentNullException(nameof(clientFactory)); - } + public ServiceIndexClient(NuGetClientFactory clientFactory) + { + _clientFactory = clientFactory ?? throw new ArgumentNullException(nameof(clientFactory)); + } - public async Task GetAsync(CancellationToken cancellationToken = default) - { - return await _clientFactory.GetServiceIndexAsync(cancellationToken); - } + public async Task GetAsync(CancellationToken cancellationToken = default) + { + return await _clientFactory.GetServiceIndexAsync(cancellationToken); } } -} +} \ No newline at end of file diff --git a/src/BaGet.Web/BaGet.Web.csproj b/src/BaGet.Web/BaGet.Web.csproj index 276e6569..db8cb602 100644 --- a/src/BaGet.Web/BaGet.Web.csproj +++ b/src/BaGet.Web/BaGet.Web.csproj @@ -1,7 +1,7 @@ - + - netcoreapp3.1 + net6.0 NuGet BaGet's NuGet server implementation @@ -15,12 +15,12 @@ - - + + - + \ No newline at end of file diff --git a/src/BaGet.Web/BaGet.Web.csproj.DotSettings b/src/BaGet.Web/BaGet.Web.csproj.DotSettings new file mode 100644 index 00000000..1b6ef36a --- /dev/null +++ b/src/BaGet.Web/BaGet.Web.csproj.DotSettings @@ -0,0 +1,4 @@ + + True + True + True \ No newline at end of file diff --git a/src/BaGet.Web/BaGetEndpointBuilder.cs b/src/BaGet.Web/BaGetEndpointBuilder.cs index d485eb40..78b5c1a4 100644 --- a/src/BaGet.Web/BaGetEndpointBuilder.cs +++ b/src/BaGet.Web/BaGetEndpointBuilder.cs @@ -1,130 +1,124 @@ -using BaGet.Web; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Routing; -using Microsoft.AspNetCore.Routing.Constraints; +namespace BaGet.Web; -namespace BaGet +public class BaGetEndpointBuilder { - public class BaGetEndpointBuilder + public void MapEndpoints(IEndpointRouteBuilder endpoints) { - public void MapEndpoints(IEndpointRouteBuilder endpoints) - { - endpoints.MapRazorPages(); - - MapServiceIndexRoutes(endpoints); - MapPackagePublishRoutes(endpoints); - MapSymbolRoutes(endpoints); - MapSearchRoutes(endpoints); - MapPackageMetadataRoutes(endpoints); - MapPackageContentRoutes(endpoints); - } - - public void MapServiceIndexRoutes(IEndpointRouteBuilder endpoints) - { - endpoints.MapControllerRoute( - name: Routes.IndexRouteName, - pattern: "v3/index.json", - defaults: new { controller = "ServiceIndex", action = "Get" }); - } - - public void MapPackagePublishRoutes(IEndpointRouteBuilder endpoints) - { - endpoints.MapControllerRoute( - name: Routes.UploadPackageRouteName, - pattern: "api/v2/package", - defaults: new { controller = "PackagePublish", action = "Upload" }, - constraints: new { httpMethod = new HttpMethodRouteConstraint("PUT") }); - - endpoints.MapControllerRoute( - name: Routes.DeleteRouteName, - pattern: "api/v2/package/{id}/{version}", - defaults: new { controller = "PackagePublish", action = "Delete" }, - constraints: new { httpMethod = new HttpMethodRouteConstraint("DELETE") }); - - endpoints.MapControllerRoute( - name: Routes.RelistRouteName, - pattern: "api/v2/package/{id}/{version}", - defaults: new { controller = "PackagePublish", action = "Relist" }, - constraints: new { httpMethod = new HttpMethodRouteConstraint("POST") }); - } - - public void MapSymbolRoutes(IEndpointRouteBuilder endpoints) - { - endpoints.MapControllerRoute( - name: Routes.UploadSymbolRouteName, - pattern: "api/v2/symbol", - defaults: new { controller = "Symbol", action = "Upload" }, - constraints: new { httpMethod = new HttpMethodRouteConstraint("PUT") }); - - endpoints.MapControllerRoute( - name: Routes.SymbolDownloadRouteName, - pattern: "api/download/symbols/{file}/{key}/{file2}", - defaults: new { controller = "Symbol", action = "Get" }); - - endpoints.MapControllerRoute( - name: Routes.PrefixedSymbolDownloadRouteName, - pattern: "api/download/symbols/{prefix}/{file}/{key}/{file2}", - defaults: new { controller = "Symbol", action = "Get" }); - } - - public void MapSearchRoutes(IEndpointRouteBuilder endpoints) - { - endpoints.MapControllerRoute( - name: Routes.SearchRouteName, - pattern: "v3/search", - defaults: new { controller = "Search", action = "Search" }); - - endpoints.MapControllerRoute( - name: Routes.AutocompleteRouteName, - pattern: "v3/autocomplete", - defaults: new { controller = "Search", action = "Autocomplete" }); - - // This is an unofficial API to find packages that depend on a given package. - endpoints.MapControllerRoute( - name: Routes.DependentsRouteName, - pattern: "v3/dependents", - defaults: new { controller = "Search", action = "Dependents" }); - } - - public void MapPackageMetadataRoutes(IEndpointRouteBuilder endpoints) - { - endpoints.MapControllerRoute( - name: Routes.RegistrationIndexRouteName, - pattern: "v3/registration/{id}/index.json", - defaults: new { controller = "PackageMetadata", action = "RegistrationIndex" }); - - endpoints.MapControllerRoute( - name: Routes.RegistrationLeafRouteName, - pattern: "v3/registration/{id}/{version}.json", - defaults: new { controller = "PackageMetadata", action = "RegistrationLeaf" }); - } - - public void MapPackageContentRoutes(IEndpointRouteBuilder endpoints) - { - endpoints.MapControllerRoute( - name: Routes.PackageVersionsRouteName, - pattern: "v3/package/{id}/index.json", - defaults: new { controller = "PackageContent", action = "GetPackageVersions" }); - - endpoints.MapControllerRoute( - name: Routes.PackageDownloadRouteName, - pattern: "v3/package/{id}/{version}/{idVersion}.nupkg", - defaults: new { controller = "PackageContent", action = "DownloadPackage" }); - - endpoints.MapControllerRoute( - name: Routes.PackageDownloadManifestRouteName, - pattern: "v3/package/{id}/{version}/{id2}.nuspec", - defaults: new { controller = "PackageContent", action = "DownloadNuspec" }); - - endpoints.MapControllerRoute( - name: Routes.PackageDownloadReadmeRouteName, - pattern: "v3/package/{id}/{version}/readme", - defaults: new { controller = "PackageContent", action = "DownloadReadme" }); - - endpoints.MapControllerRoute( - name: Routes.PackageDownloadIconRouteName, - pattern: "v3/package/{id}/{version}/icon", - defaults: new { controller = "PackageContent", action = "DownloadIcon" }); - } + endpoints.MapRazorPages(); + + MapServiceIndexRoutes(endpoints); + MapPackagePublishRoutes(endpoints); + MapSymbolRoutes(endpoints); + MapSearchRoutes(endpoints); + MapPackageMetadataRoutes(endpoints); + MapPackageContentRoutes(endpoints); + } + + public void MapServiceIndexRoutes(IEndpointRouteBuilder endpoints) + { + endpoints.MapControllerRoute( + name: Routes.IndexRouteName, + pattern: "v3/index.json", + defaults: new { controller = "ServiceIndex", action = "Get" }); + } + + public void MapPackagePublishRoutes(IEndpointRouteBuilder endpoints) + { + endpoints.MapControllerRoute( + name: Routes.UploadPackageRouteName, + pattern: "api/v2/package", + defaults: new { controller = "PackagePublish", action = "Upload" }, + constraints: new { httpMethod = new HttpMethodRouteConstraint("PUT") }); + + endpoints.MapControllerRoute( + name: Routes.DeleteRouteName, + pattern: "api/v2/package/{id}/{version}", + defaults: new { controller = "PackagePublish", action = "Delete" }, + constraints: new { httpMethod = new HttpMethodRouteConstraint("DELETE") }); + + endpoints.MapControllerRoute( + name: Routes.RelistRouteName, + pattern: "api/v2/package/{id}/{version}", + defaults: new { controller = "PackagePublish", action = "Relist" }, + constraints: new { httpMethod = new HttpMethodRouteConstraint("POST") }); + } + + public void MapSymbolRoutes(IEndpointRouteBuilder endpoints) + { + endpoints.MapControllerRoute( + name: Routes.UploadSymbolRouteName, + pattern: "api/v2/symbol", + defaults: new { controller = "Symbol", action = "Upload" }, + constraints: new { httpMethod = new HttpMethodRouteConstraint("PUT") }); + + endpoints.MapControllerRoute( + name: Routes.SymbolDownloadRouteName, + pattern: "api/download/symbols/{file}/{key}/{file2}", + defaults: new { controller = "Symbol", action = "Get" }); + + endpoints.MapControllerRoute( + name: Routes.PrefixedSymbolDownloadRouteName, + pattern: "api/download/symbols/{prefix}/{file}/{key}/{file2}", + defaults: new { controller = "Symbol", action = "Get" }); + } + + public void MapSearchRoutes(IEndpointRouteBuilder endpoints) + { + endpoints.MapControllerRoute( + name: Routes.SearchRouteName, + pattern: "v3/search", + defaults: new { controller = "Search", action = "Search" }); + + endpoints.MapControllerRoute( + name: Routes.AutocompleteRouteName, + pattern: "v3/autocomplete", + defaults: new { controller = "Search", action = "Autocomplete" }); + + // This is an unofficial API to find packages that depend on a given package. + endpoints.MapControllerRoute( + name: Routes.DependentsRouteName, + pattern: "v3/dependents", + defaults: new { controller = "Search", action = "Dependents" }); + } + + public void MapPackageMetadataRoutes(IEndpointRouteBuilder endpoints) + { + endpoints.MapControllerRoute( + name: Routes.RegistrationIndexRouteName, + pattern: "v3/registration/{id}/index.json", + defaults: new { controller = "PackageMetadata", action = "RegistrationIndex" }); + + endpoints.MapControllerRoute( + name: Routes.RegistrationLeafRouteName, + pattern: "v3/registration/{id}/{version}.json", + defaults: new { controller = "PackageMetadata", action = "RegistrationLeaf" }); + } + + public void MapPackageContentRoutes(IEndpointRouteBuilder endpoints) + { + endpoints.MapControllerRoute( + name: Routes.PackageVersionsRouteName, + pattern: "v3/package/{id}/index.json", + defaults: new { controller = "PackageContent", action = "GetPackageVersions" }); + + endpoints.MapControllerRoute( + name: Routes.PackageDownloadRouteName, + pattern: "v3/package/{id}/{version}/{idVersion}.nupkg", + defaults: new { controller = "PackageContent", action = "DownloadPackage" }); + + endpoints.MapControllerRoute( + name: Routes.PackageDownloadManifestRouteName, + pattern: "v3/package/{id}/{version}/{id2}.nuspec", + defaults: new { controller = "PackageContent", action = "DownloadNuspec" }); + + endpoints.MapControllerRoute( + name: Routes.PackageDownloadReadmeRouteName, + pattern: "v3/package/{id}/{version}/readme", + defaults: new { controller = "PackageContent", action = "DownloadReadme" }); + + endpoints.MapControllerRoute( + name: Routes.PackageDownloadIconRouteName, + pattern: "v3/package/{id}/{version}/icon", + defaults: new { controller = "PackageContent", action = "DownloadIcon" }); } } diff --git a/src/BaGet.Web/BaGetUrlGenerator.cs b/src/BaGet.Web/BaGetUrlGenerator.cs index 48b61bb5..b51b68de 100644 --- a/src/BaGet.Web/BaGetUrlGenerator.cs +++ b/src/BaGet.Web/BaGetUrlGenerator.cs @@ -1,165 +1,158 @@ -using System; -using BaGet.Core; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; -using NuGet.Versioning; +namespace BaGet.Web; -namespace BaGet.Web +// TODO: This should validate the "Host" header against known valid values +public class BaGetUrlGenerator : IUrlGenerator { - // TODO: This should validate the "Host" header against known valid values - public class BaGetUrlGenerator : IUrlGenerator - { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly LinkGenerator _linkGenerator; - - public BaGetUrlGenerator(IHttpContextAccessor httpContextAccessor, LinkGenerator linkGenerator) - { - _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor)); - _linkGenerator = linkGenerator ?? throw new ArgumentNullException(nameof(linkGenerator)); - } - - public string GetServiceIndexUrl() - { - return _linkGenerator.GetUriByRouteValues( - _httpContextAccessor.HttpContext, - Routes.IndexRouteName, - values: null); - } - - public string GetPackageContentResourceUrl() - { - return AbsoluteUrl("v3/package"); - } - - public string GetPackageMetadataResourceUrl() - { - return AbsoluteUrl("v3/registration"); - } - - public string GetPackagePublishResourceUrl() - { - return _linkGenerator.GetUriByRouteValues( - _httpContextAccessor.HttpContext, - Routes.UploadPackageRouteName, - values: null); - } - - public string GetSymbolPublishResourceUrl() - { - return _linkGenerator.GetUriByRouteValues( - _httpContextAccessor.HttpContext, - Routes.UploadSymbolRouteName, - values: null); - } - - public string GetSearchResourceUrl() - { - return _linkGenerator.GetUriByRouteValues( - _httpContextAccessor.HttpContext, - Routes.SearchRouteName, - values: null); - } - - public string GetAutocompleteResourceUrl() - { - return _linkGenerator.GetUriByRouteValues( - _httpContextAccessor.HttpContext, - Routes.AutocompleteRouteName, - values: null); - } - - public string GetRegistrationIndexUrl(string id) - { - return _linkGenerator.GetUriByRouteValues( - _httpContextAccessor.HttpContext, - Routes.RegistrationIndexRouteName, - values: new { Id = id.ToLowerInvariant() }); - } - - public string GetRegistrationPageUrl(string id, NuGetVersion lower, NuGetVersion upper) - { - // BaGet does not support paging the registration resource. - throw new NotImplementedException(); - } - - public string GetRegistrationLeafUrl(string id, NuGetVersion version) - { - return _linkGenerator.GetUriByRouteValues( - _httpContextAccessor.HttpContext, - Routes.RegistrationLeafRouteName, - values: new - { - Id = id.ToLowerInvariant(), - Version = version.ToNormalizedString().ToLowerInvariant(), - }); - } - - public string GetPackageVersionsUrl(string id) - { - return _linkGenerator.GetUriByRouteValues( - _httpContextAccessor.HttpContext, - Routes.PackageVersionsRouteName, - values: new { Id = id.ToLowerInvariant() }); - } - - public string GetPackageDownloadUrl(string id, NuGetVersion version) - { - id = id.ToLowerInvariant(); - var versionString = version.ToNormalizedString().ToLowerInvariant(); - - return _linkGenerator.GetUriByRouteValues( - _httpContextAccessor.HttpContext, - Routes.PackageDownloadRouteName, - values: new - { - Id = id, - Version = versionString, - IdVersion = $"{id}.{versionString}" - }); - } - - public string GetPackageManifestDownloadUrl(string id, NuGetVersion version) - { - id = id.ToLowerInvariant(); - var versionString = version.ToNormalizedString().ToLowerInvariant(); - - return _linkGenerator.GetUriByRouteValues( - _httpContextAccessor.HttpContext, - Routes.PackageDownloadRouteName, - values: new - { - Id = id, - Version = versionString, - Id2 = id, - }); - } - - public string GetPackageIconDownloadUrl(string id, NuGetVersion version) - { - id = id.ToLowerInvariant(); - var versionString = version.ToNormalizedString().ToLowerInvariant(); - - return _linkGenerator.GetUriByRouteValues( - _httpContextAccessor.HttpContext, - Routes.PackageDownloadIconRouteName, - values: new - { - Id = id, - Version = versionString - }); - } - - private string AbsoluteUrl(string relativePath) - { - var request = _httpContextAccessor.HttpContext.Request; - - return string.Concat( - request.Scheme, - "://", - request.Host.ToUriComponent(), - request.PathBase.ToUriComponent(), - "/", - relativePath); - } + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly LinkGenerator _linkGenerator; + + public BaGetUrlGenerator(IHttpContextAccessor httpContextAccessor, LinkGenerator linkGenerator) + { + _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor)); + _linkGenerator = linkGenerator ?? throw new ArgumentNullException(nameof(linkGenerator)); + } + + public string GetServiceIndexUrl() + { + return _linkGenerator.GetUriByRouteValues( + _httpContextAccessor.HttpContext, + Routes.IndexRouteName, + values: null); + } + + public string GetPackageContentResourceUrl() + { + return AbsoluteUrl("v3/package"); + } + + public string GetPackageMetadataResourceUrl() + { + return AbsoluteUrl("v3/registration"); + } + + public string GetPackagePublishResourceUrl() + { + return _linkGenerator.GetUriByRouteValues( + _httpContextAccessor.HttpContext, + Routes.UploadPackageRouteName, + values: null); + } + + public string GetSymbolPublishResourceUrl() + { + return _linkGenerator.GetUriByRouteValues( + _httpContextAccessor.HttpContext, + Routes.UploadSymbolRouteName, + values: null); + } + + public string GetSearchResourceUrl() + { + return _linkGenerator.GetUriByRouteValues( + _httpContextAccessor.HttpContext, + Routes.SearchRouteName, + values: null); + } + + public string GetAutocompleteResourceUrl() + { + return _linkGenerator.GetUriByRouteValues( + _httpContextAccessor.HttpContext, + Routes.AutocompleteRouteName, + values: null); + } + + public string GetRegistrationIndexUrl(string id) + { + return _linkGenerator.GetUriByRouteValues( + _httpContextAccessor.HttpContext, + Routes.RegistrationIndexRouteName, + values: new { Id = id.ToLowerInvariant() }); + } + + public string GetRegistrationPageUrl(string id, NuGetVersion lower, NuGetVersion upper) + { + // BaGet does not support paging the registration resource. + throw new NotImplementedException(); + } + + public string GetRegistrationLeafUrl(string id, NuGetVersion version) + { + return _linkGenerator.GetUriByRouteValues( + _httpContextAccessor.HttpContext, + Routes.RegistrationLeafRouteName, + values: new + { + Id = id.ToLowerInvariant(), + Version = version.ToNormalizedString().ToLowerInvariant(), + }); + } + + public string GetPackageVersionsUrl(string id) + { + return _linkGenerator.GetUriByRouteValues( + _httpContextAccessor.HttpContext, + Routes.PackageVersionsRouteName, + values: new { Id = id.ToLowerInvariant() }); + } + + public string GetPackageDownloadUrl(string id, NuGetVersion version) + { + id = id.ToLowerInvariant(); + var versionString = version.ToNormalizedString().ToLowerInvariant(); + + return _linkGenerator.GetUriByRouteValues( + _httpContextAccessor.HttpContext, + Routes.PackageDownloadRouteName, + values: new + { + Id = id, + Version = versionString, + IdVersion = $"{id}.{versionString}" + }); + } + + public string GetPackageManifestDownloadUrl(string id, NuGetVersion version) + { + id = id.ToLowerInvariant(); + var versionString = version.ToNormalizedString().ToLowerInvariant(); + + return _linkGenerator.GetUriByRouteValues( + _httpContextAccessor.HttpContext, + Routes.PackageDownloadRouteName, + values: new + { + Id = id, + Version = versionString, + Id2 = id, + }); + } + + public string GetPackageIconDownloadUrl(string id, NuGetVersion version) + { + id = id.ToLowerInvariant(); + var versionString = version.ToNormalizedString().ToLowerInvariant(); + + return _linkGenerator.GetUriByRouteValues( + _httpContextAccessor.HttpContext, + Routes.PackageDownloadIconRouteName, + values: new + { + Id = id, + Version = versionString + }); + } + + private string AbsoluteUrl(string relativePath) + { + var request = _httpContextAccessor.HttpContext.Request; + + return string.Concat( + request.Scheme, + "://", + request.Host.ToUriComponent(), + request.PathBase.ToUriComponent(), + "/", + relativePath); } } diff --git a/src/BaGet.Web/Controllers/PackageContentController.cs b/src/BaGet.Web/Controllers/PackageContentController.cs index b5bd2b5a..9ae24859 100644 --- a/src/BaGet.Web/Controllers/PackageContentController.cs +++ b/src/BaGet.Web/Controllers/PackageContentController.cs @@ -1,99 +1,93 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using BaGet.Core; using BaGet.Protocol.Models; using Microsoft.AspNetCore.Mvc; -using NuGet.Versioning; -namespace BaGet.Web +namespace BaGet.Web; + +/// +/// The Package Content resource, used to download content from packages. +/// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource +/// +public class PackageContentController : Controller { - /// - /// The Package Content resource, used to download content from packages. - /// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource - /// - public class PackageContentController : Controller + private readonly IPackageContentService _content; + + public PackageContentController(IPackageContentService content) { - private readonly IPackageContentService _content; + _content = content ?? throw new ArgumentNullException(nameof(content)); + } - public PackageContentController(IPackageContentService content) + public async Task> GetPackageVersionsAsync(string id, CancellationToken cancellationToken) + { + var versions = await _content.GetPackageVersionsOrNullAsync(id, cancellationToken); + if (versions == null) { - _content = content ?? throw new ArgumentNullException(nameof(content)); + return NotFound(); } - public async Task> GetPackageVersionsAsync(string id, CancellationToken cancellationToken) + return versions; + } + + public async Task DownloadPackageAsync(string id, string version, CancellationToken cancellationToken) + { + if (!NuGetVersion.TryParse(version, out var nugetVersion)) { - var versions = await _content.GetPackageVersionsOrNullAsync(id, cancellationToken); - if (versions == null) - { - return NotFound(); - } + return NotFound(); + } - return versions; + var packageStream = await _content.GetPackageContentStreamOrNullAsync(id, nugetVersion, cancellationToken); + if (packageStream == null) + { + return NotFound(); } - public async Task DownloadPackageAsync(string id, string version, CancellationToken cancellationToken) + return File(packageStream, "application/octet-stream"); + } + + public async Task DownloadNuspecAsync(string id, string version, CancellationToken cancellationToken) + { + if (!NuGetVersion.TryParse(version, out var nugetVersion)) { - if (!NuGetVersion.TryParse(version, out var nugetVersion)) - { - return NotFound(); - } - - var packageStream = await _content.GetPackageContentStreamOrNullAsync(id, nugetVersion, cancellationToken); - if (packageStream == null) - { - return NotFound(); - } - - return File(packageStream, "application/octet-stream"); + return NotFound(); } - public async Task DownloadNuspecAsync(string id, string version, CancellationToken cancellationToken) + var nuspecStream = await _content.GetPackageManifestStreamOrNullAsync(id, nugetVersion, cancellationToken); + if (nuspecStream == null) { - if (!NuGetVersion.TryParse(version, out var nugetVersion)) - { - return NotFound(); - } - - var nuspecStream = await _content.GetPackageManifestStreamOrNullAsync(id, nugetVersion, cancellationToken); - if (nuspecStream == null) - { - return NotFound(); - } - - return File(nuspecStream, "text/xml"); + return NotFound(); } - public async Task DownloadReadmeAsync(string id, string version, CancellationToken cancellationToken) + return File(nuspecStream, "text/xml"); + } + + public async Task DownloadReadmeAsync(string id, string version, CancellationToken cancellationToken) + { + if (!NuGetVersion.TryParse(version, out var nugetVersion)) + { + return NotFound(); + } + + var readmeStream = await _content.GetPackageReadmeStreamOrNullAsync(id, nugetVersion, cancellationToken); + if (readmeStream == null) + { + return NotFound(); + } + + return File(readmeStream, "text/markdown"); + } + + public async Task DownloadIconAsync(string id, string version, CancellationToken cancellationToken) + { + if (!NuGetVersion.TryParse(version, out var nugetVersion)) { - if (!NuGetVersion.TryParse(version, out var nugetVersion)) - { - return NotFound(); - } - - var readmeStream = await _content.GetPackageReadmeStreamOrNullAsync(id, nugetVersion, cancellationToken); - if (readmeStream == null) - { - return NotFound(); - } - - return File(readmeStream, "text/markdown"); + return NotFound(); } - public async Task DownloadIconAsync(string id, string version, CancellationToken cancellationToken) + var iconStream = await _content.GetPackageIconStreamOrNullAsync(id, nugetVersion, cancellationToken); + if (iconStream == null) { - if (!NuGetVersion.TryParse(version, out var nugetVersion)) - { - return NotFound(); - } - - var iconStream = await _content.GetPackageIconStreamOrNullAsync(id, nugetVersion, cancellationToken); - if (iconStream == null) - { - return NotFound(); - } - - return File(iconStream, "image/xyz"); + return NotFound(); } + + return File(iconStream, "image/xyz"); } } diff --git a/src/BaGet.Web/Controllers/PackageMetadataController.cs b/src/BaGet.Web/Controllers/PackageMetadataController.cs index 18ca1fde..a674dbf5 100644 --- a/src/BaGet.Web/Controllers/PackageMetadataController.cs +++ b/src/BaGet.Web/Controllers/PackageMetadataController.cs @@ -1,55 +1,49 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using BaGet.Core; using BaGet.Protocol.Models; using Microsoft.AspNetCore.Mvc; -using NuGet.Versioning; -namespace BaGet.Web +namespace BaGet.Web; + +/// +/// The Package Metadata resource, used to fetch packages' information. +/// See: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource +/// +public class PackageMetadataController : Controller { - /// - /// The Package Metadata resource, used to fetch packages' information. - /// See: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource - /// - public class PackageMetadataController : Controller + private readonly IPackageMetadataService _metadata; + + public PackageMetadataController(IPackageMetadataService metadata) { - private readonly IPackageMetadataService _metadata; + _metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); + } - public PackageMetadataController(IPackageMetadataService metadata) + // GET v3/registration/{id}.json + [HttpGet] + public async Task> RegistrationIndexAsync(string id, CancellationToken cancellationToken) + { + var index = await _metadata.GetRegistrationIndexOrNullAsync(id, cancellationToken); + if (index == null) { - _metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); + return NotFound(); } - // GET v3/registration/{id}.json - [HttpGet] - public async Task> RegistrationIndexAsync(string id, CancellationToken cancellationToken) - { - var index = await _metadata.GetRegistrationIndexOrNullAsync(id, cancellationToken); - if (index == null) - { - return NotFound(); - } + return index; + } - return index; + // GET v3/registration/{id}/{version}.json + [HttpGet] + public async Task> RegistrationLeafAsync(string id, string version, CancellationToken cancellationToken) + { + if (!NuGetVersion.TryParse(version, out var nugetVersion)) + { + return NotFound(); } - // GET v3/registration/{id}/{version}.json - [HttpGet] - public async Task> RegistrationLeafAsync(string id, string version, CancellationToken cancellationToken) + var leaf = await _metadata.GetRegistrationLeafOrNullAsync(id, nugetVersion, cancellationToken); + if (leaf == null) { - if (!NuGetVersion.TryParse(version, out var nugetVersion)) - { - return NotFound(); - } - - var leaf = await _metadata.GetRegistrationLeafOrNullAsync(id, nugetVersion, cancellationToken); - if (leaf == null) - { - return NotFound(); - } - - return leaf; + return NotFound(); } + + return leaf; } -} +} \ No newline at end of file diff --git a/src/BaGet.Web/Controllers/PackagePublishController.cs b/src/BaGet.Web/Controllers/PackagePublishController.cs index 8adb0b5f..f8772ef5 100644 --- a/src/BaGet.Web/Controllers/PackagePublishController.cs +++ b/src/BaGet.Web/Controllers/PackagePublishController.cs @@ -1,139 +1,132 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using BaGet.Core; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using NuGet.Versioning; -namespace BaGet.Web +namespace BaGet.Web; + +public class PackagePublishController : Controller { - public class PackagePublishController : Controller + private readonly IAuthenticationService _authentication; + private readonly IPackageIndexingService _indexer; + private readonly IPackageDatabase _packages; + private readonly IPackageDeletionService _deleteService; + private readonly IOptionsSnapshot _options; + private readonly ILogger _logger; + + public PackagePublishController( + IAuthenticationService authentication, + IPackageIndexingService indexer, + IPackageDatabase packages, + IPackageDeletionService deletionService, + IOptionsSnapshot options, + ILogger logger) + { + _authentication = authentication ?? throw new ArgumentNullException(nameof(authentication)); + _indexer = indexer ?? throw new ArgumentNullException(nameof(indexer)); + _packages = packages ?? throw new ArgumentNullException(nameof(packages)); + _deleteService = deletionService ?? throw new ArgumentNullException(nameof(deletionService)); + _options = options ?? throw new ArgumentNullException(nameof(options)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + // See: https://docs.microsoft.com/en-us/nuget/api/package-publish-resource#push-a-package + public async Task Upload(CancellationToken cancellationToken) { - private readonly IAuthenticationService _authentication; - private readonly IPackageIndexingService _indexer; - private readonly IPackageDatabase _packages; - private readonly IPackageDeletionService _deleteService; - private readonly IOptionsSnapshot _options; - private readonly ILogger _logger; - - public PackagePublishController( - IAuthenticationService authentication, - IPackageIndexingService indexer, - IPackageDatabase packages, - IPackageDeletionService deletionService, - IOptionsSnapshot options, - ILogger logger) + if (_options.Value.IsReadOnlyMode || + !await _authentication.AuthenticateAsync(Request.GetApiKey(), cancellationToken)) { - _authentication = authentication ?? throw new ArgumentNullException(nameof(authentication)); - _indexer = indexer ?? throw new ArgumentNullException(nameof(indexer)); - _packages = packages ?? throw new ArgumentNullException(nameof(packages)); - _deleteService = deletionService ?? throw new ArgumentNullException(nameof(deletionService)); - _options = options ?? throw new ArgumentNullException(nameof(options)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + HttpContext.Response.StatusCode = 401; + return; } - // See: https://docs.microsoft.com/en-us/nuget/api/package-publish-resource#push-a-package - public async Task Upload(CancellationToken cancellationToken) + try { - if (_options.Value.IsReadOnlyMode || - !await _authentication.AuthenticateAsync(Request.GetApiKey(), cancellationToken)) - { - HttpContext.Response.StatusCode = 401; - return; - } - - try + using (var uploadStream = await Request.GetUploadStreamOrNullAsync(cancellationToken)) { - using (var uploadStream = await Request.GetUploadStreamOrNullAsync(cancellationToken)) + if (uploadStream == null) { - if (uploadStream == null) - { - HttpContext.Response.StatusCode = 400; - return; - } + HttpContext.Response.StatusCode = 400; + return; + } - var result = await _indexer.IndexAsync(uploadStream, cancellationToken); + var result = await _indexer.IndexAsync(uploadStream, cancellationToken); - switch (result) - { - case PackageIndexingResult.InvalidPackage: - HttpContext.Response.StatusCode = 400; - break; + switch (result) + { + case PackageIndexingResult.InvalidPackage: + HttpContext.Response.StatusCode = 400; + break; - case PackageIndexingResult.PackageAlreadyExists: - HttpContext.Response.StatusCode = 409; - break; + case PackageIndexingResult.PackageAlreadyExists: + HttpContext.Response.StatusCode = 409; + break; - case PackageIndexingResult.Success: - HttpContext.Response.StatusCode = 201; - break; - } + case PackageIndexingResult.Success: + HttpContext.Response.StatusCode = 201; + break; } } - catch (Exception e) - { - _logger.LogError(e, "Exception thrown during package upload"); + } + catch (Exception e) + { + _logger.LogError(e, "Exception thrown during package upload"); - HttpContext.Response.StatusCode = 500; - } + HttpContext.Response.StatusCode = 500; } + } - [HttpDelete] - public async Task Delete(string id, string version, CancellationToken cancellationToken) + [HttpDelete] + public async Task Delete(string id, string version, CancellationToken cancellationToken) + { + if (_options.Value.IsReadOnlyMode) { - if (_options.Value.IsReadOnlyMode) - { - return Unauthorized(); - } + return Unauthorized(); + } - if (!NuGetVersion.TryParse(version, out var nugetVersion)) - { - return NotFound(); - } + if (!NuGetVersion.TryParse(version, out var nugetVersion)) + { + return NotFound(); + } - if (!await _authentication.AuthenticateAsync(Request.GetApiKey(), cancellationToken)) - { - return Unauthorized(); - } + if (!await _authentication.AuthenticateAsync(Request.GetApiKey(), cancellationToken)) + { + return Unauthorized(); + } - if (await _deleteService.TryDeletePackageAsync(id, nugetVersion, cancellationToken)) - { - return NoContent(); - } - else - { - return NotFound(); - } + if (await _deleteService.TryDeletePackageAsync(id, nugetVersion, cancellationToken)) + { + return NoContent(); + } + else + { + return NotFound(); } + } - [HttpPost] - public async Task Relist(string id, string version, CancellationToken cancellationToken) + [HttpPost] + public async Task Relist(string id, string version, CancellationToken cancellationToken) + { + if (_options.Value.IsReadOnlyMode) { - if (_options.Value.IsReadOnlyMode) - { - return Unauthorized(); - } + return Unauthorized(); + } - if (!NuGetVersion.TryParse(version, out var nugetVersion)) - { - return NotFound(); - } + if (!NuGetVersion.TryParse(version, out var nugetVersion)) + { + return NotFound(); + } - if (!await _authentication.AuthenticateAsync(Request.GetApiKey(), cancellationToken)) - { - return Unauthorized(); - } + if (!await _authentication.AuthenticateAsync(Request.GetApiKey(), cancellationToken)) + { + return Unauthorized(); + } - if (await _packages.RelistPackageAsync(id, nugetVersion, cancellationToken)) - { - return Ok(); - } - else - { - return NotFound(); - } + if (await _packages.RelistPackageAsync(id, nugetVersion, cancellationToken)) + { + return Ok(); + } + else + { + return NotFound(); } } -} +} \ No newline at end of file diff --git a/src/BaGet.Web/Controllers/SearchController.cs b/src/BaGet.Web/Controllers/SearchController.cs index 19f0fa8d..9e682c46 100644 --- a/src/BaGet.Web/Controllers/SearchController.cs +++ b/src/BaGet.Web/Controllers/SearchController.cs @@ -1,97 +1,92 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using BaGet.Core; using BaGet.Protocol.Models; using Microsoft.AspNetCore.Mvc; -namespace BaGet.Web +namespace BaGet.Web; + +public class SearchController : Controller { - public class SearchController : Controller + private readonly ISearchService _searchService; + + public SearchController(ISearchService searchService) { - private readonly ISearchService _searchService; + _searchService = searchService ?? throw new ArgumentNullException(nameof(searchService)); + } + + public async Task> SearchAsync( + [FromQuery(Name = "q")] string query = null, + [FromQuery]int skip = 0, + [FromQuery]int take = 20, + [FromQuery]bool prerelease = false, + [FromQuery]string semVerLevel = null, - public SearchController(ISearchService searchService) + // These are unofficial parameters + [FromQuery]string packageType = null, + [FromQuery]string framework = null, + CancellationToken cancellationToken = default) + { + var request = new SearchRequest { - _searchService = searchService ?? throw new ArgumentNullException(nameof(searchService)); - } + Skip = skip, + Take = take, + IncludePrerelease = prerelease, + IncludeSemVer2 = semVerLevel == "2.0.0", + PackageType = packageType, + Framework = framework, + Query = query ?? string.Empty, + }; + + return await _searchService.SearchAsync(request, cancellationToken); + } - public async Task> SearchAsync( - [FromQuery(Name = "q")] string query = null, - [FromQuery]int skip = 0, - [FromQuery]int take = 20, - [FromQuery]bool prerelease = false, - [FromQuery]string semVerLevel = null, + public async Task> AutocompleteAsync( + [FromQuery(Name = "q")] string autocompleteQuery = null, + [FromQuery(Name = "id")] string versionsQuery = null, + [FromQuery]bool prerelease = false, + [FromQuery]string semVerLevel = null, + [FromQuery]int skip = 0, + [FromQuery]int take = 20, - // These are unofficial parameters - [FromQuery]string packageType = null, - [FromQuery]string framework = null, - CancellationToken cancellationToken = default) + // These are unofficial parameters + [FromQuery]string packageType = null, + CancellationToken cancellationToken = default) + { + // If only "id" is provided, find package versions. Otherwise, find package IDs. + if (versionsQuery != null && autocompleteQuery == null) { - var request = new SearchRequest + var request = new VersionsRequest { - Skip = skip, - Take = take, IncludePrerelease = prerelease, IncludeSemVer2 = semVerLevel == "2.0.0", - PackageType = packageType, - Framework = framework, - Query = query ?? string.Empty, + PackageId = versionsQuery, }; - return await _searchService.SearchAsync(request, cancellationToken); + return await _searchService.ListPackageVersionsAsync(request, cancellationToken); } - - public async Task> AutocompleteAsync( - [FromQuery(Name = "q")] string autocompleteQuery = null, - [FromQuery(Name = "id")] string versionsQuery = null, - [FromQuery]bool prerelease = false, - [FromQuery]string semVerLevel = null, - [FromQuery]int skip = 0, - [FromQuery]int take = 20, - - // These are unofficial parameters - [FromQuery]string packageType = null, - CancellationToken cancellationToken = default) + else { - // If only "id" is provided, find package versions. Otherwise, find package IDs. - if (versionsQuery != null && autocompleteQuery == null) + var request = new AutocompleteRequest { - var request = new VersionsRequest - { - IncludePrerelease = prerelease, - IncludeSemVer2 = semVerLevel == "2.0.0", - PackageId = versionsQuery, - }; - - return await _searchService.ListPackageVersionsAsync(request, cancellationToken); - } - else - { - var request = new AutocompleteRequest - { - IncludePrerelease = prerelease, - IncludeSemVer2 = semVerLevel == "2.0.0", - PackageType = packageType, - Skip = skip, - Take = take, - Query = autocompleteQuery, - }; + IncludePrerelease = prerelease, + IncludeSemVer2 = semVerLevel == "2.0.0", + PackageType = packageType, + Skip = skip, + Take = take, + Query = autocompleteQuery, + }; - return await _searchService.AutocompleteAsync(request, cancellationToken); - } + return await _searchService.AutocompleteAsync(request, cancellationToken); } + } - public async Task> DependentsAsync( - [FromQuery] string packageId = null, - CancellationToken cancellationToken = default) + public async Task> DependentsAsync( + [FromQuery] string packageId = null, + CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(packageId)) { - if (string.IsNullOrWhiteSpace(packageId)) - { - return BadRequest(); - } - - return await _searchService.FindDependentsAsync(packageId, cancellationToken); + return BadRequest(); } + + return await _searchService.FindDependentsAsync(packageId, cancellationToken); } -} +} \ No newline at end of file diff --git a/src/BaGet.Web/Controllers/ServiceIndexController.cs b/src/BaGet.Web/Controllers/ServiceIndexController.cs index af1806ff..6d4768a4 100644 --- a/src/BaGet.Web/Controllers/ServiceIndexController.cs +++ b/src/BaGet.Web/Controllers/ServiceIndexController.cs @@ -1,29 +1,24 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using BaGet.Core; using BaGet.Protocol.Models; using Microsoft.AspNetCore.Mvc; -namespace BaGet.Web +namespace BaGet.Web; + +/// +/// The NuGet Service Index. This aids NuGet client to discover this server's services. +/// +public class ServiceIndexController : Controller { - /// - /// The NuGet Service Index. This aids NuGet client to discover this server's services. - /// - public class ServiceIndexController : Controller - { - private readonly IServiceIndexService _serviceIndex; + private readonly IServiceIndexService _serviceIndex; - public ServiceIndexController(IServiceIndexService serviceIndex) - { - _serviceIndex = serviceIndex ?? throw new ArgumentNullException(nameof(serviceIndex)); - } + public ServiceIndexController(IServiceIndexService serviceIndex) + { + _serviceIndex = serviceIndex ?? throw new ArgumentNullException(nameof(serviceIndex)); + } - // GET v3/index - [HttpGet] - public async Task GetAsync(CancellationToken cancellationToken) - { - return await _serviceIndex.GetAsync(cancellationToken); - } + // GET v3/index + [HttpGet] + public async Task GetAsync(CancellationToken cancellationToken) + { + return await _serviceIndex.GetAsync(cancellationToken); } -} +} \ No newline at end of file diff --git a/src/BaGet.Web/Controllers/SymbolController.cs b/src/BaGet.Web/Controllers/SymbolController.cs index 632f60cc..0ed54e11 100644 --- a/src/BaGet.Web/Controllers/SymbolController.cs +++ b/src/BaGet.Web/Controllers/SymbolController.cs @@ -1,89 +1,83 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using BaGet.Core; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace BaGet.Web +namespace BaGet.Web; + +public class SymbolController : Controller { - public class SymbolController : Controller + private readonly IAuthenticationService _authentication; + private readonly ISymbolIndexingService _indexer; + private readonly ISymbolStorageService _storage; + private readonly IOptionsSnapshot _options; + private readonly ILogger _logger; + + public SymbolController( + IAuthenticationService authentication, + ISymbolIndexingService indexer, + ISymbolStorageService storage, + IOptionsSnapshot options, + ILogger logger) { - private readonly IAuthenticationService _authentication; - private readonly ISymbolIndexingService _indexer; - private readonly ISymbolStorageService _storage; - private readonly IOptionsSnapshot _options; - private readonly ILogger _logger; + _authentication = authentication ?? throw new ArgumentNullException(nameof(authentication)); + _indexer = indexer ?? throw new ArgumentNullException(nameof(indexer)); + _storage = storage ?? throw new ArgumentNullException(nameof(storage)); + _options = options ?? throw new ArgumentNullException(nameof(options)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public SymbolController( - IAuthenticationService authentication, - ISymbolIndexingService indexer, - ISymbolStorageService storage, - IOptionsSnapshot options, - ILogger logger) + // See: https://docs.microsoft.com/en-us/nuget/api/package-publish-resource#push-a-package + public async Task Upload(CancellationToken cancellationToken) + { + if (_options.Value.IsReadOnlyMode || !await _authentication.AuthenticateAsync(Request.GetApiKey(), cancellationToken)) { - _authentication = authentication ?? throw new ArgumentNullException(nameof(authentication)); - _indexer = indexer ?? throw new ArgumentNullException(nameof(indexer)); - _storage = storage ?? throw new ArgumentNullException(nameof(storage)); - _options = options ?? throw new ArgumentNullException(nameof(options)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + HttpContext.Response.StatusCode = 401; + return; } - // See: https://docs.microsoft.com/en-us/nuget/api/package-publish-resource#push-a-package - public async Task Upload(CancellationToken cancellationToken) + try { - if (_options.Value.IsReadOnlyMode || !await _authentication.AuthenticateAsync(Request.GetApiKey(), cancellationToken)) + using (var uploadStream = await Request.GetUploadStreamOrNullAsync(cancellationToken)) { - HttpContext.Response.StatusCode = 401; - return; - } - - try - { - using (var uploadStream = await Request.GetUploadStreamOrNullAsync(cancellationToken)) + if (uploadStream == null) { - if (uploadStream == null) - { - HttpContext.Response.StatusCode = 400; - return; - } + HttpContext.Response.StatusCode = 400; + return; + } - var result = await _indexer.IndexAsync(uploadStream, cancellationToken); + var result = await _indexer.IndexAsync(uploadStream, cancellationToken); - switch (result) - { - case SymbolIndexingResult.InvalidSymbolPackage: - HttpContext.Response.StatusCode = 400; - break; + switch (result) + { + case SymbolIndexingResult.InvalidSymbolPackage: + HttpContext.Response.StatusCode = 400; + break; - case SymbolIndexingResult.PackageNotFound: - HttpContext.Response.StatusCode = 404; - break; + case SymbolIndexingResult.PackageNotFound: + HttpContext.Response.StatusCode = 404; + break; - case SymbolIndexingResult.Success: - HttpContext.Response.StatusCode = 201; - break; - } + case SymbolIndexingResult.Success: + HttpContext.Response.StatusCode = 201; + break; } } - catch (Exception e) - { - _logger.LogError(e, "Exception thrown during symbol upload"); + } + catch (Exception e) + { + _logger.LogError(e, "Exception thrown during symbol upload"); - HttpContext.Response.StatusCode = 500; - } + HttpContext.Response.StatusCode = 500; } + } - public async Task Get(string file, string key) + public async Task Get(string file, string key) + { + var pdbStream = await _storage.GetPortablePdbContentStreamOrNullAsync(file, key); + if (pdbStream == null) { - var pdbStream = await _storage.GetPortablePdbContentStreamOrNullAsync(file, key); - if (pdbStream == null) - { - return NotFound(); - } - - return File(pdbStream, "application/octet-stream"); + return NotFound(); } + + return File(pdbStream, "application/octet-stream"); } -} +} \ No newline at end of file diff --git a/src/BaGet.Web/Extensions/HttpRequestExtensions.cs b/src/BaGet.Web/Extensions/HttpRequestExtensions.cs index 1ba13d75..9bde8d9b 100644 --- a/src/BaGet.Web/Extensions/HttpRequestExtensions.cs +++ b/src/BaGet.Web/Extensions/HttpRequestExtensions.cs @@ -1,44 +1,37 @@ -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using BaGet.Core; -using Microsoft.AspNetCore.Http; +namespace BaGet.Web; -namespace BaGet.Web +public static class HttpRequestExtensions { - public static class HttpRequestExtensions - { - public const string ApiKeyHeader = "X-NuGet-ApiKey"; + public const string ApiKeyHeader = "X-NuGet-ApiKey"; - public static async Task GetUploadStreamOrNullAsync(this HttpRequest request, CancellationToken cancellationToken) + public static async Task GetUploadStreamOrNullAsync(this HttpRequest request, CancellationToken cancellationToken) + { + // Try to get the nupkg from the multipart/form-data. If that's empty, + // fallback to the request's body. + Stream rawUploadStream = null; + try { - // Try to get the nupkg from the multipart/form-data. If that's empty, - // fallback to the request's body. - Stream rawUploadStream = null; - try + if (request.HasFormContentType && request.Form.Files.Count > 0) { - if (request.HasFormContentType && request.Form.Files.Count > 0) - { - rawUploadStream = request.Form.Files[0].OpenReadStream(); - } - else - { - rawUploadStream = request.Body; - } - - // Convert the upload stream into a temporary file stream to - // minimize memory usage. - return await rawUploadStream?.AsTemporaryFileStreamAsync(cancellationToken); + rawUploadStream = request.Form.Files[0].OpenReadStream(); } - finally + else { - rawUploadStream?.Dispose(); + rawUploadStream = request.Body; } - } - public static string GetApiKey(this HttpRequest request) + // Convert the upload stream into a temporary file stream to + // minimize memory usage. + return await rawUploadStream?.AsTemporaryFileStreamAsync(cancellationToken); + } + finally { - return request.Headers[ApiKeyHeader]; + rawUploadStream?.Dispose(); } } + + public static string GetApiKey(this HttpRequest request) + { + return request.Headers[ApiKeyHeader]; + } } diff --git a/src/BaGet.Web/Extensions/IApplicationBuilderExtensions.cs b/src/BaGet.Web/Extensions/IApplicationBuilderExtensions.cs index f9bffac2..62db6eda 100644 --- a/src/BaGet.Web/Extensions/IApplicationBuilderExtensions.cs +++ b/src/BaGet.Web/Extensions/IApplicationBuilderExtensions.cs @@ -1,15 +1,11 @@ -using System; -using Microsoft.AspNetCore.Builder; +namespace BaGet.Web; -namespace BaGet.Web +public static class IApplicationBuilderExtensions { - public static class IApplicationBuilderExtensions + public static IApplicationBuilder UseOperationCancelledMiddleware(this IApplicationBuilder app) { - public static IApplicationBuilder UseOperationCancelledMiddleware(this IApplicationBuilder app) - { - if (app == null) throw new ArgumentNullException(nameof(app)); + if (app == null) throw new ArgumentNullException(nameof(app)); - return app.UseMiddleware(); - } + return app.UseMiddleware(); } -} +} \ No newline at end of file diff --git a/src/BaGet.Web/Extensions/IHostExtensions.cs b/src/BaGet.Web/Extensions/IHostExtensions.cs index e2757fa4..633b6c8e 100644 --- a/src/BaGet.Web/Extensions/IHostExtensions.cs +++ b/src/BaGet.Web/Extensions/IHostExtensions.cs @@ -1,49 +1,44 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using BaGet.Core; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; -namespace BaGet.Web +namespace BaGet.Web; + +public static class IHostExtensions { - public static class IHostExtensions + public static IHostBuilder UseBaGet(this IHostBuilder host, Action configure) { - public static IHostBuilder UseBaGet(this IHostBuilder host, Action configure) + return host.ConfigureServices(services => { - return host.ConfigureServices(services => - { - services.AddBaGetWebApplication(configure); - }); - } + services.AddBaGetWebApplication(configure); + }); + } - public static async Task RunMigrationsAsync( - this IHost host, - CancellationToken cancellationToken = default) - { - // Run migrations if necessary. - var options = host.Services.GetRequiredService>(); + public static async Task RunMigrationsAsync( + this IHost host, + CancellationToken cancellationToken = default) + { + // Run migrations if necessary. + var options = host.Services.GetRequiredService>(); - if (options.Value.RunMigrationsAtStartup) + if (options.Value.RunMigrationsAtStartup) + { + using (var scope = host.Services.CreateScope()) { - using (var scope = host.Services.CreateScope()) + var ctx = scope.ServiceProvider.GetService(); + if (ctx != null) { - var ctx = scope.ServiceProvider.GetService(); - if (ctx != null) - { - await ctx.RunMigrationsAsync(cancellationToken); - } + await ctx.RunMigrationsAsync(cancellationToken); } } } + } - public static bool ValidateStartupOptions(this IHost host) - { - return host - .Services - .GetRequiredService() - .Validate(); - } + public static bool ValidateStartupOptions(this IHost host) + { + return host + .Services + .GetRequiredService() + .Validate(); } -} +} \ No newline at end of file diff --git a/src/BaGet.Web/Extensions/IServiceCollectionExtensions.cs b/src/BaGet.Web/Extensions/IServiceCollectionExtensions.cs index 39964850..cdb8344b 100644 --- a/src/BaGet.Web/Extensions/IServiceCollectionExtensions.cs +++ b/src/BaGet.Web/Extensions/IServiceCollectionExtensions.cs @@ -1,35 +1,24 @@ -using System; -using BaGet.Core; -using BaGet.Web; -using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; +using System.Text.Json.Serialization; -namespace BaGet +namespace BaGet.Web; + +public static class IServiceCollectionExtensions { - public static class IServiceCollectionExtensions + public static IServiceCollection AddBaGetWebApplication(this IServiceCollection services, Action configureAction) { - public static IServiceCollection AddBaGetWebApplication( - this IServiceCollection services, - Action configureAction) - { - services - .AddRouting(options => options.LowercaseUrls = true) + services.AddRouting(options => options.LowercaseUrls = true) .AddControllers() .AddApplicationPart(typeof(PackageContentController).Assembly) - .SetCompatibilityVersion(CompatibilityVersion.Version_3_0) - .AddJsonOptions(options => - { - options.JsonSerializerOptions.IgnoreNullValues = true; - }); + .AddJsonOptions(options => options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull); - services.AddRazorPages(); + services.AddRazorPages(); - services.AddHttpContextAccessor(); - services.AddTransient(); + services.AddHttpContextAccessor(); + services.AddTransient(); - services.AddBaGetApplication(configureAction); + services.AddBaGetApplication(configureAction); - return services; - } + return services; } } diff --git a/src/BaGet.Web/Extensions/RazorExtensions.cs b/src/BaGet.Web/Extensions/RazorExtensions.cs index 1a48ecde..87b1e8fd 100644 --- a/src/BaGet.Web/Extensions/RazorExtensions.cs +++ b/src/BaGet.Web/Extensions/RazorExtensions.cs @@ -1,12 +1,11 @@ using Humanizer; -namespace BaGet.Web +namespace BaGet.Web; + +public static class RazorExtensions { - public static class RazorExtensions + public static string ToMetric(this long value) { - public static string ToMetric(this long value) - { - return ((double) value).ToMetric(); - } + return ((double) value).ToMetric(); } -} +} \ No newline at end of file diff --git a/src/BaGet.Web/GlobalUsings.cs b/src/BaGet.Web/GlobalUsings.cs new file mode 100644 index 00000000..dbf5fc86 --- /dev/null +++ b/src/BaGet.Web/GlobalUsings.cs @@ -0,0 +1,21 @@ +// Global using directives + +global using System; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Net; +global using System.Threading; +global using System.Threading.Tasks; +global using BaGet.Core; +global using Markdig; +global using Microsoft.AspNetCore.Builder; +global using Microsoft.AspNetCore.Html; +global using Microsoft.AspNetCore.Http; +global using Microsoft.AspNetCore.Mvc.RazorPages; +//global using Microsoft.AspNetCore.Razor.TagHelpers; +global using Microsoft.AspNetCore.Routing; +global using Microsoft.AspNetCore.Routing.Constraints; +global using Microsoft.Extensions.Logging; +global using NuGet.Frameworks; +global using NuGet.Versioning; diff --git a/src/BaGet.Web/NavLinkTagHelper.cs b/src/BaGet.Web/NavLinkTagHelper.cs index 2f41b5f3..a543f79e 100644 --- a/src/BaGet.Web/NavLinkTagHelper.cs +++ b/src/BaGet.Web/NavLinkTagHelper.cs @@ -1,41 +1,37 @@ -using System; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Razor.TagHelpers; -namespace BaGet.Web +namespace BaGet.Web; + +[HtmlTargetElement(Attributes = "nav-link")] +public class NavLinkTagHelper : TagHelper { - [HtmlTargetElement(Attributes = "nav-link")] - public class NavLinkTagHelper : TagHelper - { - private readonly IHttpContextAccessor _accessor; + private readonly IHttpContextAccessor _accessor; - public NavLinkTagHelper(IHttpContextAccessor accessor) - { - _accessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); - } + public NavLinkTagHelper(IHttpContextAccessor accessor) + { + _accessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); + } - [HtmlAttributeName("asp-page")] - public string Page { get; set; } + [HtmlAttributeName("asp-page")] + public string Page { get; set; } - public override void Process(TagHelperContext context, TagHelperOutput output) + public override void Process(TagHelperContext context, TagHelperOutput output) + { + if (IsActiveLink()) { - if (IsActiveLink()) - { - output.Attributes.SetAttribute("class", "active"); - } + output.Attributes.SetAttribute("class", "active"); } + } - private bool IsActiveLink() - { - var endpoint = _accessor.HttpContext?.GetEndpoint(); - var pageDescriptor = endpoint?.Metadata.GetMetadata(); + private bool IsActiveLink() + { + var endpoint = _accessor.HttpContext?.GetEndpoint(); + var pageDescriptor = endpoint?.Metadata.GetMetadata(); - if (pageDescriptor == null) return false; - if (pageDescriptor.AreaName != null) return false; - if (pageDescriptor.ViewEnginePath != Page) return false; + if (pageDescriptor == null) return false; + if (pageDescriptor.AreaName != null) return false; + if (pageDescriptor.ViewEnginePath != Page) return false; - return true; - } + return true; } } diff --git a/src/BaGet.Web/OperationCancelledMiddleware.cs b/src/BaGet.Web/OperationCancelledMiddleware.cs index e4f1faf7..6b7ff019 100644 --- a/src/BaGet.Web/OperationCancelledMiddleware.cs +++ b/src/BaGet.Web/OperationCancelledMiddleware.cs @@ -1,59 +1,52 @@ -using System; -using System.Net; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; +namespace BaGet.Web; -namespace BaGet.Web +/// +/// Captures and converts to HTTP 409 response. +/// Based off: https://github.com/aspnet/AspNetCore/blob/28157e62597bf0e043bc7e937e44c5ec81946b83/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddleware.cs +/// +public class OperationCancelledMiddleware { - /// - /// Captures and converts to HTTP 409 response. - /// Based off: https://github.com/aspnet/AspNetCore/blob/28157e62597bf0e043bc7e937e44c5ec81946b83/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddleware.cs - /// - public class OperationCancelledMiddleware + private readonly RequestDelegate _next; + private readonly ILogger _logger; + + public OperationCancelledMiddleware(RequestDelegate next, ILogger logger) { - private readonly RequestDelegate _next; - private readonly ILogger _logger; + _next = next ?? throw new ArgumentNullException(nameof(next)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public OperationCancelledMiddleware(RequestDelegate next, ILogger logger) + public async Task Invoke(HttpContext context) + { + try { - _next = next ?? throw new ArgumentNullException(nameof(next)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + await _next(context); } - - public async Task Invoke(HttpContext context) + catch (Exception e) when (ShouldHandleException(context, e)) { try { - await _next(context); + _logger.LogWarning("Request cancelled"); + + context.Response.Clear(); + context.Response.StatusCode = (int)HttpStatusCode.Conflict; + return; } - catch (Exception e) when (ShouldHandleException(context, e)) + catch (Exception) { - try - { - _logger.LogWarning("Request cancelled"); - - context.Response.Clear(); - context.Response.StatusCode = (int)HttpStatusCode.Conflict; - return; - } - catch (Exception) - { - // If there's an exception, rethrow the original exception. - } - - throw; + // If there's an exception, rethrow the original exception. } - bool ShouldHandleException(HttpContext ctx, Exception e) - { - if (ctx.Response.HasStarted) return false; + throw; + } - if (e is OperationCanceledException) return true; - if (e.InnerException is OperationCanceledException) return true; + bool ShouldHandleException(HttpContext ctx, Exception e) + { + if (ctx.Response.HasStarted) return false; - return false; - } + if (e is OperationCanceledException) return true; + if (e.InnerException is OperationCanceledException) return true; + + return false; } } } diff --git a/src/BaGet.Web/Pages/Index.cshtml b/src/BaGet.Web/Pages/Index.cshtml index 303e1c0c..67c3c0af 100644 --- a/src/BaGet.Web/Pages/Index.cshtml +++ b/src/BaGet.Web/Pages/Index.cshtml @@ -1,5 +1,4 @@ @page -@using BaGet.Protocol.Models @model IndexModel @{ diff --git a/src/BaGet.Web/Pages/Index.cshtml.cs b/src/BaGet.Web/Pages/Index.cshtml.cs index f9f5e15e..0e91caec 100644 --- a/src/BaGet.Web/Pages/Index.cshtml.cs +++ b/src/BaGet.Web/Pages/Index.cshtml.cs @@ -1,67 +1,60 @@ -using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.Threading; -using System.Threading.Tasks; -using BaGet.Core; using BaGet.Protocol.Models; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -namespace BaGet.Web -{ - public class IndexModel : PageModel - { - private readonly ISearchService _search; - - public IndexModel(ISearchService search) - { - _search = search ?? throw new ArgumentNullException(nameof(search)); - } - - public const int ResultsPerPage = 20; - - [BindProperty(Name = "q", SupportsGet = true)] - public string Query { get; set; } +namespace BaGet.Web; - [BindProperty(Name = "p", SupportsGet = true)] - [Range(1, int.MaxValue)] - public int PageIndex { get; set; } = 1; +public class IndexModel : PageModel +{ + private readonly ISearchService _search; - [BindProperty(SupportsGet = true)] - public string PackageType { get; set; } = "any"; + public IndexModel(ISearchService search) + { + _search = search ?? throw new ArgumentNullException(nameof(search)); + } - [BindProperty(SupportsGet = true)] - public string Framework { get; set; } = "any"; + public const int ResultsPerPage = 20; - [BindProperty(SupportsGet = true)] - public bool Prerelease { get; set; } = true; + [BindProperty(Name = "q", SupportsGet = true)] + public string Query { get; set; } - public IReadOnlyList Packages { get; private set; } + [BindProperty(Name = "p", SupportsGet = true)] + [Range(1, int.MaxValue)] + public int PageIndex { get; set; } = 1; - public async Task OnGetAsync(CancellationToken cancellationToken) - { - if (!ModelState.IsValid) return BadRequest(); + [BindProperty(SupportsGet = true)] + public string PackageType { get; set; } = "any"; - var packageType = PackageType == "any" ? null : PackageType; - var framework = Framework == "any" ? null : Framework; + [BindProperty(SupportsGet = true)] + public string Framework { get; set; } = "any"; - var search = await _search.SearchAsync( - new SearchRequest - { - Skip = (PageIndex - 1) * ResultsPerPage, - Take = ResultsPerPage, - IncludePrerelease = Prerelease, - IncludeSemVer2 = true, - PackageType = packageType, - Framework = framework, - Query = Query, - }, - cancellationToken); + [BindProperty(SupportsGet = true)] + public bool Prerelease { get; set; } = true; - Packages = search.Data; + public IReadOnlyList Packages { get; private set; } - return Page(); - } + public async Task OnGetAsync(CancellationToken cancellationToken) + { + if (!ModelState.IsValid) return BadRequest(); + + var packageType = PackageType == "any" ? null : PackageType; + var framework = Framework == "any" ? null : Framework; + + var search = await _search.SearchAsync( + new SearchRequest + { + Skip = (PageIndex - 1) * ResultsPerPage, + Take = ResultsPerPage, + IncludePrerelease = Prerelease, + IncludeSemVer2 = true, + PackageType = packageType, + Framework = framework, + Query = Query, + }, + cancellationToken); + + Packages = search.Data; + + return Page(); } } diff --git a/src/BaGet.Web/Pages/Package.cshtml b/src/BaGet.Web/Pages/Package.cshtml index 8e42cd6d..6ea83941 100644 --- a/src/BaGet.Web/Pages/Package.cshtml +++ b/src/BaGet.Web/Pages/Package.cshtml @@ -1,4 +1,6 @@ @page "/packages/{id}/{version?}" +@using Humanizer +@using Microsoft.AspNetCore.Mvc.TagHelpers @model PackageModel @{ diff --git a/src/BaGet.Web/Pages/Package.cshtml.cs b/src/BaGet.Web/Pages/Package.cshtml.cs index 8841648e..8b0214bc 100644 --- a/src/BaGet.Web/Pages/Package.cshtml.cs +++ b/src/BaGet.Web/Pages/Package.cshtml.cs @@ -1,238 +1,224 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using BaGet.Core; -using Markdig; -using Microsoft.AspNetCore.Html; -using Microsoft.AspNetCore.Mvc.RazorPages; -using NuGet.Frameworks; -using NuGet.Versioning; - -namespace BaGet.Web +namespace BaGet.Web; + +public class PackageModel : PageModel { - public class PackageModel : PageModel + private static readonly MarkdownPipeline MarkdownPipeline; + + private readonly IPackageService _packages; + private readonly IPackageContentService _content; + private readonly ISearchService _search; + private readonly IUrlGenerator _url; + + static PackageModel() { - private static readonly MarkdownPipeline MarkdownPipeline; + MarkdownPipeline = new MarkdownPipelineBuilder() + .UseAdvancedExtensions() + .Build(); + } - private readonly IPackageService _packages; - private readonly IPackageContentService _content; - private readonly ISearchService _search; - private readonly IUrlGenerator _url; + public PackageModel( + IPackageService packages, + IPackageContentService content, + ISearchService search, + IUrlGenerator url) + { + _packages = packages ?? throw new ArgumentNullException(nameof(packages)); + _content = content ?? throw new ArgumentNullException(nameof(content)); + _search = search ?? throw new ArgumentNullException(nameof(search)); + _url = url ?? throw new ArgumentNullException(nameof(url)); + } - static PackageModel() - { - MarkdownPipeline = new MarkdownPipelineBuilder() - .UseAdvancedExtensions() - .Build(); - } + public bool Found { get; private set; } - public PackageModel( - IPackageService packages, - IPackageContentService content, - ISearchService search, - IUrlGenerator url) - { - _packages = packages ?? throw new ArgumentNullException(nameof(packages)); - _content = content ?? throw new ArgumentNullException(nameof(content)); - _search = search ?? throw new ArgumentNullException(nameof(search)); - _url = url ?? throw new ArgumentNullException(nameof(url)); - } + public Package Package { get; private set; } - public bool Found { get; private set; } + public bool IsDotnetTemplate { get; private set; } + public bool IsDotnetTool { get; private set; } + public DateTime LastUpdated { get; private set; } + public long TotalDownloads { get; private set; } - public Package Package { get; private set; } + public IReadOnlyList UsedBy { get; set; } + public IReadOnlyList DependencyGroups { get; private set; } + public IReadOnlyList Versions { get; private set; } - public bool IsDotnetTemplate { get; private set; } - public bool IsDotnetTool { get; private set; } - public DateTime LastUpdated { get; private set; } - public long TotalDownloads { get; private set; } + public HtmlString Readme { get; private set; } - public IReadOnlyList UsedBy { get; set; } - public IReadOnlyList DependencyGroups { get; private set; } - public IReadOnlyList Versions { get; private set; } + public string IconUrl { get; private set; } + public string LicenseUrl { get; private set; } + public string PackageDownloadUrl { get; private set; } - public HtmlString Readme { get; private set; } + public async Task OnGetAsync(string id, string version, CancellationToken cancellationToken) + { + var packages = await _packages.FindPackagesAsync(id, cancellationToken); + var listedPackages = packages.Where(p => p.Listed).ToList(); - public string IconUrl { get; private set; } - public string LicenseUrl { get; private set; } - public string PackageDownloadUrl { get; private set; } + // Try to find the requested version. + if (NuGetVersion.TryParse(version, out var requestedVersion)) + { + Package = packages.SingleOrDefault(p => p.Version == requestedVersion); + } - public async Task OnGetAsync(string id, string version, CancellationToken cancellationToken) + // Otherwise try to display the latest version. + if (Package == null) { - var packages = await _packages.FindPackagesAsync(id, cancellationToken); - var listedPackages = packages.Where(p => p.Listed).ToList(); + Package = listedPackages.OrderByDescending(p => p.Version).FirstOrDefault(); + } - // Try to find the requested version. - if (NuGetVersion.TryParse(version, out var requestedVersion)) - { - Package = packages.SingleOrDefault(p => p.Version == requestedVersion); - } + if (Package == null) + { + Package = new Package { Id = id }; + Found = false; + return; + } - // Otherwise try to display the latest version. - if (Package == null) - { - Package = listedPackages.OrderByDescending(p => p.Version).FirstOrDefault(); - } + var packageVersion = Package.Version; - if (Package == null) - { - Package = new Package { Id = id }; - Found = false; - return; - } + Found = true; + IsDotnetTemplate = Package.PackageTypes.Any(t => t.Name.Equals("Template", StringComparison.OrdinalIgnoreCase)); + IsDotnetTool = Package.PackageTypes.Any(t => t.Name.Equals("DotnetTool", StringComparison.OrdinalIgnoreCase)); + LastUpdated = packages.Max(p => p.Published); + TotalDownloads = packages.Sum(p => p.Downloads); - var packageVersion = Package.Version; + var dependents = await _search.FindDependentsAsync(Package.Id, cancellationToken); - Found = true; - IsDotnetTemplate = Package.PackageTypes.Any(t => t.Name.Equals("Template", StringComparison.OrdinalIgnoreCase)); - IsDotnetTool = Package.PackageTypes.Any(t => t.Name.Equals("DotnetTool", StringComparison.OrdinalIgnoreCase)); - LastUpdated = packages.Max(p => p.Published); - TotalDownloads = packages.Sum(p => p.Downloads); + UsedBy = dependents.Data; + DependencyGroups = ToDependencyGroups(Package); + Versions = ToVersions(listedPackages, packageVersion); - var dependents = await _search.FindDependentsAsync(Package.Id, cancellationToken); + if (Package.HasReadme) + { + Readme = await GetReadmeHtmlStringOrNullAsync(Package.Id, packageVersion, cancellationToken); + } - UsedBy = dependents.Data; - DependencyGroups = ToDependencyGroups(Package); - Versions = ToVersions(listedPackages, packageVersion); + IconUrl = Package.HasEmbeddedIcon + ? _url.GetPackageIconDownloadUrl(Package.Id, packageVersion) + : Package.IconUrlString; + LicenseUrl = Package.LicenseUrlString; + PackageDownloadUrl = _url.GetPackageDownloadUrl(Package.Id, packageVersion); + } - if (Package.HasReadme) + private IReadOnlyList ToDependencyGroups(Package package) + { + return package + .Dependencies + .GroupBy(d => d.TargetFramework) + .Select(group => { - Readme = await GetReadmeHtmlStringOrNullAsync(Package.Id, packageVersion, cancellationToken); - } + return new DependencyGroupModel + { + Name = PrettifyTargetFramework(group.Key), + Dependencies = group + .Where(d => d.Id != null) + .Select(d => new DependencyModel + { + PackageId = d.Id, + VersionSpec = (d.VersionRange != null) + ? VersionRange.Parse(d.VersionRange).PrettyPrint() + : string.Empty + }) + .ToList() + }; + }) + .ToList(); + } - IconUrl = Package.HasEmbeddedIcon - ? _url.GetPackageIconDownloadUrl(Package.Id, packageVersion) - : Package.IconUrlString; - LicenseUrl = Package.LicenseUrlString; - PackageDownloadUrl = _url.GetPackageDownloadUrl(Package.Id, packageVersion); - } + private string PrettifyTargetFramework(string targetFramework) + { + if (targetFramework == null) return "All Frameworks"; - private IReadOnlyList ToDependencyGroups(Package package) + NuGetFramework framework; + try { - return package - .Dependencies - .GroupBy(d => d.TargetFramework) - .Select(group => - { - return new DependencyGroupModel - { - Name = PrettifyTargetFramework(group.Key), - Dependencies = group - .Where(d => d.Id != null) - .Select(d => new DependencyModel - { - PackageId = d.Id, - VersionSpec = (d.VersionRange != null) - ? VersionRange.Parse(d.VersionRange).PrettyPrint() - : string.Empty - }) - .ToList() - }; - }) - .ToList(); + framework = NuGetFramework.Parse(targetFramework); } - - private string PrettifyTargetFramework(string targetFramework) + catch (Exception) { - if (targetFramework == null) return "All Frameworks"; - - NuGetFramework framework; - try - { - framework = NuGetFramework.Parse(targetFramework); - } - catch (Exception) - { - return targetFramework; - } - - string frameworkName; - if (framework.Framework.Equals(FrameworkConstants.FrameworkIdentifiers.NetCoreApp, - StringComparison.OrdinalIgnoreCase)) - { - frameworkName = (framework.Version.Major >= 5) - ? ".NET" - : ".NET Core"; - } - else if (framework.Framework.Equals(FrameworkConstants.FrameworkIdentifiers.NetStandard, - StringComparison.OrdinalIgnoreCase)) - { - frameworkName = ".NET Standard"; - } - else if (framework.Framework.Equals(FrameworkConstants.FrameworkIdentifiers.Net, - StringComparison.OrdinalIgnoreCase)) - { - frameworkName = ".NET Framework"; - } - else - { - frameworkName = framework.Framework; - } - - var frameworkVersion = (framework.Version.Build == 0) - ? framework.Version.ToString(2) - : framework.Version.ToString(3); - - return $"{frameworkName} {frameworkVersion}"; + return targetFramework; } - private IReadOnlyList ToVersions(IReadOnlyList packages, NuGetVersion selectedVersion) + string frameworkName; + if (framework.Framework.Equals(FrameworkConstants.FrameworkIdentifiers.NetCoreApp, + StringComparison.OrdinalIgnoreCase)) { - return packages - .Select(p => new VersionModel - { - Version = p.Version, - Downloads = p.Downloads, - Selected = p.Version == selectedVersion, - LastUpdated = p.Published, - }) - .OrderByDescending(m => m.Version) - .ToList(); + frameworkName = (framework.Version.Major >= 5) + ? ".NET" + : ".NET Core"; } - - private async Task GetReadmeHtmlStringOrNullAsync( - string packageId, - NuGetVersion packageVersion, - CancellationToken cancellationToken) + else if (framework.Framework.Equals(FrameworkConstants.FrameworkIdentifiers.NetStandard, + StringComparison.OrdinalIgnoreCase)) { - string readme; - using (var readmeStream = await _content.GetPackageReadmeStreamOrNullAsync(packageId, packageVersion, cancellationToken)) - { - if (readmeStream == null) return null; - - using (var reader = new StreamReader(readmeStream)) - { - readme = await reader.ReadToEndAsync(); - } - } - - var readmeHtml = Markdown.ToHtml(readme, MarkdownPipeline); - return new HtmlString(readmeHtml); + frameworkName = ".NET Standard"; } - - public class DependencyGroupModel + else if (framework.Framework.Equals(FrameworkConstants.FrameworkIdentifiers.Net, + StringComparison.OrdinalIgnoreCase)) { - public string Name { get; set; } - public IReadOnlyList Dependencies { get; set; } + frameworkName = ".NET Framework"; } - - // TODO: Convert this to records. - public class DependencyModel + else { - public string PackageId { get; set; } - public string VersionSpec { get; set; } + frameworkName = framework.Framework; } - // TODO: Convert this to records. - public class VersionModel + var frameworkVersion = (framework.Version.Build == 0) + ? framework.Version.ToString(2) + : framework.Version.ToString(3); + + return $"{frameworkName} {frameworkVersion}"; + } + + private IReadOnlyList ToVersions(IReadOnlyList packages, NuGetVersion selectedVersion) + { + return packages + .Select(p => new VersionModel + { + Version = p.Version, + Downloads = p.Downloads, + Selected = p.Version == selectedVersion, + LastUpdated = p.Published, + }) + .OrderByDescending(m => m.Version) + .ToList(); + } + + private async Task GetReadmeHtmlStringOrNullAsync( + string packageId, + NuGetVersion packageVersion, + CancellationToken cancellationToken) + { + string readme; + using (var readmeStream = await _content.GetPackageReadmeStreamOrNullAsync(packageId, packageVersion, cancellationToken)) { - public NuGetVersion Version { get; set; } - public long Downloads { get; set; } - public bool Selected { get; set; } - public DateTime LastUpdated { get; set; } + if (readmeStream == null) return null; + + using (var reader = new StreamReader(readmeStream)) + { + readme = await reader.ReadToEndAsync(); + } } + + var readmeHtml = Markdown.ToHtml(readme, MarkdownPipeline); + return new HtmlString(readmeHtml); + } + + public class DependencyGroupModel + { + public string Name { get; set; } + public IReadOnlyList Dependencies { get; set; } + } + + // TODO: Convert this to records. + public class DependencyModel + { + public string PackageId { get; set; } + public string VersionSpec { get; set; } + } + + // TODO: Convert this to records. + public class VersionModel + { + public NuGetVersion Version { get; set; } + public long Downloads { get; set; } + public bool Selected { get; set; } + public DateTime LastUpdated { get; set; } } } diff --git a/src/BaGet.Web/Pages/_ViewImports.cshtml b/src/BaGet.Web/Pages/_ViewImports.cshtml index 311ed1e1..d0f3cb89 100644 --- a/src/BaGet.Web/Pages/_ViewImports.cshtml +++ b/src/BaGet.Web/Pages/_ViewImports.cshtml @@ -1,7 +1,3 @@ -@using BaGet -@using BaGet.Core -@using Humanizer -@using Microsoft.AspNetCore.Html @namespace BaGet.Web @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, BaGet.Web diff --git a/src/BaGet.Web/Routes.cs b/src/BaGet.Web/Routes.cs index ff31ad38..ecbb1421 100644 --- a/src/BaGet.Web/Routes.cs +++ b/src/BaGet.Web/Routes.cs @@ -1,23 +1,22 @@ -namespace BaGet.Web +namespace BaGet.Web; + +public class Routes { - public class Routes - { - public const string IndexRouteName = "index"; - public const string UploadPackageRouteName = "upload-package"; - public const string UploadSymbolRouteName = "upload-symbol"; - public const string DeleteRouteName = "delete"; - public const string RelistRouteName = "relist"; - public const string SearchRouteName = "search"; - public const string AutocompleteRouteName = "autocomplete"; - public const string DependentsRouteName = "dependents"; - public const string RegistrationIndexRouteName = "registration-index"; - public const string RegistrationLeafRouteName = "registration-leaf"; - public const string PackageVersionsRouteName = "package-versions"; - public const string PackageDownloadRouteName = "package-download"; - public const string PackageDownloadManifestRouteName = "package-download-manifest"; - public const string PackageDownloadReadmeRouteName = "package-download-readme"; - public const string PackageDownloadIconRouteName = "package-download-icon"; - public const string SymbolDownloadRouteName = "symbol-download"; - public const string PrefixedSymbolDownloadRouteName = "prefixed-symbol-download"; - } -} + public const string IndexRouteName = "index"; + public const string UploadPackageRouteName = "upload-package"; + public const string UploadSymbolRouteName = "upload-symbol"; + public const string DeleteRouteName = "delete"; + public const string RelistRouteName = "relist"; + public const string SearchRouteName = "search"; + public const string AutocompleteRouteName = "autocomplete"; + public const string DependentsRouteName = "dependents"; + public const string RegistrationIndexRouteName = "registration-index"; + public const string RegistrationLeafRouteName = "registration-leaf"; + public const string PackageVersionsRouteName = "package-versions"; + public const string PackageDownloadRouteName = "package-download"; + public const string PackageDownloadManifestRouteName = "package-download-manifest"; + public const string PackageDownloadReadmeRouteName = "package-download-readme"; + public const string PackageDownloadIconRouteName = "package-download-icon"; + public const string SymbolDownloadRouteName = "symbol-download"; + public const string PrefixedSymbolDownloadRouteName = "prefixed-symbol-download"; +} \ No newline at end of file diff --git a/src/BaGet/BaGet.csproj b/src/BaGet/BaGet.csproj index 880268ce..52cf215d 100644 --- a/src/BaGet/BaGet.csproj +++ b/src/BaGet/BaGet.csproj @@ -1,13 +1,13 @@ - netcoreapp3.1 + net6.0 - - - + + + @@ -24,4 +24,8 @@ - + + + + + \ No newline at end of file diff --git a/src/BaGet/ConfigureBaGetOptions.cs b/src/BaGet/ConfigureBaGetOptions.cs index b28a658d..aed7db14 100644 --- a/src/BaGet/ConfigureBaGetOptions.cs +++ b/src/BaGet/ConfigureBaGetOptions.cs @@ -1,119 +1,114 @@ -using System; -using System.Collections.Generic; -using System.Linq; using BaGet.Core; -using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Cors.Infrastructure; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.Extensions.Options; -namespace BaGet -{ - /// - /// BaGet's options configuration, specific to the default BaGet application. - /// Don't use this if you are embedding BaGet into your own custom ASP.NET Core application. - /// - public class ConfigureBaGetOptions - : IConfigureOptions +namespace BaGet; + +/// +/// BaGet's options configuration, specific to the default BaGet application. +/// Don't use this if you are embedding BaGet into your own custom ASP.NET Core application. +/// +public class ConfigureBaGetOptions + : IConfigureOptions , IConfigureOptions , IConfigureOptions , IConfigureOptions , IValidateOptions - { - public const string CorsPolicy = "AllowAll"; - - private static readonly HashSet ValidDatabaseTypes - = new HashSet(StringComparer.OrdinalIgnoreCase) - { - "AzureTable", - "MySql", - "PostgreSql", - "Sqlite", - "SqlServer", - }; - - private static readonly HashSet ValidStorageTypes - = new HashSet(StringComparer.OrdinalIgnoreCase) - { - "AliyunOss", - "AwsS3", - "AzureBlobStorage", - "Filesystem", - "GoogleCloud", - "Null", - }; - - private static readonly HashSet ValidSearchTypes - = new HashSet(StringComparer.OrdinalIgnoreCase) - { - "AzureSearch", - "Database", - "Null", - }; - - public void Configure(CorsOptions options) - { - // TODO: Consider disabling this on production builds. - options.AddPolicy( - CorsPolicy, - builder => builder.AllowAnyOrigin() - .AllowAnyMethod() - .AllowAnyHeader()); - } +{ + public const string CorsPolicy = "AllowAll"; - public void Configure(FormOptions options) + private static readonly HashSet ValidDatabaseTypes + = new HashSet(StringComparer.OrdinalIgnoreCase) { - options.MultipartBodyLengthLimit = int.MaxValue; - } - - public void Configure(ForwardedHeadersOptions options) + "AzureTable", + "MySql", + "PostgreSql", + "Sqlite", + "SqlServer", + }; + + private static readonly HashSet ValidStorageTypes + = new HashSet(StringComparer.OrdinalIgnoreCase) + { + "AliyunOss", + "AwsS3", + "AzureBlobStorage", + "Filesystem", + "GoogleCloud", + "Null", + }; + + private static readonly HashSet ValidSearchTypes + = new HashSet(StringComparer.OrdinalIgnoreCase) { - options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; + "AzureSearch", + "Database", + "Null", + }; + + public void Configure(CorsOptions options) + { + // TODO: Consider disabling this on production builds. + options.AddPolicy( + CorsPolicy, + builder => builder.AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader()); + } + + public void Configure(FormOptions options) + { + options.MultipartBodyLengthLimit = int.MaxValue; + } + + public void Configure(ForwardedHeadersOptions options) + { + options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; + + // Do not restrict to local network/proxy + options.KnownNetworks.Clear(); + options.KnownProxies.Clear(); + } + + public void Configure(IISServerOptions options) + { + options.MaxRequestBodySize = 262144000; + } + + public ValidateOptionsResult Validate(string name, BaGetOptions options) + { + var failures = new List(); + + if (options.Database == null) failures.Add($"The '{nameof(BaGetOptions.Database)}' config is required"); + if (options.Mirror == null) failures.Add($"The '{nameof(BaGetOptions.Mirror)}' config is required"); + if (options.Search == null) failures.Add($"The '{nameof(BaGetOptions.Search)}' config is required"); + if (options.Storage == null) failures.Add($"The '{nameof(BaGetOptions.Storage)}' config is required"); - // Do not restrict to local network/proxy - options.KnownNetworks.Clear(); - options.KnownProxies.Clear(); + if (!ValidDatabaseTypes.Contains(options.Database?.Type)) + { + failures.Add( + $"The '{nameof(BaGetOptions.Database)}:{nameof(DatabaseOptions.Type)}' config is invalid. " + + $"Allowed values: {string.Join(", ", ValidDatabaseTypes)}"); } - public void Configure(IISServerOptions options) + if (!ValidStorageTypes.Contains(options.Storage?.Type)) { - options.MaxRequestBodySize = 262144000; + failures.Add( + $"The '{nameof(BaGetOptions.Storage)}:{nameof(StorageOptions.Type)}' config is invalid. " + + $"Allowed values: {string.Join(", ", ValidStorageTypes)}"); } - public ValidateOptionsResult Validate(string name, BaGetOptions options) + if (!ValidSearchTypes.Contains(options.Search?.Type)) { - var failures = new List(); - - if (options.Database == null) failures.Add($"The '{nameof(BaGetOptions.Database)}' config is required"); - if (options.Mirror == null) failures.Add($"The '{nameof(BaGetOptions.Mirror)}' config is required"); - if (options.Search == null) failures.Add($"The '{nameof(BaGetOptions.Search)}' config is required"); - if (options.Storage == null) failures.Add($"The '{nameof(BaGetOptions.Storage)}' config is required"); - - if (!ValidDatabaseTypes.Contains(options.Database?.Type)) - { - failures.Add( - $"The '{nameof(BaGetOptions.Database)}:{nameof(DatabaseOptions.Type)}' config is invalid. " + - $"Allowed values: {string.Join(", ", ValidDatabaseTypes)}"); - } - - if (!ValidStorageTypes.Contains(options.Storage?.Type)) - { - failures.Add( - $"The '{nameof(BaGetOptions.Storage)}:{nameof(StorageOptions.Type)}' config is invalid. " + - $"Allowed values: {string.Join(", ", ValidStorageTypes)}"); - } - - if (!ValidSearchTypes.Contains(options.Search?.Type)) - { - failures.Add( - $"The '{nameof(BaGetOptions.Search)}:{nameof(SearchOptions.Type)}' config is invalid. " + - $"Allowed values: {string.Join(", ", ValidSearchTypes)}"); - } - - if (failures.Any()) return ValidateOptionsResult.Fail(failures); - - return ValidateOptionsResult.Success; + failures.Add( + $"The '{nameof(BaGetOptions.Search)}:{nameof(SearchOptions.Type)}' config is invalid. " + + $"Allowed values: {string.Join(", ", ValidSearchTypes)}"); } + + if (failures.Any()) return ValidateOptionsResult.Fail(failures); + + return ValidateOptionsResult.Success; } -} +} \ No newline at end of file diff --git a/src/BaGet/ConfigureRazorRuntimeCompilation.cs b/src/BaGet/ConfigureRazorRuntimeCompilation.cs index 80490934..00158aa0 100644 --- a/src/BaGet/ConfigureRazorRuntimeCompilation.cs +++ b/src/BaGet/ConfigureRazorRuntimeCompilation.cs @@ -1,32 +1,28 @@ -using System; -using System.IO; -using Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation; +using Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation; using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; -namespace BaGet +namespace BaGet; + +public class ConfigureRazorRuntimeCompilation : IConfigureOptions { - public class ConfigureRazorRuntimeCompilation : IConfigureOptions - { - private readonly IHostEnvironment _env; + private readonly IHostEnvironment _env; - public ConfigureRazorRuntimeCompilation(IHostEnvironment env) - { - _env = env ?? throw new ArgumentNullException(nameof(env)); - } + public ConfigureRazorRuntimeCompilation(IHostEnvironment env) + { + _env = env ?? throw new ArgumentNullException(nameof(env)); + } - public void Configure(MvcRazorRuntimeCompilationOptions options) - { - var path = Path.Combine(_env.ContentRootPath, "..", "BaGet.Web"); + public void Configure(MvcRazorRuntimeCompilationOptions options) + { + var path = Path.Combine(_env.ContentRootPath, "..", "BaGet.Web"); - // Try to enable Razor "hot reload". - if (!_env.IsDevelopment()) return; - if (!Directory.Exists(path)) return; + // Try to enable Razor "hot reload". + if (!_env.IsDevelopment()) return; + if (!Directory.Exists(path)) return; - var provider = new PhysicalFileProvider(Path.GetFullPath(path)); + var provider = new PhysicalFileProvider(Path.GetFullPath(path)); - options.FileProviders.Add(provider); - } + options.FileProviders.Add(provider); } -} +} \ No newline at end of file diff --git a/src/BaGet/Program.cs b/src/BaGet/Program.cs index 5eeca3f6..01d42675 100644 --- a/src/BaGet/Program.cs +++ b/src/BaGet/Program.cs @@ -1,84 +1,77 @@ -using System; -using System.Threading.Tasks; using BaGet.Core; using BaGet.Web; using McMaster.Extensions.CommandLineUtils; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -namespace BaGet +namespace BaGet; + +public class Program { - public class Program + public static async Task Main(string[] args) { - public static async Task Main(string[] args) + var host = CreateHostBuilder(args).Build(); + if (!host.ValidateStartupOptions()) { - var host = CreateHostBuilder(args).Build(); - if (!host.ValidateStartupOptions()) - { - return; - } + return; + } - var app = new CommandLineApplication - { - Name = "baget", - Description = "A light-weight NuGet service", - }; + var app = new CommandLineApplication + { + Name = "baget", + Description = "A light-weight NuGet service", + }; - app.HelpOption(inherited: true); + app.HelpOption(inherited: true); - app.Command("import", import => + app.Command("import", import => + { + import.Command("downloads", downloads => { - import.Command("downloads", downloads => + downloads.OnExecuteAsync(async cancellationToken => { - downloads.OnExecuteAsync(async cancellationToken => + using (var scope = host.Services.CreateScope()) { - using (var scope = host.Services.CreateScope()) - { - var importer = scope.ServiceProvider.GetRequiredService(); + var importer = scope.ServiceProvider.GetRequiredService(); - await importer.ImportAsync(cancellationToken); - } - }); + await importer.ImportAsync(cancellationToken); + } }); }); + }); - app.Option("--urls", "The URLs that BaGet should bind to.", CommandOptionType.SingleValue); + app.Option("--urls", "The URLs that BaGet should bind to.", CommandOptionType.SingleValue); - app.OnExecuteAsync(async cancellationToken => - { - await host.RunMigrationsAsync(cancellationToken); - await host.RunAsync(cancellationToken); - }); + app.OnExecuteAsync(async cancellationToken => + { + await host.RunMigrationsAsync(cancellationToken); + await host.RunAsync(cancellationToken); + }); - await app.ExecuteAsync(args); - } + await app.ExecuteAsync(args); + } - public static IHostBuilder CreateHostBuilder(string[] args) - { - return Host - .CreateDefaultBuilder(args) - .ConfigureAppConfiguration((ctx, config) => - { - var root = Environment.GetEnvironmentVariable("BAGET_CONFIG_ROOT"); + public static IHostBuilder CreateHostBuilder(string[] args) + { + return Host + .CreateDefaultBuilder(args) + .ConfigureAppConfiguration((ctx, config) => + { + var root = Environment.GetEnvironmentVariable("BAGET_CONFIG_ROOT"); - if (!string.IsNullOrEmpty(root)) - { - config.SetBasePath(root); - } - }) - .ConfigureWebHostDefaults(web => + if (!string.IsNullOrEmpty(root)) { - web.ConfigureKestrel(options => - { - // Remove the upload limit from Kestrel. If needed, an upload limit can - // be enforced by a reverse proxy server, like IIS. - options.Limits.MaxRequestBodySize = null; - }); - - web.UseStartup(); + config.SetBasePath(root); + } + }) + .ConfigureWebHostDefaults(web => + { + web.ConfigureKestrel(options => + { + // Remove the upload limit from Kestrel. If needed, an upload limit can + // be enforced by a reverse proxy server, like IIS. + options.Limits.MaxRequestBodySize = null; }); - } + + web.UseStartup(); + }); } -} +} \ No newline at end of file diff --git a/src/BaGet/Startup.cs b/src/BaGet/Startup.cs index f637267b..c2c9e96a 100644 --- a/src/BaGet/Startup.cs +++ b/src/BaGet/Startup.cs @@ -1,105 +1,99 @@ -using System; +using BaGet.Azure; using BaGet.Core; using BaGet.Web; -using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Cors.Infrastructure; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; -namespace BaGet +namespace BaGet; + +public class Startup { - public class Startup + public Startup(IConfiguration configuration) { - public Startup(IConfiguration configuration) - { - Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); - } + Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + } - private IConfiguration Configuration { get; } + private IConfiguration Configuration { get; } - public void ConfigureServices(IServiceCollection services) - { - // TODO: Ideally we'd use: - // - // services.ConfigureOptions(); - // - // However, "ConfigureOptions" doesn't register validations as expected. - // We'll instead register all these configurations manually. - // See: https://github.com/dotnet/runtime/issues/38491 - services.AddTransient, ConfigureBaGetOptions>(); - services.AddTransient, ConfigureBaGetOptions>(); - services.AddTransient, ConfigureBaGetOptions>(); - services.AddTransient, ConfigureBaGetOptions>(); - services.AddTransient, ConfigureBaGetOptions>(); - - services.AddBaGetOptions(nameof(IISServerOptions)); - services.AddBaGetWebApplication(ConfigureBaGetApplication); - - // You can swap between implementations of subsystems like storage and search using BaGet's configuration. - // Each subsystem's implementation has a provider that reads the configuration to determine if it should be - // activated. BaGet will run through all its providers until it finds one that is active. - services.AddScoped(DependencyInjectionExtensions.GetServiceFromProviders); - services.AddTransient(DependencyInjectionExtensions.GetServiceFromProviders); - services.AddTransient(DependencyInjectionExtensions.GetServiceFromProviders); - services.AddTransient(DependencyInjectionExtensions.GetServiceFromProviders); - services.AddTransient(DependencyInjectionExtensions.GetServiceFromProviders); - - services.AddSingleton, ConfigureRazorRuntimeCompilation>(); - - services.AddCors(); - } + public void ConfigureServices(IServiceCollection services) + { + // TODO: Ideally we'd use: + // + // services.ConfigureOptions(); + // + // However, "ConfigureOptions" doesn't register validations as expected. + // We'll instead register all these configurations manually. + // See: https://github.com/dotnet/runtime/issues/38491 + services.AddTransient, ConfigureBaGetOptions>(); + services.AddTransient, ConfigureBaGetOptions>(); + services.AddTransient, ConfigureBaGetOptions>(); + services.AddTransient, ConfigureBaGetOptions>(); + services.AddTransient, ConfigureBaGetOptions>(); + + services.AddBaGetOptions(nameof(IISServerOptions)); + services.AddBaGetWebApplication(ConfigureBaGetApplication); + + // You can swap between implementations of subsystems like storage and search using BaGet's configuration. + // Each subsystem's implementation has a provider that reads the configuration to determine if it should be + // activated. BaGet will run through all its providers until it finds one that is active. + services.AddScoped(DependencyInjectionExtensions.GetServiceFromProviders); + services.AddTransient(DependencyInjectionExtensions.GetServiceFromProviders); + services.AddTransient(DependencyInjectionExtensions.GetServiceFromProviders); + services.AddTransient(DependencyInjectionExtensions.GetServiceFromProviders); + services.AddTransient(DependencyInjectionExtensions.GetServiceFromProviders); + + services.AddSingleton, ConfigureRazorRuntimeCompilation>(); + + services.AddCors(); + } - private void ConfigureBaGetApplication(BaGetApplication app) - { - // Add database providers. - app.AddAzureTableDatabase(); - app.AddMySqlDatabase(); - app.AddPostgreSqlDatabase(); - app.AddSqliteDatabase(); - app.AddSqlServerDatabase(); - - // Add storage providers. - app.AddFileStorage(); - app.AddAliyunOssStorage(); - app.AddAwsS3Storage(); - app.AddAzureBlobStorage(); - app.AddGoogleCloudStorage(); - - // Add search providers. - app.AddAzureSearch(); - } + private void ConfigureBaGetApplication(BaGetApplication app) + { + // Add database providers. + app.AddAzureTableDatabase(); + app.AddMySqlDatabase(); + app.AddPostgreSqlDatabase(); + app.AddSqliteDatabase(); + app.AddSqlServerDatabase(); + + // Add storage providers. + app.AddFileStorage(); + app.AddAliyunOssStorage(); + app.AddAwsS3Storage(); + app.AddAzureBlobStorage(); + app.AddGoogleCloudStorage(); + + // Add search providers. + app.AddAzureSearch(); + } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - var options = Configuration.Get(); + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + var options = Configuration.Get(); - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - app.UseStatusCodePages(); - } + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseStatusCodePages(); + } - app.UseForwardedHeaders(); - app.UsePathBase(options.PathBase); + app.UseForwardedHeaders(); + app.UsePathBase(options.PathBase); - app.UseStaticFiles(); - app.UseRouting(); + app.UseStaticFiles(); + app.UseRouting(); - app.UseCors(ConfigureBaGetOptions.CorsPolicy); - app.UseOperationCancelledMiddleware(); + app.UseCors(ConfigureBaGetOptions.CorsPolicy); + app.UseOperationCancelledMiddleware(); - app.UseEndpoints(endpoints => - { - var baget = new BaGetEndpointBuilder(); + app.UseEndpoints(endpoints => + { + var baget = new BaGetEndpointBuilder(); - baget.MapEndpoints(endpoints); - }); - } + baget.MapEndpoints(endpoints); + }); } } diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 919be142..ac56489a 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -15,7 +15,8 @@ true - 7.2 + preview + true $(NoWarn);1591 @@ -35,20 +36,13 @@ true - - - 3.1.18 - 3.1.18 - 3.1.18 - 5.10.0 - - - + + diff --git a/tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj b/tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj index 50ca5e84..3ac76755 100644 --- a/tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj +++ b/tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj @@ -1,11 +1,25 @@ - netcoreapp3.1 + net6.0 - + + + all + runtime; build; native; contentfiles; analyzers + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + \ No newline at end of file diff --git a/tests/BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj b/tests/BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj index 83f963a3..e1757427 100644 --- a/tests/BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj +++ b/tests/BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net6.0 @@ -23,4 +23,18 @@ - + + + all + runtime; build; native; contentfiles; analyzers + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + \ No newline at end of file diff --git a/tests/BaGet.Protocol.Tests/RawCatalogClientTests.cs b/tests/BaGet.Protocol.Tests/RawCatalogClientTests.cs index 55ecc029..f6561592 100644 --- a/tests/BaGet.Protocol.Tests/RawCatalogClientTests.cs +++ b/tests/BaGet.Protocol.Tests/RawCatalogClientTests.cs @@ -2,7 +2,6 @@ using System.Linq; using System.Threading.Tasks; using BaGet.Protocol.Internal; -using BaGet.Protocol.Models; using Xunit; namespace BaGet.Protocol.Tests diff --git a/tests/BaGet.Protocol.Tests/RawPackageMetadataClientTests.cs b/tests/BaGet.Protocol.Tests/RawPackageMetadataClientTests.cs index 352ca31e..eae3037c 100644 --- a/tests/BaGet.Protocol.Tests/RawPackageMetadataClientTests.cs +++ b/tests/BaGet.Protocol.Tests/RawPackageMetadataClientTests.cs @@ -1,6 +1,5 @@ using System.Threading.Tasks; using BaGet.Protocol.Internal; -using NuGet.Versioning; using Xunit; namespace BaGet.Protocol.Tests diff --git a/tests/BaGet.Protocol.Tests/Support/ProtocolFixture.cs b/tests/BaGet.Protocol.Tests/Support/ProtocolFixture.cs index cd790587..00c567a9 100644 --- a/tests/BaGet.Protocol.Tests/Support/ProtocolFixture.cs +++ b/tests/BaGet.Protocol.Tests/Support/ProtocolFixture.cs @@ -1,4 +1,3 @@ -using System.Net; using System.Net.Http; using BaGet.Protocol.Internal; diff --git a/tests/BaGet.Tests/ApiIntegrationTests.cs b/tests/BaGet.Tests/ApiIntegrationTests.cs index 547f51b7..01eaac43 100644 --- a/tests/BaGet.Tests/ApiIntegrationTests.cs +++ b/tests/BaGet.Tests/ApiIntegrationTests.cs @@ -6,46 +6,46 @@ using Xunit; using Xunit.Abstractions; -namespace BaGet.Tests +namespace BaGet.Tests; + +public class ApiIntegrationTests : IDisposable { - public class ApiIntegrationTests : IDisposable - { - private readonly BaGetApplication _app; - private readonly HttpClient _client; + private readonly BaGetApplication _app; + private readonly HttpClient _client; - private readonly Stream _packageStream; - private readonly Stream _symbolPackageStream; + private readonly Stream _packageStream; + private readonly Stream _symbolPackageStream; - public ApiIntegrationTests(ITestOutputHelper output) - { - _app = new BaGetApplication(output); - _client = _app.CreateClient(); + public ApiIntegrationTests(ITestOutputHelper output) + { + _app = new BaGetApplication(output); + _client = _app.CreateClient(); - _packageStream = TestResources.GetResourceStream(TestResources.Package); - _symbolPackageStream = TestResources.GetResourceStream(TestResources.SymbolPackage); - } + _packageStream = TestResources.GetResourceStream(TestResources.Package); + _symbolPackageStream = TestResources.GetResourceStream(TestResources.SymbolPackage); + } - [Fact] - public async Task IndexReturnsOk() - { - using var response = await _client.GetAsync("v3/index.json"); - var content = await response.Content.ReadAsStringAsync(); + [Fact] + public async Task IndexReturnsOk() + { + using var response = await _client.GetAsync("v3/index.json"); + var content = await response.Content.ReadAsStringAsync(); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(TestData.ServiceIndex, content); - } + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(TestData.ServiceIndex, content); + } - [Fact] - public async Task SearchReturnsOk() - { - await _app.AddPackageAsync(_packageStream); + [Fact] + public async Task SearchReturnsOk() + { + await _app.AddPackageAsync(_packageStream); - using var response = await _client.GetAsync("v3/search"); - var content = await response.Content.ReadAsStreamAsync(); - var json = content.ToPrettifiedJson(); + using var response = await _client.GetAsync("v3/search"); + var content = await response.Content.ReadAsStreamAsync(); + var json = content.ToPrettifiedJson(); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(@"{ + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(@"{ ""@context"": { ""@vocab"": ""http://schema.nuget.org/schema#"", ""@base"": ""http://localhost/v3/registration"" @@ -77,17 +77,17 @@ public async Task SearchReturnsOk() } ] }", json); - } + } - [Fact] - public async Task SearchReturnsEmpty() - { - using var response = await _client.GetAsync("v3/search?q=PackageDoesNotExist"); - var content = await response.Content.ReadAsStreamAsync(); - var json = content.ToPrettifiedJson(); + [Fact] + public async Task SearchReturnsEmpty() + { + using var response = await _client.GetAsync("v3/search?q=PackageDoesNotExist"); + var content = await response.Content.ReadAsStreamAsync(); + var json = content.ToPrettifiedJson(); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(@"{ + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(@"{ ""@context"": { ""@vocab"": ""http://schema.nuget.org/schema#"", ""@base"": ""http://localhost/v3/registration"" @@ -95,19 +95,19 @@ public async Task SearchReturnsEmpty() ""totalHits"": 0, ""data"": [] }", json); - } + } - [Fact] - public async Task AutocompleteReturnsOk() - { - await _app.AddPackageAsync(_packageStream); + [Fact] + public async Task AutocompleteReturnsOk() + { + await _app.AddPackageAsync(_packageStream); - using var response = await _client.GetAsync("v3/autocomplete"); - var content = await response.Content.ReadAsStreamAsync(); - var json = content.ToPrettifiedJson(); + using var response = await _client.GetAsync("v3/autocomplete"); + var content = await response.Content.ReadAsStreamAsync(); + var json = content.ToPrettifiedJson(); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(@"{ + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(@"{ ""@context"": { ""@vocab"": ""http://schema.nuget.org/schema#"" }, @@ -116,36 +116,36 @@ public async Task AutocompleteReturnsOk() ""TestData"" ] }", json); - } + } - [Fact] - public async Task AutocompleteReturnsEmpty() - { - using var response = await _client.GetAsync("v3/autocomplete?q=PackageDoesNotExist"); - var content = await response.Content.ReadAsStreamAsync(); - var json = content.ToPrettifiedJson(); + [Fact] + public async Task AutocompleteReturnsEmpty() + { + using var response = await _client.GetAsync("v3/autocomplete?q=PackageDoesNotExist"); + var content = await response.Content.ReadAsStreamAsync(); + var json = content.ToPrettifiedJson(); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(@"{ + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(@"{ ""@context"": { ""@vocab"": ""http://schema.nuget.org/schema#"" }, ""totalHits"": 0, ""data"": [] }", json); - } + } - [Fact] - public async Task AutocompleteVersionsReturnsOk() - { - await _app.AddPackageAsync(_packageStream); + [Fact] + public async Task AutocompleteVersionsReturnsOk() + { + await _app.AddPackageAsync(_packageStream); - using var response = await _client.GetAsync("v3/autocomplete?id=TestData"); - var content = await response.Content.ReadAsStreamAsync(); - var json = content.ToPrettifiedJson(); + using var response = await _client.GetAsync("v3/autocomplete?id=TestData"); + var content = await response.Content.ReadAsStreamAsync(); + var json = content.ToPrettifiedJson(); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(@"{ + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(@"{ ""@context"": { ""@vocab"": ""http://schema.nuget.org/schema#"" }, @@ -154,95 +154,95 @@ public async Task AutocompleteVersionsReturnsOk() ""1.2.3"" ] }", json); - } + } - [Fact] - public async Task AutocompleteVersionsReturnsEmpty() - { - using var response = await _client.GetAsync("v3/autocomplete?id=PackageDoesNotExist"); - var content = await response.Content.ReadAsStreamAsync(); - var json = content.ToPrettifiedJson(); + [Fact] + public async Task AutocompleteVersionsReturnsEmpty() + { + using var response = await _client.GetAsync("v3/autocomplete?id=PackageDoesNotExist"); + var content = await response.Content.ReadAsStreamAsync(); + var json = content.ToPrettifiedJson(); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(@"{ + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(@"{ ""@context"": { ""@vocab"": ""http://schema.nuget.org/schema#"" }, ""totalHits"": 0, ""data"": [] }", json); - } + } - [Fact] - public async Task VersionListReturnsOk() - { - await _app.AddPackageAsync(_packageStream); + [Fact] + public async Task VersionListReturnsOk() + { + await _app.AddPackageAsync(_packageStream); - var response = await _client.GetAsync("v3/package/TestData/index.json"); - var content = await response.Content.ReadAsStringAsync(); + var response = await _client.GetAsync("v3/package/TestData/index.json"); + var content = await response.Content.ReadAsStringAsync(); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(@"{""versions"":[""1.2.3""]}", content); - } + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(@"{""versions"":[""1.2.3""]}", content); + } - [Fact] - public async Task VersionListReturnsNotFound() - { - using var response = await _client.GetAsync("v3/package/PackageDoesNotExist/index.json"); + [Fact] + public async Task VersionListReturnsNotFound() + { + using var response = await _client.GetAsync("v3/package/PackageDoesNotExist/index.json"); - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); - } + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } - [Fact] - public async Task PackageDownloadReturnsOk() - { - await _app.AddPackageAsync(_packageStream); + [Fact] + public async Task PackageDownloadReturnsOk() + { + await _app.AddPackageAsync(_packageStream); - using var response = await _client.GetAsync("v3/package/TestData/1.2.3/TestData.1.2.3.nupkg"); + using var response = await _client.GetAsync("v3/package/TestData/1.2.3/TestData.1.2.3.nupkg"); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } - [Fact] - public async Task PackageDownloadReturnsNotFound() - { - using var response = await _client.GetAsync( - "v3/package/PackageDoesNotExist/1.0.0/PackageDoesNotExist.1.0.0.nupkg"); + [Fact] + public async Task PackageDownloadReturnsNotFound() + { + using var response = await _client.GetAsync( + "v3/package/PackageDoesNotExist/1.0.0/PackageDoesNotExist.1.0.0.nupkg"); - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); - } + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } - [Fact] - public async Task NuspecDownloadReturnsOk() - { - await _app.AddPackageAsync(_packageStream); + [Fact] + public async Task NuspecDownloadReturnsOk() + { + await _app.AddPackageAsync(_packageStream); - using var response = await _client.GetAsync( - "v3/package/TestData/1.2.3/TestData.1.2.3.nuspec"); + using var response = await _client.GetAsync( + "v3/package/TestData/1.2.3/TestData.1.2.3.nuspec"); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } - [Fact] - public async Task NuspecDownloadReturnsNotFound() - { - using var response = await _client.GetAsync( - "v3/package/PackageDoesNotExist/1.0.0/PackageDoesNotExist.1.0.0.nuspec"); + [Fact] + public async Task NuspecDownloadReturnsNotFound() + { + using var response = await _client.GetAsync( + "v3/package/PackageDoesNotExist/1.0.0/PackageDoesNotExist.1.0.0.nuspec"); - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); - } + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } - [Fact] - public async Task PackageMetadataReturnsOk() - { - await _app.AddPackageAsync(_packageStream); + [Fact] + public async Task PackageMetadataReturnsOk() + { + await _app.AddPackageAsync(_packageStream); - using var response = await _client.GetAsync("v3/registration/TestData/index.json"); - var content = await response.Content.ReadAsStreamAsync(); - var json = content.ToPrettifiedJson(); + using var response = await _client.GetAsync("v3/registration/TestData/index.json"); + var content = await response.Content.ReadAsStreamAsync(); + var json = content.ToPrettifiedJson(); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(@"{ + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(@"{ ""@id"": ""http://localhost/v3/registration/testdata/index.json"", ""@type"": [ ""catalog:CatalogRoot"", @@ -297,27 +297,27 @@ public async Task PackageMetadataReturnsOk() ], ""totalDownloads"": 0 }", json); - } + } - [Fact] - public async Task PackageMetadataReturnsNotFound() - { - using var response = await _client.GetAsync("v3/registration/PackageDoesNotExist/index.json"); + [Fact] + public async Task PackageMetadataReturnsNotFound() + { + using var response = await _client.GetAsync("v3/registration/PackageDoesNotExist/index.json"); - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); - } + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } - [Fact] - public async Task PackageMetadataLeafReturnsOk() - { - await _app.AddPackageAsync(_packageStream); + [Fact] + public async Task PackageMetadataLeafReturnsOk() + { + await _app.AddPackageAsync(_packageStream); - using var response = await _client.GetAsync("v3/registration/TestData/1.2.3.json"); - var content = await response.Content.ReadAsStreamAsync(); - var json = content.ToPrettifiedJson(); + using var response = await _client.GetAsync("v3/registration/TestData/1.2.3.json"); + var content = await response.Content.ReadAsStreamAsync(); + var json = content.ToPrettifiedJson(); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(@"{ + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(@"{ ""@id"": ""http://localhost/v3/registration/testdata/1.2.3.json"", ""@type"": [ ""Package"", @@ -328,78 +328,77 @@ public async Task PackageMetadataLeafReturnsOk() ""published"": ""2020-01-01T00:00:00Z"", ""registration"": ""http://localhost/v3/registration/testdata/index.json"" }", json); - } + } - [Fact] - public async Task PackageMetadataLeafReturnsNotFound() - { - using var response = await _client.GetAsync("v3/registration/PackageDoesNotExist/1.0.0.json"); + [Fact] + public async Task PackageMetadataLeafReturnsNotFound() + { + using var response = await _client.GetAsync("v3/registration/PackageDoesNotExist/1.0.0.json"); - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); - } + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } - [Fact] - public async Task PackageDependentsReturnsOk() - { - using var response = await _client.GetAsync("v3/dependents?packageId=TestData"); + [Fact] + public async Task PackageDependentsReturnsOk() + { + using var response = await _client.GetAsync("v3/dependents?packageId=TestData"); - var content = await response.Content.ReadAsStreamAsync(); - var json = content.ToPrettifiedJson(); + var content = await response.Content.ReadAsStreamAsync(); + var json = content.ToPrettifiedJson(); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(@"{ + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(@"{ ""totalHits"": 0, ""data"": [] }", json); - } + } - [Fact] - public async Task PackageDependentsReturnsBadRequest() - { - using var response = await _client.GetAsync("v3/dependents"); + [Fact] + public async Task PackageDependentsReturnsBadRequest() + { + using var response = await _client.GetAsync("v3/dependents"); - Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - } + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + } - [Fact] - public async Task SymbolDownloadReturnsOk() - { - await _app.AddPackageAsync(_packageStream); - await _app.AddSymbolPackageAsync(_symbolPackageStream); + [Fact] + public async Task SymbolDownloadReturnsOk() + { + await _app.AddPackageAsync(_packageStream); + await _app.AddSymbolPackageAsync(_symbolPackageStream); - using var response = await _client.GetAsync( - "api/download/symbols/testdata.pdb/16F71ED8DD574AA2AD4A22D29E9C981Bffffffff/testdata.pdb"); + using var response = await _client.GetAsync( + "api/download/symbols/testdata.pdb/16F71ED8DD574AA2AD4A22D29E9C981Bffffffff/testdata.pdb"); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } - [Theory] - [InlineData("api/download/symbols/testdata.pdb/16F71ED8DD574AA2AD4A22D29E9C981B1/testdata.pdb")] - [InlineData("api/download/symbols/testdata.pdb/16F71ED8DD574AA2AD4A22D29E9C981B/testdata.pdb")] - [InlineData("api/download/symbols/testprefix/testdata.pdb/16F71ED8DD574AA2AD4A22D29E9C981Bffffffff/testdata.pdb")] - public async Task MalformedSymbolDownloadReturnsOk(string uri) - { - await _app.AddPackageAsync(_packageStream); - await _app.AddSymbolPackageAsync(_symbolPackageStream); + [Theory] + [InlineData("api/download/symbols/testdata.pdb/16F71ED8DD574AA2AD4A22D29E9C981B1/testdata.pdb")] + [InlineData("api/download/symbols/testdata.pdb/16F71ED8DD574AA2AD4A22D29E9C981B/testdata.pdb")] + [InlineData("api/download/symbols/testprefix/testdata.pdb/16F71ED8DD574AA2AD4A22D29E9C981Bffffffff/testdata.pdb")] + public async Task MalformedSymbolDownloadReturnsOk(string uri) + { + await _app.AddPackageAsync(_packageStream); + await _app.AddSymbolPackageAsync(_symbolPackageStream); - using var response = await _client.GetAsync(uri); + using var response = await _client.GetAsync(uri); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } - [Fact] - public async Task SymbolDownloadReturnsNotFound() - { - using var response = await _client.GetAsync( - "api/download/symbols/doesnotexist.pdb/16F71ED8DD574AA2AD4A22D29E9C981Bffffffff/doesnotexist.pdb"); + [Fact] + public async Task SymbolDownloadReturnsNotFound() + { + using var response = await _client.GetAsync( + "api/download/symbols/doesnotexist.pdb/16F71ED8DD574AA2AD4A22D29E9C981Bffffffff/doesnotexist.pdb"); - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); - } + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } - public void Dispose() - { - _app.Dispose(); - _client.Dispose(); - } + public void Dispose() + { + _app.Dispose(); + _client.Dispose(); } -} +} \ No newline at end of file diff --git a/tests/BaGet.Tests/BaGet.Tests.csproj b/tests/BaGet.Tests/BaGet.Tests.csproj index 4f70442a..522f388a 100644 --- a/tests/BaGet.Tests/BaGet.Tests.csproj +++ b/tests/BaGet.Tests/BaGet.Tests.csproj @@ -1,8 +1,8 @@ - + - netcoreapp3.1 - 8.0 + net6.0 + preview @@ -10,9 +10,9 @@ - - - + + + @@ -41,4 +41,18 @@ - + + + all + runtime; build; native; contentfiles; analyzers + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + \ No newline at end of file diff --git a/tests/BaGet.Tests/BaGetClientIntegrationTests.cs b/tests/BaGet.Tests/BaGetClientIntegrationTests.cs index c73aa376..ec6661fa 100644 --- a/tests/BaGet.Tests/BaGetClientIntegrationTests.cs +++ b/tests/BaGet.Tests/BaGetClientIntegrationTests.cs @@ -8,225 +8,224 @@ using Xunit; using Xunit.Abstractions; -namespace BaGet.Tests +namespace BaGet.Tests; + +/// +/// Uses BaGet's client SDK to interact with the BaGet test host. +/// +public class BaGetClientIntegrationTests : IDisposable { - /// - /// Uses BaGet's client SDK to interact with the BaGet test host. - /// - public class BaGetClientIntegrationTests : IDisposable - { - private readonly BaGetApplication _app; - private readonly HttpClient _httpClient; - private readonly NuGetClientFactory _clientFactory; - private readonly NuGetClient _client; + private readonly BaGetApplication _app; + private readonly HttpClient _httpClient; + private readonly NuGetClientFactory _clientFactory; + private readonly NuGetClient _client; - private readonly Stream _packageStream; + private readonly Stream _packageStream; - public BaGetClientIntegrationTests(ITestOutputHelper output) - { - _app = new BaGetApplication(output); + public BaGetClientIntegrationTests(ITestOutputHelper output) + { + _app = new BaGetApplication(output); - var serviceIndexUrl = new Uri(_app.Server.BaseAddress, "v3/index.json"); + var serviceIndexUrl = new Uri(_app.Server.BaseAddress, "v3/index.json"); - _httpClient = _app.CreateClient(); - _clientFactory = new NuGetClientFactory(_httpClient, serviceIndexUrl.AbsoluteUri); - _client = new NuGetClient(_clientFactory); + _httpClient = _app.CreateClient(); + _clientFactory = new NuGetClientFactory(_httpClient, serviceIndexUrl.AbsoluteUri); + _client = new NuGetClient(_clientFactory); - _packageStream = TestResources.GetResourceStream(TestResources.Package); - } + _packageStream = TestResources.GetResourceStream(TestResources.Package); + } - [Fact] - public async Task ValidIndex() - { - var client = _clientFactory.CreateServiceIndexClient(); - var index = await client.GetAsync(); - - Assert.Equal("3.0.0", index.Version); - Assert.Equal(12, index.Resources.Count); - - Assert.NotEmpty(index.GetResourceUrl(new[] { "PackageBaseAddress/3.0.0" })); - Assert.NotEmpty(index.GetResourceUrl(new[] { "PackagePublish/2.0.0" })); - Assert.NotEmpty(index.GetResourceUrl(new[] { "RegistrationsBaseUrl" })); - Assert.NotEmpty(index.GetResourceUrl(new[] { "SearchAutocompleteService" })); - Assert.NotEmpty(index.GetResourceUrl(new[] { "SearchQueryService" })); - Assert.NotEmpty(index.GetResourceUrl(new[] { "SymbolPackagePublish/4.9.0" })); - } + [Fact] + public async Task ValidIndex() + { + var client = _clientFactory.CreateServiceIndexClient(); + var index = await client.GetAsync(); + + Assert.Equal("3.0.0", index.Version); + Assert.Equal(12, index.Resources.Count); + + Assert.NotEmpty(index.GetResourceUrl(new[] { "PackageBaseAddress/3.0.0" })); + Assert.NotEmpty(index.GetResourceUrl(new[] { "PackagePublish/2.0.0" })); + Assert.NotEmpty(index.GetResourceUrl(new[] { "RegistrationsBaseUrl" })); + Assert.NotEmpty(index.GetResourceUrl(new[] { "SearchAutocompleteService" })); + Assert.NotEmpty(index.GetResourceUrl(new[] { "SearchQueryService" })); + Assert.NotEmpty(index.GetResourceUrl(new[] { "SymbolPackagePublish/4.9.0" })); + } - [Fact] - public async Task SearchReturnsResults() - { - await _app.AddPackageAsync(_packageStream); + [Fact] + public async Task SearchReturnsResults() + { + await _app.AddPackageAsync(_packageStream); - var results = await _client.SearchAsync(); + var results = await _client.SearchAsync(); - var result = Assert.Single(results); - var author = Assert.Single(result.Authors); - var version = Assert.Single(result.Versions); + var result = Assert.Single(results); + var author = Assert.Single(result.Authors); + var version = Assert.Single(result.Versions); - Assert.Equal("TestData", result.PackageId); - Assert.Equal("1.2.3", result.Version); - Assert.Equal("Test description", result.Description); - Assert.Equal("Test author", author); - Assert.Equal(0, result.TotalDownloads); + Assert.Equal("TestData", result.PackageId); + Assert.Equal("1.2.3", result.Version); + Assert.Equal("Test description", result.Description); + Assert.Equal("Test author", author); + Assert.Equal(0, result.TotalDownloads); - Assert.Equal("1.2.3", version.Version); - Assert.Equal(0, version.Downloads); - } + Assert.Equal("1.2.3", version.Version); + Assert.Equal(0, version.Downloads); + } - [Fact] - public async Task SearchReturnsEmpty() - { - var results = await _client.SearchAsync("PackageDoesNotExist"); + [Fact] + public async Task SearchReturnsEmpty() + { + var results = await _client.SearchAsync("PackageDoesNotExist"); - Assert.Empty(results); - } + Assert.Empty(results); + } - [Fact] - public async Task AutocompleteReturnsResults() - { - await _app.AddPackageAsync(_packageStream); + [Fact] + public async Task AutocompleteReturnsResults() + { + await _app.AddPackageAsync(_packageStream); - var results = await _client.AutocompleteAsync(); + var results = await _client.AutocompleteAsync(); - var result = Assert.Single(results); + var result = Assert.Single(results); - Assert.Equal("TestData", result); - } + Assert.Equal("TestData", result); + } - [Fact] - public async Task AutocompleteReturnsEmpty() - { - var results = await _client.AutocompleteAsync("PackageDoesNotExist"); + [Fact] + public async Task AutocompleteReturnsEmpty() + { + var results = await _client.AutocompleteAsync("PackageDoesNotExist"); - Assert.Empty(results); - } + Assert.Empty(results); + } - [Fact] - public async Task AutocompleteVersions() - { - await _app.AddPackageAsync(_packageStream); + [Fact] + public async Task AutocompleteVersions() + { + await _app.AddPackageAsync(_packageStream); - var client = _clientFactory.CreateAutocompleteClient(); - var results = await client.ListPackageVersionsAsync("TestData"); + var client = _clientFactory.CreateAutocompleteClient(); + var results = await client.ListPackageVersionsAsync("TestData"); - var result = Assert.Single(results.Data); + var result = Assert.Single(results.Data); - Assert.Equal(1, results.TotalHits); - Assert.Equal("1.2.3", result); - } + Assert.Equal(1, results.TotalHits); + Assert.Equal("1.2.3", result); + } - [Fact] - public async Task AutocompleteVersionsReturnsEmpty() - { - var client = _clientFactory.CreateAutocompleteClient(); - var results = await client.ListPackageVersionsAsync("PackageDoesNotExist"); + [Fact] + public async Task AutocompleteVersionsReturnsEmpty() + { + var client = _clientFactory.CreateAutocompleteClient(); + var results = await client.ListPackageVersionsAsync("PackageDoesNotExist"); - Assert.Empty(results.Data); - Assert.Equal(0, results.TotalHits); - } + Assert.Empty(results.Data); + Assert.Equal(0, results.TotalHits); + } - [Fact] - public async Task VersionListReturnsResults() - { - await _app.AddPackageAsync(_packageStream); + [Fact] + public async Task VersionListReturnsResults() + { + await _app.AddPackageAsync(_packageStream); - var versions = await _client.ListPackageVersionsAsync("TestData"); + var versions = await _client.ListPackageVersionsAsync("TestData"); - var version = Assert.Single(versions); + var version = Assert.Single(versions); - Assert.Equal("1.2.3", version.ToNormalizedString()); - } + Assert.Equal("1.2.3", version.ToNormalizedString()); + } - [Fact] - public async Task VersionListReturnsEmpty() - { - var versions = await _client.ListPackageVersionsAsync("PackageDoesNotExist"); + [Fact] + public async Task VersionListReturnsEmpty() + { + var versions = await _client.ListPackageVersionsAsync("PackageDoesNotExist"); - Assert.Empty(versions); - } + Assert.Empty(versions); + } - [Theory] - [InlineData("TestData", "1.0.0", false)] - [InlineData("TestData", "1.2.3", true)] - [InlineData("PackageDoesNotExists", "1.0.0", false)] - public async Task PackageDownloadWorks(string packageId, string packageVersion, bool exists) - { - await _app.AddPackageAsync(_packageStream); + [Theory] + [InlineData("TestData", "1.0.0", false)] + [InlineData("TestData", "1.2.3", true)] + [InlineData("PackageDoesNotExists", "1.0.0", false)] + public async Task PackageDownloadWorks(string packageId, string packageVersion, bool exists) + { + await _app.AddPackageAsync(_packageStream); - try - { - var version = NuGetVersion.Parse(packageVersion); + try + { + var version = NuGetVersion.Parse(packageVersion); - using var memoryStream = new MemoryStream(); - using var packageStream = await _client.DownloadPackageAsync(packageId, version); + using var memoryStream = new MemoryStream(); + using var packageStream = await _client.DownloadPackageAsync(packageId, version); - await packageStream.CopyToAsync(memoryStream); - memoryStream.Position = 0; + await packageStream.CopyToAsync(memoryStream); + memoryStream.Position = 0; - Assert.True(exists); - Assert.Equal(exists, memoryStream.Length > 0); - } - catch (PackageNotFoundException) - { - Assert.False(exists); - } + Assert.True(exists); + Assert.Equal(exists, memoryStream.Length > 0); } - - [Theory] - [InlineData("TestData", "1.0.0", false)] - [InlineData("TestData", "1.2.3", true)] - [InlineData("PackageDoesNotExists", "1.0.0", false)] - public async Task ManifestDownloadWorks(string packageId, string packageVersion, bool exists) + catch (PackageNotFoundException) { - await _app.AddPackageAsync(_packageStream); + Assert.False(exists); + } + } + + [Theory] + [InlineData("TestData", "1.0.0", false)] + [InlineData("TestData", "1.2.3", true)] + [InlineData("PackageDoesNotExists", "1.0.0", false)] + public async Task ManifestDownloadWorks(string packageId, string packageVersion, bool exists) + { + await _app.AddPackageAsync(_packageStream); - try - { - var version = NuGetVersion.Parse(packageVersion); + try + { + var version = NuGetVersion.Parse(packageVersion); - using var memoryStream = new MemoryStream(); - using var packageStream = await _client.DownloadPackageManifestAsync(packageId, version); + using var memoryStream = new MemoryStream(); + using var packageStream = await _client.DownloadPackageManifestAsync(packageId, version); - await packageStream.CopyToAsync(memoryStream); - memoryStream.Position = 0; + await packageStream.CopyToAsync(memoryStream); + memoryStream.Position = 0; - Assert.True(exists); - Assert.Equal(exists, memoryStream.Length > 0); - } - catch (PackageNotFoundException) - { - Assert.False(exists); - } + Assert.True(exists); + Assert.Equal(exists, memoryStream.Length > 0); } - - [Fact] - public async Task PackageMetadataReturnsOk() + catch (PackageNotFoundException) { - await _app.AddPackageAsync(_packageStream); + Assert.False(exists); + } + } - var packages = await _client.GetPackageMetadataAsync("TestData"); + [Fact] + public async Task PackageMetadataReturnsOk() + { + await _app.AddPackageAsync(_packageStream); - var package = Assert.Single(packages); + var packages = await _client.GetPackageMetadataAsync("TestData"); - Assert.Equal("TestData", package.PackageId); - Assert.Equal("1.2.3", package.Version); - Assert.Equal("Test description", package.Description); - Assert.Equal("Test author", package.Authors); - Assert.True(package.Listed); - } + var package = Assert.Single(packages); - [Fact] - public async Task PackageMetadataReturnsEmty() - { - var packages = await _client.GetPackageMetadataAsync("PackageDoesNotExist"); + Assert.Equal("TestData", package.PackageId); + Assert.Equal("1.2.3", package.Version); + Assert.Equal("Test description", package.Description); + Assert.Equal("Test author", package.Authors); + Assert.True(package.Listed); + } - Assert.Empty(packages); - } + [Fact] + public async Task PackageMetadataReturnsEmty() + { + var packages = await _client.GetPackageMetadataAsync("PackageDoesNotExist"); - public void Dispose() - { - _app.Dispose(); - _httpClient.Dispose(); - } + Assert.Empty(packages); + } + + public void Dispose() + { + _app.Dispose(); + _httpClient.Dispose(); } -} +} \ No newline at end of file diff --git a/tests/BaGet.Tests/HostIntegrationTests.cs b/tests/BaGet.Tests/HostIntegrationTests.cs index 618d78ad..615d0267 100644 --- a/tests/BaGet.Tests/HostIntegrationTests.cs +++ b/tests/BaGet.Tests/HostIntegrationTests.cs @@ -6,70 +6,69 @@ using Microsoft.Extensions.DependencyInjection; using Xunit; -namespace BaGet.Tests +namespace BaGet.Tests; + +public class HostIntegrationTests { - public class HostIntegrationTests - { - private readonly string DatabaseTypeKey = "Database:Type"; - private readonly string ConnectionStringKey = "Database:ConnectionString"; + private readonly string DatabaseTypeKey = "Database:Type"; + private readonly string ConnectionStringKey = "Database:ConnectionString"; - [Fact] - public void ThrowsIfDatabaseTypeInvalid() + [Fact] + public void ThrowsIfDatabaseTypeInvalid() + { + var provider = BuildServiceProvider(new Dictionary { - var provider = BuildServiceProvider(new Dictionary - { - { DatabaseTypeKey, "InvalidType" } - }); + { DatabaseTypeKey, "InvalidType" } + }); - Assert.Throws( - () => provider.GetRequiredService()); - } + Assert.Throws( + () => provider.GetRequiredService()); + } - [Fact] - public void ReturnsDatabaseContext() + [Fact] + public void ReturnsDatabaseContext() + { + var provider = BuildServiceProvider(new Dictionary { - var provider = BuildServiceProvider(new Dictionary - { - { DatabaseTypeKey, "Sqlite" }, - { ConnectionStringKey, "..." } - }); + { DatabaseTypeKey, "Sqlite" }, + { ConnectionStringKey, "..." } + }); - Assert.NotNull(provider.GetRequiredService()); - } + Assert.NotNull(provider.GetRequiredService()); + } - [Fact] - public void ReturnsSqliteContext() + [Fact] + public void ReturnsSqliteContext() + { + var provider = BuildServiceProvider(new Dictionary { - var provider = BuildServiceProvider(new Dictionary - { - { DatabaseTypeKey, "Sqlite" }, - { ConnectionStringKey, "..." } - }); + { DatabaseTypeKey, "Sqlite" }, + { ConnectionStringKey, "..." } + }); - Assert.NotNull(provider.GetRequiredService()); - } + Assert.NotNull(provider.GetRequiredService()); + } - [Fact] - public void DefaultsToSqlite() - { - var provider = BuildServiceProvider(); + [Fact] + public void DefaultsToSqlite() + { + var provider = BuildServiceProvider(); - var context = provider.GetRequiredService(); + var context = provider.GetRequiredService(); - Assert.IsType(context); - } + Assert.IsType(context); + } - private IServiceProvider BuildServiceProvider(Dictionary configs = null) - { - var host = Program - .CreateHostBuilder(new string[0]) - .ConfigureAppConfiguration((ctx, config) => - { - config.AddInMemoryCollection(configs ?? new Dictionary()); - }) - .Build(); + private IServiceProvider BuildServiceProvider(Dictionary configs = null) + { + var host = Program + .CreateHostBuilder(new string[0]) + .ConfigureAppConfiguration((ctx, config) => + { + config.AddInMemoryCollection(configs ?? new Dictionary()); + }) + .Build(); - return host.Services; - } + return host.Services; } -} +} \ No newline at end of file diff --git a/tests/BaGet.Tests/MirrorIntegrationTests.cs b/tests/BaGet.Tests/MirrorIntegrationTests.cs index 8f614177..7e0288ff 100644 --- a/tests/BaGet.Tests/MirrorIntegrationTests.cs +++ b/tests/BaGet.Tests/MirrorIntegrationTests.cs @@ -6,36 +6,36 @@ using Xunit; using Xunit.Abstractions; -namespace BaGet.Tests +namespace BaGet.Tests; + +public class MirrorIntegrationTests : IDisposable { - public class MirrorIntegrationTests : IDisposable - { - private readonly BaGetApplication _upstream; - private readonly BaGetApplication _downstream; - private readonly HttpClient _downstreamClient; - private readonly Stream _packageStream; + private readonly BaGetApplication _upstream; + private readonly BaGetApplication _downstream; + private readonly HttpClient _downstreamClient; + private readonly Stream _packageStream; - public MirrorIntegrationTests(ITestOutputHelper output) - { - _upstream = new BaGetApplication(output); - _downstream = new BaGetApplication(output, _upstream.CreateClient()); + public MirrorIntegrationTests(ITestOutputHelper output) + { + _upstream = new BaGetApplication(output); + _downstream = new BaGetApplication(output, _upstream.CreateClient()); - _downstreamClient = _downstream.CreateClient(); - _packageStream = TestResources.GetResourceStream(TestResources.Package); - } + _downstreamClient = _downstream.CreateClient(); + _packageStream = TestResources.GetResourceStream(TestResources.Package); + } - [Fact] - public async Task SearchExcludesUpstream() - { - await _upstream.AddPackageAsync(_packageStream); + [Fact] + public async Task SearchExcludesUpstream() + { + await _upstream.AddPackageAsync(_packageStream); - using var downstreamResponse = await _downstreamClient.GetAsync("v3/search"); - var downstreamContent = await downstreamResponse.Content.ReadAsStreamAsync(); - var downstreamJson = downstreamContent.ToPrettifiedJson(); + using var downstreamResponse = await _downstreamClient.GetAsync("v3/search"); + var downstreamContent = await downstreamResponse.Content.ReadAsStreamAsync(); + var downstreamJson = downstreamContent.ToPrettifiedJson(); - // The downstream package source should not have the package. - Assert.Equal(HttpStatusCode.OK, downstreamResponse.StatusCode); - Assert.Equal(@"{ + // The downstream package source should not have the package. + Assert.Equal(HttpStatusCode.OK, downstreamResponse.StatusCode); + Assert.Equal(@"{ ""@context"": { ""@vocab"": ""http://schema.nuget.org/schema#"", ""@base"": ""http://localhost/v3/registration"" @@ -43,52 +43,52 @@ public async Task SearchExcludesUpstream() ""totalHits"": 0, ""data"": [] }", downstreamJson); - } + } - [Fact] - public async Task VersionListIncludesUpstream() - { - await _upstream.AddPackageAsync(_packageStream); + [Fact] + public async Task VersionListIncludesUpstream() + { + await _upstream.AddPackageAsync(_packageStream); - var response = await _downstreamClient.GetAsync("v3/package/TestData/index.json"); - var content = await response.Content.ReadAsStringAsync(); + var response = await _downstreamClient.GetAsync("v3/package/TestData/index.json"); + var content = await response.Content.ReadAsStringAsync(); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(@"{""versions"":[""1.2.3""]}", content); - } + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(@"{""versions"":[""1.2.3""]}", content); + } - [Fact] - public async Task PackageDownloadIncludesUpstream() - { - await _upstream.AddPackageAsync(_packageStream); + [Fact] + public async Task PackageDownloadIncludesUpstream() + { + await _upstream.AddPackageAsync(_packageStream); - using var response = await _downstreamClient.GetAsync("v3/package/TestData/1.2.3/TestData.1.2.3.nupkg"); + using var response = await _downstreamClient.GetAsync("v3/package/TestData/1.2.3/TestData.1.2.3.nupkg"); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } - [Fact] - public async Task NuspecDownloadIncludesUpstream() - { - await _upstream.AddPackageAsync(_packageStream); + [Fact] + public async Task NuspecDownloadIncludesUpstream() + { + await _upstream.AddPackageAsync(_packageStream); - using var response = await _downstreamClient.GetAsync( - "v3/package/TestData/1.2.3/TestData.1.2.3.nuspec"); + using var response = await _downstreamClient.GetAsync( + "v3/package/TestData/1.2.3/TestData.1.2.3.nuspec"); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } - [Fact] - public async Task PackageMetadataIncludesUpstream() - { - await _upstream.AddPackageAsync(_packageStream); + [Fact] + public async Task PackageMetadataIncludesUpstream() + { + await _upstream.AddPackageAsync(_packageStream); - using var response = await _downstreamClient.GetAsync("v3/registration/TestData/index.json"); - var content = await response.Content.ReadAsStreamAsync(); - var json = content.ToPrettifiedJson(); + using var response = await _downstreamClient.GetAsync("v3/registration/TestData/index.json"); + var content = await response.Content.ReadAsStreamAsync(); + var json = content.ToPrettifiedJson(); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(@"{ + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(@"{ ""@id"": ""http://localhost/v3/registration/testdata/index.json"", ""@type"": [ ""catalog:CatalogRoot"", @@ -140,19 +140,19 @@ public async Task PackageMetadataIncludesUpstream() ], ""totalDownloads"": 0 }", json); - } + } - [Fact] - public async Task PackageMetadataLeafIncludesUpstream() - { - await _upstream.AddPackageAsync(_packageStream); + [Fact] + public async Task PackageMetadataLeafIncludesUpstream() + { + await _upstream.AddPackageAsync(_packageStream); - using var response = await _downstreamClient.GetAsync("v3/registration/TestData/1.2.3.json"); - var content = await response.Content.ReadAsStreamAsync(); - var json = content.ToPrettifiedJson(); + using var response = await _downstreamClient.GetAsync("v3/registration/TestData/1.2.3.json"); + var content = await response.Content.ReadAsStreamAsync(); + var json = content.ToPrettifiedJson(); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(@"{ + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(@"{ ""@id"": ""http://localhost/v3/registration/testdata/1.2.3.json"", ""@type"": [ ""Package"", @@ -163,12 +163,11 @@ public async Task PackageMetadataLeafIncludesUpstream() ""published"": ""2020-01-01T00:00:00Z"", ""registration"": ""http://localhost/v3/registration/testdata/index.json"" }", json); - } + } - public void Dispose() - { - _upstream.Dispose(); - _downstream.Dispose(); - } + public void Dispose() + { + _upstream.Dispose(); + _downstream.Dispose(); } -} +} \ No newline at end of file diff --git a/tests/BaGet.Tests/NuGetClientIntegrationTests.cs b/tests/BaGet.Tests/NuGetClientIntegrationTests.cs index e394ac95..bb49c24b 100644 --- a/tests/BaGet.Tests/NuGetClientIntegrationTests.cs +++ b/tests/BaGet.Tests/NuGetClientIntegrationTests.cs @@ -11,254 +11,253 @@ using Xunit; using Xunit.Abstractions; -namespace BaGet.Tests +namespace BaGet.Tests; + +/// +/// Uses the official NuGet client to interact with the BaGet test host. +/// +public class NuGetClientIntegrationTests : IDisposable { - /// - /// Uses the official NuGet client to interact with the BaGet test host. - /// - public class NuGetClientIntegrationTests : IDisposable + private readonly BaGetApplication _app; + private readonly HttpClient _client; + + private readonly Stream _packageStream; + + private readonly SourceRepository _repository; + private readonly SourceCacheContext _cache; + private readonly NuGet.Common.ILogger _logger; + private readonly CancellationToken _cancellationToken; + + public NuGetClientIntegrationTests(ITestOutputHelper output) + { + _app = new BaGetApplication(output); + _client = _app.CreateDefaultClient(); + _packageStream = TestResources.GetResourceStream(TestResources.Package); + + var sourceUri = new Uri(_app.Server.BaseAddress, "v3/index.json"); + var packageSource = new PackageSource(sourceUri.AbsoluteUri); + var providers = new List>(); + + providers.Add(new Lazy(() => new HttpSourceResourceProviderTestHost(_client))); + providers.AddRange(Repository.Provider.GetCoreV3()); + + _repository = new SourceRepository(packageSource, providers); + _cache = new SourceCacheContext { NoCache = true, MaxAge = new DateTimeOffset(), DirectDownload = true }; + _logger = NuGet.Common.NullLogger.Instance; + _cancellationToken = CancellationToken.None; + } + + [Fact] + public async Task ValidIndex() + { + var index = await _repository.GetResourceAsync(); + + Assert.Equal(12, index.Entries.Count); + + Assert.NotEmpty(index.GetServiceEntries("PackageBaseAddress/3.0.0")); + Assert.NotEmpty(index.GetServiceEntries("PackagePublish/2.0.0")); + Assert.NotEmpty(index.GetServiceEntries("RegistrationsBaseUrl")); + Assert.NotEmpty(index.GetServiceEntries("SearchAutocompleteService")); + Assert.NotEmpty(index.GetServiceEntries("SearchQueryService")); + Assert.NotEmpty(index.GetServiceEntries("SymbolPackagePublish/4.9.0")); + } + + [Fact] + public async Task SearchReturnsResults() + { + await _app.AddPackageAsync(_packageStream); + + var resource = await _repository.GetResourceAsync(); + var searchFilter = new SearchFilter(includePrerelease: true); + + var results = await resource.SearchAsync( + "", + searchFilter, + skip: 0, + take: 20, + _logger, + _cancellationToken); + + var result = Assert.Single(results); + + Assert.Equal("TestData", result.Identity.Id); + Assert.Equal("1.2.3", result.Identity.Version.ToNormalizedString()); + Assert.Equal("Test description", result.Description); + Assert.Equal("Test author", result.Authors); + Assert.Equal(0, result.DownloadCount); + + var versions = await result.GetVersionsAsync(); + var version = Assert.Single(versions); + + Assert.Equal("1.2.3", version.Version.ToNormalizedString()); + Assert.Equal(0, version.DownloadCount); + } + + [Fact] + public async Task SearchReturnsEmpty() + { + var resource = await _repository.GetResourceAsync(); + var searchFilter = new SearchFilter(includePrerelease: true); + + var results = await resource.SearchAsync( + "PackageDoesNotExist", + searchFilter, + skip: 0, + take: 20, + _logger, + _cancellationToken); + + Assert.Empty(results); + } + + [Fact] + public async Task AutocompleteReturnsResults() + { + await _app.AddPackageAsync(_packageStream); + + var resource = await _repository.GetResourceAsync(); + var results = await resource.IdStartsWith( + "", + includePrerelease: true, + _logger, + _cancellationToken); + + var result = Assert.Single(results); + + Assert.Equal("TestData", result); + } + + [Fact] + public async Task AutocompleteReturnsEmpty() + { + var resource = await _repository.GetResourceAsync(); + var results = await resource.IdStartsWith( + "PackageDoesNotExist", + includePrerelease: true, + _logger, + _cancellationToken); + + Assert.Empty(results); + } + + [Fact] + public async Task VersionListReturnsResults() + { + await _app.AddPackageAsync(_packageStream); + + var resource = await _repository.GetResourceAsync(); + var versions = await resource.GetAllVersionsAsync( + "TestData", + _cache, + _logger, + _cancellationToken); + + var version = Assert.Single(versions); + + Assert.Equal("1.2.3", version.ToNormalizedString()); + } + + [Fact] + public async Task VersionListReturnsEmpty() + { + var resource = await _repository.GetResourceAsync(); + var versions = await resource.GetAllVersionsAsync( + "PackageDoesNotExist", + _cache, + _logger, + _cancellationToken); + + Assert.Empty(versions); + } + + [Theory] + [InlineData("TestData", "1.0.0", false)] + [InlineData("TestData", "1.2.3", true)] + [InlineData("PackageDoesNotExists", "1.0.0", false)] + public async Task PackageExistsWorks(string packageId, string packageVersion, bool exists) + { + await _app.AddPackageAsync(_packageStream); + + var version = NuGetVersion.Parse(packageVersion); + var resource = await _repository.GetResourceAsync(); + var result = await resource.DoesPackageExistAsync( + packageId, + version, + _cache, + _logger, + _cancellationToken); + + Assert.Equal(exists, result); + } + + [Theory] + [InlineData("TestData", "1.0.0", false)] + [InlineData("TestData", "1.2.3", true)] + [InlineData("PackageDoesNotExists", "1.0.0", false)] + public async Task PackageDownloadWorks(string packageId, string packageVersion, bool exists) + { + await _app.AddPackageAsync(_packageStream); + + using var packageStream = new MemoryStream(); + + var version = NuGetVersion.Parse(packageVersion); + var resource = await _repository.GetResourceAsync(); + var result = await resource.CopyNupkgToStreamAsync( + packageId, + version, + packageStream, + _cache, + _logger, + _cancellationToken); + + packageStream.Position = 0; + + Assert.Equal(exists, result); + Assert.Equal(exists, packageStream.Length > 0); + } + + [Fact] + public async Task PackageMetadataReturnsOk() + { + await _app.AddPackageAsync(_packageStream); + + var resource = await _repository.GetResourceAsync(); + var packages = await resource.GetMetadataAsync( + "TestData", + includePrerelease: true, + includeUnlisted: true, + _cache, + _logger, + _cancellationToken); + + var package = Assert.Single(packages); + + Assert.Equal("TestData", package.Identity.Id); + Assert.Equal("1.2.3", package.Identity.Version.ToNormalizedString()); + Assert.Equal("Test description", package.Description); + Assert.Equal("Test author", package.Authors); + Assert.True(package.IsListed); + } + + [Fact] + public async Task PackageMetadataReturnsEmty() + { + var resource = await _repository.GetResourceAsync(); + var packages = await resource.GetMetadataAsync( + "PackageDoesNotExist", + includePrerelease: true, + includeUnlisted: true, + _cache, + _logger, + _cancellationToken); + + Assert.Empty(packages); + } + + public void Dispose() { - private readonly BaGetApplication _app; - private readonly HttpClient _client; - - private readonly Stream _packageStream; - - private readonly SourceRepository _repository; - private readonly SourceCacheContext _cache; - private readonly NuGet.Common.ILogger _logger; - private readonly CancellationToken _cancellationToken; - - public NuGetClientIntegrationTests(ITestOutputHelper output) - { - _app = new BaGetApplication(output); - _client = _app.CreateDefaultClient(); - _packageStream = TestResources.GetResourceStream(TestResources.Package); - - var sourceUri = new Uri(_app.Server.BaseAddress, "v3/index.json"); - var packageSource = new PackageSource(sourceUri.AbsoluteUri); - var providers = new List>(); - - providers.Add(new Lazy(() => new HttpSourceResourceProviderTestHost(_client))); - providers.AddRange(Repository.Provider.GetCoreV3()); - - _repository = new SourceRepository(packageSource, providers); - _cache = new SourceCacheContext { NoCache = true, MaxAge = new DateTimeOffset(), DirectDownload = true }; - _logger = NuGet.Common.NullLogger.Instance; - _cancellationToken = CancellationToken.None; - } - - [Fact] - public async Task ValidIndex() - { - var index = await _repository.GetResourceAsync(); - - Assert.Equal(12, index.Entries.Count); - - Assert.NotEmpty(index.GetServiceEntries("PackageBaseAddress/3.0.0")); - Assert.NotEmpty(index.GetServiceEntries("PackagePublish/2.0.0")); - Assert.NotEmpty(index.GetServiceEntries("RegistrationsBaseUrl")); - Assert.NotEmpty(index.GetServiceEntries("SearchAutocompleteService")); - Assert.NotEmpty(index.GetServiceEntries("SearchQueryService")); - Assert.NotEmpty(index.GetServiceEntries("SymbolPackagePublish/4.9.0")); - } - - [Fact] - public async Task SearchReturnsResults() - { - await _app.AddPackageAsync(_packageStream); - - var resource = await _repository.GetResourceAsync(); - var searchFilter = new SearchFilter(includePrerelease: true); - - var results = await resource.SearchAsync( - "", - searchFilter, - skip: 0, - take: 20, - _logger, - _cancellationToken); - - var result = Assert.Single(results); - - Assert.Equal("TestData", result.Identity.Id); - Assert.Equal("1.2.3", result.Identity.Version.ToNormalizedString()); - Assert.Equal("Test description", result.Description); - Assert.Equal("Test author", result.Authors); - Assert.Equal(0, result.DownloadCount); - - var versions = await result.GetVersionsAsync(); - var version = Assert.Single(versions); - - Assert.Equal("1.2.3", version.Version.ToNormalizedString()); - Assert.Equal(0, version.DownloadCount); - } - - [Fact] - public async Task SearchReturnsEmpty() - { - var resource = await _repository.GetResourceAsync(); - var searchFilter = new SearchFilter(includePrerelease: true); - - var results = await resource.SearchAsync( - "PackageDoesNotExist", - searchFilter, - skip: 0, - take: 20, - _logger, - _cancellationToken); - - Assert.Empty(results); - } - - [Fact] - public async Task AutocompleteReturnsResults() - { - await _app.AddPackageAsync(_packageStream); - - var resource = await _repository.GetResourceAsync(); - var results = await resource.IdStartsWith( - "", - includePrerelease: true, - _logger, - _cancellationToken); - - var result = Assert.Single(results); - - Assert.Equal("TestData", result); - } - - [Fact] - public async Task AutocompleteReturnsEmpty() - { - var resource = await _repository.GetResourceAsync(); - var results = await resource.IdStartsWith( - "PackageDoesNotExist", - includePrerelease: true, - _logger, - _cancellationToken); - - Assert.Empty(results); - } - - [Fact] - public async Task VersionListReturnsResults() - { - await _app.AddPackageAsync(_packageStream); - - var resource = await _repository.GetResourceAsync(); - var versions = await resource.GetAllVersionsAsync( - "TestData", - _cache, - _logger, - _cancellationToken); - - var version = Assert.Single(versions); - - Assert.Equal("1.2.3", version.ToNormalizedString()); - } - - [Fact] - public async Task VersionListReturnsEmpty() - { - var resource = await _repository.GetResourceAsync(); - var versions = await resource.GetAllVersionsAsync( - "PackageDoesNotExist", - _cache, - _logger, - _cancellationToken); - - Assert.Empty(versions); - } - - [Theory] - [InlineData("TestData", "1.0.0", false)] - [InlineData("TestData", "1.2.3", true)] - [InlineData("PackageDoesNotExists", "1.0.0", false)] - public async Task PackageExistsWorks(string packageId, string packageVersion, bool exists) - { - await _app.AddPackageAsync(_packageStream); - - var version = NuGetVersion.Parse(packageVersion); - var resource = await _repository.GetResourceAsync(); - var result = await resource.DoesPackageExistAsync( - packageId, - version, - _cache, - _logger, - _cancellationToken); - - Assert.Equal(exists, result); - } - - [Theory] - [InlineData("TestData", "1.0.0", false)] - [InlineData("TestData", "1.2.3", true)] - [InlineData("PackageDoesNotExists", "1.0.0", false)] - public async Task PackageDownloadWorks(string packageId, string packageVersion, bool exists) - { - await _app.AddPackageAsync(_packageStream); - - using var packageStream = new MemoryStream(); - - var version = NuGetVersion.Parse(packageVersion); - var resource = await _repository.GetResourceAsync(); - var result = await resource.CopyNupkgToStreamAsync( - packageId, - version, - packageStream, - _cache, - _logger, - _cancellationToken); - - packageStream.Position = 0; - - Assert.Equal(exists, result); - Assert.Equal(exists, packageStream.Length > 0); - } - - [Fact] - public async Task PackageMetadataReturnsOk() - { - await _app.AddPackageAsync(_packageStream); - - var resource = await _repository.GetResourceAsync(); - var packages = await resource.GetMetadataAsync( - "TestData", - includePrerelease: true, - includeUnlisted: true, - _cache, - _logger, - _cancellationToken); - - var package = Assert.Single(packages); - - Assert.Equal("TestData", package.Identity.Id); - Assert.Equal("1.2.3", package.Identity.Version.ToNormalizedString()); - Assert.Equal("Test description", package.Description); - Assert.Equal("Test author", package.Authors); - Assert.True(package.IsListed); - } - - [Fact] - public async Task PackageMetadataReturnsEmty() - { - var resource = await _repository.GetResourceAsync(); - var packages = await resource.GetMetadataAsync( - "PackageDoesNotExist", - includePrerelease: true, - includeUnlisted: true, - _cache, - _logger, - _cancellationToken); - - Assert.Empty(packages); - } - - public void Dispose() - { - _app.Dispose(); - _client.Dispose(); - _cache.Dispose(); - } + _app.Dispose(); + _client.Dispose(); + _cache.Dispose(); } -} +} \ No newline at end of file diff --git a/tests/BaGet.Tests/Support/BaGetApplication.cs b/tests/BaGet.Tests/Support/BaGetApplication.cs index 9886e1bd..1681c832 100644 --- a/tests/BaGet.Tests/Support/BaGetApplication.cs +++ b/tests/BaGet.Tests/Support/BaGetApplication.cs @@ -16,126 +16,125 @@ using Moq; using Xunit.Abstractions; -namespace BaGet.Tests +namespace BaGet.Tests; + +public class BaGetApplication : WebApplicationFactory { - public class BaGetApplication : WebApplicationFactory - { - private readonly ITestOutputHelper _output; - private readonly HttpClient _upstreamClient; + private readonly ITestOutputHelper _output; + private readonly HttpClient _upstreamClient; - public BaGetApplication(ITestOutputHelper output, HttpClient upstreamClient = null) - { - _output = output ?? throw new ArgumentNullException(nameof(output)); - _upstreamClient = upstreamClient; - } + public BaGetApplication(ITestOutputHelper output, HttpClient upstreamClient = null) + { + _output = output ?? throw new ArgumentNullException(nameof(output)); + _upstreamClient = upstreamClient; + } - protected override void ConfigureWebHost(IWebHostBuilder builder) - { - // Create temporary storage paths. - var tempPath = Path.Combine( - Path.GetTempPath(), - "BaGetTests", - Guid.NewGuid().ToString("N")); - var sqlitePath = Path.Combine(tempPath, "BaGet.db"); - var storagePath = Path.Combine(tempPath, "Packages"); - - Directory.CreateDirectory(tempPath); - - builder - .UseStartup() - .UseEnvironment("Production") - .ConfigureLogging(logging => - { - // BaGet uses console logging by default. This logger throws operation - // cancelled exceptions when the host shuts down, causing the the debugger - // to pause repeatedly if CLR exceptions are enabled. - logging.ClearProviders(); - - // Pipe logs to the xunit output. - logging.AddProvider(new XunitLoggerProvider(_output)); - }) - .ConfigureAppConfiguration(config => - { - // Setup the integration test configuration. - config.AddInMemoryCollection(new Dictionary - { - { "Database:Type", "Sqlite" }, - { "Database:ConnectionString", $"Data Source={sqlitePath}" }, - { "Storage:Type", "FileSystem" }, - { "Storage:Path", storagePath }, - { "Search:Type", "Database" }, - { "Mirror:Enabled", _upstreamClient != null ? "true": "false" }, - { "Mirror:PackageSource", "http://localhost/v3/index.json" }, - }); - }) - .ConfigureServices((context, services) => + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + // Create temporary storage paths. + var tempPath = Path.Combine( + Path.GetTempPath(), + "BaGetTests", + Guid.NewGuid().ToString("N")); + var sqlitePath = Path.Combine(tempPath, "BaGet.db"); + var storagePath = Path.Combine(tempPath, "Packages"); + + Directory.CreateDirectory(tempPath); + + builder + .UseStartup() + .UseEnvironment("Production") + .ConfigureLogging(logging => + { + // BaGet uses console logging by default. This logger throws operation + // cancelled exceptions when the host shuts down, causing the the debugger + // to pause repeatedly if CLR exceptions are enabled. + logging.ClearProviders(); + + // Pipe logs to the xunit output. + logging.AddProvider(new XunitLoggerProvider(_output)); + }) + .ConfigureAppConfiguration(config => + { + // Setup the integration test configuration. + config.AddInMemoryCollection(new Dictionary { - // Make time deterministic for testing purposes. - var time = new Mock(); - time - .Setup(t => t.UtcNow) - .Returns(DateTime.Parse("2020-01-01T00:00:00.000Z")); - - services.AddSingleton(time.Object); - if (_upstreamClient != null) - { - services.AddSingleton(_upstreamClient); - } - - // Setup the integration test database. - var provider = services.BuildServiceProvider(); - var scopeFactory = provider.GetRequiredService(); - - // Ensure the database is created before we run migrations. The migrations - // can create the database too, however, migrations check whether the database exists - // first. The SQLite provider implements this by attempting to open a connection, - // and if that fails, creating the database. This throws several exceptions that - // pauses the debugger repeatedly if CLR exceptions are enabled. - // See: https://github.com/dotnet/efcore/blob/644d3c8c3a604fd0121d90eaf34f14870e19bcff/src/EFCore.Sqlite.Core/Storage/Internal/SqliteDatabaseCreator.cs#L88-L98 - using var scope = scopeFactory.CreateScope(); - var ctx = scope.ServiceProvider.GetRequiredService(); - var dbCreator = ctx.Database.GetService(); - - dbCreator.Create(); - ctx.Database.Migrate(); + { "Database:Type", "Sqlite" }, + { "Database:ConnectionString", $"Data Source={sqlitePath}" }, + { "Storage:Type", "FileSystem" }, + { "Storage:Path", storagePath }, + { "Search:Type", "Database" }, + { "Mirror:Enabled", _upstreamClient != null ? "true": "false" }, + { "Mirror:PackageSource", "http://localhost/v3/index.json" }, }); - } + }) + .ConfigureServices((context, services) => + { + // Make time deterministic for testing purposes. + var time = new Mock(); + time + .Setup(t => t.UtcNow) + .Returns(DateTime.Parse("2020-01-01T00:00:00.000Z")); + + services.AddSingleton(time.Object); + if (_upstreamClient != null) + { + services.AddSingleton(_upstreamClient); + } + + // Setup the integration test database. + var provider = services.BuildServiceProvider(); + var scopeFactory = provider.GetRequiredService(); + + // Ensure the database is created before we run migrations. The migrations + // can create the database too, however, migrations check whether the database exists + // first. The SQLite provider implements this by attempting to open a connection, + // and if that fails, creating the database. This throws several exceptions that + // pauses the debugger repeatedly if CLR exceptions are enabled. + // See: https://github.com/dotnet/efcore/blob/644d3c8c3a604fd0121d90eaf34f14870e19bcff/src/EFCore.Sqlite.Core/Storage/Internal/SqliteDatabaseCreator.cs#L88-L98 + using var scope = scopeFactory.CreateScope(); + var ctx = scope.ServiceProvider.GetRequiredService(); + var dbCreator = ctx.Database.GetService(); + + dbCreator.Create(); + ctx.Database.Migrate(); + }); } +} - internal static class BaGetWebApplicationFactoryExtensions +internal static class BaGetWebApplicationFactoryExtensions +{ + public static async Task AddPackageAsync( + this WebApplicationFactory factory, + Stream package, + CancellationToken cancellationToken = default) { - public static async Task AddPackageAsync( - this WebApplicationFactory factory, - Stream package, - CancellationToken cancellationToken = default) - { - var scopeFactory = factory.Services.GetRequiredService(); + var scopeFactory = factory.Services.GetRequiredService(); - using var scope = scopeFactory.CreateScope(); - var indexer = scope.ServiceProvider.GetRequiredService(); + using var scope = scopeFactory.CreateScope(); + var indexer = scope.ServiceProvider.GetRequiredService(); - var result = await indexer.IndexAsync(package, cancellationToken); - if (result != PackageIndexingResult.Success) - { - throw new InvalidOperationException($"Unexpected indexing result {result}"); - } + var result = await indexer.IndexAsync(package, cancellationToken); + if (result != PackageIndexingResult.Success) + { + throw new InvalidOperationException($"Unexpected indexing result {result}"); } + } - public static async Task AddSymbolPackageAsync( - this WebApplicationFactory factory, - Stream symbolPackage, - CancellationToken cancellationToken = default) - { - var scopeFactory = factory.Services.GetRequiredService(); + public static async Task AddSymbolPackageAsync( + this WebApplicationFactory factory, + Stream symbolPackage, + CancellationToken cancellationToken = default) + { + var scopeFactory = factory.Services.GetRequiredService(); - using var scope = scopeFactory.CreateScope(); - var indexer = scope.ServiceProvider.GetRequiredService(); + using var scope = scopeFactory.CreateScope(); + var indexer = scope.ServiceProvider.GetRequiredService(); - var result = await indexer.IndexAsync(symbolPackage, cancellationToken); - if (result != SymbolIndexingResult.Success) - { - throw new InvalidOperationException($"Unexpected indexing result {result}"); - } + var result = await indexer.IndexAsync(symbolPackage, cancellationToken); + if (result != SymbolIndexingResult.Success) + { + throw new InvalidOperationException($"Unexpected indexing result {result}"); } } -} +} \ No newline at end of file diff --git a/tests/BaGet.Tests/Support/HttpSourceResourceProviderTestHost.cs b/tests/BaGet.Tests/Support/HttpSourceResourceProviderTestHost.cs index 2420112a..71081e6a 100644 --- a/tests/BaGet.Tests/Support/HttpSourceResourceProviderTestHost.cs +++ b/tests/BaGet.Tests/Support/HttpSourceResourceProviderTestHost.cs @@ -8,40 +8,39 @@ using NuGet.Protocol; using NuGet.Protocol.Core.Types; -namespace BaGet.Tests -{ - /// - /// Similar to official HttpSourceResourceProvider, but uses test host. - /// - public class HttpSourceResourceProviderTestHost : ResourceProvider - { - // Only one HttpSource per source should exist. This is to reduce the number of TCP connections. - private readonly ConcurrentDictionary _cache - = new ConcurrentDictionary(); - private readonly HttpClient _httpClient; +namespace BaGet.Tests; - public HttpSourceResourceProviderTestHost(HttpClient httpClient) - : base(typeof(HttpSourceResource), - nameof(HttpSourceResource), - NuGetResourceProviderPositions.Last) - { - _httpClient = httpClient; - } +/// +/// Similar to official HttpSourceResourceProvider, but uses test host. +/// +public class HttpSourceResourceProviderTestHost : ResourceProvider +{ + // Only one HttpSource per source should exist. This is to reduce the number of TCP connections. + private readonly ConcurrentDictionary _cache + = new ConcurrentDictionary(); + private readonly HttpClient _httpClient; - public override Task> TryCreate(SourceRepository source, CancellationToken token) - { - Debug.Assert(source.PackageSource.IsHttp, "HTTP source requested for a non-http source."); + public HttpSourceResourceProviderTestHost(HttpClient httpClient) + : base(typeof(HttpSourceResource), + nameof(HttpSourceResource), + NuGetResourceProviderPositions.Last) + { + _httpClient = httpClient; + } - HttpSourceResource curResource = null; + public override Task> TryCreate(SourceRepository source, CancellationToken token) + { + Debug.Assert(source.PackageSource.IsHttp, "HTTP source requested for a non-http source."); - if (source.PackageSource.IsHttp) - { - curResource = _cache.GetOrAdd( - source.PackageSource, - packageSource => new HttpSourceResource(TestableHttpSource.Create(source, _httpClient))); - } + HttpSourceResource curResource = null; - return Task.FromResult(new Tuple(curResource != null, curResource)); + if (source.PackageSource.IsHttp) + { + curResource = _cache.GetOrAdd( + source.PackageSource, + packageSource => new HttpSourceResource(TestableHttpSource.Create(source, _httpClient))); } + + return Task.FromResult(new Tuple(curResource != null, curResource)); } -} +} \ No newline at end of file diff --git a/tests/BaGet.Tests/Support/StreamExtensions.cs b/tests/BaGet.Tests/Support/StreamExtensions.cs index 1ad59189..1605e5f5 100644 --- a/tests/BaGet.Tests/Support/StreamExtensions.cs +++ b/tests/BaGet.Tests/Support/StreamExtensions.cs @@ -1,24 +1,23 @@ using System.IO; using Newtonsoft.Json; -namespace BaGet.Tests +namespace BaGet.Tests; + +public static class StreamExtensions { - public static class StreamExtensions + public static string ToPrettifiedJson(this Stream jsonStream) { - public static string ToPrettifiedJson(this Stream jsonStream) + using var writer = new StringWriter(); + using var jsonWriter = new JsonTextWriter(writer) { - using var writer = new StringWriter(); - using var jsonWriter = new JsonTextWriter(writer) - { - Formatting = Formatting.Indented, - DateTimeZoneHandling = DateTimeZoneHandling.Utc - }; + Formatting = Formatting.Indented, + DateTimeZoneHandling = DateTimeZoneHandling.Utc + }; - using var reader = new StreamReader(jsonStream); - using var jsonReader = new JsonTextReader(reader); + using var reader = new StreamReader(jsonStream); + using var jsonReader = new JsonTextReader(reader); - jsonWriter.WriteToken(jsonReader); - return writer.ToString(); - } + jsonWriter.WriteToken(jsonReader); + return writer.ToString(); } -} +} \ No newline at end of file diff --git a/tests/BaGet.Tests/Support/TestResources.cs b/tests/BaGet.Tests/Support/TestResources.cs index dc80c3f3..ba6e40da 100644 --- a/tests/BaGet.Tests/Support/TestResources.cs +++ b/tests/BaGet.Tests/Support/TestResources.cs @@ -1,45 +1,44 @@ using System.IO; -namespace BaGet.Tests +namespace BaGet.Tests; + +public static class TestResources { - public static class TestResources - { - private const string ResourcePrefix = "BaGet.Tests.TestData."; + private const string ResourcePrefix = "BaGet.Tests.TestData."; - /// - /// Test package created with the following properties: - /// - /// Test author - /// Test description - /// 1.2.3 - /// true - /// snupkg - /// - public const string Package = "TestData.1.2.3.nupkg"; - public const string SymbolPackage = "TestData.1.2.3.snupkg"; + /// + /// Test package created with the following properties: + /// + /// Test author + /// Test description + /// 1.2.3 + /// true + /// snupkg + /// + public const string Package = "TestData.1.2.3.nupkg"; + public const string SymbolPackage = "TestData.1.2.3.snupkg"; - /// - /// Buffer the resource stream into memory so the caller doesn't have to dispose. - /// - public static MemoryStream GetResourceStream(string resourceName) - { - using var resourceStream = typeof(TestResources) - .Assembly - .GetManifestResourceStream(ResourcePrefix + resourceName); - - if (resourceStream == null) - { - return null; - } + /// + /// Buffer the resource stream into memory so the caller doesn't have to dispose. + /// + public static MemoryStream GetResourceStream(string resourceName) + { + using var resourceStream = typeof(TestResources) + .Assembly + .GetManifestResourceStream(ResourcePrefix + resourceName); - var bufferedStream = new MemoryStream(); - using (resourceStream) - { - resourceStream.CopyTo(bufferedStream); - } + if (resourceStream == null) + { + return null; + } - bufferedStream.Position = 0; - return bufferedStream; + var bufferedStream = new MemoryStream(); + using (resourceStream) + { + resourceStream.CopyTo(bufferedStream); } + + bufferedStream.Position = 0; + return bufferedStream; } -} +} \ No newline at end of file diff --git a/tests/BaGet.Tests/Support/TestableHttpSource.cs b/tests/BaGet.Tests/Support/TestableHttpSource.cs index a7e022cb..b3e8844c 100644 --- a/tests/BaGet.Tests/Support/TestableHttpSource.cs +++ b/tests/BaGet.Tests/Support/TestableHttpSource.cs @@ -7,49 +7,48 @@ using NuGet.Protocol; using NuGet.Protocol.Core.Types; -namespace BaGet.Tests +namespace BaGet.Tests; + +public class TestableHttpSource : HttpSource { - public class TestableHttpSource : HttpSource + public static TestableHttpSource Create(SourceRepository source, HttpClient client) { - public static TestableHttpSource Create(SourceRepository source, HttpClient client) - { - Func> factory = () => source.GetResourceAsync(); - - var httpCacheDirectory = Path.Combine( - Path.GetTempPath(), - "BaGetTests", - Guid.NewGuid().ToString("N")); - - return new TestableHttpSource( - source.PackageSource, - factory, - NullThrottle.Instance, - client, - httpCacheDirectory); - } + Func> factory = () => source.GetResourceAsync(); - private TestableHttpSource( - PackageSource packageSource, - Func> messageHandlerFactory, - IThrottle throttle, - HttpClient httpClient, - string httpCacheDirectory) - : base(packageSource, messageHandlerFactory, throttle) - { - // Set the HTTP client on the parent's private field. - var flags = BindingFlags.NonPublic | BindingFlags.Instance; - var field = typeof(HttpSource).GetField("_httpClient", flags); + var httpCacheDirectory = Path.Combine( + Path.GetTempPath(), + "BaGetTests", + Guid.NewGuid().ToString("N")); - if (field == null) - { - throw new InvalidOperationException( - $"Could not find field '_httpClient' with flags '{flags}' " + - $"on {nameof(HttpSource)}"); - } + return new TestableHttpSource( + source.PackageSource, + factory, + NullThrottle.Instance, + client, + httpCacheDirectory); + } - field.SetValue(this, httpClient); + private TestableHttpSource( + PackageSource packageSource, + Func> messageHandlerFactory, + IThrottle throttle, + HttpClient httpClient, + string httpCacheDirectory) + : base(packageSource, messageHandlerFactory, throttle) + { + // Set the HTTP client on the parent's private field. + var flags = BindingFlags.NonPublic | BindingFlags.Instance; + var field = typeof(HttpSource).GetField("_httpClient", flags); - HttpCacheDirectory = httpCacheDirectory; + if (field == null) + { + throw new InvalidOperationException( + $"Could not find field '_httpClient' with flags '{flags}' " + + $"on {nameof(HttpSource)}"); } + + field.SetValue(this, httpClient); + + HttpCacheDirectory = httpCacheDirectory; } -} +} \ No newline at end of file diff --git a/tests/BaGet.Tests/Support/XunitLogger.cs b/tests/BaGet.Tests/Support/XunitLogger.cs index 1c3d1964..7c0c0599 100644 --- a/tests/BaGet.Tests/Support/XunitLogger.cs +++ b/tests/BaGet.Tests/Support/XunitLogger.cs @@ -2,39 +2,38 @@ using Microsoft.Extensions.Logging; using Xunit.Abstractions; -namespace BaGet.Tests +namespace BaGet.Tests; + +// Based off: +// https://stackoverflow.com/questions/46169169/net-core-2-0-configurelogging-xunit-test +public class XunitLogger : ILogger { - // Based off: - // https://stackoverflow.com/questions/46169169/net-core-2-0-configurelogging-xunit-test - public class XunitLogger : ILogger + private readonly ITestOutputHelper _output; + private readonly string _category; + + public XunitLogger(ITestOutputHelper output, string category) { - private readonly ITestOutputHelper _output; - private readonly string _category; + _output = output ?? throw new ArgumentNullException(nameof(output)); + _category = category ?? throw new ArgumentNullException(nameof(category)); + } - public XunitLogger(ITestOutputHelper output, string category) - { - _output = output ?? throw new ArgumentNullException(nameof(output)); - _category = category ?? throw new ArgumentNullException(nameof(category)); - } + public IDisposable BeginScope(TState state) => NullScope.Instance; + public bool IsEnabled(LogLevel logLevel) => true; - public IDisposable BeginScope(TState state) => NullScope.Instance; - public bool IsEnabled(LogLevel logLevel) => true; + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + _output.WriteLine($"{_category} [{eventId}] {formatter(state, exception)}"); - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + if (exception != null) { - _output.WriteLine($"{_category} [{eventId}] {formatter(state, exception)}"); - - if (exception != null) - { - _output.WriteLine(exception.ToString()); - } + _output.WriteLine(exception.ToString()); } + } - private class NullScope : IDisposable - { - public static readonly NullScope Instance = new NullScope(); + private class NullScope : IDisposable + { + public static readonly NullScope Instance = new NullScope(); - public void Dispose() { } - } + public void Dispose() { } } -} +} \ No newline at end of file diff --git a/tests/BaGet.Tests/Support/XunitLoggerProvider.cs b/tests/BaGet.Tests/Support/XunitLoggerProvider.cs index efc71d7e..9315e209 100644 --- a/tests/BaGet.Tests/Support/XunitLoggerProvider.cs +++ b/tests/BaGet.Tests/Support/XunitLoggerProvider.cs @@ -2,21 +2,20 @@ using Microsoft.Extensions.Logging; using Xunit.Abstractions; -namespace BaGet.Tests +namespace BaGet.Tests; + +// Based off: +// https://stackoverflow.com/questions/46169169/net-core-2-0-configurelogging-xunit-test +public class XunitLoggerProvider : ILoggerProvider { - // Based off: - // https://stackoverflow.com/questions/46169169/net-core-2-0-configurelogging-xunit-test - public class XunitLoggerProvider : ILoggerProvider - { - private readonly ITestOutputHelper _output; + private readonly ITestOutputHelper _output; - public XunitLoggerProvider(ITestOutputHelper output) - { - _output = output ?? throw new ArgumentNullException(nameof(output)); - } + public XunitLoggerProvider(ITestOutputHelper output) + { + _output = output ?? throw new ArgumentNullException(nameof(output)); + } - public ILogger CreateLogger(string category) => new XunitLogger(_output, category); + public ILogger CreateLogger(string category) => new XunitLogger(_output, category); - public void Dispose() { } - } -} + public void Dispose() { } +} \ No newline at end of file diff --git a/tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj b/tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj index fb144a8f..d30d9081 100644 --- a/tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj +++ b/tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj @@ -1,11 +1,25 @@ - + - netcoreapp3.1 + net6.0 - + + + all + runtime; build; native; contentfiles; analyzers + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + \ No newline at end of file From 4f637751b89378f3e37fa256a70e2eacecf20e72 Mon Sep 17 00:00:00 2001 From: Kannan Chithambaranathan Date: Tue, 23 Aug 2022 00:56:15 +0530 Subject: [PATCH 2/7] Project Updated --- .editorconfig | 146 ------------------ BaGet.sln | 1 - .../Sample02_Search.cs | 1 - .../Sample03_Metadata.cs | 1 - .../BaGetWebApplication.csproj | 6 +- samples/BaGetWebApplication/Program.cs | 2 +- samples/BaGetWebApplication/appsettings.json | 18 +++ .../AliyunApplicationExtensions.cs | 11 +- src/BaGet.Aliyun/AliyunStorageOptions.cs | 2 - src/BaGet.Aliyun/AliyunStorageService.cs | 4 - src/BaGet.Aliyun/GlobalUsings.cs | 9 ++ src/BaGet.Aws/AwsApplicationExtensions.cs | 9 -- src/BaGet.Aws/GlobalUsings.cs | 12 ++ src/BaGet.Aws/S3StorageOptions.cs | 3 - src/BaGet.Aws/S3StorageService.cs | 5 - src/BaGet.Azure/GlobalUsings.cs | 4 +- .../ApiKeyAuthenticationService.cs | 2 - src/BaGet.Core/BaGet.Core.csproj.DotSettings | 15 ++ src/BaGet.Core/BaGetApplication.cs | 2 - src/BaGet.Core/Configuration/BindOptions.cs | 3 - .../Configuration/DatabaseOptions.cs | 2 - .../Configuration/FileSystemStorageOptions.cs | 2 - src/BaGet.Core/Configuration/MirrorOptions.cs | 2 - .../Content/DefaultPackageContentService.cs | 16 +- .../Content/IPackageContentService.cs | 3 - src/BaGet.Core/Entities/AbstractContext.cs | 3 - .../Converters/StringArrayComparer.cs | 2 - .../Converters/StringArrayToJsonConverter.cs | 3 - .../Converters/UriToStringConverter.cs | 2 - src/BaGet.Core/Entities/IContext.cs | 3 - src/BaGet.Core/Entities/NullContext.cs | 3 - src/BaGet.Core/Entities/Package.cs | 2 - .../Extensions/BaGetApplicationExtensions.cs | 8 +- ...DependencyInjectionExtensions.Providers.cs | 5 - .../DependencyInjectionExtensions.cs | 17 +- src/BaGet.Core/Extensions/IProvider.cs | 2 - .../PackageArchiveReaderExtensions.cs | 11 +- src/BaGet.Core/Extensions/StreamExtensions.cs | 6 +- src/BaGet.Core/GlobalUsings.cs | 32 ++++ src/BaGet.Core/IPackageDatabase.cs | 2 - src/BaGet.Core/IPackageService.cs | 2 - src/BaGet.Core/IUrlGenerator.cs | 2 - .../Indexing/FrameworkCompatibilityService.cs | 15 +- .../Indexing/IPackageDeletionService.cs | 2 - .../Indexing/PackageDeletionService.cs | 4 - .../Indexing/PackageIndexingService.cs | 4 - .../Indexing/SymbolIndexingService.cs | 18 +-- .../Metadata/BaGetPackageMetadata.cs | 3 - .../Metadata/BaGetRegistrationIndexPage.cs | 3 - .../BaGetRegistrationIndexPageItem.cs | 3 - .../BaGetRegistrationIndexResponse.cs | 3 - .../Metadata/DefaultPackageMetadataService.cs | 3 - .../Metadata/IPackageMetadataService.cs | 3 - .../Metadata/RegistrationBuilder.cs | 2 - src/BaGet.Core/PackageDatabase.cs | 3 - src/BaGet.Core/PackageService.cs | 3 - .../Search/DatabaseSearchService.cs | 3 - src/BaGet.Core/Search/DependentsResponse.cs | 2 - .../Search/ISearchResponseBuilder.cs | 4 +- src/BaGet.Core/Search/ISearchService.cs | 2 - src/BaGet.Core/Search/NullSearchService.cs | 2 - .../Search/SearchResponseBuilder.cs | 4 +- .../ServiceIndex/BaGetServiceIndex.cs | 2 - .../ServiceIndex/IServiceIndexService.cs | 2 - src/BaGet.Core/Storage/FileStorageService.cs | 2 - .../Storage/IPackageStorageService.cs | 2 - .../Storage/PackageStorageService.cs | 7 +- .../Clients/DisabledUpstreamClient.cs | 4 +- .../Upstream/Clients/V2UpstreamClient.cs | 13 +- .../Upstream/Clients/V3UpstreamClient.cs | 5 - src/BaGet.Core/Upstream/DownloadsImporter.cs | 3 - src/BaGet.Core/Upstream/IUpstreamClient.cs | 4 +- .../Upstream/PackageDownloadsJsonSource.cs | 5 - .../Validation/RequiredIfAttribute.cs | 3 - .../Validation/ValidateBaGetOptions.cs | 3 - .../Validation/ValidateStartupOptions.cs | 3 - src/BaGet.Database.MySql/GlobalUsings.cs | 10 ++ .../Migrations/20181212113156_Initial.cs | 3 - .../20190303071640_AddSearchDimensions.cs | 3 - ...14215810_AddOriginalVersionStringColumn.cs | 4 +- ...00109085155_AddReleaseNotesStringColumn.cs | 4 +- ...20200210004047_AddHasEmbeddedIconColumn.cs | 5 +- ...10919191554_RemoveReleaseNotesMaxLength.cs | 5 +- .../MySqlApplicationExtensions.cs | 6 - src/BaGet.Database.MySql/MySqlContext.cs | 4 - src/BaGet.Database.PostgreSql/GlobalUsings.cs | 10 ++ .../Migrations/20190311172227_Initial.cs | 5 +- ...14220105_AddOriginalVersionStringColumn.cs | 4 +- ...00109085355_AddReleaseNotesStringColumn.cs | 4 +- ...0200208053115_FixVersionCaseSensitivity.cs | 3 - ...20200210004256_AddHasEmbeddedIconColumn.cs | 4 +- ...10919191649_RemoveReleaseNotesMaxLength.cs | 4 +- .../PostgreSqlApplicationExtensions.cs | 6 - .../PostgreSqlContext.cs | 4 - src/BaGet.Database.SqlServer/GlobalUsings.cs | 10 ++ .../Migrations/20180804082816_Initial.cs | 3 - .../20190303071621_AddSearchDimensions.cs | 3 - ...14215959_AddOriginalVersionStringColumn.cs | 4 +- ...00109085508_AddReleaseNotesStringColumn.cs | 4 +- ...20200210004408_AddHasEmbeddedIconColumn.cs | 4 +- ...10919191928_RemoveReleaseNotesMaxLength.cs | 4 +- .../SqlServerApplicationExtensions.cs | 6 - .../SqlServerContext.cs | 4 - src/BaGet.Database.Sqlite/GlobalUsings.cs | 9 ++ .../Migrations/20180804082808_Initial.cs | 2 - .../20190303072658_AddSearchDimensions.cs | 2 - ...14215110_AddOriginalVersionStringColumn.cs | 4 +- ...00109071618_AddReleaseNotesStringColumn.cs | 4 +- ...0200208044959_FixVersionCaseSensitivity.cs | 2 - ...20200210004344_AddHasEmbeddedIconColumn.cs | 4 +- ...10919190849_RemoveReleaseNotesMaxLength.cs | 4 +- .../SqliteApplicationExtensions.cs | 10 +- src/BaGet.Database.Sqlite/SqliteContext.cs | 4 - src/BaGet.Gcp/GlobalUsings.cs | 12 ++ .../GoogleCloudApplicationExtensions.cs | 5 - src/BaGet.Gcp/GoogleCloudStorageOptions.cs | 3 - src/BaGet.Gcp/GoogleCloudStorageService.cs | 7 - .../BaGet.Protocol.csproj.DotSettings | 9 ++ src/BaGet.Protocol/Catalog/CatalogClient.cs | 2 - .../Catalog/CatalogProcessor.cs | 5 +- .../Catalog/CatalogProcessorOptions.cs | 2 +- src/BaGet.Protocol/Catalog/FileCursor.cs | 6 +- src/BaGet.Protocol/Catalog/ICatalogClient.cs | 2 - .../Catalog/ICatalogLeafProcessor.cs | 4 +- src/BaGet.Protocol/Catalog/ICursor.cs | 2 +- .../Catalog/NullCatalogClient.cs | 4 +- src/BaGet.Protocol/Catalog/NullCursor.cs | 2 +- .../Catalog/RawCatalogClient.cs | 4 +- .../PackageDependencyRangeJsonConverter.cs | 5 +- .../StringOrStringArrayJsonConverter.cs | 5 +- .../Extensions/CatalogModelExtensions.cs | 4 - .../Extensions/HttpClientExtensions.cs | 3 - .../NuGetClientFactoryExtensions.cs | 3 - .../PackageContentModelExtensions.cs | 3 - .../PackageMetadataModelExtensions.cs | 3 - .../Extensions/SearchModelExtensions.cs | 3 - .../Extensions/ServiceIndexModelExtensions.cs | 2 - src/BaGet.Protocol/GlobalUsings.cs | 9 ++ src/BaGet.Protocol/Models/AlternatePackage.cs | 4 +- .../Models/AutocompleteContext.cs | 4 +- .../Models/AutocompleteResponse.cs | 4 +- src/BaGet.Protocol/Models/CatalogIndex.cs | 4 +- src/BaGet.Protocol/Models/CatalogLeaf.cs | 4 +- src/BaGet.Protocol/Models/CatalogLeafItem.cs | 4 +- src/BaGet.Protocol/Models/CatalogPage.cs | 4 +- src/BaGet.Protocol/Models/CatalogPageItem.cs | 4 +- .../Models/DependencyGroupItem.cs | 4 +- src/BaGet.Protocol/Models/DependencyItem.cs | 5 +- src/BaGet.Protocol/Models/ICatalogLeafItem.cs | 2 +- .../Models/PackageDeleteCatalogLeaf.cs | 2 +- .../Models/PackageDeprecation.cs | 4 +- .../Models/PackageDetailsCatalogLeaf.cs | 4 +- src/BaGet.Protocol/Models/PackageMetadata.cs | 4 +- .../Models/PackageNotFoundException.cs | 4 +- .../Models/PackageVersionsResponse.cs | 4 +- .../Models/ProtocolException.cs | 4 +- .../Models/RegistrationIndexPage.cs | 4 +- .../Models/RegistrationIndexPageItem.cs | 4 +- .../Models/RegistrationIndexResponse.cs | 4 +- .../Models/RegistrationLeafResponse.cs | 4 +- .../Models/RegistrationPageResponse.cs | 2 +- src/BaGet.Protocol/Models/SearchContext.cs | 4 +- src/BaGet.Protocol/Models/SearchResponse.cs | 4 +- src/BaGet.Protocol/Models/SearchResult.cs | 5 +- .../Models/SearchResultPackageType.cs | 4 +- .../Models/SearchResultVersion.cs | 4 +- src/BaGet.Protocol/Models/ServiceIndexItem.cs | 4 +- .../Models/ServiceIndexResponse.cs | 4 +- src/BaGet.Protocol/NuGetClient.cs | 4 - src/BaGet.Protocol/NuGetClientFactory.cs | 3 - .../PackageContent/IPackageContentClient.cs | 3 - .../PackageContent/PackageContentClient.cs | 3 - .../PackageContent/RawPackageContentClient.cs | 6 +- .../PackageMetadata/IPackageMetadataClient.cs | 2 - .../PackageMetadata/PackageMetadataClient.cs | 2 - .../RawPackageMetadataClient.cs | 4 +- .../Search/AutocompleteClient.cs | 2 - .../Search/IAutocompleteClient.cs | 2 - src/BaGet.Protocol/Search/ISearchClient.cs | 2 - .../Search/NullAutocompleteClient.cs | 4 +- .../Search/RawAutocompleteClient.cs | 4 +- src/BaGet.Protocol/Search/RawSearchClient.cs | 5 +- src/BaGet.Protocol/Search/SearchClient.cs | 2 - .../ServiceIndex/IServiceIndexClient.cs | 2 - .../ServiceIndex/RawServiceIndexClient.cs | 4 +- .../ServiceIndex/ServiceIndexClient.cs | 2 - .../Controllers/PackageContentController.cs | 3 - .../Controllers/PackageMetadataController.cs | 3 - .../Controllers/PackagePublishController.cs | 3 - src/BaGet.Web/Controllers/SearchController.cs | 3 - .../Controllers/ServiceIndexController.cs | 3 - src/BaGet.Web/Controllers/SymbolController.cs | 3 - src/BaGet.Web/Extensions/IHostExtensions.cs | 4 - .../IServiceCollectionExtensions.cs | 3 - src/BaGet.Web/Extensions/RazorExtensions.cs | 4 +- src/BaGet.Web/GlobalUsings.cs | 9 ++ src/BaGet.Web/NavLinkTagHelper.cs | 2 - src/BaGet.Web/Pages/Index.cshtml.cs | 4 - src/BaGet.Web/Pages/Package.cshtml | 1 - src/BaGet/ConfigureBaGetOptions.cs | 13 +- src/BaGet/ConfigureRazorRuntimeCompilation.cs | 6 +- src/BaGet/GlobalUsings.cs | 12 ++ src/BaGet/Program.cs | 4 - src/BaGet/Startup.cs | 8 - tests/BaGet.Core.Tests/Metadata/ModelTests.cs | 2 +- .../Upstream/V3UpstreamClientTests.cs | 1 - ...ackageDependencyRangeJsonConverterTests.cs | 1 - .../StringOrStringArrayJsonConverterTests.cs | 1 - .../RawAutocompleteClientTests.cs | 1 - .../RawCatalogClientTests.cs | 1 - .../RawPackageContentClientTests.cs | 1 - .../RawPackageMetadataClientTests.cs | 1 - .../RawSearchClientTests.cs | 1 - .../RawServiceIndexClientTests.cs | 1 - .../Support/ProtocolFixture.cs | 1 - .../BaGetClientIntegrationTests.cs | 1 - .../BaGet.Web.Tests/Pages/IndexModelFacts.cs | 2 +- 217 files changed, 278 insertions(+), 811 deletions(-) delete mode 100644 .editorconfig create mode 100644 src/BaGet.Aliyun/GlobalUsings.cs create mode 100644 src/BaGet.Aws/GlobalUsings.cs create mode 100644 src/BaGet.Core/BaGet.Core.csproj.DotSettings create mode 100644 src/BaGet.Core/GlobalUsings.cs create mode 100644 src/BaGet.Database.MySql/GlobalUsings.cs create mode 100644 src/BaGet.Database.PostgreSql/GlobalUsings.cs create mode 100644 src/BaGet.Database.SqlServer/GlobalUsings.cs create mode 100644 src/BaGet.Database.Sqlite/GlobalUsings.cs create mode 100644 src/BaGet.Gcp/GlobalUsings.cs create mode 100644 src/BaGet.Protocol/BaGet.Protocol.csproj.DotSettings create mode 100644 src/BaGet.Protocol/GlobalUsings.cs create mode 100644 src/BaGet/GlobalUsings.cs diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 84873213..00000000 --- a/.editorconfig +++ /dev/null @@ -1,146 +0,0 @@ -; This is the default for the codeline. -root = true - -[*] -indent_style = space -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true -dotnet_style_coalesce_expression = true:suggestion -dotnet_style_null_propagation = true:suggestion -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion -dotnet_style_prefer_auto_properties = true:silent -dotnet_style_object_initializer = true:suggestion -dotnet_style_collection_initializer = true:suggestion -dotnet_style_prefer_simplified_boolean_expressions = true:suggestion -dotnet_style_prefer_conditional_expression_over_assignment = true:silent -dotnet_style_prefer_conditional_expression_over_return = true:silent -dotnet_style_explicit_tuple_names = true:suggestion -dotnet_style_prefer_inferred_tuple_names = true:suggestion -dotnet_style_operator_placement_when_wrapping = beginning_of_line -tab_width = 4 -indent_size = 4 -end_of_line = crlf -dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion -dotnet_style_prefer_simplified_interpolation = true:suggestion -dotnet_style_prefer_compound_assignment = true:suggestion -dotnet_style_namespace_match_folder = true:suggestion -dotnet_style_readonly_field = true:warning -dotnet_style_predefined_type_for_locals_parameters_members = true:warning -dotnet_style_predefined_type_for_member_access = true:warning - -[*.{htm,html,js,ts,tsx,css,sass,scss,less,svg,vue}] -indent_size = 2 - -[*.{xml,config,*proj,nuspec,props,resx,targets,yml,tasks}] -indent_size = 2 - -[*.json] -indent_size = 2 - -[*.{ps1,psm1}] -indent_size = 4 - -[*.sh] -indent_size = 4 -end_of_line = lf - -[*.cs] -indent_size = 4 - -# .NET Coding Conventions - -# Organize usings -dotnet_sort_system_directives_first = true:warning - -# this. preferences -dotnet_style_qualification_for_field = false:warning -dotnet_style_qualification_for_property = false:warning -dotnet_style_qualification_for_method = false:warning -dotnet_style_qualification_for_event = false:warning - -# Language keywords vs BCL type preferences -dotnet_style_predefined_type_for_locals_parameters_members = true:warning -dotnet_style_predefined_type_for_member_access = true:warning - -# Modifier preferences -dotnet_style_require_accessibility_modifiers = always:warning -dotnet_style_readonly_field = true:warning - -# Naming Conventions - -# Style Definitions -dotnet_naming_style.pascal_case_style.capitalization = pascal_case - -# Use PascalCase for constant fields -dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields -dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style -dotnet_naming_symbols.constant_fields.applicable_kinds = field -dotnet_naming_symbols.constant_fields.applicable_accessibilities = * -dotnet_naming_symbols.constant_fields.required_modifiers = const - -# C# Code Style Rules - -# var preferences -csharp_style_var_for_built_in_types = true:warning -csharp_style_var_when_type_is_apparent = true:warning -csharp_style_var_elsewhere = true:warning - -# Modifier preferences -csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async - -# New line preferences -csharp_new_line_before_open_brace = all -csharp_new_line_before_else = true -csharp_new_line_before_catch = true -csharp_new_line_before_finally = true - -# Indentation preferences -csharp_indent_case_contents = true -csharp_indent_switch_labels = true - -# Space preferences -csharp_space_after_cast = false -csharp_space_after_keywords_in_control_flow_statements = true -csharp_space_between_method_call_parameter_list_parentheses = false -csharp_space_between_method_declaration_parameter_list_parentheses = false -csharp_space_between_parentheses = false -csharp_space_before_colon_in_inheritance_clause = true -csharp_space_after_colon_in_inheritance_clause = true -csharp_space_around_binary_operators = before_and_after -csharp_space_between_method_declaration_empty_parameter_list_parentheses = false -csharp_space_between_method_call_name_and_opening_parenthesis = false -csharp_space_between_method_call_empty_parameter_list_parentheses = false - -# Wrapping preferences -csharp_preserve_single_line_statements = true -csharp_preserve_single_line_blocks = true -csharp_using_directive_placement = outside_namespace:silent -csharp_prefer_simple_using_statement = true:suggestion -csharp_prefer_braces = true:silent -csharp_style_namespace_declarations = block_scoped:silent -csharp_style_prefer_method_group_conversion = true:silent -csharp_style_prefer_top_level_statements = true:silent -csharp_style_expression_bodied_methods = false:silent -csharp_style_expression_bodied_constructors = false:silent -csharp_style_expression_bodied_operators = false:silent -csharp_style_expression_bodied_properties = true:silent -csharp_style_expression_bodied_indexers = true:silent -csharp_style_expression_bodied_accessors = true:silent -csharp_style_expression_bodied_lambdas = true:silent -csharp_style_expression_bodied_local_functions = false:silent -csharp_indent_labels = one_less_than_current -csharp_style_throw_expression = true:suggestion -csharp_style_prefer_null_check_over_type_check = true:suggestion -csharp_prefer_simple_default_expression = true:suggestion -csharp_style_prefer_local_over_anonymous_function = true:suggestion -csharp_style_prefer_index_operator = true:suggestion -csharp_style_prefer_range_operator = true:suggestion -csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion -csharp_style_prefer_tuple_swap = true:suggestion -csharp_style_prefer_utf8_string_literals = true:suggestion -csharp_style_inlined_variable_declaration = true:suggestion -csharp_style_deconstructed_variable_declaration = true:suggestion -csharp_style_unused_value_assignment_preference = discard_variable:suggestion -csharp_style_unused_value_expression_statement_preference = discard_variable:silent diff --git a/BaGet.sln b/BaGet.sln index ce69b704..c2636445 100644 --- a/BaGet.sln +++ b/BaGet.sln @@ -25,7 +25,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BaGet.Aws", "src\BaGet.Aws\ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0B44364D-952B-497A-82E0-C9AAE94E0369}" ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig src\Directory.Build.props = src\Directory.Build.props nuget.config = nuget.config EndProjectSection diff --git a/samples/BaGet.Protocol.Samples.Tests/Sample02_Search.cs b/samples/BaGet.Protocol.Samples.Tests/Sample02_Search.cs index 2d3cc8ac..7edda390 100644 --- a/samples/BaGet.Protocol.Samples.Tests/Sample02_Search.cs +++ b/samples/BaGet.Protocol.Samples.Tests/Sample02_Search.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using BaGet.Protocol.Models; using NuGet.Versioning; using Xunit; diff --git a/samples/BaGet.Protocol.Samples.Tests/Sample03_Metadata.cs b/samples/BaGet.Protocol.Samples.Tests/Sample03_Metadata.cs index da959f4e..e1252fb6 100644 --- a/samples/BaGet.Protocol.Samples.Tests/Sample03_Metadata.cs +++ b/samples/BaGet.Protocol.Samples.Tests/Sample03_Metadata.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using BaGet.Protocol.Models; using NuGet.Versioning; using Xunit; diff --git a/samples/BaGetWebApplication/BaGetWebApplication.csproj b/samples/BaGetWebApplication/BaGetWebApplication.csproj index 605ea314..5cae439d 100644 --- a/samples/BaGetWebApplication/BaGetWebApplication.csproj +++ b/samples/BaGetWebApplication/BaGetWebApplication.csproj @@ -9,4 +9,8 @@ - + + + + + \ No newline at end of file diff --git a/samples/BaGetWebApplication/Program.cs b/samples/BaGetWebApplication/Program.cs index e58c6bcb..f82adb75 100644 --- a/samples/BaGetWebApplication/Program.cs +++ b/samples/BaGetWebApplication/Program.cs @@ -25,4 +25,4 @@ await app.RunMigrationsAsync(); -await app.RunAsync(); +await app.RunAsync(); \ No newline at end of file diff --git a/samples/BaGetWebApplication/appsettings.json b/samples/BaGetWebApplication/appsettings.json index 8ce15955..5f8f0873 100644 --- a/samples/BaGetWebApplication/appsettings.json +++ b/samples/BaGetWebApplication/appsettings.json @@ -1,7 +1,25 @@ { + "ApiKey": "b2a2c53f-7243-45ae-af13-991cb0bdfed1", + "PackageDeletionBehavior": "HardDelete", + "AllowPackageOverwrites": true, "Database": { + "Type": "Sqlite", "ConnectionString": "Data Source=baget.db" }, + "Storage": { + "Type": "FileSystem", + "Path": "C://inetpub//sites//nuget.brushtailtech.com" + }, + "Search": { + "Type": "Database" + }, + "Mirror": { + "Enabled": false, + + // Uncomment this to use the NuGet v2 protocol + //"Legacy": true, + "PackageSource": "https://api.nuget.org/v3/index.json" + }, "Logging": { "LogLevel": { diff --git a/src/BaGet.Aliyun/AliyunApplicationExtensions.cs b/src/BaGet.Aliyun/AliyunApplicationExtensions.cs index e14289cb..a49685b7 100644 --- a/src/BaGet.Aliyun/AliyunApplicationExtensions.cs +++ b/src/BaGet.Aliyun/AliyunApplicationExtensions.cs @@ -1,10 +1,3 @@ -using Aliyun.OSS; -using BaGet.Aliyun; -using BaGet.Core; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Options; - namespace BaGet; public static class AliyunApplicationExtensions @@ -33,9 +26,7 @@ public static BaGetApplication AddAliyunOssStorage(this BaGetApplication app) return app; } - public static BaGetApplication AddAliyunOssStorage( - this BaGetApplication app, - Action configure) + public static BaGetApplication AddAliyunOssStorage(this BaGetApplication app, Action configure) { app.AddAliyunOssStorage(); app.Services.Configure(configure); diff --git a/src/BaGet.Aliyun/AliyunStorageOptions.cs b/src/BaGet.Aliyun/AliyunStorageOptions.cs index 9dc86a15..ffaed580 100644 --- a/src/BaGet.Aliyun/AliyunStorageOptions.cs +++ b/src/BaGet.Aliyun/AliyunStorageOptions.cs @@ -1,5 +1,3 @@ -using System.ComponentModel.DataAnnotations; - namespace BaGet.Aliyun; public class AliyunStorageOptions diff --git a/src/BaGet.Aliyun/AliyunStorageService.cs b/src/BaGet.Aliyun/AliyunStorageService.cs index 3509fb27..4b7338ca 100644 --- a/src/BaGet.Aliyun/AliyunStorageService.cs +++ b/src/BaGet.Aliyun/AliyunStorageService.cs @@ -1,7 +1,3 @@ -using Aliyun.OSS; -using BaGet.Core; -using Microsoft.Extensions.Options; - namespace BaGet.Aliyun; public class AliyunStorageService : IStorageService diff --git a/src/BaGet.Aliyun/GlobalUsings.cs b/src/BaGet.Aliyun/GlobalUsings.cs new file mode 100644 index 00000000..4f1c6d30 --- /dev/null +++ b/src/BaGet.Aliyun/GlobalUsings.cs @@ -0,0 +1,9 @@ +// Global using directives + +global using System.ComponentModel.DataAnnotations; +global using Aliyun.OSS; +global using BaGet.Aliyun; +global using BaGet.Core; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Microsoft.Extensions.Options; \ No newline at end of file diff --git a/src/BaGet.Aws/AwsApplicationExtensions.cs b/src/BaGet.Aws/AwsApplicationExtensions.cs index 1d3e4fce..c671cdb6 100644 --- a/src/BaGet.Aws/AwsApplicationExtensions.cs +++ b/src/BaGet.Aws/AwsApplicationExtensions.cs @@ -1,12 +1,3 @@ -using Amazon; -using Amazon.Runtime; -using Amazon.S3; -using BaGet.Aws; -using BaGet.Core; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Options; - namespace BaGet; public static class AwsApplicationExtensions diff --git a/src/BaGet.Aws/GlobalUsings.cs b/src/BaGet.Aws/GlobalUsings.cs new file mode 100644 index 00000000..d1a3e82e --- /dev/null +++ b/src/BaGet.Aws/GlobalUsings.cs @@ -0,0 +1,12 @@ +// Global using directives + +global using System.ComponentModel.DataAnnotations; +global using Amazon; +global using Amazon.Runtime; +global using Amazon.S3; +global using Amazon.S3.Model; +global using BaGet.Aws; +global using BaGet.Core; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Microsoft.Extensions.Options; \ No newline at end of file diff --git a/src/BaGet.Aws/S3StorageOptions.cs b/src/BaGet.Aws/S3StorageOptions.cs index 96b16975..5cde1278 100644 --- a/src/BaGet.Aws/S3StorageOptions.cs +++ b/src/BaGet.Aws/S3StorageOptions.cs @@ -1,6 +1,3 @@ -using System.ComponentModel.DataAnnotations; -using BaGet.Core; - namespace BaGet.Aws; public class S3StorageOptions diff --git a/src/BaGet.Aws/S3StorageService.cs b/src/BaGet.Aws/S3StorageService.cs index a10cbadd..54b3d31a 100644 --- a/src/BaGet.Aws/S3StorageService.cs +++ b/src/BaGet.Aws/S3StorageService.cs @@ -1,8 +1,3 @@ -using Amazon.S3; -using Amazon.S3.Model; -using BaGet.Core; -using Microsoft.Extensions.Options; - namespace BaGet.Aws; public class S3StorageService : IStorageService diff --git a/src/BaGet.Azure/GlobalUsings.cs b/src/BaGet.Azure/GlobalUsings.cs index 253effb4..d548b77b 100644 --- a/src/BaGet.Azure/GlobalUsings.cs +++ b/src/BaGet.Azure/GlobalUsings.cs @@ -1,7 +1,6 @@ // Global using directives global using BaGet.Core; -global using BaGet.Protocol.Models; global using Microsoft.Azure.Cosmos.Table; global using Microsoft.Azure.Documents; global using Microsoft.Azure.Search; @@ -17,7 +16,8 @@ global using System.ComponentModel.DataAnnotations; global using System.Net; global using System.Text; -global using SearchResult = BaGet.Protocol.Models.SearchResult; +global using BaGet.Protocol; +global using SearchResult = BaGet.Protocol.SearchResult; global using TableStorageAccount = Microsoft.Azure.Cosmos.Table.CloudStorageAccount; global using StorageException = Microsoft.Azure.Cosmos.Table.StorageException; global using TableStorageException = Microsoft.Azure.Cosmos.Table.StorageException; diff --git a/src/BaGet.Core/Authentication/ApiKeyAuthenticationService.cs b/src/BaGet.Core/Authentication/ApiKeyAuthenticationService.cs index 3cae9099..668ec984 100644 --- a/src/BaGet.Core/Authentication/ApiKeyAuthenticationService.cs +++ b/src/BaGet.Core/Authentication/ApiKeyAuthenticationService.cs @@ -1,5 +1,3 @@ -using Microsoft.Extensions.Options; - namespace BaGet.Core; public class ApiKeyAuthenticationService : IAuthenticationService diff --git a/src/BaGet.Core/BaGet.Core.csproj.DotSettings b/src/BaGet.Core/BaGet.Core.csproj.DotSettings new file mode 100644 index 00000000..b7ab6a80 --- /dev/null +++ b/src/BaGet.Core/BaGet.Core.csproj.DotSettings @@ -0,0 +1,15 @@ + + True + True + True + True + True + True + True + True + True + True + True + True + True + True \ No newline at end of file diff --git a/src/BaGet.Core/BaGetApplication.cs b/src/BaGet.Core/BaGetApplication.cs index d3783bb9..95e84d39 100644 --- a/src/BaGet.Core/BaGetApplication.cs +++ b/src/BaGet.Core/BaGetApplication.cs @@ -1,5 +1,3 @@ -using Microsoft.Extensions.DependencyInjection; - namespace BaGet.Core; public class BaGetApplication diff --git a/src/BaGet.Core/Configuration/BindOptions.cs b/src/BaGet.Core/Configuration/BindOptions.cs index b16c8ebe..c66afe02 100644 --- a/src/BaGet.Core/Configuration/BindOptions.cs +++ b/src/BaGet.Core/Configuration/BindOptions.cs @@ -1,6 +1,3 @@ -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Options; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/Configuration/DatabaseOptions.cs b/src/BaGet.Core/Configuration/DatabaseOptions.cs index e25e8474..23d924c8 100644 --- a/src/BaGet.Core/Configuration/DatabaseOptions.cs +++ b/src/BaGet.Core/Configuration/DatabaseOptions.cs @@ -1,5 +1,3 @@ -using System.ComponentModel.DataAnnotations; - namespace BaGet.Core; public class DatabaseOptions diff --git a/src/BaGet.Core/Configuration/FileSystemStorageOptions.cs b/src/BaGet.Core/Configuration/FileSystemStorageOptions.cs index 9d5c2384..0286eba8 100644 --- a/src/BaGet.Core/Configuration/FileSystemStorageOptions.cs +++ b/src/BaGet.Core/Configuration/FileSystemStorageOptions.cs @@ -1,5 +1,3 @@ -using System.ComponentModel.DataAnnotations; - namespace BaGet.Core; public class FileSystemStorageOptions : IValidatableObject diff --git a/src/BaGet.Core/Configuration/MirrorOptions.cs b/src/BaGet.Core/Configuration/MirrorOptions.cs index c374d55a..825f68be 100644 --- a/src/BaGet.Core/Configuration/MirrorOptions.cs +++ b/src/BaGet.Core/Configuration/MirrorOptions.cs @@ -1,5 +1,3 @@ -using System.ComponentModel.DataAnnotations; - namespace BaGet.Core; public class MirrorOptions : IValidatableObject diff --git a/src/BaGet.Core/Content/DefaultPackageContentService.cs b/src/BaGet.Core/Content/DefaultPackageContentService.cs index de8603e7..098d284f 100644 --- a/src/BaGet.Core/Content/DefaultPackageContentService.cs +++ b/src/BaGet.Core/Content/DefaultPackageContentService.cs @@ -1,6 +1,3 @@ -using BaGet.Protocol.Models; -using NuGet.Versioning; - namespace BaGet.Core; /// @@ -11,17 +8,13 @@ public class DefaultPackageContentService : IPackageContentService private readonly IPackageService _packages; private readonly IPackageStorageService _storage; - public DefaultPackageContentService( - IPackageService packages, - IPackageStorageService storage) + public DefaultPackageContentService(IPackageService packages, IPackageStorageService storage) { _packages = packages ?? throw new ArgumentNullException(nameof(packages)); _storage = storage ?? throw new ArgumentNullException(nameof(storage)); } - public async Task GetPackageVersionsOrNullAsync( - string id, - CancellationToken cancellationToken = default) + public async Task GetPackageVersionsOrNullAsync(string id, CancellationToken cancellationToken = default) { var versions = await _packages.FindPackageVersionsAsync(id, cancellationToken); if (!versions.Any()) @@ -38,10 +31,7 @@ public async Task GetPackageVersionsOrNullAsync( }; } - public async Task GetPackageContentStreamOrNullAsync( - string id, - NuGetVersion version, - CancellationToken cancellationToken = default) + public async Task GetPackageContentStreamOrNullAsync(string id, NuGetVersion version, CancellationToken cancellationToken = default) { if (!await _packages.ExistsAsync(id, version, cancellationToken)) { diff --git a/src/BaGet.Core/Content/IPackageContentService.cs b/src/BaGet.Core/Content/IPackageContentService.cs index a5118700..c061bba3 100644 --- a/src/BaGet.Core/Content/IPackageContentService.cs +++ b/src/BaGet.Core/Content/IPackageContentService.cs @@ -1,6 +1,3 @@ -using BaGet.Protocol.Models; -using NuGet.Versioning; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/Entities/AbstractContext.cs b/src/BaGet.Core/Entities/AbstractContext.cs index 45e94e96..2fc5737c 100644 --- a/src/BaGet.Core/Entities/AbstractContext.cs +++ b/src/BaGet.Core/Entities/AbstractContext.cs @@ -1,6 +1,3 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - namespace BaGet.Core; public abstract class AbstractContext : DbContext, IContext where TContext : DbContext diff --git a/src/BaGet.Core/Entities/Converters/StringArrayComparer.cs b/src/BaGet.Core/Entities/Converters/StringArrayComparer.cs index 483767e8..a1d1473d 100644 --- a/src/BaGet.Core/Entities/Converters/StringArrayComparer.cs +++ b/src/BaGet.Core/Entities/Converters/StringArrayComparer.cs @@ -1,5 +1,3 @@ -using Microsoft.EntityFrameworkCore.ChangeTracking; - namespace BaGet.Core; public class StringArrayComparer : ValueComparer diff --git a/src/BaGet.Core/Entities/Converters/StringArrayToJsonConverter.cs b/src/BaGet.Core/Entities/Converters/StringArrayToJsonConverter.cs index bc95e409..82a40dd2 100644 --- a/src/BaGet.Core/Entities/Converters/StringArrayToJsonConverter.cs +++ b/src/BaGet.Core/Entities/Converters/StringArrayToJsonConverter.cs @@ -1,6 +1,3 @@ -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Newtonsoft.Json; - namespace BaGet.Core; public class StringArrayToJsonConverter : ValueConverter diff --git a/src/BaGet.Core/Entities/Converters/UriToStringConverter.cs b/src/BaGet.Core/Entities/Converters/UriToStringConverter.cs index 134e4ec9..122ca6e7 100644 --- a/src/BaGet.Core/Entities/Converters/UriToStringConverter.cs +++ b/src/BaGet.Core/Entities/Converters/UriToStringConverter.cs @@ -1,5 +1,3 @@ -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - namespace BaGet.Core; public class UriToStringConverter : ValueConverter diff --git a/src/BaGet.Core/Entities/IContext.cs b/src/BaGet.Core/Entities/IContext.cs index 92f3be0d..3907b772 100644 --- a/src/BaGet.Core/Entities/IContext.cs +++ b/src/BaGet.Core/Entities/IContext.cs @@ -1,6 +1,3 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; - namespace BaGet.Core; public interface IContext diff --git a/src/BaGet.Core/Entities/NullContext.cs b/src/BaGet.Core/Entities/NullContext.cs index 0e88d62b..ccada5f4 100644 --- a/src/BaGet.Core/Entities/NullContext.cs +++ b/src/BaGet.Core/Entities/NullContext.cs @@ -1,6 +1,3 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; - namespace BaGet.Core; public class NullContext : IContext diff --git a/src/BaGet.Core/Entities/Package.cs b/src/BaGet.Core/Entities/Package.cs index 0f886d80..6543e955 100644 --- a/src/BaGet.Core/Entities/Package.cs +++ b/src/BaGet.Core/Entities/Package.cs @@ -1,5 +1,3 @@ -using NuGet.Versioning; - namespace BaGet.Core; // See NuGetGallery's: https://github.com/NuGet/NuGetGallery/blob/master/src/NuGetGallery.Core/Entities/Package.cs diff --git a/src/BaGet.Core/Extensions/BaGetApplicationExtensions.cs b/src/BaGet.Core/Extensions/BaGetApplicationExtensions.cs index eed7017a..abc0abe8 100644 --- a/src/BaGet.Core/Extensions/BaGetApplicationExtensions.cs +++ b/src/BaGet.Core/Extensions/BaGetApplicationExtensions.cs @@ -1,7 +1,3 @@ -using BaGet.Core; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - namespace BaGet; public static class BaGetApplicationExtensions @@ -12,9 +8,7 @@ public static BaGetApplication AddFileStorage(this BaGetApplication app) return app; } - public static BaGetApplication AddFileStorage( - this BaGetApplication app, - Action configure) + public static BaGetApplication AddFileStorage(this BaGetApplication app, Action configure) { app.AddFileStorage(); app.Services.Configure(configure); diff --git a/src/BaGet.Core/Extensions/DependencyInjectionExtensions.Providers.cs b/src/BaGet.Core/Extensions/DependencyInjectionExtensions.Providers.cs index fc5a8fb2..f785ad3d 100644 --- a/src/BaGet.Core/Extensions/DependencyInjectionExtensions.Providers.cs +++ b/src/BaGet.Core/Extensions/DependencyInjectionExtensions.Providers.cs @@ -1,8 +1,3 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - namespace BaGet.Core; public static partial class DependencyInjectionExtensions diff --git a/src/BaGet.Core/Extensions/DependencyInjectionExtensions.cs b/src/BaGet.Core/Extensions/DependencyInjectionExtensions.cs index 56780e0b..a5c89888 100644 --- a/src/BaGet.Core/Extensions/DependencyInjectionExtensions.cs +++ b/src/BaGet.Core/Extensions/DependencyInjectionExtensions.cs @@ -1,18 +1,8 @@ -using System.Net; -using System.Reflection; -using BaGet.Protocol; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Options; - namespace BaGet.Core; public static partial class DependencyInjectionExtensions { - public static IServiceCollection AddBaGetApplication( - this IServiceCollection services, - Action configureAction) + public static IServiceCollection AddBaGetApplication(this IServiceCollection services, Action configureAction) { var app = new BaGetApplication(services); @@ -37,10 +27,7 @@ public static IServiceCollection AddBaGetApplication( /// If null, the root configuration will be used to configure the options. /// /// The dependency injection container. - public static IServiceCollection AddBaGetOptions( - this IServiceCollection services, - string key = null) - where TOptions : class + public static IServiceCollection AddBaGetOptions(this IServiceCollection services, string key = null) where TOptions : class { services.AddSingleton>(new ValidateBaGetOptions(key)); services.AddSingleton>(provider => diff --git a/src/BaGet.Core/Extensions/IProvider.cs b/src/BaGet.Core/Extensions/IProvider.cs index a2e4dc7e..a2fc451a 100644 --- a/src/BaGet.Core/Extensions/IProvider.cs +++ b/src/BaGet.Core/Extensions/IProvider.cs @@ -1,5 +1,3 @@ -using Microsoft.Extensions.Configuration; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/Extensions/PackageArchiveReaderExtensions.cs b/src/BaGet.Core/Extensions/PackageArchiveReaderExtensions.cs index eced52e6..fc43dec4 100644 --- a/src/BaGet.Core/Extensions/PackageArchiveReaderExtensions.cs +++ b/src/BaGet.Core/Extensions/PackageArchiveReaderExtensions.cs @@ -1,6 +1,3 @@ -using NuGet.Common; -using NuGet.Packaging; - namespace BaGet.Core; using NuGetPackageType = NuGet.Packaging.Core.PackageType; @@ -13,9 +10,7 @@ public static bool HasReadme(this PackageArchiveReader package) public static bool HasEmbeddedIcon(this PackageArchiveReader package) => !string.IsNullOrEmpty(package.NuspecReader.GetIcon()); - public async static Task GetReadmeAsync( - this PackageArchiveReader package, - CancellationToken cancellationToken) + public async static Task GetReadmeAsync(this PackageArchiveReader package, CancellationToken cancellationToken) { var readmePath = package.NuspecReader.GetReadme(); if (readmePath == null) @@ -26,9 +21,7 @@ public async static Task GetReadmeAsync( return await package.GetStreamAsync(readmePath, cancellationToken); } - public async static Task GetIconAsync( - this PackageArchiveReader package, - CancellationToken cancellationToken) + public async static Task GetIconAsync(this PackageArchiveReader package, CancellationToken cancellationToken) { return await package.GetStreamAsync( PathUtility.StripLeadingDirectorySeparators(package.NuspecReader.GetIcon()), diff --git a/src/BaGet.Core/Extensions/StreamExtensions.cs b/src/BaGet.Core/Extensions/StreamExtensions.cs index f2410235..898f6337 100644 --- a/src/BaGet.Core/Extensions/StreamExtensions.cs +++ b/src/BaGet.Core/Extensions/StreamExtensions.cs @@ -1,5 +1,3 @@ -using System.Security.Cryptography; - namespace BaGet.Core; public static class StreamExtensions @@ -14,9 +12,7 @@ public static class StreamExtensions /// 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) + public static async Task AsTemporaryFileStreamAsync(this Stream original, CancellationToken cancellationToken = default) { var result = new FileStream( Path.GetTempFileName(), diff --git a/src/BaGet.Core/GlobalUsings.cs b/src/BaGet.Core/GlobalUsings.cs new file mode 100644 index 00000000..5f1dba8b --- /dev/null +++ b/src/BaGet.Core/GlobalUsings.cs @@ -0,0 +1,32 @@ +// Global using directives + +global using System.Collections.Concurrent; +global using System.ComponentModel.DataAnnotations; +global using System.Globalization; +global using System.Net; +global using System.Reflection; +global using System.Reflection.Metadata; +global using System.Security.Cryptography; +global using System.Text.Json.Serialization; +global using BaGet.Core; +global using BaGet.Protocol; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.EntityFrameworkCore.ChangeTracking; +global using Microsoft.EntityFrameworkCore.Infrastructure; +global using Microsoft.EntityFrameworkCore.Metadata.Builders; +global using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.Options; +global using Newtonsoft.Json; +global using Newtonsoft.Json.Linq; +global using NuGet.Common; +global using NuGet.Configuration; +global using NuGet.Frameworks; +global using NuGet.Packaging; +global using NuGet.Protocol; +global using NuGet.Protocol.Core.Types; +global using NuGet.Versioning; +global using static NuGet.Frameworks.FrameworkConstants; \ No newline at end of file diff --git a/src/BaGet.Core/IPackageDatabase.cs b/src/BaGet.Core/IPackageDatabase.cs index 204793ae..88c3aaed 100644 --- a/src/BaGet.Core/IPackageDatabase.cs +++ b/src/BaGet.Core/IPackageDatabase.cs @@ -1,5 +1,3 @@ -using NuGet.Versioning; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/IPackageService.cs b/src/BaGet.Core/IPackageService.cs index 6e324695..875983b1 100644 --- a/src/BaGet.Core/IPackageService.cs +++ b/src/BaGet.Core/IPackageService.cs @@ -1,5 +1,3 @@ -using NuGet.Versioning; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/IUrlGenerator.cs b/src/BaGet.Core/IUrlGenerator.cs index 44b61538..03ad5021 100644 --- a/src/BaGet.Core/IUrlGenerator.cs +++ b/src/BaGet.Core/IUrlGenerator.cs @@ -1,5 +1,3 @@ -using NuGet.Versioning; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/Indexing/FrameworkCompatibilityService.cs b/src/BaGet.Core/Indexing/FrameworkCompatibilityService.cs index d15b514f..d5670ed7 100644 --- a/src/BaGet.Core/Indexing/FrameworkCompatibilityService.cs +++ b/src/BaGet.Core/Indexing/FrameworkCompatibilityService.cs @@ -1,10 +1,5 @@ -using System.Collections.Concurrent; -using NuGet.Frameworks; - namespace BaGet.Core; -using static NuGet.Frameworks.FrameworkConstants; - public class FrameworkCompatibilityService : IFrameworkCompatibilityService { private const string AnyFramework = "any"; @@ -15,10 +10,12 @@ public class FrameworkCompatibilityService : IFrameworkCompatibilityService static FrameworkCompatibilityService() { - var supportedFrameworks = new HashSet(); - supportedFrameworks.Add(FrameworkIdentifiers.NetStandard); - supportedFrameworks.Add(FrameworkIdentifiers.NetCoreApp); - supportedFrameworks.Add(FrameworkIdentifiers.Net); + var supportedFrameworks = new HashSet + { + FrameworkIdentifiers.NetStandard, + FrameworkIdentifiers.NetCoreApp, + FrameworkIdentifiers.Net + }; CompatibilityMapping = DefaultFrameworkMappings.Instance.CompatibilityMappings.ToList(); CompatibleFrameworks = new ConcurrentDictionary>(); diff --git a/src/BaGet.Core/Indexing/IPackageDeletionService.cs b/src/BaGet.Core/Indexing/IPackageDeletionService.cs index 0bb6c168..c62a35d9 100644 --- a/src/BaGet.Core/Indexing/IPackageDeletionService.cs +++ b/src/BaGet.Core/Indexing/IPackageDeletionService.cs @@ -1,5 +1,3 @@ -using NuGet.Versioning; - namespace BaGet.Core; public interface IPackageDeletionService diff --git a/src/BaGet.Core/Indexing/PackageDeletionService.cs b/src/BaGet.Core/Indexing/PackageDeletionService.cs index 5ceea9d7..693fe3db 100644 --- a/src/BaGet.Core/Indexing/PackageDeletionService.cs +++ b/src/BaGet.Core/Indexing/PackageDeletionService.cs @@ -1,7 +1,3 @@ -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using NuGet.Versioning; - namespace BaGet.Core; public class PackageDeletionService : IPackageDeletionService diff --git a/src/BaGet.Core/Indexing/PackageIndexingService.cs b/src/BaGet.Core/Indexing/PackageIndexingService.cs index 96b76d6b..17f45dad 100644 --- a/src/BaGet.Core/Indexing/PackageIndexingService.cs +++ b/src/BaGet.Core/Indexing/PackageIndexingService.cs @@ -1,7 +1,3 @@ -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using NuGet.Packaging; - namespace BaGet.Core; public class PackageIndexingService : IPackageIndexingService diff --git a/src/BaGet.Core/Indexing/SymbolIndexingService.cs b/src/BaGet.Core/Indexing/SymbolIndexingService.cs index ff115d5b..89e04401 100644 --- a/src/BaGet.Core/Indexing/SymbolIndexingService.cs +++ b/src/BaGet.Core/Indexing/SymbolIndexingService.cs @@ -1,7 +1,3 @@ -using System.Reflection.Metadata; -using Microsoft.Extensions.Logging; -using NuGet.Packaging; - namespace BaGet.Core; // Based off: https://github.com/NuGet/NuGetGallery/blob/master/src/NuGetGallery/Services/SymbolPackageUploadService.cs @@ -22,10 +18,7 @@ public class SymbolIndexingService : ISymbolIndexingService private readonly ISymbolStorageService _storage; private readonly ILogger _logger; - public SymbolIndexingService( - IPackageDatabase packages, - ISymbolStorageService storage, - ILogger logger) + public SymbolIndexingService(IPackageDatabase packages, ISymbolStorageService storage, ILogger logger) { _packages = packages ?? throw new ArgumentNullException(nameof(packages)); _storage = storage ?? throw new ArgumentNullException(nameof(storage)); @@ -86,9 +79,7 @@ public async Task IndexAsync(Stream stream, CancellationTo } } - private async Task> GetSymbolPackagePdbPathsOrNullAsync( - PackageArchiveReader symbolPackage, - CancellationToken cancellationToken) + private async Task> GetSymbolPackagePdbPathsOrNullAsync(PackageArchiveReader symbolPackage, CancellationToken cancellationToken) { try { @@ -127,10 +118,7 @@ bool IsValidSymbolFileInfo(FileInfo file) return entries.Select(e => new FileInfo(e)).All(IsValidSymbolFileInfo); } - private async Task ExtractPortablePdbAsync( - PackageArchiveReader symbolPackage, - string pdbPath, - CancellationToken cancellationToken) + private async Task ExtractPortablePdbAsync(PackageArchiveReader symbolPackage, string pdbPath, CancellationToken cancellationToken) { // TODO: Validate that the PDB has a corresponding DLL // See: https://github.com/NuGet/NuGet.Jobs/blob/master/src/Validation.Symbols/SymbolsValidatorService.cs#L170 diff --git a/src/BaGet.Core/Metadata/BaGetPackageMetadata.cs b/src/BaGet.Core/Metadata/BaGetPackageMetadata.cs index 01d29166..6dfa48dc 100644 --- a/src/BaGet.Core/Metadata/BaGetPackageMetadata.cs +++ b/src/BaGet.Core/Metadata/BaGetPackageMetadata.cs @@ -1,6 +1,3 @@ -using System.Text.Json.Serialization; -using BaGet.Protocol.Models; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/Metadata/BaGetRegistrationIndexPage.cs b/src/BaGet.Core/Metadata/BaGetRegistrationIndexPage.cs index 95137faf..51092455 100644 --- a/src/BaGet.Core/Metadata/BaGetRegistrationIndexPage.cs +++ b/src/BaGet.Core/Metadata/BaGetRegistrationIndexPage.cs @@ -1,6 +1,3 @@ -using System.Text.Json.Serialization; -using BaGet.Protocol.Models; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/Metadata/BaGetRegistrationIndexPageItem.cs b/src/BaGet.Core/Metadata/BaGetRegistrationIndexPageItem.cs index 19da43eb..263d97b4 100644 --- a/src/BaGet.Core/Metadata/BaGetRegistrationIndexPageItem.cs +++ b/src/BaGet.Core/Metadata/BaGetRegistrationIndexPageItem.cs @@ -1,6 +1,3 @@ -using System.Text.Json.Serialization; -using BaGet.Protocol.Models; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/Metadata/BaGetRegistrationIndexResponse.cs b/src/BaGet.Core/Metadata/BaGetRegistrationIndexResponse.cs index 6153943c..a92a83dc 100644 --- a/src/BaGet.Core/Metadata/BaGetRegistrationIndexResponse.cs +++ b/src/BaGet.Core/Metadata/BaGetRegistrationIndexResponse.cs @@ -1,6 +1,3 @@ -using System.Text.Json.Serialization; -using BaGet.Protocol.Models; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/Metadata/DefaultPackageMetadataService.cs b/src/BaGet.Core/Metadata/DefaultPackageMetadataService.cs index 2dbeb35d..1934079a 100644 --- a/src/BaGet.Core/Metadata/DefaultPackageMetadataService.cs +++ b/src/BaGet.Core/Metadata/DefaultPackageMetadataService.cs @@ -1,6 +1,3 @@ -using BaGet.Protocol.Models; -using NuGet.Versioning; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/Metadata/IPackageMetadataService.cs b/src/BaGet.Core/Metadata/IPackageMetadataService.cs index 8a6f6fd3..f9998d28 100644 --- a/src/BaGet.Core/Metadata/IPackageMetadataService.cs +++ b/src/BaGet.Core/Metadata/IPackageMetadataService.cs @@ -1,6 +1,3 @@ -using BaGet.Protocol.Models; -using NuGet.Versioning; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/Metadata/RegistrationBuilder.cs b/src/BaGet.Core/Metadata/RegistrationBuilder.cs index 02ee0705..654ee352 100644 --- a/src/BaGet.Core/Metadata/RegistrationBuilder.cs +++ b/src/BaGet.Core/Metadata/RegistrationBuilder.cs @@ -1,5 +1,3 @@ -using BaGet.Protocol.Models; - namespace BaGet.Core; public class RegistrationBuilder diff --git a/src/BaGet.Core/PackageDatabase.cs b/src/BaGet.Core/PackageDatabase.cs index e052ce92..12736031 100644 --- a/src/BaGet.Core/PackageDatabase.cs +++ b/src/BaGet.Core/PackageDatabase.cs @@ -1,6 +1,3 @@ -using Microsoft.EntityFrameworkCore; -using NuGet.Versioning; - namespace BaGet.Core; public class PackageDatabase : IPackageDatabase diff --git a/src/BaGet.Core/PackageService.cs b/src/BaGet.Core/PackageService.cs index 22575c15..960a8a2e 100644 --- a/src/BaGet.Core/PackageService.cs +++ b/src/BaGet.Core/PackageService.cs @@ -1,6 +1,3 @@ -using Microsoft.Extensions.Logging; -using NuGet.Versioning; - namespace BaGet.Core; public class PackageService : IPackageService diff --git a/src/BaGet.Core/Search/DatabaseSearchService.cs b/src/BaGet.Core/Search/DatabaseSearchService.cs index a3d44be7..62830307 100644 --- a/src/BaGet.Core/Search/DatabaseSearchService.cs +++ b/src/BaGet.Core/Search/DatabaseSearchService.cs @@ -1,6 +1,3 @@ -using BaGet.Protocol.Models; -using Microsoft.EntityFrameworkCore; - namespace BaGet.Core; public class DatabaseSearchService : ISearchService diff --git a/src/BaGet.Core/Search/DependentsResponse.cs b/src/BaGet.Core/Search/DependentsResponse.cs index ca4f519c..d7617ddd 100644 --- a/src/BaGet.Core/Search/DependentsResponse.cs +++ b/src/BaGet.Core/Search/DependentsResponse.cs @@ -1,5 +1,3 @@ -using System.Text.Json.Serialization; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/Search/ISearchResponseBuilder.cs b/src/BaGet.Core/Search/ISearchResponseBuilder.cs index 84d793a4..3bbc5a03 100644 --- a/src/BaGet.Core/Search/ISearchResponseBuilder.cs +++ b/src/BaGet.Core/Search/ISearchResponseBuilder.cs @@ -1,6 +1,4 @@ -using BaGet.Protocol.Models; - -namespace BaGet.Core; +namespace BaGet.Core; public interface ISearchResponseBuilder { diff --git a/src/BaGet.Core/Search/ISearchService.cs b/src/BaGet.Core/Search/ISearchService.cs index cd8ff6c8..4242e8f5 100644 --- a/src/BaGet.Core/Search/ISearchService.cs +++ b/src/BaGet.Core/Search/ISearchService.cs @@ -1,5 +1,3 @@ -using BaGet.Protocol.Models; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/Search/NullSearchService.cs b/src/BaGet.Core/Search/NullSearchService.cs index b210b30e..41f8af35 100644 --- a/src/BaGet.Core/Search/NullSearchService.cs +++ b/src/BaGet.Core/Search/NullSearchService.cs @@ -1,5 +1,3 @@ -using BaGet.Protocol.Models; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/Search/SearchResponseBuilder.cs b/src/BaGet.Core/Search/SearchResponseBuilder.cs index bee6265b..aa16578f 100644 --- a/src/BaGet.Core/Search/SearchResponseBuilder.cs +++ b/src/BaGet.Core/Search/SearchResponseBuilder.cs @@ -1,6 +1,4 @@ -using BaGet.Protocol.Models; - -namespace BaGet.Core; +namespace BaGet.Core; public class SearchResponseBuilder : ISearchResponseBuilder { diff --git a/src/BaGet.Core/ServiceIndex/BaGetServiceIndex.cs b/src/BaGet.Core/ServiceIndex/BaGetServiceIndex.cs index 17b69b6a..d92b5d4c 100644 --- a/src/BaGet.Core/ServiceIndex/BaGetServiceIndex.cs +++ b/src/BaGet.Core/ServiceIndex/BaGetServiceIndex.cs @@ -1,5 +1,3 @@ -using BaGet.Protocol.Models; - namespace BaGet.Core; public class BaGetServiceIndex : IServiceIndexService diff --git a/src/BaGet.Core/ServiceIndex/IServiceIndexService.cs b/src/BaGet.Core/ServiceIndex/IServiceIndexService.cs index b47ce3cf..15e2533a 100644 --- a/src/BaGet.Core/ServiceIndex/IServiceIndexService.cs +++ b/src/BaGet.Core/ServiceIndex/IServiceIndexService.cs @@ -1,5 +1,3 @@ -using BaGet.Protocol.Models; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/Storage/FileStorageService.cs b/src/BaGet.Core/Storage/FileStorageService.cs index a63302d8..6dfe47a7 100644 --- a/src/BaGet.Core/Storage/FileStorageService.cs +++ b/src/BaGet.Core/Storage/FileStorageService.cs @@ -1,5 +1,3 @@ -using Microsoft.Extensions.Options; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/Storage/IPackageStorageService.cs b/src/BaGet.Core/Storage/IPackageStorageService.cs index d8e19e37..8b7f2287 100644 --- a/src/BaGet.Core/Storage/IPackageStorageService.cs +++ b/src/BaGet.Core/Storage/IPackageStorageService.cs @@ -1,5 +1,3 @@ -using NuGet.Versioning; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/Storage/PackageStorageService.cs b/src/BaGet.Core/Storage/PackageStorageService.cs index 55b1be7f..0993db72 100644 --- a/src/BaGet.Core/Storage/PackageStorageService.cs +++ b/src/BaGet.Core/Storage/PackageStorageService.cs @@ -1,6 +1,3 @@ -using Microsoft.Extensions.Logging; -using NuGet.Versioning; - namespace BaGet.Core; public class PackageStorageService : IPackageStorageService @@ -16,9 +13,7 @@ public class PackageStorageService : IPackageStorageService private readonly IStorageService _storage; private readonly ILogger _logger; - public PackageStorageService( - IStorageService storage, - ILogger logger) + public PackageStorageService(IStorageService storage, ILogger logger) { _storage = storage ?? throw new ArgumentNullException(nameof(storage)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); diff --git a/src/BaGet.Core/Upstream/Clients/DisabledUpstreamClient.cs b/src/BaGet.Core/Upstream/Clients/DisabledUpstreamClient.cs index 7e0d04f3..eee2b9a7 100644 --- a/src/BaGet.Core/Upstream/Clients/DisabledUpstreamClient.cs +++ b/src/BaGet.Core/Upstream/Clients/DisabledUpstreamClient.cs @@ -1,6 +1,4 @@ -using NuGet.Versioning; - -namespace BaGet.Core; +namespace BaGet.Core; /// /// The client used when there are no upstream package sources. diff --git a/src/BaGet.Core/Upstream/Clients/V2UpstreamClient.cs b/src/BaGet.Core/Upstream/Clients/V2UpstreamClient.cs index 5d40a5e6..4c618d42 100644 --- a/src/BaGet.Core/Upstream/Clients/V2UpstreamClient.cs +++ b/src/BaGet.Core/Upstream/Clients/V2UpstreamClient.cs @@ -1,12 +1,3 @@ -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using NuGet.Common; -using NuGet.Configuration; -using NuGet.Packaging; -using NuGet.Protocol; -using NuGet.Protocol.Core.Types; -using NuGet.Versioning; - namespace BaGet.Core; using ILogger = Microsoft.Extensions.Logging.ILogger; @@ -22,9 +13,7 @@ public class V2UpstreamClient : IUpstreamClient, IDisposable private readonly INuGetLogger _ngLogger; private readonly ILogger _logger; - public V2UpstreamClient( - IOptionsSnapshot options, - ILogger logger) + public V2UpstreamClient(IOptionsSnapshot options, ILogger logger) { if (options is null) { diff --git a/src/BaGet.Core/Upstream/Clients/V3UpstreamClient.cs b/src/BaGet.Core/Upstream/Clients/V3UpstreamClient.cs index c8b7480d..d45421a5 100644 --- a/src/BaGet.Core/Upstream/Clients/V3UpstreamClient.cs +++ b/src/BaGet.Core/Upstream/Clients/V3UpstreamClient.cs @@ -1,8 +1,3 @@ -using BaGet.Protocol; -using BaGet.Protocol.Models; -using NuGet.Versioning; -using Microsoft.Extensions.Logging; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/Upstream/DownloadsImporter.cs b/src/BaGet.Core/Upstream/DownloadsImporter.cs index 837dbbfe..51286358 100644 --- a/src/BaGet.Core/Upstream/DownloadsImporter.cs +++ b/src/BaGet.Core/Upstream/DownloadsImporter.cs @@ -1,6 +1,3 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; - namespace BaGet.Core; public class DownloadsImporter diff --git a/src/BaGet.Core/Upstream/IUpstreamClient.cs b/src/BaGet.Core/Upstream/IUpstreamClient.cs index 93a01dfb..4afd933a 100644 --- a/src/BaGet.Core/Upstream/IUpstreamClient.cs +++ b/src/BaGet.Core/Upstream/IUpstreamClient.cs @@ -1,6 +1,4 @@ -using NuGet.Versioning; - -namespace BaGet.Core; +namespace BaGet.Core; /// /// A client to interact with an upstream package source. diff --git a/src/BaGet.Core/Upstream/PackageDownloadsJsonSource.cs b/src/BaGet.Core/Upstream/PackageDownloadsJsonSource.cs index d075f060..500d20ca 100644 --- a/src/BaGet.Core/Upstream/PackageDownloadsJsonSource.cs +++ b/src/BaGet.Core/Upstream/PackageDownloadsJsonSource.cs @@ -1,8 +1,3 @@ -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using NuGet.Versioning; - namespace BaGet.Core; // See https://github.com/NuGet/NuGet.Services.Metadata/blob/master/src/NuGet.Indexing/Downloads.cs diff --git a/src/BaGet.Core/Validation/RequiredIfAttribute.cs b/src/BaGet.Core/Validation/RequiredIfAttribute.cs index 6ab44aee..9bd2543a 100644 --- a/src/BaGet.Core/Validation/RequiredIfAttribute.cs +++ b/src/BaGet.Core/Validation/RequiredIfAttribute.cs @@ -1,6 +1,3 @@ -using System.ComponentModel.DataAnnotations; -using System.Globalization; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/Validation/ValidateBaGetOptions.cs b/src/BaGet.Core/Validation/ValidateBaGetOptions.cs index bfde8dc7..f12a41a6 100644 --- a/src/BaGet.Core/Validation/ValidateBaGetOptions.cs +++ b/src/BaGet.Core/Validation/ValidateBaGetOptions.cs @@ -1,6 +1,3 @@ -using System.ComponentModel.DataAnnotations; -using Microsoft.Extensions.Options; - namespace BaGet.Core; /// diff --git a/src/BaGet.Core/Validation/ValidateStartupOptions.cs b/src/BaGet.Core/Validation/ValidateStartupOptions.cs index 5ad54df7..b8c4f569 100644 --- a/src/BaGet.Core/Validation/ValidateStartupOptions.cs +++ b/src/BaGet.Core/Validation/ValidateStartupOptions.cs @@ -1,6 +1,3 @@ -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - namespace BaGet.Core; /// diff --git a/src/BaGet.Database.MySql/GlobalUsings.cs b/src/BaGet.Database.MySql/GlobalUsings.cs new file mode 100644 index 00000000..d690c62d --- /dev/null +++ b/src/BaGet.Database.MySql/GlobalUsings.cs @@ -0,0 +1,10 @@ +// Global using directives + +global using BaGet.Core; +global using BaGet.Database.MySql; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.EntityFrameworkCore.Metadata; +global using Microsoft.EntityFrameworkCore.Migrations; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Options; +global using MySqlConnector; \ No newline at end of file diff --git a/src/BaGet.Database.MySql/Migrations/20181212113156_Initial.cs b/src/BaGet.Database.MySql/Migrations/20181212113156_Initial.cs index db339aa4..184785f4 100644 --- a/src/BaGet.Database.MySql/Migrations/20181212113156_Initial.cs +++ b/src/BaGet.Database.MySql/Migrations/20181212113156_Initial.cs @@ -1,6 +1,3 @@ -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - namespace BaGet.Database.MySql.Migrations { public partial class Initial : Migration diff --git a/src/BaGet.Database.MySql/Migrations/20190303071640_AddSearchDimensions.cs b/src/BaGet.Database.MySql/Migrations/20190303071640_AddSearchDimensions.cs index f727bc9a..758f9da0 100644 --- a/src/BaGet.Database.MySql/Migrations/20190303071640_AddSearchDimensions.cs +++ b/src/BaGet.Database.MySql/Migrations/20190303071640_AddSearchDimensions.cs @@ -1,6 +1,3 @@ -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - namespace BaGet.Database.MySql.Migrations { public partial class AddSearchDimensions : Migration diff --git a/src/BaGet.Database.MySql/Migrations/20190914215810_AddOriginalVersionStringColumn.cs b/src/BaGet.Database.MySql/Migrations/20190914215810_AddOriginalVersionStringColumn.cs index 7334fd0b..df4dd9c0 100644 --- a/src/BaGet.Database.MySql/Migrations/20190914215810_AddOriginalVersionStringColumn.cs +++ b/src/BaGet.Database.MySql/Migrations/20190914215810_AddOriginalVersionStringColumn.cs @@ -1,6 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace BaGet.Database.MySql.Migrations +namespace BaGet.Database.MySql.Migrations { public partial class AddOriginalVersionStringColumn : Migration { diff --git a/src/BaGet.Database.MySql/Migrations/20200109085155_AddReleaseNotesStringColumn.cs b/src/BaGet.Database.MySql/Migrations/20200109085155_AddReleaseNotesStringColumn.cs index 6f7b57da..5815af9c 100644 --- a/src/BaGet.Database.MySql/Migrations/20200109085155_AddReleaseNotesStringColumn.cs +++ b/src/BaGet.Database.MySql/Migrations/20200109085155_AddReleaseNotesStringColumn.cs @@ -1,6 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace BaGet.Database.MySql.Migrations +namespace BaGet.Database.MySql.Migrations { public partial class AddReleaseNotesStringColumn : Migration { diff --git a/src/BaGet.Database.MySql/Migrations/20200210004047_AddHasEmbeddedIconColumn.cs b/src/BaGet.Database.MySql/Migrations/20200210004047_AddHasEmbeddedIconColumn.cs index f3a31d9f..7a8a01f3 100644 --- a/src/BaGet.Database.MySql/Migrations/20200210004047_AddHasEmbeddedIconColumn.cs +++ b/src/BaGet.Database.MySql/Migrations/20200210004047_AddHasEmbeddedIconColumn.cs @@ -1,7 +1,4 @@ -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace BaGet.Database.MySql.Migrations +namespace BaGet.Database.MySql.Migrations { public partial class AddHasEmbeddedIconColumn : Migration { diff --git a/src/BaGet.Database.MySql/Migrations/20210919191554_RemoveReleaseNotesMaxLength.cs b/src/BaGet.Database.MySql/Migrations/20210919191554_RemoveReleaseNotesMaxLength.cs index 34e62b1a..5a1265a1 100644 --- a/src/BaGet.Database.MySql/Migrations/20210919191554_RemoveReleaseNotesMaxLength.cs +++ b/src/BaGet.Database.MySql/Migrations/20210919191554_RemoveReleaseNotesMaxLength.cs @@ -1,7 +1,4 @@ -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace BaGet.Database.MySql.Migrations +namespace BaGet.Database.MySql.Migrations { public partial class RemoveReleaseNotesMaxLength : Migration { diff --git a/src/BaGet.Database.MySql/MySqlApplicationExtensions.cs b/src/BaGet.Database.MySql/MySqlApplicationExtensions.cs index 9a60bfd1..276aa592 100644 --- a/src/BaGet.Database.MySql/MySqlApplicationExtensions.cs +++ b/src/BaGet.Database.MySql/MySqlApplicationExtensions.cs @@ -1,9 +1,3 @@ -using BaGet.Core; -using BaGet.Database.MySql; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - namespace BaGet; public static class MySqlApplicationExtensions diff --git a/src/BaGet.Database.MySql/MySqlContext.cs b/src/BaGet.Database.MySql/MySqlContext.cs index 0895d062..07484355 100644 --- a/src/BaGet.Database.MySql/MySqlContext.cs +++ b/src/BaGet.Database.MySql/MySqlContext.cs @@ -1,7 +1,3 @@ -using BaGet.Core; -using Microsoft.EntityFrameworkCore; -using MySqlConnector; - namespace BaGet.Database.MySql; public class MySqlContext : AbstractContext diff --git a/src/BaGet.Database.PostgreSql/GlobalUsings.cs b/src/BaGet.Database.PostgreSql/GlobalUsings.cs new file mode 100644 index 00000000..ff795146 --- /dev/null +++ b/src/BaGet.Database.PostgreSql/GlobalUsings.cs @@ -0,0 +1,10 @@ +// Global using directives + +global using BaGet.Core; +global using BaGet.Database.PostgreSql; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.EntityFrameworkCore.Migrations; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Options; +global using Npgsql; +global using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; \ No newline at end of file diff --git a/src/BaGet.Database.PostgreSql/Migrations/20190311172227_Initial.cs b/src/BaGet.Database.PostgreSql/Migrations/20190311172227_Initial.cs index f308e0e8..181e3a5e 100644 --- a/src/BaGet.Database.PostgreSql/Migrations/20190311172227_Initial.cs +++ b/src/BaGet.Database.PostgreSql/Migrations/20190311172227_Initial.cs @@ -1,7 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -namespace BaGet.Database.PostgreSql.Migrations +namespace BaGet.Database.PostgreSql.Migrations { public partial class Initial : Migration { diff --git a/src/BaGet.Database.PostgreSql/Migrations/20190914220105_AddOriginalVersionStringColumn.cs b/src/BaGet.Database.PostgreSql/Migrations/20190914220105_AddOriginalVersionStringColumn.cs index 11431fdb..8eb3d75c 100644 --- a/src/BaGet.Database.PostgreSql/Migrations/20190914220105_AddOriginalVersionStringColumn.cs +++ b/src/BaGet.Database.PostgreSql/Migrations/20190914220105_AddOriginalVersionStringColumn.cs @@ -1,6 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace BaGet.Database.PostgreSql.Migrations +namespace BaGet.Database.PostgreSql.Migrations { public partial class AddOriginalVersionStringColumn : Migration { diff --git a/src/BaGet.Database.PostgreSql/Migrations/20200109085355_AddReleaseNotesStringColumn.cs b/src/BaGet.Database.PostgreSql/Migrations/20200109085355_AddReleaseNotesStringColumn.cs index 45cd0eac..06b1f825 100644 --- a/src/BaGet.Database.PostgreSql/Migrations/20200109085355_AddReleaseNotesStringColumn.cs +++ b/src/BaGet.Database.PostgreSql/Migrations/20200109085355_AddReleaseNotesStringColumn.cs @@ -1,6 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace BaGet.Database.PostgreSql.Migrations +namespace BaGet.Database.PostgreSql.Migrations { public partial class AddReleaseNotesStringColumn : Migration { diff --git a/src/BaGet.Database.PostgreSql/Migrations/20200208053115_FixVersionCaseSensitivity.cs b/src/BaGet.Database.PostgreSql/Migrations/20200208053115_FixVersionCaseSensitivity.cs index 148b14d3..30c38f88 100644 --- a/src/BaGet.Database.PostgreSql/Migrations/20200208053115_FixVersionCaseSensitivity.cs +++ b/src/BaGet.Database.PostgreSql/Migrations/20200208053115_FixVersionCaseSensitivity.cs @@ -1,6 +1,3 @@ -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - namespace BaGet.Database.PostgreSql.Migrations { public partial class FixVersionCaseSensitivity : Migration diff --git a/src/BaGet.Database.PostgreSql/Migrations/20200210004256_AddHasEmbeddedIconColumn.cs b/src/BaGet.Database.PostgreSql/Migrations/20200210004256_AddHasEmbeddedIconColumn.cs index 704cc1ce..4984c362 100644 --- a/src/BaGet.Database.PostgreSql/Migrations/20200210004256_AddHasEmbeddedIconColumn.cs +++ b/src/BaGet.Database.PostgreSql/Migrations/20200210004256_AddHasEmbeddedIconColumn.cs @@ -1,6 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace BaGet.Database.PostgreSql.Migrations +namespace BaGet.Database.PostgreSql.Migrations { public partial class AddHasEmbeddedIconColumn : Migration { diff --git a/src/BaGet.Database.PostgreSql/Migrations/20210919191649_RemoveReleaseNotesMaxLength.cs b/src/BaGet.Database.PostgreSql/Migrations/20210919191649_RemoveReleaseNotesMaxLength.cs index daa3eac4..9fa4a3e8 100644 --- a/src/BaGet.Database.PostgreSql/Migrations/20210919191649_RemoveReleaseNotesMaxLength.cs +++ b/src/BaGet.Database.PostgreSql/Migrations/20210919191649_RemoveReleaseNotesMaxLength.cs @@ -1,6 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace BaGet.Database.PostgreSql.Migrations +namespace BaGet.Database.PostgreSql.Migrations { public partial class RemoveReleaseNotesMaxLength : Migration { diff --git a/src/BaGet.Database.PostgreSql/PostgreSqlApplicationExtensions.cs b/src/BaGet.Database.PostgreSql/PostgreSqlApplicationExtensions.cs index d6771901..17d3b8e8 100644 --- a/src/BaGet.Database.PostgreSql/PostgreSqlApplicationExtensions.cs +++ b/src/BaGet.Database.PostgreSql/PostgreSqlApplicationExtensions.cs @@ -1,9 +1,3 @@ -using BaGet.Core; -using BaGet.Database.PostgreSql; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - namespace BaGet; public static class PostgreSqlApplicationExtensions diff --git a/src/BaGet.Database.PostgreSql/PostgreSqlContext.cs b/src/BaGet.Database.PostgreSql/PostgreSqlContext.cs index d1988115..246d33ce 100644 --- a/src/BaGet.Database.PostgreSql/PostgreSqlContext.cs +++ b/src/BaGet.Database.PostgreSql/PostgreSqlContext.cs @@ -1,7 +1,3 @@ -using BaGet.Core; -using Microsoft.EntityFrameworkCore; -using Npgsql; - namespace BaGet.Database.PostgreSql; public class PostgreSqlContext : AbstractContext diff --git a/src/BaGet.Database.SqlServer/GlobalUsings.cs b/src/BaGet.Database.SqlServer/GlobalUsings.cs new file mode 100644 index 00000000..a981ee5d --- /dev/null +++ b/src/BaGet.Database.SqlServer/GlobalUsings.cs @@ -0,0 +1,10 @@ +// Global using directives + +global using BaGet.Core; +global using BaGet.Database.SqlServer; +global using Microsoft.Data.SqlClient; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.EntityFrameworkCore.Metadata; +global using Microsoft.EntityFrameworkCore.Migrations; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Options; \ No newline at end of file diff --git a/src/BaGet.Database.SqlServer/Migrations/20180804082816_Initial.cs b/src/BaGet.Database.SqlServer/Migrations/20180804082816_Initial.cs index dacf55d9..170ac74d 100644 --- a/src/BaGet.Database.SqlServer/Migrations/20180804082816_Initial.cs +++ b/src/BaGet.Database.SqlServer/Migrations/20180804082816_Initial.cs @@ -1,6 +1,3 @@ -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - namespace BaGet.Database.SqlServer.Migrations { public partial class Initial : Migration diff --git a/src/BaGet.Database.SqlServer/Migrations/20190303071621_AddSearchDimensions.cs b/src/BaGet.Database.SqlServer/Migrations/20190303071621_AddSearchDimensions.cs index de11daa6..55ec41ed 100644 --- a/src/BaGet.Database.SqlServer/Migrations/20190303071621_AddSearchDimensions.cs +++ b/src/BaGet.Database.SqlServer/Migrations/20190303071621_AddSearchDimensions.cs @@ -1,6 +1,3 @@ -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - namespace BaGet.Database.SqlServer.Migrations { public partial class AddSearchDimensions : Migration diff --git a/src/BaGet.Database.SqlServer/Migrations/20190914215959_AddOriginalVersionStringColumn.cs b/src/BaGet.Database.SqlServer/Migrations/20190914215959_AddOriginalVersionStringColumn.cs index 7a4f7cd1..85eddbb2 100644 --- a/src/BaGet.Database.SqlServer/Migrations/20190914215959_AddOriginalVersionStringColumn.cs +++ b/src/BaGet.Database.SqlServer/Migrations/20190914215959_AddOriginalVersionStringColumn.cs @@ -1,6 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace BaGet.Database.SqlServer.Migrations +namespace BaGet.Database.SqlServer.Migrations { public partial class AddOriginalVersionStringColumn : Migration { diff --git a/src/BaGet.Database.SqlServer/Migrations/20200109085508_AddReleaseNotesStringColumn.cs b/src/BaGet.Database.SqlServer/Migrations/20200109085508_AddReleaseNotesStringColumn.cs index 4d5f8459..426f5b51 100644 --- a/src/BaGet.Database.SqlServer/Migrations/20200109085508_AddReleaseNotesStringColumn.cs +++ b/src/BaGet.Database.SqlServer/Migrations/20200109085508_AddReleaseNotesStringColumn.cs @@ -1,6 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace BaGet.Database.SqlServer.Migrations +namespace BaGet.Database.SqlServer.Migrations { public partial class AddReleaseNotesStringColumn : Migration { diff --git a/src/BaGet.Database.SqlServer/Migrations/20200210004408_AddHasEmbeddedIconColumn.cs b/src/BaGet.Database.SqlServer/Migrations/20200210004408_AddHasEmbeddedIconColumn.cs index 1cff5d14..e980d793 100644 --- a/src/BaGet.Database.SqlServer/Migrations/20200210004408_AddHasEmbeddedIconColumn.cs +++ b/src/BaGet.Database.SqlServer/Migrations/20200210004408_AddHasEmbeddedIconColumn.cs @@ -1,6 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace BaGet.Database.SqlServer.Migrations +namespace BaGet.Database.SqlServer.Migrations { public partial class AddHasEmbeddedIconColumn : Migration { diff --git a/src/BaGet.Database.SqlServer/Migrations/20210919191928_RemoveReleaseNotesMaxLength.cs b/src/BaGet.Database.SqlServer/Migrations/20210919191928_RemoveReleaseNotesMaxLength.cs index 1d1080e9..7ef464a5 100644 --- a/src/BaGet.Database.SqlServer/Migrations/20210919191928_RemoveReleaseNotesMaxLength.cs +++ b/src/BaGet.Database.SqlServer/Migrations/20210919191928_RemoveReleaseNotesMaxLength.cs @@ -1,6 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace BaGet.Database.SqlServer.Migrations +namespace BaGet.Database.SqlServer.Migrations { public partial class RemoveReleaseNotesMaxLength : Migration { diff --git a/src/BaGet.Database.SqlServer/SqlServerApplicationExtensions.cs b/src/BaGet.Database.SqlServer/SqlServerApplicationExtensions.cs index dcecc168..8f4ee37c 100644 --- a/src/BaGet.Database.SqlServer/SqlServerApplicationExtensions.cs +++ b/src/BaGet.Database.SqlServer/SqlServerApplicationExtensions.cs @@ -1,9 +1,3 @@ -using BaGet.Core; -using BaGet.Database.SqlServer; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - namespace BaGet; public static class SqlServerApplicationExtensions diff --git a/src/BaGet.Database.SqlServer/SqlServerContext.cs b/src/BaGet.Database.SqlServer/SqlServerContext.cs index 15219af4..370f1697 100644 --- a/src/BaGet.Database.SqlServer/SqlServerContext.cs +++ b/src/BaGet.Database.SqlServer/SqlServerContext.cs @@ -1,7 +1,3 @@ -using BaGet.Core; -using Microsoft.Data.SqlClient; -using Microsoft.EntityFrameworkCore; - namespace BaGet.Database.SqlServer; public class SqlServerContext : AbstractContext diff --git a/src/BaGet.Database.Sqlite/GlobalUsings.cs b/src/BaGet.Database.Sqlite/GlobalUsings.cs new file mode 100644 index 00000000..b5a0e234 --- /dev/null +++ b/src/BaGet.Database.Sqlite/GlobalUsings.cs @@ -0,0 +1,9 @@ +// Global using directives + +global using BaGet.Core; +global using BaGet.Database.Sqlite; +global using Microsoft.Data.Sqlite; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.EntityFrameworkCore.Migrations; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Options; \ No newline at end of file diff --git a/src/BaGet.Database.Sqlite/Migrations/20180804082808_Initial.cs b/src/BaGet.Database.Sqlite/Migrations/20180804082808_Initial.cs index cde1429f..e2a5e04f 100644 --- a/src/BaGet.Database.Sqlite/Migrations/20180804082808_Initial.cs +++ b/src/BaGet.Database.Sqlite/Migrations/20180804082808_Initial.cs @@ -1,5 +1,3 @@ -using Microsoft.EntityFrameworkCore.Migrations; - namespace BaGet.Database.Sqlite.Migrations { public partial class Initial : Migration diff --git a/src/BaGet.Database.Sqlite/Migrations/20190303072658_AddSearchDimensions.cs b/src/BaGet.Database.Sqlite/Migrations/20190303072658_AddSearchDimensions.cs index ff6c16c2..e6c09f50 100644 --- a/src/BaGet.Database.Sqlite/Migrations/20190303072658_AddSearchDimensions.cs +++ b/src/BaGet.Database.Sqlite/Migrations/20190303072658_AddSearchDimensions.cs @@ -1,5 +1,3 @@ -using Microsoft.EntityFrameworkCore.Migrations; - namespace BaGet.Database.Sqlite.Migrations { public partial class AddSearchDimensions : Migration diff --git a/src/BaGet.Database.Sqlite/Migrations/20190914215110_AddOriginalVersionStringColumn.cs b/src/BaGet.Database.Sqlite/Migrations/20190914215110_AddOriginalVersionStringColumn.cs index 730ce1f4..11c5d9aa 100644 --- a/src/BaGet.Database.Sqlite/Migrations/20190914215110_AddOriginalVersionStringColumn.cs +++ b/src/BaGet.Database.Sqlite/Migrations/20190914215110_AddOriginalVersionStringColumn.cs @@ -1,6 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace BaGet.Database.Sqlite.Migrations +namespace BaGet.Database.Sqlite.Migrations { public partial class AddOriginalVersionStringColumn : Migration { diff --git a/src/BaGet.Database.Sqlite/Migrations/20200109071618_AddReleaseNotesStringColumn.cs b/src/BaGet.Database.Sqlite/Migrations/20200109071618_AddReleaseNotesStringColumn.cs index 5de5d390..fe07895a 100644 --- a/src/BaGet.Database.Sqlite/Migrations/20200109071618_AddReleaseNotesStringColumn.cs +++ b/src/BaGet.Database.Sqlite/Migrations/20200109071618_AddReleaseNotesStringColumn.cs @@ -1,6 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace BaGet.Database.Sqlite.Migrations +namespace BaGet.Database.Sqlite.Migrations { public partial class AddReleaseNotesStringColumn : Migration { diff --git a/src/BaGet.Database.Sqlite/Migrations/20200208044959_FixVersionCaseSensitivity.cs b/src/BaGet.Database.Sqlite/Migrations/20200208044959_FixVersionCaseSensitivity.cs index cbdbe047..182a419d 100644 --- a/src/BaGet.Database.Sqlite/Migrations/20200208044959_FixVersionCaseSensitivity.cs +++ b/src/BaGet.Database.Sqlite/Migrations/20200208044959_FixVersionCaseSensitivity.cs @@ -1,5 +1,3 @@ -using Microsoft.EntityFrameworkCore.Migrations; - namespace BaGet.Database.Sqlite.Migrations { public partial class FixVersionCaseSensitivity : Migration diff --git a/src/BaGet.Database.Sqlite/Migrations/20200210004344_AddHasEmbeddedIconColumn.cs b/src/BaGet.Database.Sqlite/Migrations/20200210004344_AddHasEmbeddedIconColumn.cs index df4897b4..b432a717 100644 --- a/src/BaGet.Database.Sqlite/Migrations/20200210004344_AddHasEmbeddedIconColumn.cs +++ b/src/BaGet.Database.Sqlite/Migrations/20200210004344_AddHasEmbeddedIconColumn.cs @@ -1,6 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace BaGet.Database.Sqlite.Migrations +namespace BaGet.Database.Sqlite.Migrations { public partial class AddHasEmbeddedIconColumn : Migration { diff --git a/src/BaGet.Database.Sqlite/Migrations/20210919190849_RemoveReleaseNotesMaxLength.cs b/src/BaGet.Database.Sqlite/Migrations/20210919190849_RemoveReleaseNotesMaxLength.cs index 7077efbf..e5c230f0 100644 --- a/src/BaGet.Database.Sqlite/Migrations/20210919190849_RemoveReleaseNotesMaxLength.cs +++ b/src/BaGet.Database.Sqlite/Migrations/20210919190849_RemoveReleaseNotesMaxLength.cs @@ -1,6 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace BaGet.Database.Sqlite.Migrations +namespace BaGet.Database.Sqlite.Migrations { public partial class RemoveReleaseNotesMaxLength : Migration { diff --git a/src/BaGet.Database.Sqlite/SqliteApplicationExtensions.cs b/src/BaGet.Database.Sqlite/SqliteApplicationExtensions.cs index b5af3012..d6d730ad 100644 --- a/src/BaGet.Database.Sqlite/SqliteApplicationExtensions.cs +++ b/src/BaGet.Database.Sqlite/SqliteApplicationExtensions.cs @@ -1,9 +1,3 @@ -using BaGet.Core; -using BaGet.Database.Sqlite; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - namespace BaGet; public static class SqliteApplicationExtensions @@ -20,9 +14,7 @@ public static BaGetApplication AddSqliteDatabase(this BaGetApplication app) return app; } - public static BaGetApplication AddSqliteDatabase( - this BaGetApplication app, - Action configure) + public static BaGetApplication AddSqliteDatabase(this BaGetApplication app, Action configure) { app.AddSqliteDatabase(); app.Services.Configure(configure); diff --git a/src/BaGet.Database.Sqlite/SqliteContext.cs b/src/BaGet.Database.Sqlite/SqliteContext.cs index bbc4a00d..96267720 100644 --- a/src/BaGet.Database.Sqlite/SqliteContext.cs +++ b/src/BaGet.Database.Sqlite/SqliteContext.cs @@ -1,7 +1,3 @@ -using BaGet.Core; -using Microsoft.Data.Sqlite; -using Microsoft.EntityFrameworkCore; - namespace BaGet.Database.Sqlite; public class SqliteContext : AbstractContext diff --git a/src/BaGet.Gcp/GlobalUsings.cs b/src/BaGet.Gcp/GlobalUsings.cs new file mode 100644 index 00000000..d1e2e2c7 --- /dev/null +++ b/src/BaGet.Gcp/GlobalUsings.cs @@ -0,0 +1,12 @@ +// Global using directives + +global using System.ComponentModel.DataAnnotations; +global using System.Net; +global using System.Security.Cryptography; +global using BaGet.Core; +global using BaGet.Gcp; +global using Google; +global using Google.Cloud.Storage.V1; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Microsoft.Extensions.Options; \ No newline at end of file diff --git a/src/BaGet.Gcp/GoogleCloudApplicationExtensions.cs b/src/BaGet.Gcp/GoogleCloudApplicationExtensions.cs index ec069114..1d4f0e16 100644 --- a/src/BaGet.Gcp/GoogleCloudApplicationExtensions.cs +++ b/src/BaGet.Gcp/GoogleCloudApplicationExtensions.cs @@ -1,8 +1,3 @@ -using BaGet.Core; -using BaGet.Gcp; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - namespace BaGet; public static class GoogleCloudApplicationExtensions diff --git a/src/BaGet.Gcp/GoogleCloudStorageOptions.cs b/src/BaGet.Gcp/GoogleCloudStorageOptions.cs index 9c847e23..e7f42d13 100644 --- a/src/BaGet.Gcp/GoogleCloudStorageOptions.cs +++ b/src/BaGet.Gcp/GoogleCloudStorageOptions.cs @@ -1,6 +1,3 @@ -using System.ComponentModel.DataAnnotations; -using BaGet.Core; - namespace BaGet.Gcp; public class GoogleCloudStorageOptions : StorageOptions diff --git a/src/BaGet.Gcp/GoogleCloudStorageService.cs b/src/BaGet.Gcp/GoogleCloudStorageService.cs index 39761f48..3904bf64 100644 --- a/src/BaGet.Gcp/GoogleCloudStorageService.cs +++ b/src/BaGet.Gcp/GoogleCloudStorageService.cs @@ -1,10 +1,3 @@ -using System.Net; -using System.Security.Cryptography; -using BaGet.Core; -using Google; -using Google.Cloud.Storage.V1; -using Microsoft.Extensions.Options; - namespace BaGet.Gcp; public class GoogleCloudStorageService : IStorageService diff --git a/src/BaGet.Protocol/BaGet.Protocol.csproj.DotSettings b/src/BaGet.Protocol/BaGet.Protocol.csproj.DotSettings new file mode 100644 index 00000000..701cc276 --- /dev/null +++ b/src/BaGet.Protocol/BaGet.Protocol.csproj.DotSettings @@ -0,0 +1,9 @@ + + True + True + True + True + True + True + True + True \ No newline at end of file diff --git a/src/BaGet.Protocol/Catalog/CatalogClient.cs b/src/BaGet.Protocol/Catalog/CatalogClient.cs index 31a350b6..582030e2 100644 --- a/src/BaGet.Protocol/Catalog/CatalogClient.cs +++ b/src/BaGet.Protocol/Catalog/CatalogClient.cs @@ -1,5 +1,3 @@ -using BaGet.Protocol.Models; - namespace BaGet.Protocol; public partial class NuGetClientFactory diff --git a/src/BaGet.Protocol/Catalog/CatalogProcessor.cs b/src/BaGet.Protocol/Catalog/CatalogProcessor.cs index 98e20428..f5accbcc 100644 --- a/src/BaGet.Protocol/Catalog/CatalogProcessor.cs +++ b/src/BaGet.Protocol/Catalog/CatalogProcessor.cs @@ -1,7 +1,4 @@ -using BaGet.Protocol.Models; -using Microsoft.Extensions.Logging; - -namespace BaGet.Protocol.Catalog; +namespace BaGet.Protocol; /// /// Processes catalog leafs in chronological order. diff --git a/src/BaGet.Protocol/Catalog/CatalogProcessorOptions.cs b/src/BaGet.Protocol/Catalog/CatalogProcessorOptions.cs index 23a02722..f22b52ea 100644 --- a/src/BaGet.Protocol/Catalog/CatalogProcessorOptions.cs +++ b/src/BaGet.Protocol/Catalog/CatalogProcessorOptions.cs @@ -1,4 +1,4 @@ -namespace BaGet.Protocol.Catalog; +namespace BaGet.Protocol; /// /// The options to configure . diff --git a/src/BaGet.Protocol/Catalog/FileCursor.cs b/src/BaGet.Protocol/Catalog/FileCursor.cs index e8f6aa05..647154fc 100644 --- a/src/BaGet.Protocol/Catalog/FileCursor.cs +++ b/src/BaGet.Protocol/Catalog/FileCursor.cs @@ -1,8 +1,4 @@ -using System.Text.Json; -using System.Text.Json.Serialization; -using Microsoft.Extensions.Logging; - -namespace BaGet.Protocol.Catalog; +namespace BaGet.Protocol; /// /// A cursor implementation which stores the cursor in local file. The cursor value is written to the file as diff --git a/src/BaGet.Protocol/Catalog/ICatalogClient.cs b/src/BaGet.Protocol/Catalog/ICatalogClient.cs index 25298941..d4197a03 100644 --- a/src/BaGet.Protocol/Catalog/ICatalogClient.cs +++ b/src/BaGet.Protocol/Catalog/ICatalogClient.cs @@ -1,5 +1,3 @@ -using BaGet.Protocol.Models; - namespace BaGet.Protocol; /// diff --git a/src/BaGet.Protocol/Catalog/ICatalogLeafProcessor.cs b/src/BaGet.Protocol/Catalog/ICatalogLeafProcessor.cs index 856569b8..c8e736cf 100644 --- a/src/BaGet.Protocol/Catalog/ICatalogLeafProcessor.cs +++ b/src/BaGet.Protocol/Catalog/ICatalogLeafProcessor.cs @@ -1,6 +1,4 @@ -using BaGet.Protocol.Models; - -namespace BaGet.Protocol.Catalog; +namespace BaGet.Protocol; /// /// An interface which allows custom processing of catalog leaves. This interface should be implemented when the diff --git a/src/BaGet.Protocol/Catalog/ICursor.cs b/src/BaGet.Protocol/Catalog/ICursor.cs index 2e774e74..a91bb0e8 100644 --- a/src/BaGet.Protocol/Catalog/ICursor.cs +++ b/src/BaGet.Protocol/Catalog/ICursor.cs @@ -1,4 +1,4 @@ -namespace BaGet.Protocol.Catalog; +namespace BaGet.Protocol; /// /// The NuGet Catalog resource is an append-only data structure indexed by time. diff --git a/src/BaGet.Protocol/Catalog/NullCatalogClient.cs b/src/BaGet.Protocol/Catalog/NullCatalogClient.cs index 9987776f..edd18c98 100644 --- a/src/BaGet.Protocol/Catalog/NullCatalogClient.cs +++ b/src/BaGet.Protocol/Catalog/NullCatalogClient.cs @@ -1,6 +1,4 @@ -using BaGet.Protocol.Models; - -namespace BaGet.Protocol.Internal; +namespace BaGet.Protocol; public class NullCatalogClient : ICatalogClient { diff --git a/src/BaGet.Protocol/Catalog/NullCursor.cs b/src/BaGet.Protocol/Catalog/NullCursor.cs index f85c12e3..f8b68f2b 100644 --- a/src/BaGet.Protocol/Catalog/NullCursor.cs +++ b/src/BaGet.Protocol/Catalog/NullCursor.cs @@ -1,4 +1,4 @@ -namespace BaGet.Protocol.Catalog; +namespace BaGet.Protocol; /// /// A cursor that does not persist any state. Use this with a diff --git a/src/BaGet.Protocol/Catalog/RawCatalogClient.cs b/src/BaGet.Protocol/Catalog/RawCatalogClient.cs index b730fa39..2ccade20 100644 --- a/src/BaGet.Protocol/Catalog/RawCatalogClient.cs +++ b/src/BaGet.Protocol/Catalog/RawCatalogClient.cs @@ -1,6 +1,4 @@ -using BaGet.Protocol.Models; - -namespace BaGet.Protocol.Internal; +namespace BaGet.Protocol; public class RawCatalogClient : ICatalogClient { diff --git a/src/BaGet.Protocol/Converters/PackageDependencyRangeJsonConverter.cs b/src/BaGet.Protocol/Converters/PackageDependencyRangeJsonConverter.cs index ac5a1568..4e831fb5 100644 --- a/src/BaGet.Protocol/Converters/PackageDependencyRangeJsonConverter.cs +++ b/src/BaGet.Protocol/Converters/PackageDependencyRangeJsonConverter.cs @@ -1,7 +1,4 @@ -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Internal; +namespace BaGet.Protocol; /// /// This is an internal API that may be changed or removed without notice in any release. diff --git a/src/BaGet.Protocol/Converters/StringOrStringArrayJsonConverter.cs b/src/BaGet.Protocol/Converters/StringOrStringArrayJsonConverter.cs index 56ddfe5a..4136c4d5 100644 --- a/src/BaGet.Protocol/Converters/StringOrStringArrayJsonConverter.cs +++ b/src/BaGet.Protocol/Converters/StringOrStringArrayJsonConverter.cs @@ -1,7 +1,4 @@ -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Internal; +namespace BaGet.Protocol; /// /// This is an internal API that may be changed or removed without notice in any release. diff --git a/src/BaGet.Protocol/Extensions/CatalogModelExtensions.cs b/src/BaGet.Protocol/Extensions/CatalogModelExtensions.cs index e4585af1..922b91d4 100644 --- a/src/BaGet.Protocol/Extensions/CatalogModelExtensions.cs +++ b/src/BaGet.Protocol/Extensions/CatalogModelExtensions.cs @@ -1,7 +1,3 @@ -using BaGet.Protocol.Models; -using NuGet.Frameworks; -using NuGet.Versioning; - namespace BaGet.Protocol; /// diff --git a/src/BaGet.Protocol/Extensions/HttpClientExtensions.cs b/src/BaGet.Protocol/Extensions/HttpClientExtensions.cs index 74c0d6de..24fbe58b 100644 --- a/src/BaGet.Protocol/Extensions/HttpClientExtensions.cs +++ b/src/BaGet.Protocol/Extensions/HttpClientExtensions.cs @@ -1,6 +1,3 @@ -using System.Net; -using System.Text.Json; - namespace BaGet.Protocol; internal static class HttpClientExtensions diff --git a/src/BaGet.Protocol/Extensions/NuGetClientFactoryExtensions.cs b/src/BaGet.Protocol/Extensions/NuGetClientFactoryExtensions.cs index 0efd3876..50706246 100644 --- a/src/BaGet.Protocol/Extensions/NuGetClientFactoryExtensions.cs +++ b/src/BaGet.Protocol/Extensions/NuGetClientFactoryExtensions.cs @@ -1,6 +1,3 @@ -using BaGet.Protocol.Catalog; -using Microsoft.Extensions.Logging; - namespace BaGet.Protocol; public static class NuGetClientFactoryExtensions diff --git a/src/BaGet.Protocol/Extensions/PackageContentModelExtensions.cs b/src/BaGet.Protocol/Extensions/PackageContentModelExtensions.cs index 8727b8c9..1bf6b16a 100644 --- a/src/BaGet.Protocol/Extensions/PackageContentModelExtensions.cs +++ b/src/BaGet.Protocol/Extensions/PackageContentModelExtensions.cs @@ -1,6 +1,3 @@ -using BaGet.Protocol.Models; -using NuGet.Versioning; - namespace BaGet.Protocol; /// diff --git a/src/BaGet.Protocol/Extensions/PackageMetadataModelExtensions.cs b/src/BaGet.Protocol/Extensions/PackageMetadataModelExtensions.cs index 5fc909de..3086d39b 100644 --- a/src/BaGet.Protocol/Extensions/PackageMetadataModelExtensions.cs +++ b/src/BaGet.Protocol/Extensions/PackageMetadataModelExtensions.cs @@ -1,6 +1,3 @@ -using BaGet.Protocol.Models; -using NuGet.Versioning; - namespace BaGet.Protocol; /// diff --git a/src/BaGet.Protocol/Extensions/SearchModelExtensions.cs b/src/BaGet.Protocol/Extensions/SearchModelExtensions.cs index 299afe9f..bb7360d4 100644 --- a/src/BaGet.Protocol/Extensions/SearchModelExtensions.cs +++ b/src/BaGet.Protocol/Extensions/SearchModelExtensions.cs @@ -1,6 +1,3 @@ -using BaGet.Protocol.Models; -using NuGet.Versioning; - namespace BaGet.Protocol; /// diff --git a/src/BaGet.Protocol/Extensions/ServiceIndexModelExtensions.cs b/src/BaGet.Protocol/Extensions/ServiceIndexModelExtensions.cs index 14fc82e7..dbaee0dc 100644 --- a/src/BaGet.Protocol/Extensions/ServiceIndexModelExtensions.cs +++ b/src/BaGet.Protocol/Extensions/ServiceIndexModelExtensions.cs @@ -1,5 +1,3 @@ -using BaGet.Protocol.Models; - namespace BaGet.Protocol; /// diff --git a/src/BaGet.Protocol/GlobalUsings.cs b/src/BaGet.Protocol/GlobalUsings.cs new file mode 100644 index 00000000..76fc782c --- /dev/null +++ b/src/BaGet.Protocol/GlobalUsings.cs @@ -0,0 +1,9 @@ +// Global using directives + +global using System.Net; +global using System.Text; +global using System.Text.Json; +global using System.Text.Json.Serialization; +global using Microsoft.Extensions.Logging; +global using NuGet.Frameworks; +global using NuGet.Versioning; \ No newline at end of file diff --git a/src/BaGet.Protocol/Models/AlternatePackage.cs b/src/BaGet.Protocol/Models/AlternatePackage.cs index 63dbc383..f0f51382 100644 --- a/src/BaGet.Protocol/Models/AlternatePackage.cs +++ b/src/BaGet.Protocol/Models/AlternatePackage.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// /// The alternate package that should be used instead of a deprecated package. diff --git a/src/BaGet.Protocol/Models/AutocompleteContext.cs b/src/BaGet.Protocol/Models/AutocompleteContext.cs index 5862759a..782585d8 100644 --- a/src/BaGet.Protocol/Models/AutocompleteContext.cs +++ b/src/BaGet.Protocol/Models/AutocompleteContext.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; public class AutocompleteContext { diff --git a/src/BaGet.Protocol/Models/AutocompleteResponse.cs b/src/BaGet.Protocol/Models/AutocompleteResponse.cs index e4f69026..5072edcc 100644 --- a/src/BaGet.Protocol/Models/AutocompleteResponse.cs +++ b/src/BaGet.Protocol/Models/AutocompleteResponse.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// /// The package ids that matched the autocomplete query. diff --git a/src/BaGet.Protocol/Models/CatalogIndex.cs b/src/BaGet.Protocol/Models/CatalogIndex.cs index 12e296e1..5ee33e03 100644 --- a/src/BaGet.Protocol/Models/CatalogIndex.cs +++ b/src/BaGet.Protocol/Models/CatalogIndex.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; // This class is based off: https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/CatalogIndex.cs /// diff --git a/src/BaGet.Protocol/Models/CatalogLeaf.cs b/src/BaGet.Protocol/Models/CatalogLeaf.cs index dd0ae8a4..2f7c6193 100644 --- a/src/BaGet.Protocol/Models/CatalogLeaf.cs +++ b/src/BaGet.Protocol/Models/CatalogLeaf.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; // This classed is based off: https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/CatalogLeaf.cs /// diff --git a/src/BaGet.Protocol/Models/CatalogLeafItem.cs b/src/BaGet.Protocol/Models/CatalogLeafItem.cs index 315c8e60..dac18e85 100644 --- a/src/BaGet.Protocol/Models/CatalogLeafItem.cs +++ b/src/BaGet.Protocol/Models/CatalogLeafItem.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; // This class is based off https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/CatalogLeafItem.cs /// diff --git a/src/BaGet.Protocol/Models/CatalogPage.cs b/src/BaGet.Protocol/Models/CatalogPage.cs index 9fc12719..0b06b202 100644 --- a/src/BaGet.Protocol/Models/CatalogPage.cs +++ b/src/BaGet.Protocol/Models/CatalogPage.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; // This class is based off https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/CatalogPage.cs /// diff --git a/src/BaGet.Protocol/Models/CatalogPageItem.cs b/src/BaGet.Protocol/Models/CatalogPageItem.cs index 13783d93..df4428ad 100644 --- a/src/BaGet.Protocol/Models/CatalogPageItem.cs +++ b/src/BaGet.Protocol/Models/CatalogPageItem.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; // This class is based off https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/CatalogPageItem.cs /// diff --git a/src/BaGet.Protocol/Models/DependencyGroupItem.cs b/src/BaGet.Protocol/Models/DependencyGroupItem.cs index 1f111515..c35a1c8e 100644 --- a/src/BaGet.Protocol/Models/DependencyGroupItem.cs +++ b/src/BaGet.Protocol/Models/DependencyGroupItem.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// /// The dependencies of the package for a specific target framework. diff --git a/src/BaGet.Protocol/Models/DependencyItem.cs b/src/BaGet.Protocol/Models/DependencyItem.cs index 555e84bc..f613add2 100644 --- a/src/BaGet.Protocol/Models/DependencyItem.cs +++ b/src/BaGet.Protocol/Models/DependencyItem.cs @@ -1,7 +1,4 @@ -using System.Text.Json.Serialization; -using BaGet.Protocol.Internal; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// /// Represents a package dependency. diff --git a/src/BaGet.Protocol/Models/ICatalogLeafItem.cs b/src/BaGet.Protocol/Models/ICatalogLeafItem.cs index 6aa1cdc2..024ffeeb 100644 --- a/src/BaGet.Protocol/Models/ICatalogLeafItem.cs +++ b/src/BaGet.Protocol/Models/ICatalogLeafItem.cs @@ -1,4 +1,4 @@ -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; // This class is based off https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/ICatalogLeafItem.cs /// diff --git a/src/BaGet.Protocol/Models/PackageDeleteCatalogLeaf.cs b/src/BaGet.Protocol/Models/PackageDeleteCatalogLeaf.cs index 90444cac..53da8c4f 100644 --- a/src/BaGet.Protocol/Models/PackageDeleteCatalogLeaf.cs +++ b/src/BaGet.Protocol/Models/PackageDeleteCatalogLeaf.cs @@ -1,4 +1,4 @@ -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; // This class is based off https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/PackageDeleteCatalogLeaf.cs /// diff --git a/src/BaGet.Protocol/Models/PackageDeprecation.cs b/src/BaGet.Protocol/Models/PackageDeprecation.cs index 7a965d75..c9537459 100644 --- a/src/BaGet.Protocol/Models/PackageDeprecation.cs +++ b/src/BaGet.Protocol/Models/PackageDeprecation.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// /// A package's metadata. diff --git a/src/BaGet.Protocol/Models/PackageDetailsCatalogLeaf.cs b/src/BaGet.Protocol/Models/PackageDetailsCatalogLeaf.cs index 319f85d9..1e038d70 100644 --- a/src/BaGet.Protocol/Models/PackageDetailsCatalogLeaf.cs +++ b/src/BaGet.Protocol/Models/PackageDetailsCatalogLeaf.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// This class is based off https://github.com/NuGet/NuGet.Services.Metadata/blob/64af0b59c5a79e0143f0808b39946df9f16cb2e7/src/NuGet.Protocol.Catalog/Models/PackageDetailsCatalogLeaf.cs /// diff --git a/src/BaGet.Protocol/Models/PackageMetadata.cs b/src/BaGet.Protocol/Models/PackageMetadata.cs index 86f8f8e5..38df772b 100644 --- a/src/BaGet.Protocol/Models/PackageMetadata.cs +++ b/src/BaGet.Protocol/Models/PackageMetadata.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// /// A package's metadata. diff --git a/src/BaGet.Protocol/Models/PackageNotFoundException.cs b/src/BaGet.Protocol/Models/PackageNotFoundException.cs index 4def0f69..0cabfa1a 100644 --- a/src/BaGet.Protocol/Models/PackageNotFoundException.cs +++ b/src/BaGet.Protocol/Models/PackageNotFoundException.cs @@ -1,6 +1,4 @@ -using NuGet.Versioning; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// /// An exception thrown when a package could not be found on the NuGet server. diff --git a/src/BaGet.Protocol/Models/PackageVersionsResponse.cs b/src/BaGet.Protocol/Models/PackageVersionsResponse.cs index cf062542..23155ea4 100644 --- a/src/BaGet.Protocol/Models/PackageVersionsResponse.cs +++ b/src/BaGet.Protocol/Models/PackageVersionsResponse.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// /// The full list of versions for a single package. diff --git a/src/BaGet.Protocol/Models/ProtocolException.cs b/src/BaGet.Protocol/Models/ProtocolException.cs index dec539bf..77b5556b 100644 --- a/src/BaGet.Protocol/Models/ProtocolException.cs +++ b/src/BaGet.Protocol/Models/ProtocolException.cs @@ -1,6 +1,4 @@ -using System.Net; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// /// An exception that is thrown when an API has returned an unexpected result. diff --git a/src/BaGet.Protocol/Models/RegistrationIndexPage.cs b/src/BaGet.Protocol/Models/RegistrationIndexPage.cs index 5c4b9d3e..3eec4b2f 100644 --- a/src/BaGet.Protocol/Models/RegistrationIndexPage.cs +++ b/src/BaGet.Protocol/Models/RegistrationIndexPage.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// /// The registration page object found in the registration index. diff --git a/src/BaGet.Protocol/Models/RegistrationIndexPageItem.cs b/src/BaGet.Protocol/Models/RegistrationIndexPageItem.cs index 4a34353e..a6b59d8c 100644 --- a/src/BaGet.Protocol/Models/RegistrationIndexPageItem.cs +++ b/src/BaGet.Protocol/Models/RegistrationIndexPageItem.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// /// An item in the that references a . diff --git a/src/BaGet.Protocol/Models/RegistrationIndexResponse.cs b/src/BaGet.Protocol/Models/RegistrationIndexResponse.cs index e6308d23..294d90b5 100644 --- a/src/BaGet.Protocol/Models/RegistrationIndexResponse.cs +++ b/src/BaGet.Protocol/Models/RegistrationIndexResponse.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// /// The metadata for a package and all of its versions. diff --git a/src/BaGet.Protocol/Models/RegistrationLeafResponse.cs b/src/BaGet.Protocol/Models/RegistrationLeafResponse.cs index c5269633..b9bc3f2f 100644 --- a/src/BaGet.Protocol/Models/RegistrationLeafResponse.cs +++ b/src/BaGet.Protocol/Models/RegistrationLeafResponse.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// /// The metadata for a single version of a package. diff --git a/src/BaGet.Protocol/Models/RegistrationPageResponse.cs b/src/BaGet.Protocol/Models/RegistrationPageResponse.cs index c7a4e819..670bf938 100644 --- a/src/BaGet.Protocol/Models/RegistrationPageResponse.cs +++ b/src/BaGet.Protocol/Models/RegistrationPageResponse.cs @@ -1,4 +1,4 @@ -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// /// A page of package metadata entries. diff --git a/src/BaGet.Protocol/Models/SearchContext.cs b/src/BaGet.Protocol/Models/SearchContext.cs index 19e231b2..fb2cbfc3 100644 --- a/src/BaGet.Protocol/Models/SearchContext.cs +++ b/src/BaGet.Protocol/Models/SearchContext.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; public class SearchContext { diff --git a/src/BaGet.Protocol/Models/SearchResponse.cs b/src/BaGet.Protocol/Models/SearchResponse.cs index 67b417d1..036643eb 100644 --- a/src/BaGet.Protocol/Models/SearchResponse.cs +++ b/src/BaGet.Protocol/Models/SearchResponse.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// /// The response to a search query. diff --git a/src/BaGet.Protocol/Models/SearchResult.cs b/src/BaGet.Protocol/Models/SearchResult.cs index 3502a8ec..4a56f944 100644 --- a/src/BaGet.Protocol/Models/SearchResult.cs +++ b/src/BaGet.Protocol/Models/SearchResult.cs @@ -1,7 +1,4 @@ -using System.Text.Json.Serialization; -using BaGet.Protocol.Internal; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// /// A package that matched a search query. diff --git a/src/BaGet.Protocol/Models/SearchResultPackageType.cs b/src/BaGet.Protocol/Models/SearchResultPackageType.cs index 7bddbdf8..988252f0 100644 --- a/src/BaGet.Protocol/Models/SearchResultPackageType.cs +++ b/src/BaGet.Protocol/Models/SearchResultPackageType.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// /// A single package type from a . diff --git a/src/BaGet.Protocol/Models/SearchResultVersion.cs b/src/BaGet.Protocol/Models/SearchResultVersion.cs index 9145fbb8..4590f00f 100644 --- a/src/BaGet.Protocol/Models/SearchResultVersion.cs +++ b/src/BaGet.Protocol/Models/SearchResultVersion.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// /// A single version from a . diff --git a/src/BaGet.Protocol/Models/ServiceIndexItem.cs b/src/BaGet.Protocol/Models/ServiceIndexItem.cs index f984117a..777a49f1 100644 --- a/src/BaGet.Protocol/Models/ServiceIndexItem.cs +++ b/src/BaGet.Protocol/Models/ServiceIndexItem.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// /// A resource in the . diff --git a/src/BaGet.Protocol/Models/ServiceIndexResponse.cs b/src/BaGet.Protocol/Models/ServiceIndexResponse.cs index 4eca6890..0d7d1eaf 100644 --- a/src/BaGet.Protocol/Models/ServiceIndexResponse.cs +++ b/src/BaGet.Protocol/Models/ServiceIndexResponse.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace BaGet.Protocol.Models; +namespace BaGet.Protocol; /// /// The entry point for a NuGet package source used by the client to discover NuGet APIs. diff --git a/src/BaGet.Protocol/NuGetClient.cs b/src/BaGet.Protocol/NuGetClient.cs index 482ea6f2..f10b375c 100644 --- a/src/BaGet.Protocol/NuGetClient.cs +++ b/src/BaGet.Protocol/NuGetClient.cs @@ -1,7 +1,3 @@ -using System.Net; -using BaGet.Protocol.Models; -using NuGet.Versioning; - namespace BaGet.Protocol; /// diff --git a/src/BaGet.Protocol/NuGetClientFactory.cs b/src/BaGet.Protocol/NuGetClientFactory.cs index b0c376aa..12ac72a6 100644 --- a/src/BaGet.Protocol/NuGetClientFactory.cs +++ b/src/BaGet.Protocol/NuGetClientFactory.cs @@ -1,6 +1,3 @@ -using BaGet.Protocol.Internal; -using BaGet.Protocol.Models; - namespace BaGet.Protocol; /// diff --git a/src/BaGet.Protocol/PackageContent/IPackageContentClient.cs b/src/BaGet.Protocol/PackageContent/IPackageContentClient.cs index 4b40c334..b2e73823 100644 --- a/src/BaGet.Protocol/PackageContent/IPackageContentClient.cs +++ b/src/BaGet.Protocol/PackageContent/IPackageContentClient.cs @@ -1,6 +1,3 @@ -using BaGet.Protocol.Models; -using NuGet.Versioning; - namespace BaGet.Protocol; /// diff --git a/src/BaGet.Protocol/PackageContent/PackageContentClient.cs b/src/BaGet.Protocol/PackageContent/PackageContentClient.cs index df012a44..fdb663c9 100644 --- a/src/BaGet.Protocol/PackageContent/PackageContentClient.cs +++ b/src/BaGet.Protocol/PackageContent/PackageContentClient.cs @@ -1,6 +1,3 @@ -using BaGet.Protocol.Models; -using NuGet.Versioning; - namespace BaGet.Protocol; public partial class NuGetClientFactory diff --git a/src/BaGet.Protocol/PackageContent/RawPackageContentClient.cs b/src/BaGet.Protocol/PackageContent/RawPackageContentClient.cs index c65c8cd4..999a627a 100644 --- a/src/BaGet.Protocol/PackageContent/RawPackageContentClient.cs +++ b/src/BaGet.Protocol/PackageContent/RawPackageContentClient.cs @@ -1,8 +1,4 @@ -using System.Net; -using BaGet.Protocol.Models; -using NuGet.Versioning; - -namespace BaGet.Protocol.Internal; +namespace BaGet.Protocol; /// /// The client to interact with an upstream source's Package Content resource. diff --git a/src/BaGet.Protocol/PackageMetadata/IPackageMetadataClient.cs b/src/BaGet.Protocol/PackageMetadata/IPackageMetadataClient.cs index d24eec1d..c1e1e908 100644 --- a/src/BaGet.Protocol/PackageMetadata/IPackageMetadataClient.cs +++ b/src/BaGet.Protocol/PackageMetadata/IPackageMetadataClient.cs @@ -1,5 +1,3 @@ -using BaGet.Protocol.Models; - namespace BaGet.Protocol; /// diff --git a/src/BaGet.Protocol/PackageMetadata/PackageMetadataClient.cs b/src/BaGet.Protocol/PackageMetadata/PackageMetadataClient.cs index a0bd04da..2aed780f 100644 --- a/src/BaGet.Protocol/PackageMetadata/PackageMetadataClient.cs +++ b/src/BaGet.Protocol/PackageMetadata/PackageMetadataClient.cs @@ -1,5 +1,3 @@ -using BaGet.Protocol.Models; - namespace BaGet.Protocol; public partial class NuGetClientFactory diff --git a/src/BaGet.Protocol/PackageMetadata/RawPackageMetadataClient.cs b/src/BaGet.Protocol/PackageMetadata/RawPackageMetadataClient.cs index 35e4c2f3..11def208 100644 --- a/src/BaGet.Protocol/PackageMetadata/RawPackageMetadataClient.cs +++ b/src/BaGet.Protocol/PackageMetadata/RawPackageMetadataClient.cs @@ -1,6 +1,4 @@ -using BaGet.Protocol.Models; - -namespace BaGet.Protocol.Internal; +namespace BaGet.Protocol; /// /// The client to interact with an upstream source's Package Metadata resource. diff --git a/src/BaGet.Protocol/Search/AutocompleteClient.cs b/src/BaGet.Protocol/Search/AutocompleteClient.cs index ce961de0..9d66a41a 100644 --- a/src/BaGet.Protocol/Search/AutocompleteClient.cs +++ b/src/BaGet.Protocol/Search/AutocompleteClient.cs @@ -1,5 +1,3 @@ -using BaGet.Protocol.Models; - namespace BaGet.Protocol; public partial class NuGetClientFactory diff --git a/src/BaGet.Protocol/Search/IAutocompleteClient.cs b/src/BaGet.Protocol/Search/IAutocompleteClient.cs index b8f31540..bc1c44ec 100644 --- a/src/BaGet.Protocol/Search/IAutocompleteClient.cs +++ b/src/BaGet.Protocol/Search/IAutocompleteClient.cs @@ -1,5 +1,3 @@ -using BaGet.Protocol.Models; - namespace BaGet.Protocol; /// diff --git a/src/BaGet.Protocol/Search/ISearchClient.cs b/src/BaGet.Protocol/Search/ISearchClient.cs index d5e45f9d..3dfa8617 100644 --- a/src/BaGet.Protocol/Search/ISearchClient.cs +++ b/src/BaGet.Protocol/Search/ISearchClient.cs @@ -1,5 +1,3 @@ -using BaGet.Protocol.Models; - namespace BaGet.Protocol; /// diff --git a/src/BaGet.Protocol/Search/NullAutocompleteClient.cs b/src/BaGet.Protocol/Search/NullAutocompleteClient.cs index 81a0719d..8f89d14c 100644 --- a/src/BaGet.Protocol/Search/NullAutocompleteClient.cs +++ b/src/BaGet.Protocol/Search/NullAutocompleteClient.cs @@ -1,6 +1,4 @@ -using BaGet.Protocol.Models; - -namespace BaGet.Protocol.Internal; +namespace BaGet.Protocol; public class NullAutocompleteClient : IAutocompleteClient { diff --git a/src/BaGet.Protocol/Search/RawAutocompleteClient.cs b/src/BaGet.Protocol/Search/RawAutocompleteClient.cs index d9d66170..64e51345 100644 --- a/src/BaGet.Protocol/Search/RawAutocompleteClient.cs +++ b/src/BaGet.Protocol/Search/RawAutocompleteClient.cs @@ -1,6 +1,4 @@ -using BaGet.Protocol.Models; - -namespace BaGet.Protocol.Internal; +namespace BaGet.Protocol; /// /// The client used to search for packages. diff --git a/src/BaGet.Protocol/Search/RawSearchClient.cs b/src/BaGet.Protocol/Search/RawSearchClient.cs index ab68fc68..3c6178a8 100644 --- a/src/BaGet.Protocol/Search/RawSearchClient.cs +++ b/src/BaGet.Protocol/Search/RawSearchClient.cs @@ -1,7 +1,4 @@ -using System.Text; -using BaGet.Protocol.Models; - -namespace BaGet.Protocol.Internal; +namespace BaGet.Protocol; /// /// The client used to search for packages. diff --git a/src/BaGet.Protocol/Search/SearchClient.cs b/src/BaGet.Protocol/Search/SearchClient.cs index 63a28a58..cc00ef59 100644 --- a/src/BaGet.Protocol/Search/SearchClient.cs +++ b/src/BaGet.Protocol/Search/SearchClient.cs @@ -1,5 +1,3 @@ -using BaGet.Protocol.Models; - namespace BaGet.Protocol; public partial class NuGetClientFactory diff --git a/src/BaGet.Protocol/ServiceIndex/IServiceIndexClient.cs b/src/BaGet.Protocol/ServiceIndex/IServiceIndexClient.cs index f46aa555..209dfc0f 100644 --- a/src/BaGet.Protocol/ServiceIndex/IServiceIndexClient.cs +++ b/src/BaGet.Protocol/ServiceIndex/IServiceIndexClient.cs @@ -1,5 +1,3 @@ -using BaGet.Protocol.Models; - namespace BaGet.Protocol; /// diff --git a/src/BaGet.Protocol/ServiceIndex/RawServiceIndexClient.cs b/src/BaGet.Protocol/ServiceIndex/RawServiceIndexClient.cs index d249cfff..ee804530 100644 --- a/src/BaGet.Protocol/ServiceIndex/RawServiceIndexClient.cs +++ b/src/BaGet.Protocol/ServiceIndex/RawServiceIndexClient.cs @@ -1,6 +1,4 @@ -using BaGet.Protocol.Models; - -namespace BaGet.Protocol.Internal; +namespace BaGet.Protocol; /// /// The NuGet Service Index client, used to discover other resources. diff --git a/src/BaGet.Protocol/ServiceIndex/ServiceIndexClient.cs b/src/BaGet.Protocol/ServiceIndex/ServiceIndexClient.cs index 7d5050e2..39b8ebdc 100644 --- a/src/BaGet.Protocol/ServiceIndex/ServiceIndexClient.cs +++ b/src/BaGet.Protocol/ServiceIndex/ServiceIndexClient.cs @@ -1,5 +1,3 @@ -using BaGet.Protocol.Models; - namespace BaGet.Protocol; public partial class NuGetClientFactory diff --git a/src/BaGet.Web/Controllers/PackageContentController.cs b/src/BaGet.Web/Controllers/PackageContentController.cs index 9ae24859..0d94ae2d 100644 --- a/src/BaGet.Web/Controllers/PackageContentController.cs +++ b/src/BaGet.Web/Controllers/PackageContentController.cs @@ -1,6 +1,3 @@ -using BaGet.Protocol.Models; -using Microsoft.AspNetCore.Mvc; - namespace BaGet.Web; /// diff --git a/src/BaGet.Web/Controllers/PackageMetadataController.cs b/src/BaGet.Web/Controllers/PackageMetadataController.cs index a674dbf5..7b8b9778 100644 --- a/src/BaGet.Web/Controllers/PackageMetadataController.cs +++ b/src/BaGet.Web/Controllers/PackageMetadataController.cs @@ -1,6 +1,3 @@ -using BaGet.Protocol.Models; -using Microsoft.AspNetCore.Mvc; - namespace BaGet.Web; /// diff --git a/src/BaGet.Web/Controllers/PackagePublishController.cs b/src/BaGet.Web/Controllers/PackagePublishController.cs index f8772ef5..0bdc39ef 100644 --- a/src/BaGet.Web/Controllers/PackagePublishController.cs +++ b/src/BaGet.Web/Controllers/PackagePublishController.cs @@ -1,6 +1,3 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Options; - namespace BaGet.Web; public class PackagePublishController : Controller diff --git a/src/BaGet.Web/Controllers/SearchController.cs b/src/BaGet.Web/Controllers/SearchController.cs index 9e682c46..b2ab4a74 100644 --- a/src/BaGet.Web/Controllers/SearchController.cs +++ b/src/BaGet.Web/Controllers/SearchController.cs @@ -1,6 +1,3 @@ -using BaGet.Protocol.Models; -using Microsoft.AspNetCore.Mvc; - namespace BaGet.Web; public class SearchController : Controller diff --git a/src/BaGet.Web/Controllers/ServiceIndexController.cs b/src/BaGet.Web/Controllers/ServiceIndexController.cs index 6d4768a4..dbf7c2c1 100644 --- a/src/BaGet.Web/Controllers/ServiceIndexController.cs +++ b/src/BaGet.Web/Controllers/ServiceIndexController.cs @@ -1,6 +1,3 @@ -using BaGet.Protocol.Models; -using Microsoft.AspNetCore.Mvc; - namespace BaGet.Web; /// diff --git a/src/BaGet.Web/Controllers/SymbolController.cs b/src/BaGet.Web/Controllers/SymbolController.cs index 0ed54e11..39f589ef 100644 --- a/src/BaGet.Web/Controllers/SymbolController.cs +++ b/src/BaGet.Web/Controllers/SymbolController.cs @@ -1,6 +1,3 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Options; - namespace BaGet.Web; public class SymbolController : Controller diff --git a/src/BaGet.Web/Extensions/IHostExtensions.cs b/src/BaGet.Web/Extensions/IHostExtensions.cs index 633b6c8e..6db8f291 100644 --- a/src/BaGet.Web/Extensions/IHostExtensions.cs +++ b/src/BaGet.Web/Extensions/IHostExtensions.cs @@ -1,7 +1,3 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Options; - namespace BaGet.Web; public static class IHostExtensions diff --git a/src/BaGet.Web/Extensions/IServiceCollectionExtensions.cs b/src/BaGet.Web/Extensions/IServiceCollectionExtensions.cs index cdb8344b..801e3e6d 100644 --- a/src/BaGet.Web/Extensions/IServiceCollectionExtensions.cs +++ b/src/BaGet.Web/Extensions/IServiceCollectionExtensions.cs @@ -1,6 +1,3 @@ -using Microsoft.Extensions.DependencyInjection; -using System.Text.Json.Serialization; - namespace BaGet.Web; public static class IServiceCollectionExtensions diff --git a/src/BaGet.Web/Extensions/RazorExtensions.cs b/src/BaGet.Web/Extensions/RazorExtensions.cs index 87b1e8fd..ff3742eb 100644 --- a/src/BaGet.Web/Extensions/RazorExtensions.cs +++ b/src/BaGet.Web/Extensions/RazorExtensions.cs @@ -1,6 +1,4 @@ -using Humanizer; - -namespace BaGet.Web; +namespace BaGet.Web; public static class RazorExtensions { diff --git a/src/BaGet.Web/GlobalUsings.cs b/src/BaGet.Web/GlobalUsings.cs index dbf5fc86..58c30820 100644 --- a/src/BaGet.Web/GlobalUsings.cs +++ b/src/BaGet.Web/GlobalUsings.cs @@ -2,20 +2,29 @@ global using System; global using System.Collections.Generic; +global using System.ComponentModel.DataAnnotations; global using System.IO; global using System.Linq; global using System.Net; +global using System.Text.Json.Serialization; global using System.Threading; global using System.Threading.Tasks; global using BaGet.Core; +global using BaGet.Protocol; +global using Humanizer; global using Markdig; global using Microsoft.AspNetCore.Builder; global using Microsoft.AspNetCore.Html; global using Microsoft.AspNetCore.Http; +global using Microsoft.AspNetCore.Mvc; global using Microsoft.AspNetCore.Mvc.RazorPages; +global using Microsoft.AspNetCore.Razor.TagHelpers; //global using Microsoft.AspNetCore.Razor.TagHelpers; global using Microsoft.AspNetCore.Routing; global using Microsoft.AspNetCore.Routing.Constraints; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Hosting; global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.Options; global using NuGet.Frameworks; global using NuGet.Versioning; diff --git a/src/BaGet.Web/NavLinkTagHelper.cs b/src/BaGet.Web/NavLinkTagHelper.cs index a543f79e..32c4455d 100644 --- a/src/BaGet.Web/NavLinkTagHelper.cs +++ b/src/BaGet.Web/NavLinkTagHelper.cs @@ -1,5 +1,3 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; - namespace BaGet.Web; [HtmlTargetElement(Attributes = "nav-link")] diff --git a/src/BaGet.Web/Pages/Index.cshtml.cs b/src/BaGet.Web/Pages/Index.cshtml.cs index 0e91caec..df4553bc 100644 --- a/src/BaGet.Web/Pages/Index.cshtml.cs +++ b/src/BaGet.Web/Pages/Index.cshtml.cs @@ -1,7 +1,3 @@ -using System.ComponentModel.DataAnnotations; -using BaGet.Protocol.Models; -using Microsoft.AspNetCore.Mvc; - namespace BaGet.Web; public class IndexModel : PageModel diff --git a/src/BaGet.Web/Pages/Package.cshtml b/src/BaGet.Web/Pages/Package.cshtml index 6ea83941..e0be0af8 100644 --- a/src/BaGet.Web/Pages/Package.cshtml +++ b/src/BaGet.Web/Pages/Package.cshtml @@ -1,5 +1,4 @@ @page "/packages/{id}/{version?}" -@using Humanizer @using Microsoft.AspNetCore.Mvc.TagHelpers @model PackageModel diff --git a/src/BaGet/ConfigureBaGetOptions.cs b/src/BaGet/ConfigureBaGetOptions.cs index aed7db14..8ff80ee8 100644 --- a/src/BaGet/ConfigureBaGetOptions.cs +++ b/src/BaGet/ConfigureBaGetOptions.cs @@ -1,21 +1,10 @@ -using BaGet.Core; -using Microsoft.AspNetCore.Cors.Infrastructure; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.HttpOverrides; -using Microsoft.Extensions.Options; - namespace BaGet; /// /// BaGet's options configuration, specific to the default BaGet application. /// Don't use this if you are embedding BaGet into your own custom ASP.NET Core application. /// -public class ConfigureBaGetOptions - : IConfigureOptions - , IConfigureOptions - , IConfigureOptions - , IConfigureOptions - , IValidateOptions +public class ConfigureBaGetOptions : IConfigureOptions, IConfigureOptions, IConfigureOptions, IConfigureOptions, IValidateOptions { public const string CorsPolicy = "AllowAll"; diff --git a/src/BaGet/ConfigureRazorRuntimeCompilation.cs b/src/BaGet/ConfigureRazorRuntimeCompilation.cs index 00158aa0..cb822d28 100644 --- a/src/BaGet/ConfigureRazorRuntimeCompilation.cs +++ b/src/BaGet/ConfigureRazorRuntimeCompilation.cs @@ -1,8 +1,4 @@ -using Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation; -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Options; - -namespace BaGet; +namespace BaGet; public class ConfigureRazorRuntimeCompilation : IConfigureOptions { diff --git a/src/BaGet/GlobalUsings.cs b/src/BaGet/GlobalUsings.cs new file mode 100644 index 00000000..66ab6d30 --- /dev/null +++ b/src/BaGet/GlobalUsings.cs @@ -0,0 +1,12 @@ +// Global using directives + +global using BaGet.Azure; +global using BaGet.Core; +global using BaGet.Web; +global using McMaster.Extensions.CommandLineUtils; +global using Microsoft.AspNetCore.Cors.Infrastructure; +global using Microsoft.AspNetCore.Http.Features; +global using Microsoft.AspNetCore.HttpOverrides; +global using Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation; +global using Microsoft.Extensions.FileProviders; +global using Microsoft.Extensions.Options; \ No newline at end of file diff --git a/src/BaGet/Program.cs b/src/BaGet/Program.cs index 01d42675..e0fccc7e 100644 --- a/src/BaGet/Program.cs +++ b/src/BaGet/Program.cs @@ -1,7 +1,3 @@ -using BaGet.Core; -using BaGet.Web; -using McMaster.Extensions.CommandLineUtils; - namespace BaGet; public class Program diff --git a/src/BaGet/Startup.cs b/src/BaGet/Startup.cs index c2c9e96a..31bd153b 100644 --- a/src/BaGet/Startup.cs +++ b/src/BaGet/Startup.cs @@ -1,11 +1,3 @@ -using BaGet.Azure; -using BaGet.Core; -using BaGet.Web; -using Microsoft.AspNetCore.Cors.Infrastructure; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation; -using Microsoft.Extensions.Options; - namespace BaGet; public class Startup diff --git a/tests/BaGet.Core.Tests/Metadata/ModelTests.cs b/tests/BaGet.Core.Tests/Metadata/ModelTests.cs index 61d3fcf2..cfa56a21 100644 --- a/tests/BaGet.Core.Tests/Metadata/ModelTests.cs +++ b/tests/BaGet.Core.Tests/Metadata/ModelTests.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Reflection; using System.Text.Json.Serialization; -using BaGet.Protocol.Models; +using BaGet.Protocol; using Xunit; namespace BaGet.Core.Tests.Metadata diff --git a/tests/BaGet.Core.Tests/Upstream/V3UpstreamClientTests.cs b/tests/BaGet.Core.Tests/Upstream/V3UpstreamClientTests.cs index 114cc7c2..5d7fe514 100644 --- a/tests/BaGet.Core.Tests/Upstream/V3UpstreamClientTests.cs +++ b/tests/BaGet.Core.Tests/Upstream/V3UpstreamClientTests.cs @@ -4,7 +4,6 @@ using System.Threading; using System.Threading.Tasks; using BaGet.Protocol; -using BaGet.Protocol.Models; using Microsoft.Extensions.Logging; using Moq; using NuGet.Versioning; diff --git a/tests/BaGet.Protocol.Tests/Converters/PackageDependencyRangeJsonConverterTests.cs b/tests/BaGet.Protocol.Tests/Converters/PackageDependencyRangeJsonConverterTests.cs index 5229bd79..eb581a4e 100644 --- a/tests/BaGet.Protocol.Tests/Converters/PackageDependencyRangeJsonConverterTests.cs +++ b/tests/BaGet.Protocol.Tests/Converters/PackageDependencyRangeJsonConverterTests.cs @@ -1,5 +1,4 @@ using System.Text.Json; -using BaGet.Protocol.Internal; using Xunit; namespace BaGet.Protocol.Tests diff --git a/tests/BaGet.Protocol.Tests/Converters/StringOrStringArrayJsonConverterTests.cs b/tests/BaGet.Protocol.Tests/Converters/StringOrStringArrayJsonConverterTests.cs index 9f4b77df..143485a3 100644 --- a/tests/BaGet.Protocol.Tests/Converters/StringOrStringArrayJsonConverterTests.cs +++ b/tests/BaGet.Protocol.Tests/Converters/StringOrStringArrayJsonConverterTests.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Text.Json; -using BaGet.Protocol.Internal; using Xunit; namespace BaGet.Protocol.Tests diff --git a/tests/BaGet.Protocol.Tests/RawAutocompleteClientTests.cs b/tests/BaGet.Protocol.Tests/RawAutocompleteClientTests.cs index e1171dc6..110cb7cd 100644 --- a/tests/BaGet.Protocol.Tests/RawAutocompleteClientTests.cs +++ b/tests/BaGet.Protocol.Tests/RawAutocompleteClientTests.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; -using BaGet.Protocol.Internal; using Xunit; namespace BaGet.Protocol.Tests diff --git a/tests/BaGet.Protocol.Tests/RawCatalogClientTests.cs b/tests/BaGet.Protocol.Tests/RawCatalogClientTests.cs index f6561592..fafdec36 100644 --- a/tests/BaGet.Protocol.Tests/RawCatalogClientTests.cs +++ b/tests/BaGet.Protocol.Tests/RawCatalogClientTests.cs @@ -1,7 +1,6 @@ using System; using System.Linq; using System.Threading.Tasks; -using BaGet.Protocol.Internal; using Xunit; namespace BaGet.Protocol.Tests diff --git a/tests/BaGet.Protocol.Tests/RawPackageContentClientTests.cs b/tests/BaGet.Protocol.Tests/RawPackageContentClientTests.cs index 4c66e798..aaf66bcd 100644 --- a/tests/BaGet.Protocol.Tests/RawPackageContentClientTests.cs +++ b/tests/BaGet.Protocol.Tests/RawPackageContentClientTests.cs @@ -1,6 +1,5 @@ using System; using System.Threading.Tasks; -using BaGet.Protocol.Internal; using Xunit; namespace BaGet.Protocol.Tests diff --git a/tests/BaGet.Protocol.Tests/RawPackageMetadataClientTests.cs b/tests/BaGet.Protocol.Tests/RawPackageMetadataClientTests.cs index eae3037c..696ff714 100644 --- a/tests/BaGet.Protocol.Tests/RawPackageMetadataClientTests.cs +++ b/tests/BaGet.Protocol.Tests/RawPackageMetadataClientTests.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; -using BaGet.Protocol.Internal; using Xunit; namespace BaGet.Protocol.Tests diff --git a/tests/BaGet.Protocol.Tests/RawSearchClientTests.cs b/tests/BaGet.Protocol.Tests/RawSearchClientTests.cs index ab7936f8..2a9e27bb 100644 --- a/tests/BaGet.Protocol.Tests/RawSearchClientTests.cs +++ b/tests/BaGet.Protocol.Tests/RawSearchClientTests.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; -using BaGet.Protocol.Internal; using Xunit; namespace BaGet.Protocol.Tests diff --git a/tests/BaGet.Protocol.Tests/RawServiceIndexClientTests.cs b/tests/BaGet.Protocol.Tests/RawServiceIndexClientTests.cs index 2af1670d..23f557c3 100644 --- a/tests/BaGet.Protocol.Tests/RawServiceIndexClientTests.cs +++ b/tests/BaGet.Protocol.Tests/RawServiceIndexClientTests.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; -using BaGet.Protocol.Internal; using Xunit; namespace BaGet.Protocol.Tests diff --git a/tests/BaGet.Protocol.Tests/Support/ProtocolFixture.cs b/tests/BaGet.Protocol.Tests/Support/ProtocolFixture.cs index 00c567a9..a2aaede9 100644 --- a/tests/BaGet.Protocol.Tests/Support/ProtocolFixture.cs +++ b/tests/BaGet.Protocol.Tests/Support/ProtocolFixture.cs @@ -1,5 +1,4 @@ using System.Net.Http; -using BaGet.Protocol.Internal; namespace BaGet.Protocol.Tests { diff --git a/tests/BaGet.Tests/BaGetClientIntegrationTests.cs b/tests/BaGet.Tests/BaGetClientIntegrationTests.cs index ec6661fa..0ec3cd36 100644 --- a/tests/BaGet.Tests/BaGetClientIntegrationTests.cs +++ b/tests/BaGet.Tests/BaGetClientIntegrationTests.cs @@ -3,7 +3,6 @@ using System.Net.Http; using System.Threading.Tasks; using BaGet.Protocol; -using BaGet.Protocol.Models; using NuGet.Versioning; using Xunit; using Xunit.Abstractions; diff --git a/tests/BaGet.Web.Tests/Pages/IndexModelFacts.cs b/tests/BaGet.Web.Tests/Pages/IndexModelFacts.cs index 85ee6729..059aad1a 100644 --- a/tests/BaGet.Web.Tests/Pages/IndexModelFacts.cs +++ b/tests/BaGet.Web.Tests/Pages/IndexModelFacts.cs @@ -1,7 +1,7 @@ using System.Threading; using System.Threading.Tasks; using BaGet.Core; -using BaGet.Protocol.Models; +using BaGet.Protocol; using Moq; using Xunit; From 93bf25105ff4ad6a24a4b195c8d8d87e60d77cc6 Mon Sep 17 00:00:00 2001 From: Kannan Chithambaranathan Date: Tue, 22 Nov 2022 18:40:25 +0530 Subject: [PATCH 3/7] Framework Upgraded to .Net 7 --- .../BaGet.Protocol.Samples.Tests.csproj | 36 +++--- .../BaGetWebApplication.csproj | 20 ++-- src/BaGet.Aliyun/BaGet.Aliyun.csproj | 22 ++-- src/BaGet.Aws/BaGet.Aws.csproj | 26 ++--- src/BaGet.Azure/BaGet.Azure.csproj | 28 ++--- src/BaGet.Core/BaGet.Core.csproj | 32 +++--- .../BaGet.Database.MySql.csproj | 22 ++-- .../BaGet.Database.PostgreSql.csproj | 22 ++-- .../BaGet.Database.SqlServer.csproj | 22 ++-- .../BaGet.Database.Sqlite.csproj | 24 ++-- src/BaGet.Gcp/BaGet.Gcp.csproj | 22 ++-- src/BaGet.Protocol/BaGet.Protocol.csproj | 22 ++-- src/BaGet.Web/BaGet.Web.csproj | 34 +++--- src/BaGet/BaGet.csproj | 48 ++++---- .../BaGet.Core.Tests/BaGet.Core.Tests.csproj | 38 +++--- .../BaGet.Protocol.Tests.csproj | 64 +++++------ tests/BaGet.Tests/BaGet.Tests.csproj | 108 +++++++++--------- tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj | 38 +++--- 18 files changed, 314 insertions(+), 314 deletions(-) diff --git a/samples/BaGet.Protocol.Samples.Tests/BaGet.Protocol.Samples.Tests.csproj b/samples/BaGet.Protocol.Samples.Tests/BaGet.Protocol.Samples.Tests.csproj index 6b390535..e6aadbd8 100644 --- a/samples/BaGet.Protocol.Samples.Tests/BaGet.Protocol.Samples.Tests.csproj +++ b/samples/BaGet.Protocol.Samples.Tests/BaGet.Protocol.Samples.Tests.csproj @@ -1,25 +1,25 @@ - - net6.0 + + net7.0 - false - IDE0007 - + false + IDE0007 + - - - - - - all - runtime; build; native; contentfiles; analyzers - - - + + + + + + all + runtime; build; native; contentfiles; analyzers + + + - - - + + + diff --git a/samples/BaGetWebApplication/BaGetWebApplication.csproj b/samples/BaGetWebApplication/BaGetWebApplication.csproj index 5cae439d..8bf22899 100644 --- a/samples/BaGetWebApplication/BaGetWebApplication.csproj +++ b/samples/BaGetWebApplication/BaGetWebApplication.csproj @@ -1,16 +1,16 @@ - - net6.0 - + + net7.0 + - - - - + + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet.Aliyun/BaGet.Aliyun.csproj b/src/BaGet.Aliyun/BaGet.Aliyun.csproj index b5ea641f..12595aaa 100644 --- a/src/BaGet.Aliyun/BaGet.Aliyun.csproj +++ b/src/BaGet.Aliyun/BaGet.Aliyun.csproj @@ -1,17 +1,17 @@ - - NuGet;Alibaba;Cloud - The libraries to host BaGet on Alibaba Cloud (Aliyun). - net6.0 - + + net7.0 + NuGet;Alibaba;Cloud + The libraries to host BaGet on Alibaba Cloud (Aliyun). + - - - + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet.Aws/BaGet.Aws.csproj b/src/BaGet.Aws/BaGet.Aws.csproj index 0685a977..ad78ec91 100644 --- a/src/BaGet.Aws/BaGet.Aws.csproj +++ b/src/BaGet.Aws/BaGet.Aws.csproj @@ -1,20 +1,20 @@ - - net6.0 + + net7.0 - NuGet;Amazon;Cloud - The libraries to host BaGet on AWS. - + NuGet;Amazon;Cloud + The libraries to host BaGet on AWS. + - - - - - + + + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet.Azure/BaGet.Azure.csproj b/src/BaGet.Azure/BaGet.Azure.csproj index 89064869..aedda1fe 100644 --- a/src/BaGet.Azure/BaGet.Azure.csproj +++ b/src/BaGet.Azure/BaGet.Azure.csproj @@ -1,21 +1,21 @@ - - net6.0 + + net7.0 - NuGet;Azure;Cloud - The libraries to host BaGet on Azure. - + NuGet;Azure;Cloud + The libraries to host BaGet on Azure. + - - - - - - + + + + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet.Core/BaGet.Core.csproj b/src/BaGet.Core/BaGet.Core.csproj index 328a1654..e0363902 100644 --- a/src/BaGet.Core/BaGet.Core.csproj +++ b/src/BaGet.Core/BaGet.Core.csproj @@ -1,23 +1,23 @@ - - net6.0 + + net7.0 - NuGet - The core libraries that power BaGet. - + NuGet + The core libraries that power BaGet. + - - - - - - - - + + + + + + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet.Database.MySql/BaGet.Database.MySql.csproj b/src/BaGet.Database.MySql/BaGet.Database.MySql.csproj index c6c2dca7..9e875444 100644 --- a/src/BaGet.Database.MySql/BaGet.Database.MySql.csproj +++ b/src/BaGet.Database.MySql/BaGet.Database.MySql.csproj @@ -1,18 +1,18 @@ - - net6.0 + + net7.0 - NuGet - The libraries to host BaGet on MySQL. - + NuGet + The libraries to host BaGet on MySQL. + - - - + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet.Database.PostgreSql/BaGet.Database.PostgreSql.csproj b/src/BaGet.Database.PostgreSql/BaGet.Database.PostgreSql.csproj index 34f3ae89..13806486 100644 --- a/src/BaGet.Database.PostgreSql/BaGet.Database.PostgreSql.csproj +++ b/src/BaGet.Database.PostgreSql/BaGet.Database.PostgreSql.csproj @@ -1,18 +1,18 @@ - - net6.0 + + net77.0 - NuGet - The libraries to host BaGet on PostgreSQL. - + NuGet + The libraries to host BaGet on PostgreSQL. + - - - + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet.Database.SqlServer/BaGet.Database.SqlServer.csproj b/src/BaGet.Database.SqlServer/BaGet.Database.SqlServer.csproj index e75fbc53..f194a66b 100644 --- a/src/BaGet.Database.SqlServer/BaGet.Database.SqlServer.csproj +++ b/src/BaGet.Database.SqlServer/BaGet.Database.SqlServer.csproj @@ -1,18 +1,18 @@ - - net6.0 + + net7.0 - NuGet - The libraries to host BaGet on SQL Server. - + NuGet + The libraries to host BaGet on SQL Server. + - - - + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet.Database.Sqlite/BaGet.Database.Sqlite.csproj b/src/BaGet.Database.Sqlite/BaGet.Database.Sqlite.csproj index 88988ffd..459adedf 100644 --- a/src/BaGet.Database.Sqlite/BaGet.Database.Sqlite.csproj +++ b/src/BaGet.Database.Sqlite/BaGet.Database.Sqlite.csproj @@ -1,18 +1,18 @@ - + - - net6.0 + + net7.0 - NuGet - The libraries to host BaGet on SQLite. - + NuGet + The libraries to host BaGet on SQLite. + - - - + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet.Gcp/BaGet.Gcp.csproj b/src/BaGet.Gcp/BaGet.Gcp.csproj index 2049869d..9b77daa5 100644 --- a/src/BaGet.Gcp/BaGet.Gcp.csproj +++ b/src/BaGet.Gcp/BaGet.Gcp.csproj @@ -1,18 +1,18 @@ - - net6.0 + + net7.0 - NuGet;Google;Cloud - The libraries to host BaGet on the Google Cloud Platform. - + NuGet;Google;Cloud + The libraries to host BaGet on the Google Cloud Platform. + - - - + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet.Protocol/BaGet.Protocol.csproj b/src/BaGet.Protocol/BaGet.Protocol.csproj index 8c1053ee..6fe30d8f 100644 --- a/src/BaGet.Protocol/BaGet.Protocol.csproj +++ b/src/BaGet.Protocol/BaGet.Protocol.csproj @@ -1,17 +1,17 @@ - - net6.0 + + net7.0 - NuGet;Protocol - Libraries to interact with NuGet server APIs. - + NuGet;Protocol + Libraries to interact with NuGet server APIs. + - - - - - - + + + + + + \ No newline at end of file diff --git a/src/BaGet.Web/BaGet.Web.csproj b/src/BaGet.Web/BaGet.Web.csproj index db8cb602..58e0d121 100644 --- a/src/BaGet.Web/BaGet.Web.csproj +++ b/src/BaGet.Web/BaGet.Web.csproj @@ -1,26 +1,26 @@ - - net6.0 + + net7.0 - NuGet - BaGet's NuGet server implementation - BaGet.Web + NuGet + BaGet's NuGet server implementation + BaGet.Web - true - + true + - - - + + + - - - - + + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet/BaGet.csproj b/src/BaGet/BaGet.csproj index 52cf215d..2bc470c5 100644 --- a/src/BaGet/BaGet.csproj +++ b/src/BaGet/BaGet.csproj @@ -1,31 +1,31 @@ - - net6.0 - + + net7.0 + - - - - - + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - + + + \ No newline at end of file diff --git a/tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj b/tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj index 3ac76755..de18991d 100644 --- a/tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj +++ b/tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj @@ -1,25 +1,25 @@ - - net6.0 - + + net7.0 + - - - + + + - - - all - runtime; build; native; contentfiles; analyzers - - - - - - all - runtime; build; native; contentfiles; analyzers - - + + + all + runtime; build; native; contentfiles; analyzers + + + + + + all + runtime; build; native; contentfiles; analyzers + + \ No newline at end of file diff --git a/tests/BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj b/tests/BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj index e1757427..51ed6dc4 100644 --- a/tests/BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj +++ b/tests/BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj @@ -1,40 +1,40 @@ - - net6.0 - + + net7.0 + - - - + + + - - - TestData.resx - True - True - - + + + TestData.resx + True + True + + - - - TestData.Designer.cs - ResXFileCodeGenerator - - + + + TestData.Designer.cs + ResXFileCodeGenerator + + - - - all - runtime; build; native; contentfiles; analyzers - - - - - - all - runtime; build; native; contentfiles; analyzers - - + + + all + runtime; build; native; contentfiles; analyzers + + + + + + all + runtime; build; native; contentfiles; analyzers + + \ No newline at end of file diff --git a/tests/BaGet.Tests/BaGet.Tests.csproj b/tests/BaGet.Tests/BaGet.Tests.csproj index 522f388a..07ed9901 100644 --- a/tests/BaGet.Tests/BaGet.Tests.csproj +++ b/tests/BaGet.Tests/BaGet.Tests.csproj @@ -1,58 +1,58 @@ - - net6.0 - preview - - - - - - - - - - - - - - - - - - - True - True - TestData.resx - - - - - - TestData.Designer.cs - ResXFileCodeGenerator - BaGet.Tests - - - Never - - - Never - - - - - - all - runtime; build; native; contentfiles; analyzers - - - - - - all - runtime; build; native; contentfiles; analyzers - - + + net7.0 + preview + + + + + + + + + + + + + + + + + + + True + True + TestData.resx + + + + + + TestData.Designer.cs + ResXFileCodeGenerator + BaGet.Tests + + + Never + + + Never + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + all + runtime; build; native; contentfiles; analyzers + + \ No newline at end of file diff --git a/tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj b/tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj index d30d9081..b39f30b0 100644 --- a/tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj +++ b/tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj @@ -1,25 +1,25 @@ - - net6.0 - + + net7.0 + - - - + + + - - - all - runtime; build; native; contentfiles; analyzers - - - - - - all - runtime; build; native; contentfiles; analyzers - - + + + all + runtime; build; native; contentfiles; analyzers + + + + + + all + runtime; build; native; contentfiles; analyzers + + \ No newline at end of file From cd7bc23efb2b06f9ce5feb99316ff7c80dae31fc Mon Sep 17 00:00:00 2001 From: Kannan Chithambaranathan Date: Tue, 22 Nov 2022 19:17:54 +0530 Subject: [PATCH 4/7] Project Updated --- .../BaGet.Protocol.Samples.Tests.csproj | 2 +- src/BaGet.Aws/BaGet.Aws.csproj | 6 +- src/BaGet.Azure/BaGet.Azure.csproj | 2 +- src/BaGet.Core/BaGet.Core.csproj | 12 +-- .../BaGet.Database.MySql.csproj | 3 + .../BaGet.Database.PostgreSql.csproj | 4 +- .../BaGet.Database.SqlServer.csproj | 2 +- .../BaGet.Database.Sqlite.csproj | 2 +- src/BaGet.Protocol/BaGet.Protocol.csproj | 8 +- src/BaGet.Web/BaGet.Web.csproj | 2 +- src/BaGet/BaGet.csproj | 9 +- src/Directory.Build.props | 88 +++++++++---------- .../BaGet.Core.Tests/BaGet.Core.Tests.csproj | 4 +- .../BaGet.Protocol.Tests.csproj | 4 +- tests/BaGet.Tests/BaGet.Tests.csproj | 8 +- tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj | 4 +- 16 files changed, 83 insertions(+), 77 deletions(-) diff --git a/samples/BaGet.Protocol.Samples.Tests/BaGet.Protocol.Samples.Tests.csproj b/samples/BaGet.Protocol.Samples.Tests/BaGet.Protocol.Samples.Tests.csproj index e6aadbd8..bbbbbe90 100644 --- a/samples/BaGet.Protocol.Samples.Tests/BaGet.Protocol.Samples.Tests.csproj +++ b/samples/BaGet.Protocol.Samples.Tests/BaGet.Protocol.Samples.Tests.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/BaGet.Aws/BaGet.Aws.csproj b/src/BaGet.Aws/BaGet.Aws.csproj index ad78ec91..071ea952 100644 --- a/src/BaGet.Aws/BaGet.Aws.csproj +++ b/src/BaGet.Aws/BaGet.Aws.csproj @@ -8,9 +8,9 @@ - - - + + + diff --git a/src/BaGet.Azure/BaGet.Azure.csproj b/src/BaGet.Azure/BaGet.Azure.csproj index aedda1fe..4cedc965 100644 --- a/src/BaGet.Azure/BaGet.Azure.csproj +++ b/src/BaGet.Azure/BaGet.Azure.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/BaGet.Core/BaGet.Core.csproj b/src/BaGet.Core/BaGet.Core.csproj index e0363902..253b41db 100644 --- a/src/BaGet.Core/BaGet.Core.csproj +++ b/src/BaGet.Core/BaGet.Core.csproj @@ -8,12 +8,12 @@ - - - - - - + + + + + + diff --git a/src/BaGet.Database.MySql/BaGet.Database.MySql.csproj b/src/BaGet.Database.MySql/BaGet.Database.MySql.csproj index 9e875444..c872aefd 100644 --- a/src/BaGet.Database.MySql/BaGet.Database.MySql.csproj +++ b/src/BaGet.Database.MySql/BaGet.Database.MySql.csproj @@ -9,6 +9,9 @@ + + + diff --git a/src/BaGet.Database.PostgreSql/BaGet.Database.PostgreSql.csproj b/src/BaGet.Database.PostgreSql/BaGet.Database.PostgreSql.csproj index 13806486..cf7778b3 100644 --- a/src/BaGet.Database.PostgreSql/BaGet.Database.PostgreSql.csproj +++ b/src/BaGet.Database.PostgreSql/BaGet.Database.PostgreSql.csproj @@ -1,14 +1,14 @@ - net77.0 + net7.0 NuGet The libraries to host BaGet on PostgreSQL. - + diff --git a/src/BaGet.Database.SqlServer/BaGet.Database.SqlServer.csproj b/src/BaGet.Database.SqlServer/BaGet.Database.SqlServer.csproj index f194a66b..f26aed88 100644 --- a/src/BaGet.Database.SqlServer/BaGet.Database.SqlServer.csproj +++ b/src/BaGet.Database.SqlServer/BaGet.Database.SqlServer.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/BaGet.Database.Sqlite/BaGet.Database.Sqlite.csproj b/src/BaGet.Database.Sqlite/BaGet.Database.Sqlite.csproj index 459adedf..15da5b5f 100644 --- a/src/BaGet.Database.Sqlite/BaGet.Database.Sqlite.csproj +++ b/src/BaGet.Database.Sqlite/BaGet.Database.Sqlite.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/BaGet.Protocol/BaGet.Protocol.csproj b/src/BaGet.Protocol/BaGet.Protocol.csproj index 6fe30d8f..637838db 100644 --- a/src/BaGet.Protocol/BaGet.Protocol.csproj +++ b/src/BaGet.Protocol/BaGet.Protocol.csproj @@ -8,10 +8,10 @@ - - - - + + + + \ No newline at end of file diff --git a/src/BaGet.Web/BaGet.Web.csproj b/src/BaGet.Web/BaGet.Web.csproj index 58e0d121..b7360e68 100644 --- a/src/BaGet.Web/BaGet.Web.csproj +++ b/src/BaGet.Web/BaGet.Web.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/BaGet/BaGet.csproj b/src/BaGet/BaGet.csproj index 2bc470c5..84797f22 100644 --- a/src/BaGet/BaGet.csproj +++ b/src/BaGet/BaGet.csproj @@ -5,9 +5,12 @@ - - - + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index ac56489a..a98ad7fc 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,48 +1,48 @@ - - - Loic Sharma - Copyright (c) Loic Sharma 2021 - BaGet - - packageIcon.png - MIT - 0.1.0-prerelease - https://loic-sharma.github.io/BaGet/ - - - - - true - preview - true - - - $(NoWarn);1591 - - - - - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - portable - true - true - true - snupkg - - - - true - - - - - - - - - - + + + Loic Sharma + Copyright (c) Loic Sharma 2021 + BaGet + + packageIcon.png + MIT + 0.1.0-prerelease + https://loic-sharma.github.io/BaGet/ + + + + + true + preview + true + + + $(NoWarn);1591 + + + + + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + portable + true + true + true + snupkg + + + + true + + + + + + + + + + diff --git a/tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj b/tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj index de18991d..de1a7138 100644 --- a/tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj +++ b/tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj @@ -9,11 +9,11 @@ - + all runtime; build; native; contentfiles; analyzers - + diff --git a/tests/BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj b/tests/BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj index 51ed6dc4..e5174d93 100644 --- a/tests/BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj +++ b/tests/BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj @@ -24,11 +24,11 @@ - + all runtime; build; native; contentfiles; analyzers - + diff --git a/tests/BaGet.Tests/BaGet.Tests.csproj b/tests/BaGet.Tests/BaGet.Tests.csproj index 07ed9901..f1c7d5bc 100644 --- a/tests/BaGet.Tests/BaGet.Tests.csproj +++ b/tests/BaGet.Tests/BaGet.Tests.csproj @@ -11,8 +11,8 @@ - - + + @@ -42,11 +42,11 @@ - + all runtime; build; native; contentfiles; analyzers - + diff --git a/tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj b/tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj index b39f30b0..32005b86 100644 --- a/tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj +++ b/tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj @@ -9,11 +9,11 @@ - + all runtime; build; native; contentfiles; analyzers - + From d497a9fff137090729d2dc9e26ed77ca4af4eea1 Mon Sep 17 00:00:00 2001 From: Kannan Chithambaranathan Date: Fri, 2 Dec 2022 13:16:04 +0530 Subject: [PATCH 5/7] Project Updated --- samples/BaGetWebApplication/appsettings.json | 3 +++ src/BaGet.Aws/BaGet.Aws.csproj | 4 ++-- tests/BaGet.Tests/BaGet.Tests.csproj | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/samples/BaGetWebApplication/appsettings.json b/samples/BaGetWebApplication/appsettings.json index 5f8f0873..58e5dc87 100644 --- a/samples/BaGetWebApplication/appsettings.json +++ b/samples/BaGetWebApplication/appsettings.json @@ -20,6 +20,9 @@ //"Legacy": true, "PackageSource": "https://api.nuget.org/v3/index.json" }, + "IISServerOptions": { + "MaxRequestBodySize": 262144000 + }, "Logging": { "LogLevel": { diff --git a/src/BaGet.Aws/BaGet.Aws.csproj b/src/BaGet.Aws/BaGet.Aws.csproj index 071ea952..4ff8f7a3 100644 --- a/src/BaGet.Aws/BaGet.Aws.csproj +++ b/src/BaGet.Aws/BaGet.Aws.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/tests/BaGet.Tests/BaGet.Tests.csproj b/tests/BaGet.Tests/BaGet.Tests.csproj index f1c7d5bc..fae2180f 100644 --- a/tests/BaGet.Tests/BaGet.Tests.csproj +++ b/tests/BaGet.Tests/BaGet.Tests.csproj @@ -10,7 +10,7 @@ - + From 9dab2e615c9885c407157a81bb9a2ba8961ea552 Mon Sep 17 00:00:00 2001 From: Kannan Chithambaranathan Date: Thu, 22 Dec 2022 18:08:52 +0530 Subject: [PATCH 6/7] Project Updated --- .../BaGet.Protocol.Samples.Tests.csproj | 36 +++--- .../BaGetWebApplication.csproj | 20 ++-- src/BaGet.Aliyun/BaGet.Aliyun.csproj | 22 ++-- src/BaGet.Aws/BaGet.Aws.csproj | 26 ++--- src/BaGet.Azure/BaGet.Azure.csproj | 28 ++--- src/BaGet.Core/BaGet.Core.csproj | 32 +++--- .../BaGet.Database.MySql.csproj | 23 ++-- .../BaGet.Database.PostgreSql.csproj | 22 ++-- .../BaGet.Database.SqlServer.csproj | 22 ++-- .../BaGet.Database.Sqlite.csproj | 22 ++-- src/BaGet.Gcp/BaGet.Gcp.csproj | 22 ++-- src/BaGet.Protocol/BaGet.Protocol.csproj | 22 ++-- src/BaGet.Web/BaGet.Web.csproj | 34 +++--- src/BaGet/BaGet.csproj | 51 +++++---- .../BaGet.Core.Tests/BaGet.Core.Tests.csproj | 38 +++--- .../BaGet.Protocol.Tests.csproj | 64 +++++------ tests/BaGet.Tests/BaGet.Tests.csproj | 108 +++++++++--------- tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj | 38 +++--- 18 files changed, 317 insertions(+), 313 deletions(-) diff --git a/samples/BaGet.Protocol.Samples.Tests/BaGet.Protocol.Samples.Tests.csproj b/samples/BaGet.Protocol.Samples.Tests/BaGet.Protocol.Samples.Tests.csproj index 6b390535..5d520f0c 100644 --- a/samples/BaGet.Protocol.Samples.Tests/BaGet.Protocol.Samples.Tests.csproj +++ b/samples/BaGet.Protocol.Samples.Tests/BaGet.Protocol.Samples.Tests.csproj @@ -1,25 +1,25 @@ - - net6.0 + + net7.0 - false - IDE0007 - + false + IDE0007 + - - - - - - all - runtime; build; native; contentfiles; analyzers - - - + + + + + + all + runtime; build; native; contentfiles; analyzers + + + - - - + + + diff --git a/samples/BaGetWebApplication/BaGetWebApplication.csproj b/samples/BaGetWebApplication/BaGetWebApplication.csproj index 5cae439d..8bf22899 100644 --- a/samples/BaGetWebApplication/BaGetWebApplication.csproj +++ b/samples/BaGetWebApplication/BaGetWebApplication.csproj @@ -1,16 +1,16 @@ - - net6.0 - + + net7.0 + - - - - + + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet.Aliyun/BaGet.Aliyun.csproj b/src/BaGet.Aliyun/BaGet.Aliyun.csproj index b5ea641f..12595aaa 100644 --- a/src/BaGet.Aliyun/BaGet.Aliyun.csproj +++ b/src/BaGet.Aliyun/BaGet.Aliyun.csproj @@ -1,17 +1,17 @@ - - NuGet;Alibaba;Cloud - The libraries to host BaGet on Alibaba Cloud (Aliyun). - net6.0 - + + net7.0 + NuGet;Alibaba;Cloud + The libraries to host BaGet on Alibaba Cloud (Aliyun). + - - - + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet.Aws/BaGet.Aws.csproj b/src/BaGet.Aws/BaGet.Aws.csproj index 0685a977..7c2b0fb6 100644 --- a/src/BaGet.Aws/BaGet.Aws.csproj +++ b/src/BaGet.Aws/BaGet.Aws.csproj @@ -1,20 +1,20 @@ - - net6.0 + + net7.0 - NuGet;Amazon;Cloud - The libraries to host BaGet on AWS. - + NuGet;Amazon;Cloud + The libraries to host BaGet on AWS. + - - - - - + + + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet.Azure/BaGet.Azure.csproj b/src/BaGet.Azure/BaGet.Azure.csproj index 89064869..4cedc965 100644 --- a/src/BaGet.Azure/BaGet.Azure.csproj +++ b/src/BaGet.Azure/BaGet.Azure.csproj @@ -1,21 +1,21 @@ - - net6.0 + + net7.0 - NuGet;Azure;Cloud - The libraries to host BaGet on Azure. - + NuGet;Azure;Cloud + The libraries to host BaGet on Azure. + - - - - - - + + + + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet.Core/BaGet.Core.csproj b/src/BaGet.Core/BaGet.Core.csproj index 328a1654..50e406ca 100644 --- a/src/BaGet.Core/BaGet.Core.csproj +++ b/src/BaGet.Core/BaGet.Core.csproj @@ -1,23 +1,23 @@ - - net6.0 + + net7.0 - NuGet - The core libraries that power BaGet. - + NuGet + The core libraries that power BaGet. + - - - - - - - - + + + + + + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet.Database.MySql/BaGet.Database.MySql.csproj b/src/BaGet.Database.MySql/BaGet.Database.MySql.csproj index c6c2dca7..99847a69 100644 --- a/src/BaGet.Database.MySql/BaGet.Database.MySql.csproj +++ b/src/BaGet.Database.MySql/BaGet.Database.MySql.csproj @@ -1,18 +1,19 @@ - - net6.0 + + net7.0 - NuGet - The libraries to host BaGet on MySQL. - + NuGet + The libraries to host BaGet on MySQL. + - - - + + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet.Database.PostgreSql/BaGet.Database.PostgreSql.csproj b/src/BaGet.Database.PostgreSql/BaGet.Database.PostgreSql.csproj index 34f3ae89..1b5137cd 100644 --- a/src/BaGet.Database.PostgreSql/BaGet.Database.PostgreSql.csproj +++ b/src/BaGet.Database.PostgreSql/BaGet.Database.PostgreSql.csproj @@ -1,18 +1,18 @@ - - net6.0 + + net7.0 - NuGet - The libraries to host BaGet on PostgreSQL. - + NuGet + The libraries to host BaGet on PostgreSQL. + - - - + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet.Database.SqlServer/BaGet.Database.SqlServer.csproj b/src/BaGet.Database.SqlServer/BaGet.Database.SqlServer.csproj index e75fbc53..ab6dc32b 100644 --- a/src/BaGet.Database.SqlServer/BaGet.Database.SqlServer.csproj +++ b/src/BaGet.Database.SqlServer/BaGet.Database.SqlServer.csproj @@ -1,18 +1,18 @@ - - net6.0 + + net7.0 - NuGet - The libraries to host BaGet on SQL Server. - + NuGet + The libraries to host BaGet on SQL Server. + - - - + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet.Database.Sqlite/BaGet.Database.Sqlite.csproj b/src/BaGet.Database.Sqlite/BaGet.Database.Sqlite.csproj index 88988ffd..8da5b813 100644 --- a/src/BaGet.Database.Sqlite/BaGet.Database.Sqlite.csproj +++ b/src/BaGet.Database.Sqlite/BaGet.Database.Sqlite.csproj @@ -1,18 +1,18 @@ - - net6.0 + + net7.0 - NuGet - The libraries to host BaGet on SQLite. - + NuGet + The libraries to host BaGet on SQLite. + - - - + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet.Gcp/BaGet.Gcp.csproj b/src/BaGet.Gcp/BaGet.Gcp.csproj index 2049869d..9b77daa5 100644 --- a/src/BaGet.Gcp/BaGet.Gcp.csproj +++ b/src/BaGet.Gcp/BaGet.Gcp.csproj @@ -1,18 +1,18 @@ - - net6.0 + + net7.0 - NuGet;Google;Cloud - The libraries to host BaGet on the Google Cloud Platform. - + NuGet;Google;Cloud + The libraries to host BaGet on the Google Cloud Platform. + - - - + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet.Protocol/BaGet.Protocol.csproj b/src/BaGet.Protocol/BaGet.Protocol.csproj index 8c1053ee..9bc4dbf2 100644 --- a/src/BaGet.Protocol/BaGet.Protocol.csproj +++ b/src/BaGet.Protocol/BaGet.Protocol.csproj @@ -1,17 +1,17 @@ - - net6.0 + + net7.0 - NuGet;Protocol - Libraries to interact with NuGet server APIs. - + NuGet;Protocol + Libraries to interact with NuGet server APIs. + - - - - - - + + + + + + \ No newline at end of file diff --git a/src/BaGet.Web/BaGet.Web.csproj b/src/BaGet.Web/BaGet.Web.csproj index db8cb602..b7360e68 100644 --- a/src/BaGet.Web/BaGet.Web.csproj +++ b/src/BaGet.Web/BaGet.Web.csproj @@ -1,26 +1,26 @@ - - net6.0 + + net7.0 - NuGet - BaGet's NuGet server implementation - BaGet.Web + NuGet + BaGet's NuGet server implementation + BaGet.Web - true - + true + - - - + + + - - - - + + + + - - - + + + \ No newline at end of file diff --git a/src/BaGet/BaGet.csproj b/src/BaGet/BaGet.csproj index 52cf215d..94e1ab97 100644 --- a/src/BaGet/BaGet.csproj +++ b/src/BaGet/BaGet.csproj @@ -1,31 +1,34 @@ - - net6.0 - + + net7.0 + - - - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - + + + \ No newline at end of file diff --git a/tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj b/tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj index 3ac76755..f9e828df 100644 --- a/tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj +++ b/tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj @@ -1,25 +1,25 @@ - - net6.0 - + + net7.0 + - - - + + + - - - all - runtime; build; native; contentfiles; analyzers - - - - - - all - runtime; build; native; contentfiles; analyzers - - + + + all + runtime; build; native; contentfiles; analyzers + + + + + + all + runtime; build; native; contentfiles; analyzers + + \ No newline at end of file diff --git a/tests/BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj b/tests/BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj index e1757427..050266fb 100644 --- a/tests/BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj +++ b/tests/BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj @@ -1,40 +1,40 @@ - - net6.0 - + + net7.0 + - - - + + + - - - TestData.resx - True - True - - + + + TestData.resx + True + True + + - - - TestData.Designer.cs - ResXFileCodeGenerator - - + + + TestData.Designer.cs + ResXFileCodeGenerator + + - - - all - runtime; build; native; contentfiles; analyzers - - - - - - all - runtime; build; native; contentfiles; analyzers - - + + + all + runtime; build; native; contentfiles; analyzers + + + + + + all + runtime; build; native; contentfiles; analyzers + + \ No newline at end of file diff --git a/tests/BaGet.Tests/BaGet.Tests.csproj b/tests/BaGet.Tests/BaGet.Tests.csproj index 522f388a..85bac6b3 100644 --- a/tests/BaGet.Tests/BaGet.Tests.csproj +++ b/tests/BaGet.Tests/BaGet.Tests.csproj @@ -1,58 +1,58 @@ - - net6.0 - preview - - - - - - - - - - - - - - - - - - - True - True - TestData.resx - - - - - - TestData.Designer.cs - ResXFileCodeGenerator - BaGet.Tests - - - Never - - - Never - - - - - - all - runtime; build; native; contentfiles; analyzers - - - - - - all - runtime; build; native; contentfiles; analyzers - - + + net7.0 + preview + + + + + + + + + + + + + + + + + + + True + True + TestData.resx + + + + + + TestData.Designer.cs + ResXFileCodeGenerator + BaGet.Tests + + + Never + + + Never + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + all + runtime; build; native; contentfiles; analyzers + + \ No newline at end of file diff --git a/tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj b/tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj index d30d9081..4c3fe7ea 100644 --- a/tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj +++ b/tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj @@ -1,25 +1,25 @@ - - net6.0 - + + net7.0 + - - - + + + - - - all - runtime; build; native; contentfiles; analyzers - - - - - - all - runtime; build; native; contentfiles; analyzers - - + + + all + runtime; build; native; contentfiles; analyzers + + + + + + all + runtime; build; native; contentfiles; analyzers + + \ No newline at end of file From 961046d643ab5379fdae1337df4c32b20a68875b Mon Sep 17 00:00:00 2001 From: Kannan Chithambaranathan Date: Sat, 20 May 2023 17:11:51 +0530 Subject: [PATCH 7/7] Project Updated --- .../BaGet.Protocol.Samples.Tests.csproj | 4 ++-- .../BaGetWebApplication/BaGetWebApplication.csproj | 4 +++- samples/BaGetWebApplication/appsettings.json | 10 +++++----- src/BaGet.Aws/BaGet.Aws.csproj | 9 ++------- src/BaGet.Core/BaGet.Core.csproj | 10 +++------- src/BaGet.Database.MySql/BaGet.Database.MySql.csproj | 12 ++---------- .../BaGet.Database.PostgreSql.csproj | 6 +----- .../BaGet.Database.SqlServer.csproj | 6 +----- .../BaGet.Database.Sqlite.csproj | 6 +----- src/BaGet.Gcp/BaGet.Gcp.csproj | 2 +- src/BaGet.Protocol/BaGet.Protocol.csproj | 10 +++------- src/BaGet.Web/BaGet.Web.csproj | 2 +- src/BaGet/BaGet.csproj | 4 ++-- tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj | 4 ++-- .../BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj | 4 ++-- tests/BaGet.Tests/BaGet.Tests.csproj | 10 +++++----- tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj | 9 ++------- 17 files changed, 38 insertions(+), 74 deletions(-) diff --git a/samples/BaGet.Protocol.Samples.Tests/BaGet.Protocol.Samples.Tests.csproj b/samples/BaGet.Protocol.Samples.Tests/BaGet.Protocol.Samples.Tests.csproj index 5d520f0c..6813e034 100644 --- a/samples/BaGet.Protocol.Samples.Tests/BaGet.Protocol.Samples.Tests.csproj +++ b/samples/BaGet.Protocol.Samples.Tests/BaGet.Protocol.Samples.Tests.csproj @@ -8,8 +8,8 @@ - - + + all diff --git a/samples/BaGetWebApplication/BaGetWebApplication.csproj b/samples/BaGetWebApplication/BaGetWebApplication.csproj index 8bf22899..ce833034 100644 --- a/samples/BaGetWebApplication/BaGetWebApplication.csproj +++ b/samples/BaGetWebApplication/BaGetWebApplication.csproj @@ -2,6 +2,8 @@ net7.0 + + brushtailnuget @@ -10,7 +12,7 @@ - + \ No newline at end of file diff --git a/samples/BaGetWebApplication/appsettings.json b/samples/BaGetWebApplication/appsettings.json index 58e5dc87..34877722 100644 --- a/samples/BaGetWebApplication/appsettings.json +++ b/samples/BaGetWebApplication/appsettings.json @@ -3,13 +3,13 @@ "PackageDeletionBehavior": "HardDelete", "AllowPackageOverwrites": true, "Database": { - "Type": "Sqlite", + //"Type": "Sqlite", "ConnectionString": "Data Source=baget.db" }, - "Storage": { - "Type": "FileSystem", - "Path": "C://inetpub//sites//nuget.brushtailtech.com" - }, + //"Storage": { + // "Type": "FileSystem", + // "Path": "C://inetpub//sites//nuget.brushtailtech.com" + //}, "Search": { "Type": "Database" }, diff --git a/src/BaGet.Aws/BaGet.Aws.csproj b/src/BaGet.Aws/BaGet.Aws.csproj index e3b23556..a433baf2 100644 --- a/src/BaGet.Aws/BaGet.Aws.csproj +++ b/src/BaGet.Aws/BaGet.Aws.csproj @@ -8,13 +8,8 @@ -<<<<<<< HEAD - - -======= - - ->>>>>>> d497a9fff137090729d2dc9e26ed77ca4af4eea1 + + diff --git a/src/BaGet.Core/BaGet.Core.csproj b/src/BaGet.Core/BaGet.Core.csproj index 6694ff29..3c3c30ba 100644 --- a/src/BaGet.Core/BaGet.Core.csproj +++ b/src/BaGet.Core/BaGet.Core.csproj @@ -8,16 +8,12 @@ -<<<<<<< HEAD - -======= - ->>>>>>> d497a9fff137090729d2dc9e26ed77ca4af4eea1 + - - + + diff --git a/src/BaGet.Database.MySql/BaGet.Database.MySql.csproj b/src/BaGet.Database.MySql/BaGet.Database.MySql.csproj index 6804c029..fc302388 100644 --- a/src/BaGet.Database.MySql/BaGet.Database.MySql.csproj +++ b/src/BaGet.Database.MySql/BaGet.Database.MySql.csproj @@ -8,18 +8,10 @@ - -<<<<<<< HEAD - + + -======= - - - - - ->>>>>>> d497a9fff137090729d2dc9e26ed77ca4af4eea1 diff --git a/src/BaGet.Database.PostgreSql/BaGet.Database.PostgreSql.csproj b/src/BaGet.Database.PostgreSql/BaGet.Database.PostgreSql.csproj index 94f9b45e..3624805c 100644 --- a/src/BaGet.Database.PostgreSql/BaGet.Database.PostgreSql.csproj +++ b/src/BaGet.Database.PostgreSql/BaGet.Database.PostgreSql.csproj @@ -8,11 +8,7 @@ -<<<<<<< HEAD - -======= - ->>>>>>> d497a9fff137090729d2dc9e26ed77ca4af4eea1 + diff --git a/src/BaGet.Database.SqlServer/BaGet.Database.SqlServer.csproj b/src/BaGet.Database.SqlServer/BaGet.Database.SqlServer.csproj index 94254edc..55a16ccf 100644 --- a/src/BaGet.Database.SqlServer/BaGet.Database.SqlServer.csproj +++ b/src/BaGet.Database.SqlServer/BaGet.Database.SqlServer.csproj @@ -8,11 +8,7 @@ -<<<<<<< HEAD - -======= - ->>>>>>> d497a9fff137090729d2dc9e26ed77ca4af4eea1 + diff --git a/src/BaGet.Database.Sqlite/BaGet.Database.Sqlite.csproj b/src/BaGet.Database.Sqlite/BaGet.Database.Sqlite.csproj index 226f854e..38f342f0 100644 --- a/src/BaGet.Database.Sqlite/BaGet.Database.Sqlite.csproj +++ b/src/BaGet.Database.Sqlite/BaGet.Database.Sqlite.csproj @@ -8,11 +8,7 @@ -<<<<<<< HEAD - -======= - ->>>>>>> d497a9fff137090729d2dc9e26ed77ca4af4eea1 + diff --git a/src/BaGet.Gcp/BaGet.Gcp.csproj b/src/BaGet.Gcp/BaGet.Gcp.csproj index 9b77daa5..67210d29 100644 --- a/src/BaGet.Gcp/BaGet.Gcp.csproj +++ b/src/BaGet.Gcp/BaGet.Gcp.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/BaGet.Protocol/BaGet.Protocol.csproj b/src/BaGet.Protocol/BaGet.Protocol.csproj index b48c3073..0355ca98 100644 --- a/src/BaGet.Protocol/BaGet.Protocol.csproj +++ b/src/BaGet.Protocol/BaGet.Protocol.csproj @@ -9,13 +9,9 @@ - - -<<<<<<< HEAD - -======= - ->>>>>>> d497a9fff137090729d2dc9e26ed77ca4af4eea1 + + + \ No newline at end of file diff --git a/src/BaGet.Web/BaGet.Web.csproj b/src/BaGet.Web/BaGet.Web.csproj index b7360e68..939e90dc 100644 --- a/src/BaGet.Web/BaGet.Web.csproj +++ b/src/BaGet.Web/BaGet.Web.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/BaGet/BaGet.csproj b/src/BaGet/BaGet.csproj index 94e1ab97..cee84153 100644 --- a/src/BaGet/BaGet.csproj +++ b/src/BaGet/BaGet.csproj @@ -6,8 +6,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj b/tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj index f9e828df..37cbdc02 100644 --- a/tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj +++ b/tests/BaGet.Core.Tests/BaGet.Core.Tests.csproj @@ -13,8 +13,8 @@ all runtime; build; native; contentfiles; analyzers - - + + all diff --git a/tests/BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj b/tests/BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj index 050266fb..186ff70c 100644 --- a/tests/BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj +++ b/tests/BaGet.Protocol.Tests/BaGet.Protocol.Tests.csproj @@ -28,8 +28,8 @@ all runtime; build; native; contentfiles; analyzers - - + + all diff --git a/tests/BaGet.Tests/BaGet.Tests.csproj b/tests/BaGet.Tests/BaGet.Tests.csproj index 85bac6b3..95ec565c 100644 --- a/tests/BaGet.Tests/BaGet.Tests.csproj +++ b/tests/BaGet.Tests/BaGet.Tests.csproj @@ -10,9 +10,9 @@ - - - + + + @@ -46,8 +46,8 @@ all runtime; build; native; contentfiles; analyzers - - + + all diff --git a/tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj b/tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj index 3d24168d..cddaeed9 100644 --- a/tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj +++ b/tests/BaGet.Web.Tests/BaGet.Web.Tests.csproj @@ -13,13 +13,8 @@ all runtime; build; native; contentfiles; analyzers -<<<<<<< HEAD - - -======= - - ->>>>>>> d497a9fff137090729d2dc9e26ed77ca4af4eea1 + + all