Skip to content

Commit dc86fe8

Browse files
committed
Add custom query telemetry and response header to OData endpoint (#6583)
Progress on NuGet/Engineering#1798
1 parent 64f5a36 commit dc86fe8

16 files changed

Lines changed: 687 additions & 99 deletions

src/NuGetGallery/Controllers/ODataV1FeedController.cs

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ public class ODataV1FeedController
3030
public ODataV1FeedController(
3131
IEntityRepository<Package> packagesRepository,
3232
IGalleryConfigurationService configurationService,
33-
ISearchService searchService)
34-
: base(configurationService)
33+
ISearchService searchService,
34+
ITelemetryService telemetryService)
35+
: base(configurationService, telemetryService)
3536
{
3637
_packagesRepository = packagesRepository;
3738
_configurationService = configurationService;
@@ -56,7 +57,7 @@ public IHttpActionResult Get(ODataQueryOptions<V1FeedPackage> options)
5657
.WithoutSortOnColumn(Id, ShouldIgnoreOrderById(options))
5758
.ToV1FeedPackageQuery(_configurationService.GetSiteRoot(UseHttps()));
5859

59-
return QueryResult(options, queryable, MaxPageSize);
60+
return TrackedQueryResult(options, queryable, MaxPageSize, customQuery: true);
6061
}
6162

6263
// /api/v1/Packages/$count
@@ -108,6 +109,8 @@ private async Task<IHttpActionResult> GetCore(ODataQueryOptions<V1FeedPackage> o
108109
packages = packages.Where(p => p.Version == version);
109110
}
110111

