Beispiel #1
0
        private IEnumerable <T> Query(ResourceQuery query, CancellationToken cancellationToken)
        {
            IEnumerable <T> results = _service.Get(query, cancellationToken);

            //
            // "Developers who want to know the full number of records across all pages, MAY include the query parameter
            // $count=true to tell the server to include the count of items in the response.
            //
            if (query.TotalRows.HasValue)
            {
                HttpContext.Items.TryAdd(Constants.CountResultContextKey, query.TotalRows.Value);
            }

            HttpContext.Items.TryAdd(Constants.QueryContextKey, query);

            return(results);
        }
Beispiel #2
0
        private static bool BuildResourceQuery(IResourceDataService service, HttpContext context, IOptions <ApiOptions> options, out ResourceQuery query)
        {
            query = new ResourceQuery();

            if (service.SupportsSorting && context.Items.TryGetValue(Constants.SortContextKey, out var sortMap) &&
                sortMap != null)
            {
                query.Sorting = (List <(AccessorMember, SortDirection)>)sortMap;
                context.Items.Remove(Constants.SortContextKey);
            }

            if (service.SupportsFiltering && context.Items.TryGetValue(Constants.FilterOperationContextKey, out var filterValue) &&
                filterValue is List <(string, FilterOperator, string?)> filters)
            {
                query.Filters = filters;
            }

            if (service.SupportsCount)
            {
                // We don't check whether the request asked for counting, because we need counting to determine next page results either way,
                // so we'll attempt to always count the total records if it's supported.

                query.CountTotalRows = true;
                context.Items.Remove(Constants.CountContextKey);
            }

            if (service.SupportsSkip && context.Items.TryGetValue(Constants.SkipOperationContextKey, out var skipValue) &&
                skipValue is int skip)
            {
                query.PageOffset = skip;
                context.Items.Remove(Constants.SkipOperationContextKey);
            }

            if (service.SupportsTop & context.Items.TryGetValue(Constants.TopOperationContextKey, out var topValue) &&
                topValue is int top)
            {
                //
                // "Note that client-driven paging does not preclude server-driven paging.
                // If the page size requested by the client is larger than the default page size
                // supported by the server, the expected response would be the number of results specified by the client,
                // paginated as specified by the server paging settings."
                //

                query.PageSize = top;
                context.Items.Remove(Constants.TopOperationContextKey);
            }

            if (service.SupportsMaxPageSize &&
                context.Items.TryGetValue(Constants.MaxPageSizeContextKey, out var maxPageSizeValue) &&
                maxPageSizeValue is int maxPageSize)
            {
                query.MaxPageSize = maxPageSize;
                context.Items.Remove(Constants.MaxPageSizeContextKey);
            }

            if (service.SupportsShaping && context.Items.TryGetValue(Constants.ShapingOperationContextKey, out var shapingValue) &&
                shapingValue is List <string> include)
            {
                query.Fields = include;
                context.Items.Remove(Constants.ShapingOperationContextKey);
            }

            // If no $skip is provided, assume the query is for the first page
            query.PageOffset ??= 0;

            //
            // "Clients MAY request server-driven paging with a specific page size by specifying a $maxpagesize preference.
            //  The server SHOULD honor this preference if the specified page size is smaller than the server's default page size."
            //
            // Interpretation:
            // - If the client specifies $top, use $top as the page size.
            // - If the client omits $top but specifies $maxpagesize, use the smaller of $maxpagesize and the server's default page size.
            if (!query.PageSize.HasValue)
            {
                if (query.MaxPageSize.HasValue && query.MaxPageSize.Value < options.Value.Paging.MaxPageSize.DefaultPageSize)
                {
                    query.PageSize = query.MaxPageSize.Value;
                }
                else
                {
                    query.PageSize = options.Value.Paging.MaxPageSize.DefaultPageSize;
                }
            }

            // If we still don't have a page size, assume the query is for the default page size
            if (!query.PageSize.HasValue || query.PageSize.Value == 0)
            {
                query.PageSize = options.Value.Paging.MaxPageSize.DefaultPageSize;
            }

            //
            // "If the server can't honor $top and/or $skip,
            // the server MUST return an error to the client informing about it instead of just ignoring the query options.
            // This will avoid the risk of the client making assumptions about the data returned."
            //
            if (query.PageSize.Value > options.Value.Paging.MaxPageSize.MaxPageSize)
            {
                return(false);
            }

            if (service.SupportsSearch && context.Items.TryGetValue(Constants.SearchOperationContextKey, out var searchValue) &&
                searchValue is string searchQuery)
            {
                query.SearchQuery = searchQuery;
                context.Items.Remove(Constants.SearchOperationContextKey);
            }

            return(true);
        }