public QueryResult( ODataQueryOptions <TModel> queryOptions, IQueryable <TModel> queryable, System.Web.Http.ApiController controller, int maxPageSize, long?totalResults, Func <ODataQueryOptions <TModel>, ODataQuerySettings, long?, Uri> generateNextLink) { _queryOptions = queryOptions; _queryable = queryable; _controller = controller; _totalResults = totalResults; _generateNextLink = generateNextLink; var queryDictionary = HttpUtility.ParseQueryString(queryOptions.Request.RequestUri.Query); _semVerLevelKey = SemVerLevelKey.ForSemVerLevel(queryDictionary["semVerLevel"]); if (_totalResults.HasValue && generateNextLink != null) { _isPagedResult = true; } // todo: if we decide to no longer support projections //AllowedQueryOptions = AllowedQueryOptions.All & ~AllowedQugeteryOptions.Select _validationSettings = new ODataValidationSettings() { MaxNodeCount = 250 }; _querySettings = new ODataQuerySettings(QueryResultDefaults.DefaultQuerySettings) { PageSize = maxPageSize }; }
public async Task <IHttpActionResult> FindPackagesById( ODataQueryOptions <V2FeedPackage> options, [FromODataUri] string id, [FromUri] string semVerLevel = null) { if (string.IsNullOrEmpty(id)) { var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel); var emptyResult = Enumerable.Empty <Package>().AsQueryable() .ToV2FeedPackageQuery( GetSiteRoot(), _configurationService.Features.FriendlyLicenses, semVerLevelKey); return(TrackedQueryResult(options, emptyResult, MaxPageSize, customQuery: false)); } return(await GetCore( options, id, version : null, semVerLevel : semVerLevel, return404NotFoundWhenNoResults : false)); }
public IHttpActionResult Get( ODataQueryOptions <V2FeedPackage> options, string curatedFeedName, [FromUri] string semVerLevel = null) { var result = GetCuratedFeedResult(curatedFeedName); if (result.ActionResult != null) { return(result.ActionResult); } var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel); var queryable = result .Packages .Where(p => p.PackageStatusKey == PackageStatus.Available) .Where(SemVerLevelKey.IsPackageCompliantWithSemVerLevelPredicate(semVerLevel)) .ToV2FeedPackageQuery( _configurationService.GetSiteRoot(UseHttps()), _configurationService.Features.FriendlyLicenses, semVerLevelKey) .InterceptWith(new NormalizeVersionInterceptor()); return(TrackedQueryResult(options, queryable, MaxPageSize, customQuery: true)); }
private async Task <IHttpActionResult> FindPackagesByIdAsync( ODataQueryOptions <V2FeedPackage> options, string id, string semVerLevel, bool isNonHijackEnabled) { if (string.IsNullOrEmpty(id)) { var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel); var emptyResult = Enumerable.Empty <Package>().AsQueryable() .ToV2FeedPackageQuery( GetSiteRoot(), _configurationService.Features.FriendlyLicenses, semVerLevelKey); return(TrackedQueryResult(options, emptyResult, MaxPageSize, customQuery: false)); } return(await GetCoreAsync( options, id, version : null, semVerLevel : semVerLevel, allowHijack : true, return404NotFoundWhenNoResults : false, isNonHijackEnabled : isNonHijackEnabled)); }
public IHttpActionResult Get( ODataQueryOptions <V2FeedPackage> options, string curatedFeedName, [FromUri] string semVerLevel = null) { if (!_entities.CuratedFeeds.Any(cf => cf.Name == curatedFeedName)) { return(NotFound()); } var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel); var queryable = _curatedFeedService.GetPackages(curatedFeedName) .Where(SemVerLevelKey.IsPackageCompliantWithSemVerLevel(semVerLevel)) .ToV2FeedPackageQuery( _configurationService.GetSiteRoot(UseHttps()), _configurationService.Features.FriendlyLicenses, semVerLevelKey) .InterceptWith(new NormalizeVersionInterceptor()); return(QueryResult(options, queryable, MaxPageSize)); }
public async Task <IHttpActionResult> Get( ODataQueryOptions <V2FeedPackage> options, [FromUri] string semVerLevel = null) { // Setup the search var packages = GetAll() .Where(p => p.PackageStatusKey == PackageStatus.Available) .Where(SemVerLevelKey.IsPackageCompliantWithSemVerLevelPredicate(semVerLevel)) .WithoutSortOnColumn(Version) .WithoutSortOnColumn(Id, ShouldIgnoreOrderById(options)) .InterceptWith(new NormalizeVersionInterceptor()); var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel); bool?customQuery = null; // Try the search service try { HijackableQueryParameters hijackableQueryParameters = null; if (_searchService is ExternalSearchService && SearchHijacker.IsHijackable(options, out hijackableQueryParameters)) { var searchAdaptorResult = await SearchAdaptor.FindByIdAndVersionCore( _searchService, GetTraditionalHttpContext().Request, packages, hijackableQueryParameters.Id, hijackableQueryParameters.Version, semVerLevel : semVerLevel); // If intercepted, create a paged queryresult if (searchAdaptorResult.ResultsAreProvidedBySearchService) { customQuery = false; // Packages provided by search service packages = searchAdaptorResult.Packages; // Add explicit Take() needed to limit search hijack result set size if $top is specified var totalHits = packages.LongCount(); var pagedQueryable = packages .Take(options.Top != null ? Math.Min(options.Top.Value, MaxPageSize) : MaxPageSize) .ToV2FeedPackageQuery( GetSiteRoot(), _configurationService.Features.FriendlyLicenses, semVerLevelKey); return(TrackedQueryResult( options, pagedQueryable, MaxPageSize, totalHits, (o, s, resultCount) => SearchAdaptor.GetNextLink(Request.RequestUri, resultCount, null, o, s, semVerLevelKey), customQuery)); } else { customQuery = true; } } else { customQuery = true; } } catch (Exception ex) { // Swallowing Exception intentionally. If *anything* goes wrong in search, just fall back to the database. // We don't want to break package restores. We do want to know if this happens, so here goes: QuietLog.LogHandledException(ex); } // Reject only when try to reach database. if (!ODataQueryVerifier.AreODataOptionsAllowed(options, ODataQueryVerifier.V2Packages, _configurationService.Current.IsODataFilterEnabled, nameof(Get))) { return(BadRequest(ODataQueryVerifier.GetValidationFailedMessage(options))); } var queryable = packages.ToV2FeedPackageQuery( GetSiteRoot(), _configurationService.Features.FriendlyLicenses, semVerLevelKey); return(TrackedQueryResult(options, queryable, MaxPageSize, customQuery)); }
public IHttpActionResult GetUpdates( ODataQueryOptions <V2FeedPackage> options, [FromODataUri] string packageIds, [FromODataUri] string versions, [FromODataUri] bool includePrerelease, [FromODataUri] bool includeAllVersions, [FromODataUri] string targetFrameworks = "", [FromODataUri] string versionConstraints = "", [FromUri] string semVerLevel = null) { if (string.IsNullOrEmpty(packageIds) || string.IsNullOrEmpty(versions)) { return(TrackedQueryResult( options, Enumerable.Empty <V2FeedPackage>().AsQueryable(), MaxPageSize, customQuery: false)); } if (!ODataQueryVerifier.AreODataOptionsAllowed(options, ODataQueryVerifier.V2GetUpdates, _configurationService.Current.IsODataFilterEnabled, nameof(GetUpdates))) { return(BadRequest(ODataQueryVerifier.GetValidationFailedMessage(options))); } // Workaround https://github.com/NuGet/NuGetGallery/issues/674 for NuGet 2.1 client. // Can probably eventually be retired (when nobody uses 2.1 anymore...) // Note - it was URI un-escaping converting + to ' ', undoing that is actually a pretty conservative substitution because // space characters are never accepted as valid by VersionUtility.ParseFrameworkName. if (!string.IsNullOrEmpty(targetFrameworks)) { targetFrameworks = targetFrameworks.Replace(' ', '+'); } var idValues = packageIds.Trim().ToLowerInvariant().Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); var versionValues = versions.Trim().Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); var targetFrameworkValues = string.IsNullOrEmpty(targetFrameworks) ? null : targetFrameworks.Split('|').Select(tfx => NuGetFramework.Parse(tfx)).ToList(); var versionConstraintValues = string.IsNullOrEmpty(versionConstraints) ? new string[idValues.Length] : versionConstraints.Split('|'); if (idValues.Length == 0 || idValues.Length != versionValues.Length || idValues.Length != versionConstraintValues.Length) { // Exit early if the request looks invalid return(TrackedQueryResult( options, Enumerable.Empty <V2FeedPackage>().AsQueryable(), MaxPageSize, customQuery: false)); } var versionLookup = idValues.Select((id, i) => { NuGetVersion currentVersion; if (NuGetVersion.TryParse(versionValues[i], out currentVersion)) { VersionRange versionConstraint = null; if (versionConstraintValues[i] != null) { if (!VersionRange.TryParse(versionConstraintValues[i], out versionConstraint)) { versionConstraint = null; } } return(Tuple.Create(id, Tuple.Create(currentVersion, versionConstraint))); } return(null); }) .Where(t => t != null) .ToLookup(t => t.Item1, t => t.Item2, StringComparer.OrdinalIgnoreCase); var packages = GetAll() .Include(p => p.PackageRegistration) .Include(p => p.SupportedFrameworks) .Where(p => p.Listed && (includePrerelease || !p.IsPrerelease) && idValues.Contains(p.PackageRegistration.Id.ToLower()) && p.PackageStatusKey == PackageStatus.Available) .OrderBy(p => p.PackageRegistration.Id); var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel); var queryable = GetUpdates(packages, versionLookup, targetFrameworkValues, includeAllVersions, semVerLevel) .AsQueryable() .ToV2FeedPackageQuery( GetSiteRoot(), _configurationService.Features.FriendlyLicenses, semVerLevelKey); return(TrackedQueryResult(options, queryable, MaxPageSize, customQuery: false)); }
public async Task <IHttpActionResult> Search( ODataQueryOptions <V2FeedPackage> options, [FromODataUri] string searchTerm = "", [FromODataUri] string targetFramework = "", [FromODataUri] bool includePrerelease = false, [FromUri] string semVerLevel = null) { // Handle OData-style |-separated list of frameworks. string[] targetFrameworkList = (targetFramework ?? "").Split(new[] { '\'', '|' }, StringSplitOptions.RemoveEmptyEntries); // For now, we'll just filter on the first one. if (targetFrameworkList.Length > 0) { // Until we support multiple frameworks, we need to prefer aspnet50 over aspnetcore50. if (targetFrameworkList.Contains("aspnet50")) { targetFramework = "aspnet50"; } else { targetFramework = targetFrameworkList[0]; } } // Perform actual search var packages = GetAll() .Include(p => p.PackageRegistration) .Include(p => p.PackageRegistration.Owners) .Where(p => p.Listed && p.PackageStatusKey == PackageStatus.Available) .Where(SemVerLevelKey.IsPackageCompliantWithSemVerLevelPredicate(semVerLevel)) .OrderBy(p => p.PackageRegistration.Id).ThenBy(p => p.Version) .AsNoTracking(); // todo: search hijack should take options instead of manually parsing query options var searchAdaptorResult = await SearchAdaptor.SearchCore( _searchService, GetTraditionalHttpContext().Request, packages, searchTerm, targetFramework, includePrerelease, semVerLevel : semVerLevel); // Packages provided by search service (even when not hijacked) var query = searchAdaptorResult.Packages; var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel); bool?customQuery = null; // If intercepted, create a paged queryresult if (searchAdaptorResult.ResultsAreProvidedBySearchService) { customQuery = false; // Add explicit Take() needed to limit search hijack result set size if $top is specified var totalHits = query.LongCount(); var pagedQueryable = query .Take(options.Top != null ? Math.Min(options.Top.Value, MaxPageSize) : MaxPageSize) .ToV2FeedPackageQuery( GetSiteRoot(), _configurationService.Features.FriendlyLicenses, semVerLevelKey); return(TrackedQueryResult( options, pagedQueryable, MaxPageSize, totalHits, (o, s, resultCount) => { // The nuget.exe 2.x list command does not like the next link at the bottom when a $top is passed. // Strip it of for backward compatibility. if (o.Top == null || (resultCount.HasValue && o.Top.Value >= resultCount.Value)) { return SearchAdaptor.GetNextLink(Request.RequestUri, resultCount, new { searchTerm, targetFramework, includePrerelease }, o, s, semVerLevelKey); } return null; }, customQuery)); } else { customQuery = true; } //Reject only when try to reach database. if (!ODataQueryVerifier.AreODataOptionsAllowed(options, ODataQueryVerifier.V2Search, _configurationService.Current.IsODataFilterEnabled, nameof(Search))) { return(BadRequest(ODataQueryVerifier.GetValidationFailedMessage(options))); } // If not, just let OData handle things var queryable = query.ToV2FeedPackageQuery( GetSiteRoot(), _configurationService.Features.FriendlyLicenses, semVerLevelKey); return(TrackedQueryResult(options, queryable, MaxPageSize, customQuery)); }
private async Task <IHttpActionResult> GetCore( ODataQueryOptions <V2FeedPackage> options, string id, string version, string semVerLevel, bool return404NotFoundWhenNoResults) { var packages = GetAll() .Include(p => p.PackageRegistration) .Where(p => p.PackageStatusKey == PackageStatus.Available && p.PackageRegistration.Id.Equals(id, StringComparison.OrdinalIgnoreCase)) .Where(SemVerLevelKey.IsPackageCompliantWithSemVerLevelPredicate(semVerLevel)); if (!string.IsNullOrEmpty(version)) { NuGetVersion nugetVersion; if (NuGetVersion.TryParse(version, out nugetVersion)) { // Our APIs expect to receive normalized version strings. // We need to compare normalized versions or we can never retrieve SemVer2 package versions. var normalizedString = nugetVersion.ToNormalizedString(); packages = packages.Where(p => p.NormalizedVersion == normalizedString); } } var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel); bool?customQuery = null; // try the search service try { var searchAdaptorResult = await SearchAdaptor.FindByIdAndVersionCore( _searchService, GetTraditionalHttpContext().Request, packages, id, version, semVerLevel : semVerLevel); // If intercepted, create a paged queryresult if (searchAdaptorResult.ResultsAreProvidedBySearchService) { customQuery = false; // Packages provided by search service packages = searchAdaptorResult.Packages; // Add explicit Take() needed to limit search hijack result set size if $top is specified var totalHits = packages.LongCount(); if (totalHits == 0 && return404NotFoundWhenNoResults) { _telemetryService.TrackODataCustomQuery(customQuery); return(NotFound()); } var pagedQueryable = packages .Take(options.Top != null ? Math.Min(options.Top.Value, MaxPageSize) : MaxPageSize) .ToV2FeedPackageQuery( GetSiteRoot(), _configurationService.Features.FriendlyLicenses, semVerLevelKey); return(TrackedQueryResult( options, pagedQueryable, MaxPageSize, totalHits, (o, s, resultCount) => SearchAdaptor.GetNextLink(Request.RequestUri, resultCount, new { id }, o, s, semVerLevelKey), customQuery)); } else { customQuery = true; } } catch (Exception ex) { // Swallowing Exception intentionally. If *anything* goes wrong in search, just fall back to the database. // We don't want to break package restores. We do want to know if this happens, so here goes: QuietLog.LogHandledException(ex); } if (return404NotFoundWhenNoResults && !packages.Any()) { _telemetryService.TrackODataCustomQuery(customQuery); return(NotFound()); } var queryable = packages.ToV2FeedPackageQuery( GetSiteRoot(), _configurationService.Features.FriendlyLicenses, semVerLevelKey); return(TrackedQueryResult(options, queryable, MaxPageSize, customQuery)); }
private async Task <IHttpActionResult> GetCore( ODataQueryOptions <V2FeedPackage> options, string curatedFeedName, string id, string normalizedVersion, bool return404NotFoundWhenNoResults, string semVerLevel) { var curatedFeed = _entities.CuratedFeeds.FirstOrDefault(cf => cf.Name == curatedFeedName); if (curatedFeed == null) { return(NotFound()); } var packages = _curatedFeedService .GetPackages(curatedFeedName) .Where(p => p.PackageStatusKey == PackageStatus.Available) .Where(SemVerLevelKey.IsPackageCompliantWithSemVerLevelPredicate(semVerLevel)) .Where(p => p.PackageRegistration.Id.Equals(id, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrEmpty(normalizedVersion)) { packages = packages.Where(p => p.NormalizedVersion == normalizedVersion); } var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel); // try the search service try { var searchAdaptorResult = await SearchAdaptor.FindByIdAndVersionCore( _searchService, GetTraditionalHttpContext().Request, packages, id, normalizedVersion, curatedFeed : curatedFeed, semVerLevel : semVerLevel); // If intercepted, create a paged queryresult if (searchAdaptorResult.ResultsAreProvidedBySearchService) { // Packages provided by search service packages = searchAdaptorResult.Packages; // Add explicit Take() needed to limit search hijack result set size if $top is specified var totalHits = packages.LongCount(); if (return404NotFoundWhenNoResults && totalHits == 0) { return(NotFound()); } var pagedQueryable = packages .Take(options.Top != null ? Math.Min(options.Top.Value, MaxPageSize) : MaxPageSize) .ToV2FeedPackageQuery(GetSiteRoot(), _configurationService.Features.FriendlyLicenses, semVerLevelKey); return(QueryResult(options, pagedQueryable, MaxPageSize, totalHits, (o, s, resultCount) => SearchAdaptor.GetNextLink(Request.RequestUri, resultCount, new { id }, o, s))); } } catch (Exception ex) { // Swallowing Exception intentionally. If *anything* goes wrong in search, just fall back to the database. // We don't want to break package restores. We do want to know if this happens, so here goes: QuietLog.LogHandledException(ex); } if (return404NotFoundWhenNoResults && !packages.Any()) { return(NotFound()); } var queryable = packages.ToV2FeedPackageQuery( GetSiteRoot(), _configurationService.Features.FriendlyLicenses, semVerLevelKey); return(QueryResult(options, queryable, MaxPageSize)); }
internal static Package ReadPackage(JObject doc, string semVerLevel) { var dependencies = doc.Value <JArray>("Dependencies") .Cast <JObject>() .Select(obj => new PackageDependency() { Id = obj.Value <string>("Id"), VersionSpec = obj.Value <string>("VersionSpec"), TargetFramework = obj.Value <string>("TargetFramework") }) .ToArray(); var frameworks = doc.Value <JArray>("SupportedFrameworks") .Select(v => new PackageFramework() { TargetFramework = v.Value <string>() }) .ToArray(); var reg = doc["PackageRegistration"]; PackageRegistration registration = null; if (reg != null) { registration = new PackageRegistration() { Id = reg.Value <string>("Id"), Owners = reg.Value <JArray>("Owners") .Select(v => new User { Username = v.Value <string>() }) .ToArray(), DownloadCount = reg.Value <int>("DownloadCount"), IsVerified = reg.Value <bool>("Verified"), Key = reg.Value <int>("Key") }; } var isLatest = doc.Value <bool>("IsLatest"); var isLatestStable = doc.Value <bool>("IsLatestStable"); var semVer2 = SemVerLevelKey.ForSemVerLevel(semVerLevel) == SemVerLevelKey.SemVer2; return(new Package { Copyright = doc.Value <string>("Copyright"), Created = doc.Value <DateTime>("Created"), Description = doc.Value <string>("Description"), Dependencies = dependencies, DownloadCount = doc.Value <int>("DownloadCount"), FlattenedAuthors = doc.Value <string>("Authors"), FlattenedDependencies = doc.Value <string>("FlattenedDependencies"), Hash = doc.Value <string>("Hash"), HashAlgorithm = doc.Value <string>("HashAlgorithm"), IconUrl = doc.Value <string>("IconUrl"), IsLatest = isLatest, IsLatestStable = isLatestStable, IsLatestSemVer2 = semVer2 ? isLatest : false, IsLatestStableSemVer2 = semVer2 ? isLatestStable : false, Key = doc.Value <int>("Key"), Language = doc.Value <string>("Language"), LastUpdated = doc.Value <DateTime>("LastUpdated"), LastEdited = doc.Value <DateTime?>("LastEdited"), PackageRegistration = registration, PackageRegistrationKey = registration?.Key ?? 0, PackageFileSize = doc.Value <long>("PackageFileSize"), ProjectUrl = doc.Value <string>("ProjectUrl"), Published = doc.Value <DateTime>("Published"), ReleaseNotes = doc.Value <string>("ReleaseNotes"), RequiresLicenseAcceptance = doc.Value <bool>("RequiresLicenseAcceptance"), Summary = doc.Value <string>("Summary"), Tags = doc.Value <string>("Tags"), Title = doc.Value <string>("Title"), Version = doc.Value <string>("Version"), NormalizedVersion = doc.Value <string>("NormalizedVersion"), SupportedFrameworks = frameworks, MinClientVersion = doc.Value <string>("MinClientVersion"), LicenseUrl = doc.Value <string>("LicenseUrl"), LicenseNames = doc.Value <string>("LicenseNames"), LicenseReportUrl = doc.Value <string>("LicenseReportUrl"), HideLicenseReport = doc.Value <bool>("HideLicenseReport"), Listed = doc.Value <bool>("Listed") }); }
public async Task <IHttpActionResult> Search( ODataQueryOptions <V2FeedPackage> options, string curatedFeedName, [FromODataUri] string searchTerm = "", [FromODataUri] string targetFramework = "", [FromODataUri] bool includePrerelease = false, [FromUri] string semVerLevel = null) { if (!_entities.CuratedFeeds.Any(cf => cf.Name == curatedFeedName)) { return(NotFound()); } // Handle OData-style |-separated list of frameworks. string[] targetFrameworkList = (targetFramework ?? "").Split(new[] { '\'', '|' }, StringSplitOptions.RemoveEmptyEntries); // For now, we'll just filter on the first one. if (targetFrameworkList.Length > 0) { // Until we support multiple frameworks, we need to prefer aspnet50 over aspnetcore50. if (targetFrameworkList.Contains("aspnet50")) { targetFramework = "aspnet50"; } else { targetFramework = targetFrameworkList[0]; } } // Perform actual search var curatedFeed = _curatedFeedService.GetFeedByName(curatedFeedName, includePackages: false); var packages = _curatedFeedService.GetPackages(curatedFeedName) .Where(SemVerLevelKey.IsPackageCompliantWithSemVerLevel(semVerLevel)) .OrderBy(p => p.PackageRegistration.Id).ThenBy(p => p.Version); // todo: search hijack should take queryOptions instead of manually parsing query options var searchAdaptorResult = await SearchAdaptor.SearchCore( _searchService, GetTraditionalHttpContext().Request, packages, searchTerm, targetFramework, includePrerelease, curatedFeed : curatedFeed, semVerLevel : semVerLevel); // Packages provided by search service (even when not hijacked) var query = searchAdaptorResult.Packages; var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel); // If intercepted, create a paged queryresult if (searchAdaptorResult.ResultsAreProvidedBySearchService) { // Add explicit Take() needed to limit search hijack result set size if $top is specified var totalHits = query.LongCount(); var pagedQueryable = query .Take(options.Top != null ? Math.Min(options.Top.Value, MaxPageSize) : MaxPageSize) .ToV2FeedPackageQuery( GetSiteRoot(), _configurationService.Features.FriendlyLicenses, semVerLevelKey); return(QueryResult(options, pagedQueryable, MaxPageSize, totalHits, (o, s, resultCount) => { // The nuget.exe 2.x list command does not like the next link at the bottom when a $top is passed. // Strip it of for backward compatibility. if (o.Top == null || (resultCount.HasValue && o.Top.Value >= resultCount.Value)) { return SearchAdaptor.GetNextLink( Request.RequestUri, resultCount, new { searchTerm, targetFramework, includePrerelease }, o, s); } return null; })); } // If not, just let OData handle things var queryable = query.ToV2FeedPackageQuery( GetSiteRoot(), _configurationService.Features.FriendlyLicenses, semVerLevelKey); return(QueryResult(options, queryable, MaxPageSize)); }
private async Task <IHttpActionResult> GetCore( ODataQueryOptions <V2FeedPackage> options, string id, string version, string semVerLevel, bool allowHijack, bool return404NotFoundWhenNoResults, bool isNonHijackEnabled) { var packages = GetAll() .Include(p => p.PackageRegistration) .Where(p => p.PackageStatusKey == PackageStatus.Available && p.PackageRegistration.Id.Equals(id, StringComparison.OrdinalIgnoreCase)) .Where(SemVerLevelKey.IsPackageCompliantWithSemVerLevelPredicate(semVerLevel)); if (!string.IsNullOrEmpty(version)) { NuGetVersion nugetVersion; if (NuGetVersion.TryParse(version, out nugetVersion)) { // Our APIs expect to receive normalized version strings. // We need to compare normalized versions or we can never retrieve SemVer2 package versions. var normalizedString = nugetVersion.ToNormalizedString(); packages = packages.Where(p => p.NormalizedVersion == normalizedString); } } var semVerLevelKey = SemVerLevelKey.ForSemVerLevel(semVerLevel); bool?customQuery = null; if (allowHijack) { // try the search service try { var searchService = _searchServiceFactory.GetService(); var searchAdaptorResult = await SearchAdaptor.FindByIdAndVersionCore( searchService, GetTraditionalHttpContext().Request, packages, id, version, semVerLevel : semVerLevel); // If intercepted, create a paged queryresult if (searchAdaptorResult.ResultsAreProvidedBySearchService) { customQuery = false; // Packages provided by search service packages = searchAdaptorResult.Packages; // Add explicit Take() needed to limit search hijack result set size if $top is specified var totalHits = packages.LongCount(); if (totalHits == 0 && return404NotFoundWhenNoResults) { _telemetryService.TrackODataCustomQuery(customQuery); return(NotFound()); } var pagedQueryable = packages .Take(options.Top != null ? Math.Min(options.Top.Value, MaxPageSize) : MaxPageSize) .ToV2FeedPackageQuery( GetSiteRoot(), _configurationService.Features.FriendlyLicenses, semVerLevelKey); return(TrackedQueryResult( options, pagedQueryable, MaxPageSize, totalHits, (o, s, resultCount) => SearchAdaptor.GetNextLink(Request.RequestUri, resultCount, new { id }, o, s, semVerLevelKey), customQuery)); } else { customQuery = true; } } catch (Exception ex) { // Swallowing Exception intentionally. If *anything* goes wrong in search, just fall back to the database. // We don't want to break package restores. We do want to know if this happens, so here goes: QuietLog.LogHandledException(ex); } } // When non-hijacked queries are disabled, allow only one non-hijacked pattern: query for a specific ID and // version without any fancy OData options. This enables some monitoring and testing and is known to produce // a very fast SQL query based on an optimized index. var isSimpleLookup = !string.IsNullOrWhiteSpace(id) && !string.IsNullOrWhiteSpace(version) && options.RawValues.Expand == null && options.RawValues.Filter == null && options.RawValues.Format == null && options.RawValues.InlineCount == null && options.RawValues.OrderBy == null && options.RawValues.Select == null && options.RawValues.Skip == null && options.RawValues.SkipToken == null && options.RawValues.Top == null; if (!allowHijack || !isNonHijackEnabled) { if (!isSimpleLookup) { return(BadRequest(Strings.ODataParametersDisabled)); } customQuery = true; } if (return404NotFoundWhenNoResults && !packages.Any()) { _telemetryService.TrackODataCustomQuery(customQuery); return(NotFound()); } var queryable = packages.ToV2FeedPackageQuery( GetSiteRoot(), _configurationService.Features.FriendlyLicenses, semVerLevelKey); return(TrackedQueryResult(options, queryable, MaxPageSize, customQuery)); }