bagetter / BaGetter

A lightweight NuGet and symbol server
https://www.bagetter.com
MIT License
166 stars 38 forks source link

BaGetter fails to parse an upstream RegistrationsBaseUrl request. #89

Closed Splamy closed 4 months ago

Splamy commented 4 months ago

Describe the bug

BaGetter fails to parse an upstream RegistrationsBaseUrl request.

According to the documentation NugetV3 allows for the tags of PackageMetadata/CatalogEntry to be either a string or string[]:
https://learn.microsoft.com/en-us/nuget/api/registration-base-url-resource#catalog-entry

And it seems like Github Packages is one of the few implementations that uses this possibility since when requesting i get:

{
    "count": 1,
    "items": [
        {
            "@id": "https://nuget.pkg.github.com/FakeOrga/fakepkg/index.json",
            "lower": "1.4.1",
            "upper": "1.5.1",
            "count": 2,
            "items": [
                {
                    "@id": "https://nuget.pkg.github.com/FakeOrga/fakepkg/1.5.1.json",
                    "packageContent": "https://nuget.pkg.github.com/FakeOrga/download/fakepkg/1.5.1/fakepkg.1.5.1.nupkg",
                    "catalogEntry": {
                        "@id": "https://nuget.pkg.github.com/FakeOrga/fakepkg/1.5.1.json",
                        "authors": "Someone",
                        "copyright": "",
                        "dependencyGroups": [ ],
                        "description": "Package Description",
                        "iconUrl": "",
                        "id": "fakepkg",
                        "isPrerelease": false,
                        "language": "",
                        "licenseUrl": "",
                        "packageContent": "https://nuget.pkg.github.com/FakeOrga/download/fakepkg/1.5.1/fakepkg.1.5.1.nupkg",
                        "projectUrl": "",
                        "requireLicenseAcceptance": false,
                        "summary": "",
                        "tags": "",
                        "version": "1.5.1"
                    }
                }
            ]
        }
    ]
}

(details anonymized and shortened)

Since the current implementation only handles string[] https://github.com/bagetter/BaGetter/blob/f2447c4901355867df7f23c778f36db97f5c736f/src/BaGetter.Protocol/Models/PackageMetadata.cs#L116-L120

BaGetter will fail with

fail: BaGetter.Core.V3UpstreamClient[0]
      Failed to mirror fakepkg's upstream metadata
      System.Text.Json.JsonException: The JSON value could not be converted to System.Collections.Generic.IReadOnlyList`1[System.String]. Path: $.items[0].items[0].catalogEntry.tags | LineNumber: 0 | BytePositionInLine: 4828.
         at System.Text.Json.ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Type propertyType)
         at System.Text.Json.Serialization.JsonCollectionConverter`2.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, TCollection& value)
         at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
         at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
         at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
         at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
         at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
         at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
         at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
         at System.Text.Json.Serialization.JsonCollectionConverter`2.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, TCollection& value)
         at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
         at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
         at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
         at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
         at System.Text.Json.Serialization.JsonCollectionConverter`2.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, TCollection& value)
         at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
         at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
         at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
         at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
         at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
         at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.ContinueDeserialize(ReadBufferState& bufferState, JsonReaderState& jsonReaderState, ReadStack& readStack)
         at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsync(Stream utf8Json, CancellationToken cancellationToken)
         at BaGetter.Protocol.HttpClientExtensions.GetFromJsonOrDefaultAsync[TResult](HttpClient httpClient, String requestUri, CancellationToken cancellationToken) in D:\Extern\BaGetter\src\BaGetter.Protocol\Extensions\HttpClientExtensions.cs:line 61
         at BaGetter.Protocol.Internal.RawPackageMetadataClient.GetRegistrationIndexOrNullAsync(String packageId, CancellationToken cancellationToken) in D:\Extern\BaGetter\src\BaGetter.Protocol\PackageMetadata\RawPackageMetadataClient.cs:line 35
         at BaGetter.Protocol.NuGetClientFactory.PackageMetadataClient.GetRegistrationIndexOrNullAsync(String packageId, CancellationToken cancellationToken) in D:\Extern\BaGetter\src\BaGetter.Protocol\PackageMetadata\PackageMetadataClient.cs:line 25
         at BaGetter.Protocol.NuGetClient.GetPackageMetadataAsync(String packageId, CancellationToken cancellationToken) in D:\Extern\BaGetter\src\BaGetter.Protocol\NuGetClient.cs:line 213
         at BaGetter.Core.V3UpstreamClient.ListPackagesAsync(String id, CancellationToken cancellationToken) in D:\Extern\BaGetter\src\BaGetter.Core\Upstream\Clients\V3UpstreamClient.cs:line 60

To Reproduce

Steps to reproduce the behavior:

  1. Using the currently lastest master version of BaGetter: f2447c4
  2. Setup any Github Packages feed as upstream
  3. Configure the httpClient for auth parameters https://github.com/bagetter/BaGetter/blob/main/src/BaGetter.Core/Extensions/DependencyInjectionExtensions.cs#L190 httpClient.DefaultRequestHeaders.Authorization = new("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"Username:PAT")));
  4. Request any package metadata from the upstream mirror through BaGetter

Expected behavior

The implementation should be able to pass both variations

PR

If you want I can contribute a fix PR for this with something like https://stackoverflow.com/a/59430729/2444047 Edit: i see you already have exactly this converter, just tested and adding the converter attribute fixes it

seriouz commented 4 months ago

Thank you for bringing this up! Can you create an PR with the change?