diff --git a/src/NuGet.Protocol.Catalog/Models/PackageDetailsCatalogLeaf.cs b/src/NuGet.Protocol.Catalog/Models/PackageDetailsCatalogLeaf.cs index be905d855..1534353b5 100644 --- a/src/NuGet.Protocol.Catalog/Models/PackageDetailsCatalogLeaf.cs +++ b/src/NuGet.Protocol.Catalog/Models/PackageDetailsCatalogLeaf.cs @@ -61,6 +61,9 @@ public class PackageDetailsCatalogLeaf : CatalogLeaf [JsonProperty("packageSize")] public long PackageSize { get; set; } + [JsonProperty("packageTypes")] + public List PackageTypes { get; set; } + [JsonProperty("projectUrl")] public string ProjectUrl { get; set; } diff --git a/src/NuGet.Protocol.Catalog/Models/PackageType.cs b/src/NuGet.Protocol.Catalog/Models/PackageType.cs new file mode 100644 index 000000000..854788638 --- /dev/null +++ b/src/NuGet.Protocol.Catalog/Models/PackageType.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace NuGet.Protocol.Catalog +{ + /// + /// Source: https://docs.microsoft.com/en-us/nuget/api/catalog-resource#catalog-leaf + /// + public class PackageType + { + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("version")] + public string Version { get; set; } + } +} diff --git a/src/NuGet.Protocol.Catalog/NuGet.Protocol.Catalog.csproj b/src/NuGet.Protocol.Catalog/NuGet.Protocol.Catalog.csproj index 5a6fb8256..940af03b2 100644 --- a/src/NuGet.Protocol.Catalog/NuGet.Protocol.Catalog.csproj +++ b/src/NuGet.Protocol.Catalog/NuGet.Protocol.Catalog.csproj @@ -93,6 +93,7 @@ + diff --git a/src/NuGet.Services.AzureSearch/SearchDocumentBuilder.cs b/src/NuGet.Services.AzureSearch/SearchDocumentBuilder.cs index bd24de59a..a67700f8c 100644 --- a/src/NuGet.Services.AzureSearch/SearchDocumentBuilder.cs +++ b/src/NuGet.Services.AzureSearch/SearchDocumentBuilder.cs @@ -168,6 +168,12 @@ public SearchDocument.UpdateLatest UpdateLatestFromCatalog( { var document = new SearchDocument.UpdateLatest(); + // Determine if we have packageTypes to forward. + // Otherwise, we need to let the system know that there were no explicit package types + var packageTypes = leaf.PackageTypes != null && leaf.PackageTypes.Count > 0 ? + leaf.PackageTypes.Select(pt => pt.Name).ToArray() : + null; + PopulateUpdateLatest( document, leaf.PackageId, @@ -180,7 +186,7 @@ public SearchDocument.UpdateLatest UpdateLatestFromCatalog( isLatest: isLatest, fullVersion: fullVersion, owners: owners, - packageTypes: null); + packageTypes: packageTypes); _baseDocumentBuilder.PopulateMetadata(document, normalizedVersion, leaf); return document; diff --git a/tests/NuGet.Services.AzureSearch.Tests/SearchDocumentBuilderFacts.cs b/tests/NuGet.Services.AzureSearch.Tests/SearchDocumentBuilderFacts.cs index f0a4c172b..4589b6380 100644 --- a/tests/NuGet.Services.AzureSearch.Tests/SearchDocumentBuilderFacts.cs +++ b/tests/NuGet.Services.AzureSearch.Tests/SearchDocumentBuilderFacts.cs @@ -442,6 +442,29 @@ public async Task SetsExpectedProperties(SearchFilters searchFilters, string exp }", json); } + [Theory] + [MemberData(nameof(CatalogPackageTypesData))] + public void SetsExpectedPackageTypes(List packageTypes, string[] expectedFilterable, string[] expectedDisplay) + { + var leaf = Data.Leaf; + leaf.PackageTypes = packageTypes; + + var document = _target.UpdateLatestFromCatalog( + SearchFilters.Default, + Data.Versions, + isLatestStable: false, + isLatest: true, + normalizedVersion: Data.NormalizedVersion, + fullVersion: Data.FullVersion, + leaf: leaf, + owners: Data.Owners); + + SetDocumentLastUpdated(document); + Assert.Equal(document.FilterablePackageTypes.Length, document.PackageTypes.Length); + Assert.Equal(expectedFilterable, document.FilterablePackageTypes); + Assert.Equal(expectedDisplay, document.PackageTypes); + } + [Fact] public void LeavesNullRequiresLicenseAcceptanceAsNull() { @@ -747,8 +770,8 @@ public async Task SetsExpectedProperties(SearchFilters searchFilters, string exp } [Theory] - [MemberData(nameof(PackageTypesData))] - public async Task SetsExpectedPackageTypes(List packageTypes, string expectedFilterable, string expectedDisplay) + [MemberData(nameof(DBPackageTypesData))] + public void SetsExpectedPackageTypes(List packageTypes, string[] expectedFilterable, string[] expectedDisplay) { var package = Data.PackageEntity; package.PackageTypes = packageTypes; @@ -766,16 +789,9 @@ public async Task SetsExpectedPackageTypes(List packageTypes, strin isExcludedByDefault: false); SetDocumentLastUpdated(document); - var json = await SerializationUtilities.SerializeToJsonAsync(document); - Assert.Contains(@" - ""filterablePackageTypes"": [ - " + expectedFilterable + @" - ],", json); - - Assert.Contains(@" - ""packageTypes"": [ - " + expectedDisplay + @" - ],", json); + Assert.Equal(document.FilterablePackageTypes.Length, document.PackageTypes.Length); + Assert.Equal(expectedFilterable, document.FilterablePackageTypes); + Assert.Equal(expectedDisplay, document.PackageTypes); } [Fact] @@ -868,7 +884,60 @@ public abstract class BaseFacts new object[] { SearchFilters.IncludePrereleaseAndSemVer2, "IncludePrereleaseAndSemVer2" }, }; - public static IEnumerable PackageTypesData => new[] + public static IEnumerable CatalogPackageTypesData => new[] + { + new object[] { + new List { + new NuGet.Protocol.Catalog.PackageType + { + Name = "DotNetCliTool" + } + }, + new string[] { "dotnetclitool" }, + new string[] { "DotNetCliTool" } + }, + + new object[] { + null, + new string[] { "dependency" }, + new string[] { "Dependency" } + }, + + new object[] { + new List(), + new string[] { "dependency" }, + new string[] { "Dependency" } + }, + + new object[] { + new List { + new NuGet.Protocol.Catalog.PackageType + { + Name = "DotNetCliTool" + }, + new NuGet.Protocol.Catalog.PackageType + { + Name = "Dependency" + } + }, + new string[] { "dotnetclitool", "dependency" }, + new string[] { "DotNetCliTool", "Dependency" }, + }, + + new object[] { + new List { + new NuGet.Protocol.Catalog.PackageType + { + Name = "DotNetCliTool", + Version = "1.0.0" + } + }, + new string[] { "dotnetclitool" }, + new string[] { "DotNetCliTool" } + }, + }; + + public static IEnumerable DBPackageTypesData => new[] { new object[] { new List { @@ -877,20 +946,20 @@ public abstract class BaseFacts Name = "DotNetCliTool" } }, - @"""dotnetclitool""", - @"""DotNetCliTool""" + new string[] { "dotnetclitool" }, + new string[] { "DotNetCliTool" } }, new object[] { null, - @"""dependency""", - @"""Dependency""" + new string[] { "dependency" }, + new string[] { "Dependency" } }, new object[] { new List(), - @"""dependency""", - @"""Dependency""" + new string[] { "dependency" }, + new string[] { "Dependency" } }, new object[] { @@ -904,8 +973,8 @@ public abstract class BaseFacts Name = "Dependency" } }, - "\"dotnetclitool\",\r\n \"dependency\"", - "\"DotNetCliTool\",\r\n \"Dependency\"", + new string[] { "dotnetclitool", "dependency" }, + new string[] { "DotNetCliTool", "Dependency" }, }, new object[] { @@ -916,8 +985,8 @@ public abstract class BaseFacts Version = "1.0.0" } }, - @"""dotnetclitool""", - @"""DotNetCliTool""" + new string[] { "dotnetclitool" }, + new string[] { "DotNetCliTool" } }, };