112+
bool? customQuery = null;
113+
111114
// try the search service
112115
try
113116
{
@@ -123,6 +126,8 @@ private async Task<IHttpActionResult> GetCore(ODataQueryOptions<V1FeedPackage> o
123126
// If intercepted, create a paged queryresult
124127
if (searchAdaptorResult.ResultsAreProvidedBySearchService)
125128
{
129+
customQuery = false;
130+
126131
// Packages provided by search service
127132
packages = searchAdaptorResult.Packages;
128133

@@ -131,15 +136,25 @@ private async Task<IHttpActionResult> GetCore(ODataQueryOptions<V1FeedPackage> o
131136

132137
if (return404NotFoundWhenNoResults && totalHits == 0)
133138
{
139+
_telemetryService.TrackODataCustomQuery(customQuery);
134140
return NotFound();
135141
}
136142

137143
var pagedQueryable = packages
138144
.Take(options.Top != null ? Math.Min(options.Top.Value, MaxPageSize) : MaxPageSize)
139145
.ToV1FeedPackageQuery(GetSiteRoot());
140146

141-
return QueryResult(options, pagedQueryable, MaxPageSize, totalHits, (o, s, resultCount) =>
142-
SearchAdaptor.GetNextLink(Request.RequestUri, resultCount, new { id }, o, s));
147+
return TrackedQueryResult(
148+
options,
149+
pagedQueryable,
150+
MaxPageSize,
151+
totalHits,
152+
(o, s, resultCount) => SearchAdaptor.GetNextLink(Request.RequestUri, resultCount, new { id }, o, s),
153+
customQuery);
154+
}
155+
else
156+
{
157+
customQuery = true;
143158
}
144159
}
145160
catch (Exception ex)
@@ -151,11 +166,12 @@ private async Task<IHttpActionResult> GetCore(ODataQueryOptions<V1FeedPackage> o
151166

152167
if (return404NotFoundWhenNoResults && !packages.Any())
153168
{
169+
_telemetryService.TrackODataCustomQuery(customQuery);
154170
return NotFound();
155171
}
156172

157173
var queryable = packages.ToV1FeedPackageQuery(GetSiteRoot());
158-
return QueryResult(options, queryable, MaxPageSize);
174+
return TrackedQueryResult(options, queryable, MaxPageSize, customQuery);
159175
}
160176

161177
// /api/v1/Packages(Id=,Version=)/propertyName
@@ -213,24 +229,36 @@ public async Task<IHttpActionResult> Search(
213229
packages,
214230
searchTerm,
215231
targetFramework,
216-
false,
232+
includePrerelease: false,
217233
curatedFeed: null,
218234
semVerLevel: null);
219235

220236
// Packages provided by search service (even when not hijacked)
221237
var query = searchAdaptorResult.Packages;
238+
bool? customQuery = null;
222239

223240
// If intercepted, create a paged queryresult
224241
if (searchAdaptorResult.ResultsAreProvidedBySearchService)
225242
{
243+
customQuery = false;
244+
226245
// Add explicit Take() needed to limit search hijack result set size if $top is specified
227246
var totalHits = query.LongCount();
228247
var pagedQueryable = query
229248
.Take(options.Top != null ? Math.Min(options.Top.Value, MaxPageSize) : MaxPageSize)
230249
.ToV1FeedPackageQuery(GetSiteRoot());
231250

232-
return QueryResult(options, pagedQueryable, MaxPageSize, totalHits, (o, s, resultCount) =>
233-
SearchAdaptor.GetNextLink(Request.RequestUri, resultCount, new { searchTerm, targetFramework }, o, s));
251+
return TrackedQueryResult(
252+
options,
253+
pagedQueryable,
254+
MaxPageSize,
255+
totalHits,
256+
(o, s, resultCount) => SearchAdaptor.GetNextLink(Request.RequestUri, resultCount, new { searchTerm, targetFramework }, o, s),
257+
customQuery);
258+
}
259+
else
260+
{
261+
customQuery = true;
234262
}
235263

236264
if (!ODataQueryVerifier.AreODataOptionsAllowed(options, ODataQueryVerifier.V1Search,
@@ -241,7 +269,7 @@ public async Task<IHttpActionResult> Search(
241269

242270
// If not, just let OData handle things
243271
var queryable = query.ToV1FeedPackageQuery(GetSiteRoot());
244-
return QueryResult(options, queryable, MaxPageSize);
272+
return TrackedQueryResult(options, queryable, MaxPageSize, customQuery);
245273
}
246274

247275
// /api/v1/Search()/$count?searchTerm=&targetFramework=&includePrerelease=

src/NuGetGallery/Controllers/ODataV2CuratedFeedController.cs

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ public ODataV2CuratedFeedController(
3232
IGalleryConfigurationService configurationService,
3333
ISearchService searchService,
3434
ICuratedFeedService curatedFeedService,
35-
IEntityRepository<Package> packagesRepository)
36-
: base(configurationService)
35+
IEntityRepository<Package> packagesRepository,
36+
ITelemetryService telemetryService)
37+
: base(configurationService, telemetryService)
3738
{
3839
_configurationService = configurationService;
3940
_searchService = searchService;
@@ -68,7 +69,7 @@ public IHttpActionResult Get(
6869
semVerLevelKey)
6970
.InterceptWith(new NormalizeVersionInterceptor());
7071

71-
return QueryResult(options, queryable, MaxPageSize);
72+
return TrackedQueryResult(options, queryable, MaxPageSize, customQuery: true);
7273
}
7374

7475
// /api/v2/curated-feed/curatedFeedName/Packages/$count?semVerLevel=
@@ -108,7 +109,7 @@ public async Task<IHttpActionResult> FindPackagesById(
108109
var emptyResult = Enumerable.Empty<Package>().AsQueryable()
109110
.ToV2FeedPackageQuery(GetSiteRoot(), _configurationService.Features.FriendlyLicenses, semVerLevelKey);
110111

111-
return QueryResult(options, emptyResult, MaxPageSize);
112+
return TrackedQueryResult(options, emptyResult, MaxPageSize, customQuery: false);
112113
}
113114

114115
return await GetCore(options, curatedFeedName, id, normalizedVersion: null, return404NotFoundWhenNoResults: false, semVerLevel: semVerLevel);
@@ -153,6 +154,7 @@ private async Task<IHttpActionResult> GetCore(
153154
}
154155

155156
var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel);
157+
bool? customQuery = null;
156158

157159
// try the search service
158160
try
@@ -169,6 +171,8 @@ private async Task<IHttpActionResult> GetCore(
169171
// If intercepted, create a paged queryresult
170172
if (searchAdaptorResult.ResultsAreProvidedBySearchService)
171173
{
174+
customQuery = false;
175+
172176
// Packages provided by search service
173177
packages = searchAdaptorResult.Packages;
174178

@@ -177,15 +181,25 @@ private async Task<IHttpActionResult> GetCore(
177181

178182
if (return404NotFoundWhenNoResults && totalHits == 0)
179183
{
184+
_telemetryService.TrackODataCustomQuery(customQuery);
180185
return NotFound();
181186
}
182187

183188
var pagedQueryable = packages
184189
.Take(options.Top != null ? Math.Min(options.Top.Value, MaxPageSize) : MaxPageSize)
185190
.ToV2FeedPackageQuery(GetSiteRoot(), _configurationService.Features.FriendlyLicenses, semVerLevelKey);
186191

187-
return QueryResult(options, pagedQueryable, MaxPageSize, totalHits, (o, s, resultCount) =>
188-
SearchAdaptor.GetNextLink(Request.RequestUri, resultCount, new { id }, o, s, semVerLevelKey));
192+
return TrackedQueryResult(
193+
options,
194+
pagedQueryable,
195+
MaxPageSize,
196+
totalHits,
197+
(o, s, resultCount) => SearchAdaptor.GetNextLink(Request.RequestUri, resultCount, new { id }, o, s, semVerLevelKey),
198+
customQuery);
199+
}
200+
else
201+
{
202+
customQuery = true;
189203
}
190204
}
191205
catch (Exception ex)
@@ -197,6 +211,7 @@ private async Task<IHttpActionResult> GetCore(
197211

198212
if (return404NotFoundWhenNoResults && !packages.Any())
199213
{
214+
_telemetryService.TrackODataCustomQuery(customQuery);
200215
return NotFound();
201216
}
202217

@@ -205,7 +220,7 @@ private async Task<IHttpActionResult> GetCore(
205220
_configurationService.Features.FriendlyLicenses,
206221
semVerLevelKey);
207222

208-
return QueryResult(options, queryable, MaxPageSize);
223+
return TrackedQueryResult(options, queryable, MaxPageSize, customQuery);
209224
}
210225

211226
// /api/v2/curated-feed/curatedFeedName/Packages(Id=,Version=)/propertyName
@@ -278,10 +293,13 @@ public async Task<IHttpActionResult> Search(
278293
var query = searchAdaptorResult.Packages;
279294

280295
var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel);
296+
bool? customQuery = null;
281297

282298
// If intercepted, create a paged queryresult
283299
if (searchAdaptorResult.ResultsAreProvidedBySearchService)
284300
{
301+
customQuery = false;
302+
285303
// Add explicit Take() needed to limit search hijack result set size if $top is specified
286304
var totalHits = query.LongCount();
287305
var pagedQueryable = query
@@ -291,22 +309,32 @@ public async Task<IHttpActionResult> Search(
291309
_configurationService.Features.FriendlyLicenses,
292310
semVerLevelKey);
293311

294-
return QueryResult(options, pagedQueryable, MaxPageSize, totalHits, (o, s, resultCount) =>
295-
{
296-
// The nuget.exe 2.x list command does not like the next link at the bottom when a $top is passed.
297-
// Strip it of for backward compatibility.
298-
if (o.Top == null || (resultCount.HasValue && o.Top.Value >= resultCount.Value))
312+
return TrackedQueryResult(
313+
options,
314+
pagedQueryable,
315+
MaxPageSize,
316+
totalHits,
317+
(o, s, resultCount) =>
299318
{
300-
return SearchAdaptor.GetNextLink(
301-
Request.RequestUri,
302-
resultCount,
303-
new { searchTerm, targetFramework, includePrerelease },
304-
o,
305-
s,
306-
semVerLevelKey);
307-
}
308-
return null;
309-
});
319+
// The nuget.exe 2.x list command does not like the next link at the bottom when a $top is passed.
320+
// Strip it of for backward compatibility.
321+
if (o.Top == null || (resultCount.HasValue && o.Top.Value >= resultCount.Value))
322+
{
323+
return SearchAdaptor.GetNextLink(
324+
Request.RequestUri,
325+
resultCount,
326+
new { searchTerm, targetFramework, includePrerelease },
327+
o,
328+
s,
329+
semVerLevelKey);
330+
}
331+
return null;
332+
},
333+
customQuery);
334+
}
335+
else
336+
{
337+
customQuery = true;
310338
}
311339

312340
// If not, just let OData handle things
@@ -315,7 +343,7 @@ public async Task<IHttpActionResult> Search(
315343
_configurationService.Features.FriendlyLicenses,
316344
semVerLevelKey);
317345

318-
return QueryResult(options, queryable, MaxPageSize);
346+
return TrackedQueryResult(options, queryable, MaxPageSize, customQuery);
319347
}
320348

321349
// /api/v2/curated-feed/curatedFeedName/Search()/$count?searchTerm=&targetFramework=&includePrerelease=

0 commit comments

Comments
 (0)