Example #1
0
    /// <summary>
    /// Applies the offset pagination algorithm to the <paramref name="query"/>.
    /// </summary>
    /// <param name="query">
    /// The query on which the the offset pagination algorithm shall be applied to.
    /// </param>
    /// <param name="context">
    /// The field resolver context.
    /// </param>
    /// <param name="defaultPageSize">
    /// The default page size if no boundaries are set.
    /// </param>
    /// <param name="totalCount">
    /// The total count if already known.
    /// </param>
    /// <param name="cancellationToken">
    /// The cancellation token.
    /// </param>
    /// <typeparam name="TEntity">
    /// The entity type.
    /// </typeparam>
    /// <returns>
    /// Returns a collection segment instance that represents the result of applying the
    /// offset paging algorithm to the provided <paramref name="query"/>.
    /// </returns>
    public static ValueTask <CollectionSegment <TEntity> > ApplyOffsetPaginationAsync <TEntity>(
        this IQueryable <TEntity> query,
        IResolverContext context,
        int?defaultPageSize = null,
        int?totalCount      = null,
        CancellationToken cancellationToken = default)
    {
        if (query is null)
        {
            throw new ArgumentNullException(nameof(query));
        }

        if (context is null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        var skip = context.ArgumentValue <int?>(OffsetPagingArgumentNames.Skip);
        var take = context.ArgumentValue <int?>(OffsetPagingArgumentNames.Take) ??
                   defaultPageSize;

        var arguments = new OffsetPagingArguments(skip, take);

        if (totalCount is null && context.IsTotalCountSelected())
        {
            totalCount = query.Count();
        }

        return(QueryableOffsetPagination <TEntity> .Instance.ApplyPaginationAsync(
                   query,
                   arguments,
                   totalCount,
                   cancellationToken));
    }
Example #2
0
 /// <summary>
 /// Applies the offset pagination algorithm to the <paramref name="query"/>.
 /// </summary>
 /// <param name="query">
 /// The query on which the the offset pagination algorithm shall be applied to.
 /// </param>
 /// <param name="arguments">
 /// The offset paging arguments.
 /// </param>
 /// <param name="cancellationToken">
 /// The cancellation token.
 /// </param>
 /// <typeparam name="TEntity">
 /// The entity type.
 /// </typeparam>
 /// <returns>
 /// Returns a collection segment instance that represents the result of applying the
 /// offset paging algorithm to the provided <paramref name="query"/>.
 /// </returns>
 public static ValueTask <CollectionSegment <TEntity> > ApplyOffsetPaginationAsync <TEntity>(
     this IQueryable <TEntity> query,
     OffsetPagingArguments arguments,
     CancellationToken cancellationToken = default)
 => QueryableOffsetPagination <TEntity> .Instance.ApplyPaginationAsync(
     query,
     arguments,
     cancellationToken);
            protected override ValueTask <CollectionSegment> SliceAsync(
                IResolverContext context,
                object source,
                OffsetPagingArguments arguments)
            {
                IMongoPagingContainer <TEntity> f = CreatePagingContainer(source);

                return(ResolveAsync(context, f, arguments));
            }
Example #4
0
    async ValueTask <IPage> IPagingHandler.SliceAsync(
        IResolverContext context,
        object source)
    {
        var skip      = context.ArgumentValue <int?>(OffsetPagingArgumentNames.Skip);
        var take      = context.ArgumentValue <int?>(OffsetPagingArgumentNames.Take);
        var arguments = new OffsetPagingArguments(skip, take ?? DefaultPageSize);

        return(await SliceAsync(context, source, arguments).ConfigureAwait(false));
    }
        public static OffsetPagingArguments GetOffsetPagingArgsSafely(this IResolverContext context, int?defaultSkip = null, int?defaultTake = null)
        {
            int?skip = context?.ArgumentValueSafely <int?>(OffsetPagingArgNames.Skip);
            int?take = context?.ArgumentValueSafely <int?>(OffsetPagingArgNames.Take);

            //Initialize with default values that enable default behaviour to retrieve all results anytime
            //  the values are not specified; consistent with Cursor based paging where all params are optional.
            var pagingArgs = new OffsetPagingArguments(skip ?? defaultSkip, take ?? defaultTake);

            return(pagingArgs);
        }
Example #6
0
    /// <summary>
    /// Applies the pagination algorithm to the provided data.
    /// </summary>
    /// <param name="query">The query builder.</param>
    /// <param name="arguments">The paging arguments.</param>
    /// <param name="totalCount">Specify the total amount of elements</param>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <returns></returns>
    public async ValueTask <CollectionSegment <TEntity> > ApplyPaginationAsync(
        TQuery query,
        OffsetPagingArguments arguments,
        int?totalCount,
        CancellationToken cancellationToken)
    {
        Func <CancellationToken, ValueTask <int> > getTotalCount = totalCount is null
            ? async ct => await CountAsync(query, ct)
            : _ => new ValueTask <int>(totalCount.Value);

        TQuery sliced = query;

        if (arguments.Skip is { } skip)
        {
            sliced = ApplySkip(sliced, skip);
        }

        if (arguments.Take is { } take)
        {
            sliced = ApplyTake(sliced, take + 1);
        }

        IReadOnlyList <TEntity> items =
            await ExecuteAsync(sliced, cancellationToken).ConfigureAwait(false);

        bool hasNextPage     = items.Count == arguments.Take + 1;
        bool hasPreviousPage = (arguments.Skip ?? 0) > 0;

        CollectionSegmentInfo pageInfo = new(hasNextPage, hasPreviousPage);

        items = new SkipLastCollection <TEntity>(items, skipLast: hasNextPage);

        return(new CollectionSegment <TEntity>(
                   items,
                   pageInfo,
                   getTotalCount));
    }
Example #7
0
        protected override ValueTask <CollectionSegment> SliceAsync(IResolverContext context, object source, OffsetPagingArguments arguments)
        {
            //If Appropriate we handle the values here to ensure that no post-processing is done other than
            //  correctly mapping the results into a GraphQL Collection Segment with appropriate Paging Details...
            if (source is IPreProcessedOffsetPageResults <TEntity> pagedResults)
            {
                bool includeTotalCountEnabled = this.PagingOptions.IncludeTotalCount ?? PagingDefaults.IncludeTotalCount;
                var  graphQLParamsContext     = new GraphQLParamsContext(context);

                //Optimized to only require TotalCount value if the query actually requested it!
                if (includeTotalCountEnabled && graphQLParamsContext.IsTotalCountRequested && pagedResults.TotalCount == null)
                {
                    throw new InvalidOperationException($"Total Count is requested in the query, but was not provided with the results [{this.GetType().GetTypeName()}] from the resolvers pre-processing logic; TotalCount is null.");
                }

                int?totalCount = pagedResults.TotalCount;

                //Ensure we are null safe and return a valid empty list by default.
                var segmentResults = pagedResults?.ToList() ?? new List <TEntity>();

                var collectionSegmentInfo = new CollectionSegmentInfo(
                    hasNextPage: pagedResults?.HasNextPage ?? false,
                    hasPreviousPage: pagedResults?.HasPreviousPage ?? false
                    );

                var graphQLConnection = new CollectionSegment(
                    (IReadOnlyCollection <object>)segmentResults,
                    collectionSegmentInfo,
                    ct => new ValueTask <int>(totalCount ?? throw new InvalidOperationException())
                    );

                return(new ValueTask <CollectionSegment>(graphQLConnection));
            }

            throw new GraphQLException($"[{nameof(PreProcessedOffsetPagingHandler<TEntity>)}] cannot handle the specified data source of type [{source.GetType().Name}].");
        }
Example #8
0
 /// <summary>
 /// Applies the pagination algorithm to the provided data.
 /// </summary>
 /// <param name="query">The query builder.</param>
 /// <param name="arguments">The paging arguments.</param>
 /// <param name="cancellationToken">The cancellation token.</param>
 /// <returns></returns>
 public ValueTask <CollectionSegment <TEntity> > ApplyPaginationAsync(
     TQuery query,
     OffsetPagingArguments arguments,
     CancellationToken cancellationToken) =>
 ApplyPaginationAsync(query, arguments, null, cancellationToken);
Example #9
0
 /// <summary>
 /// The algorithm defining how to slice data of the specified <paramref name="source"/>.
 /// </summary>
 /// <param name="context">
 /// The resolver context of the execution field.
 /// </param>
 /// <param name="source">
 /// The object representing the data source, collection, or query builder.
 /// </param>
 /// <param name="arguments">
 /// The paging arguments provided by the user.
 /// </param>
 /// <returns>
 /// The <see cref="CollectionSegment"/> representing
 /// the slice of items belonging to the requested page.
 /// </returns>
 protected abstract ValueTask <CollectionSegment> SliceAsync(
     IResolverContext context,
     object source,
     OffsetPagingArguments arguments);
 public static IOffsetPageResults <T> SliceAsOffsetPage <T>(this IEnumerable <T> items, OffsetPagingArguments graphQLPagingArgs, bool includeTotalCount = true)
     where T : class
 {
     return(items.SliceAsOffsetPage(
                skip: graphQLPagingArgs.Skip,
                take: graphQLPagingArgs.Take,
                includeTotalCount: includeTotalCount
                ));
 }