Skip to content

Commit 764ce67

Browse files
committed
V2 autocomplete endpoints utilize V3 autocomplete service instead of SQL Database
1 parent dca1625 commit 764ce67

10 files changed

Lines changed: 240 additions & 23 deletions

src/NuGetGallery/App_Start/DefaultDependenciesModule.cs

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -247,22 +247,14 @@ protected override void Load(ContainerBuilder builder)
247247
.As<IAutomaticallyCuratePackageCommand>()
248248
.InstancePerLifetimeScope();
249249

250-
builder.RegisterType<PackageIdsQuery>()
251-
.AsSelf()
252-
.As<IPackageIdsQuery>()
253-
.InstancePerLifetimeScope();
254-
255-
builder.RegisterType<PackageVersionsQuery>()
256-
.AsSelf()
257-
.As<IPackageVersionsQuery>()
258-
.InstancePerLifetimeScope();
250+
ConfigureAutocomplete(builder, configuration);
259251

260252
builder.RegisterType<DiagnosticsService>()
261253
.AsSelf()
262254
.As<IDiagnosticsService>()
263255
.SingleInstance();
264256
}
265-
257+
266258
private static void ConfigureSearch(ContainerBuilder builder, ConfigurationService configuration)
267259
{
268260
if (configuration.Current.ServiceDiscoveryUri == null)
@@ -285,6 +277,34 @@ private static void ConfigureSearch(ContainerBuilder builder, ConfigurationServi
285277
.InstancePerLifetimeScope();
286278
}
287279
}
280+
private void ConfigureAutocomplete(ContainerBuilder builder, ConfigurationService configuration)
281+
{
282+
if (configuration.Current.ServiceDiscoveryUri != null &&
283+
!string.IsNullOrEmpty(configuration.Current.AutocompleteServiceResourceType))
284+
{
285+
builder.RegisterType<AutocompleteServicePackageIdsQuery>()
286+
.AsSelf()
287+
.As<IPackageIdsQuery>()
288+
.SingleInstance();
289+
290+
builder.RegisterType<AutocompleteServicePackageVersionsQuery>()
291+
.AsSelf()
292+
.As<IPackageVersionsQuery>()
293+
.InstancePerLifetimeScope();
294+
}
295+
else
296+
{
297+
builder.RegisterType<PackageIdsQuery>()
298+
.AsSelf()
299+
.As<IPackageIdsQuery>()
300+
.InstancePerLifetimeScope();
301+
302+
builder.RegisterType<PackageVersionsQuery>()
303+
.AsSelf()
304+
.As<IPackageVersionsQuery>()
305+
.InstancePerLifetimeScope();
306+
}
307+
}
288308

