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(Ok(Enumerable.Empty <V2FeedPackage>().AsQueryable()));
            }

            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(Ok(Enumerable.Empty <V2FeedPackage>().AsQueryable()));
            }

            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 = _packagesRepository.GetAll()
                           .Include(p => p.PackageRegistration)
                           .Include(p => p.SupportedFrameworks)
                           .Where(p =>
                                  p.Listed && (includePrerelease || !p.IsPrerelease) && !p.Deleted &&
                                  idValues.Contains(p.PackageRegistration.Id.ToLower()))
                           .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(QueryResult(options, queryable, MaxPageSize));
        }
        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 = _packagesRepository.GetAll()
                           .Include(p => p.PackageRegistration)
                           .Include(p => p.PackageRegistration.Owners)
                           .Where(p => p.Listed && !p.Deleted)
                           .Where(SemVerLevelKey.IsPackageCompliantWithSemVerLevel(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,
                curatedFeed : null,
                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;
                }));
            }
            //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(QueryResult(options, queryable, MaxPageSize));
        }
Esempio n. 3
0
        public async Task <IHttpActionResult> Search(
            ODataQueryOptions <V1FeedPackage> options,
            [FromODataUri] string searchTerm      = "",
            [FromODataUri] string targetFramework = "")
        {
            // 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 = _packagesRepository.GetAll()
                           .Include(p => p.PackageRegistration)
                           .Include(p => p.PackageRegistration.Owners)
                           .Where(p => p.Listed && !p.IsPrerelease && p.PackageStatusKey == PackageStatus.Available)
                           .Where(SemVerLevelKey.IsUnknownPredicate())
                           .OrderBy(p => p.PackageRegistration.Id).ThenBy(p => p.Version)
                           .AsNoTracking();

            // todo: search hijack should take queryOptions instead of manually parsing query options
            var searchAdaptorResult = await SearchAdaptor.SearchCore(
                _searchService,
                GetTraditionalHttpContext().Request,
                packages,
                searchTerm,
                targetFramework,
                includePrerelease : false,
                curatedFeed : null,
                semVerLevel : null);

            // Packages provided by search service (even when not hijacked)
            var  query       = searchAdaptorResult.Packages;
            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)
                                     .ToV1FeedPackageQuery(GetSiteRoot());

                return(TrackedQueryResult(
                           options,
                           pagedQueryable,
                           MaxPageSize,
                           totalHits,
                           (o, s, resultCount) => SearchAdaptor.GetNextLink(Request.RequestUri, resultCount, new { searchTerm, targetFramework }, o, s),
                           customQuery));
            }
            else
            {
                customQuery = true;
            }

            if (!ODataQueryVerifier.AreODataOptionsAllowed(options, ODataQueryVerifier.V1Search,
                                                           _configurationService.Current.IsODataFilterEnabled, nameof(Search)))
            {
                return(BadRequest(ODataQueryVerifier.GetValidationFailedMessage(options)));
            }

            // If not, just let OData handle things
            var queryable = query.ToV1FeedPackageQuery(GetSiteRoot());

            return(TrackedQueryResult(options, queryable, MaxPageSize, customQuery));
        }
Esempio n. 4
0
        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
            {
                var searchService = _searchServiceFactory.GetService();
                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 ODataFilterFacts()
        {
            var telemetryService = new Mock <ITelemetryService>().Object;

            queryVerifier = ODataQueryVerifier.Build(telemetryService);
        }