289309
private static void ConfigureForLocalFileSystem(ContainerBuilder builder)
290310
{

src/NuGetGallery/Configuration/AppConfiguration.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ public class AppConfiguration : IAppConfiguration
4747
/// </summary>
4848
public string SearchServiceResourceType { get; set; }
4949

50+
/// <summary>
51+
/// Gets the @type for the Autocomplete endpoint
52+
/// </summary>
53+
public string AutocompleteServiceResourceType { get; set; }
54+
5055
/// <summary>
5156
/// Gets the URI to the metrics service
5257
/// </summary>

src/NuGetGallery/Configuration/IAppConfiguration.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ public interface IAppConfiguration
5353
/// </summary>
5454
string SearchServiceResourceType { get; set; }
5555

56+
/// <summary>
57+
/// Gets the @type for the Autocomplete endpoint
58+
/// </summary>
59+
string AutocompleteServiceResourceType { get; set; }
60+
5661
/// <summary>
5762
/// Gets the URI to the metrics service
5863
/// </summary>

src/NuGetGallery/Controllers/ApiController.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -451,24 +451,24 @@ protected internal virtual INupkg ReadPackageFromRequest()
451451

452452
[HttpGet]
453453
[ActionName("PackageIDs")]
454-
public virtual ActionResult GetPackageIds(string partialId, bool? includePrerelease)
454+
public virtual async Task<ActionResult> GetPackageIds(string partialId, bool? includePrerelease)
455455
{
456456
var query = GetService<IPackageIdsQuery>();
457457
return new JsonResult
458458
{
459-
Data = (query.Execute(partialId, includePrerelease).ToArray()),
459+
Data = (await query.Execute(partialId, includePrerelease)).ToArray(),
460460
JsonRequestBehavior = JsonRequestBehavior.AllowGet
461461
};
462462
}
463463

464464
[HttpGet]
465465
[ActionName("PackageVersions")]
466-
public virtual ActionResult GetPackageVersions(string id, bool? includePrerelease)
466+
public virtual async Task<ActionResult> GetPackageVersions(string id, bool? includePrerelease)
467467
{
468468
var query = GetService<IPackageVersionsQuery>();
469469
return new JsonResult
470470
{
471-
Data = query.Execute(id, includePrerelease).ToArray(),
471+
Data = (await query.Execute(id, includePrerelease)).ToArray(),
472472
JsonRequestBehavior = JsonRequestBehavior.AllowGet
473473
};
474474
}

src/NuGetGallery/Queries/PackageIdsQuery.cs

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,66 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
34
using System;
45
using System.Collections.Generic;
56
using System.Data.Entity;
67
using System.Globalization;
8+
using System.Linq;
9+
using System.Net.Http;
10+
using System.Threading.Tasks;
11+
using Newtonsoft.Json.Linq;
12+
using NuGet.Services.Search.Client;
13+
using NuGetGallery.Configuration;
714

815
namespace NuGetGallery
916
{
1017
public interface IPackageIdsQuery
1118
{
12-
IEnumerable<string> Execute(
19+
Task<IEnumerable<string>> Execute(
1320
string partialId,
1421
bool? includePrerelease = false);
1522
}
1623

24+
public class AutocompleteServicePackageIdsQuery : IPackageIdsQuery
25+
{
26+
private readonly ServiceDiscoveryClient _serviceDiscoveryClient;
27+
private readonly string _autocompleteServiceResourceType;
28+
private readonly RetryingHttpClientWrapper _httpClient;
29+
30+
public AutocompleteServicePackageIdsQuery(IAppConfiguration configuration)
31+
{
32+
_serviceDiscoveryClient = new ServiceDiscoveryClient(configuration.ServiceDiscoveryUri);
33+
_autocompleteServiceResourceType = configuration.AutocompleteServiceResourceType;
34+
_httpClient = new RetryingHttpClientWrapper(new HttpClient());
35+
}
36+
37+
public async Task<IEnumerable<string>> Execute(string partialId, bool? includePrerelease)
38+
{
39+
if (partialId == null)
40+
{
41+
partialId = string.Empty;
42+
}
43+
44+
var queryString = "take=30&q=" + Uri.EscapeUriString(partialId);
45+
if (!includePrerelease.HasValue)
46+
{
47+
queryString += "&prerelease=false";
48+
}
49+
else
50+
{
51+
queryString += "&prerelease=" + includePrerelease.Value;
52+
}
53+
54+
var endpoints = await _serviceDiscoveryClient.GetEndpointsForResourceType(_autocompleteServiceResourceType);
55+
endpoints = endpoints.Select(e => new Uri(e + "?" + queryString)).AsEnumerable();
56+
57+
var result = await _httpClient.GetStringAsync(endpoints);
58+
var resultObject = JObject.Parse(result);
59+
60+
return resultObject["data"].Select(entry => entry.ToString());
61+
}
62+
}
63+
1764
public class PackageIdsQuery : IPackageIdsQuery
1865
{
1966
private const string PartialIdSqlFormat = @"SELECT TOP 30 pr.ID
@@ -37,24 +84,24 @@ public PackageIdsQuery(IEntitiesContext entities)
3784
_entities = entities;
3885
}
3986

40-
public IEnumerable<string> Execute(
87+
public Task<IEnumerable<string>> Execute(
4188
string partialId,
4289
bool? includePrerelease = false)
4390
{
4491
var dbContext = (DbContext)_entities;
4592

4693
if (String.IsNullOrWhiteSpace(partialId))
4794
{
48-
return dbContext.Database.SqlQuery<string>(NoPartialIdSql);
95+
return Task.FromResult(dbContext.Database.SqlQuery<string>(NoPartialIdSql));
4996
}
5097

5198
var prereleaseFilter = String.Empty;
5299
if (!includePrerelease.HasValue || !includePrerelease.Value)
53100
{
54101
prereleaseFilter = "AND p.IsPrerelease = {1}";
55102
}
56-
return dbContext.Database.SqlQuery<string>(
57-
String.Format(CultureInfo.InvariantCulture, PartialIdSqlFormat, prereleaseFilter), partialId + "%", includePrerelease ?? false);
103+
return Task.FromResult(dbContext.Database.SqlQuery<string>(
104+
String.Format(CultureInfo.InvariantCulture, PartialIdSqlFormat, prereleaseFilter), partialId + "%", includePrerelease ?? false));
58105
}
59106
}
60107
}

src/NuGetGallery/Queries/PackageVersionsQuery.cs

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,66 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
34
using System;
45
using System.Collections.Generic;
56
using System.Data.Entity;
67
using System.Globalization;
8+
using System.Linq;
9+
using System.Net.Http;
10+
using System.Threading.Tasks;
11+
using Newtonsoft.Json.Linq;
12+
using NuGet.Services.Search.Client;
13+
using NuGetGallery.Configuration;
714

815
namespace NuGetGallery
916
{
1017
public interface IPackageVersionsQuery
1118
{
12-
IEnumerable<string> Execute(
19+
Task<IEnumerable<string>> Execute(
1320
string id,
1421
bool? includePrerelease = false);
1522
}
1623

24+
public class AutocompleteServicePackageVersionsQuery : IPackageVersionsQuery
25+
{
26+
private readonly ServiceDiscoveryClient _serviceDiscoveryClient;
27+
private readonly string _autocompleteServiceResourceType;
28+
private readonly RetryingHttpClientWrapper _httpClient;
29+
30+
public AutocompleteServicePackageVersionsQuery(IAppConfiguration configuration)
31+
{
32+
_serviceDiscoveryClient = new ServiceDiscoveryClient(configuration.ServiceDiscoveryUri);
33+
_autocompleteServiceResourceType = configuration.AutocompleteServiceResourceType;
34+
_httpClient = new RetryingHttpClientWrapper(new HttpClient());
35+
}
36+
37+
public async Task<IEnumerable<string>> Execute(string id, bool? includePrerelease)
38+
{
39+
if (string.IsNullOrWhiteSpace(id))
40+
{
41+
throw new ArgumentNullException("id");
42+
}
43+
44+
var queryString = "take=30&id=" + Uri.EscapeUriString(id);
45+
if (!includePrerelease.HasValue)
46+
{
47+
queryString += "&prerelease=false";
48+
}
49+
else
50+
{
51+
queryString += "&prerelease=" + includePrerelease.Value;
52+
}
53+
54+
var endpoints = await _serviceDiscoveryClient.GetEndpointsForResourceType(_autocompleteServiceResourceType);
55+
endpoints = endpoints.Select(e => new Uri(e + "?" + queryString)).AsEnumerable();
56+
57+
var result = await _httpClient.GetStringAsync(endpoints);
58+
var resultObject = JObject.Parse(result);
59+
60+
return resultObject["data"].Select(entry => entry.ToString());
61+
}
62+
}
63+
1764
public class PackageVersionsQuery : IPackageVersionsQuery
1865
{
1966
private const string SqlFormat = @"SELECT p.[Version]
@@ -29,7 +76,7 @@ public PackageVersionsQuery(IEntitiesContext entities)
2976
_entities = entities;
3077
}
3178

32-
public IEnumerable<string> Execute(
79+
public Task<IEnumerable<string>> Execute(
3380
string id,
3481
bool? includePrerelease = false)
3582
{
@@ -45,8 +92,8 @@ public IEnumerable<string> Execute(
4592
{
4693
prereleaseFilter = "AND p.IsPrerelease = 0";
4794
}
48-
return dbContext.Database.SqlQuery<string>(
49-
String.Format(CultureInfo.InvariantCulture, SqlFormat, prereleaseFilter), id);
95+
return Task.FromResult(dbContext.Database.SqlQuery<string>(
96+
String.Format(CultureInfo.InvariantCulture, SqlFormat, prereleaseFilter), id));
5097
}
5198
}
5299
}

src/NuGetGallery/Web.config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,11 @@
7676
<!-- Example:
7777
<add key="Gallery.ServiceDiscoveryUri" value="https://api.nuget.org/v3/index.json" />
7878
<add key="Gallery.SearchServiceResourceType" value="SearchGalleryQueryService/3.0.0-rc" />
79+
<add key="Gallery.AutocompleteServiceResourceType" value="SearchAutocompleteService/3.0.0-rc" />
7980
-->
8081
<add key="Gallery.ServiceDiscoveryUri" value="" />
8182
<add key="Gallery.SearchServiceResourceType" value="" />
83+
<add key="Gallery.AutocompleteServiceResourceType" value="" />
8284
<add key="Gallery.Brand" value="NuGet Gallery" />
8385
<add key="Gallery.GalleryOwner" value="NuGet Gallery &lt;[email protected]&gt;" />
8486
<add key="Gallery.ConfirmEmailAddresses" value="true" />
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Linq;
6+
using System.Threading.Tasks;
7+
using Moq;
8+
using NuGetGallery.Configuration;
9+
using Xunit;
10+
11+
namespace NuGetGallery
12+
{
13+
public class AutocompleteServicePackageIdsQueryFacts
14+
{
15+
private IAppConfiguration GetConfiguration()
16+
{
17+
var mockConfiguration = new Mock<IAppConfiguration>();
18+
mockConfiguration.SetupGet(c => c.ServiceDiscoveryUri).Returns(new Uri("https://api.nuget.org/v3/index.json"));
19+
mockConfiguration.SetupGet(c => c.AutocompleteServiceResourceType).Returns("SearchAutocompleteService/3.0.0-rc");
20+
return mockConfiguration.Object;
21+
}
22+
23+
[Fact]
24+
public async Task ExecuteReturns30ResultsForEmptyQuery()
25+
{
26+
var query = new AutocompleteServicePackageIdsQuery(GetConfiguration());
27+
var result = await query.Execute("", false);
28+
Assert.NotEmpty(result);
29+
Assert.True(result.Count() == 30);
30+
}
31+
32+
[Fact]
33+
public async Task ExecuteReturns30ResultsForNullQuery()
34+
{
35+
var query = new AutocompleteServicePackageIdsQuery(GetConfiguration());
36+
var result = await query.Execute(null, false);
37+
Assert.NotEmpty(result);
38+
Assert.True(result.Count() == 30);
39+
}
40+
41+
[Fact]
42+
public async Task ExecuteReturnsResultsForSpecificQuery()
43+
{
44+
var query = new AutocompleteServicePackageIdsQuery(GetConfiguration());
45+
var result = await query.Execute("jquery", false);
46+
Assert.NotEmpty(result);
47+
Assert.Contains("jquery", result, StringComparer.OrdinalIgnoreCase);
48+
}
49+
}
50+
}

0 commit comments

Comments
 (0)