Beispiel #1
0
 public Connection(
     IReadOnlyCollection <Edge <T> > edges,
     ConnectionPageInfo info,
     Func <CancellationToken, ValueTask <int> > getTotalCount)
     : base(edges, info, getTotalCount)
 {
     Edges = edges;
 }
Beispiel #2
0
        public void CreateConnection_EdgesNull_ArgumentNullException()
        {
            // arrange
            var pageInfo = new ConnectionPageInfo(true, true, "a", "b", null);

            // act
            Action a = () => new Connection <string>(
                null, pageInfo, ct => throw new NotSupportedException());

            // assert
            Assert.Throws <ArgumentNullException>(a);
        }
Beispiel #3
0
        public void CreateConnection_CountIsNull_ArgumentNullException()
        {
            // arrange
            var pageInfo = new ConnectionPageInfo(true, true, "a", "b", null);
            var edges    = new List <Edge <string> >();

            // act
            Action a = () => new Connection <string>(
                edges, pageInfo, null);

            // assert
            Assert.Throws <ArgumentNullException>(a);
        }
Beispiel #4
0
        public void CreateConnection_PageInfoAndEdges_PassedCorrectly()
        {
            // arrange
            var pageInfo = new ConnectionPageInfo(true, true, "a", "b", null);
            var edges    = new List <Edge <string> >();

            // act
            var connection = new Connection(
                edges,
                pageInfo,
                _ => throw new NotSupportedException());

            // assert
            Assert.Equal(pageInfo, connection.Info);
            Assert.Equal(edges, connection.Edges);
        }
        public void CreatePageInfo_ArgumentsArePassedCorrectly(
            bool hasNextPage, bool hasPreviousPage,
            string startCursor, string endCursor,
            int?totalCount)
        {
            // arrange
            // act
            var pageInfo = new ConnectionPageInfo(
                hasNextPage, hasPreviousPage,
                startCursor, endCursor,
                totalCount);

            // assert
            Assert.Equal(hasNextPage, pageInfo.HasNextPage);
            Assert.Equal(hasPreviousPage, pageInfo.HasPreviousPage);
            Assert.Equal(startCursor, pageInfo.StartCursor);
            Assert.Equal(endCursor, pageInfo.EndCursor);
            Assert.Equal(totalCount, pageInfo.TotalCount);
        }
        private async ValueTask <Connection> ResolveAsync(
            IQueryable <TEntity> source,
            CursorPagingArguments arguments     = default,
            CancellationToken cancellationToken = default)
        {
            var count = await Task.Run(source.Count, cancellationToken)
                        .ConfigureAwait(false);

            int?after = arguments.After is { } a
                ? (int?)IndexEdge <TEntity> .DeserializeCursor(a)
                : null;

            int?before = arguments.Before is { } b
                ? (int?)IndexEdge <TEntity> .DeserializeCursor(b)
                : null;

            IReadOnlyList <IndexEdge <TEntity> > selectedEdges =
                await GetSelectedEdgesAsync(
                    source, arguments.First, arguments.Last, after, before, cancellationToken)
                .ConfigureAwait(false);

            IndexEdge <TEntity>?firstEdge = selectedEdges.Count == 0
                ? null
                : selectedEdges[0];

            IndexEdge <TEntity>?lastEdge = selectedEdges.Count == 0
                ? null
                : selectedEdges[selectedEdges.Count - 1];

            var pageInfo = new ConnectionPageInfo(
                lastEdge?.Index < count - 1,
                firstEdge?.Index > 0,
                firstEdge?.Cursor,
                lastEdge?.Cursor,
                count);

            return(new Connection <TEntity>(
                       selectedEdges,
                       pageInfo,
                       ct => new ValueTask <int>(pageInfo.TotalCount ?? 0)));
        }
        /// <summary>
        /// Applies the pagination algorithm to the provided query.
        /// </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 the connection.
        /// </returns>
        public async ValueTask <Connection <TEntity> > ApplyPaginationAsync(
            TQuery query,
            CursorPagingArguments arguments,
            int?totalCount,
            CancellationToken cancellationToken)
        {
            if (query is null)
            {
                throw new ArgumentNullException(nameof(query));
            }

            var maxElementCount = int.MaxValue;
            Func <CancellationToken, ValueTask <int> > executeCount = totalCount is null ?
                                                                      ct => CountAsync(query, ct)
                : _ => new ValueTask <int>(totalCount.Value);

            // We only need the maximal element count if no `before` counter is set and no `first`
            // argument is provided.
            if (arguments.Before is null && arguments.First is null)
            {
                var count = await executeCount(cancellationToken);

                maxElementCount = count;

                // in case we already know the total count, we override the countAsync parameter
                // so that we do not have to fetch the count twice
                executeCount = _ => new ValueTask <int>(count);
            }

            CursorPagingRange range = SliceRange(arguments, maxElementCount);

            var skip = range.Start;
            var take = range.Count();

            // we fetch one element more than we requested
            if (take != maxElementCount)
            {
                take++;
            }

            TQuery slicedSource = query;

            if (skip != 0)
            {
                slicedSource = ApplySkip(query, skip);
            }

            if (take != maxElementCount)
            {
                slicedSource = ApplyTake(slicedSource, take);
            }

            IReadOnlyList <Edge <TEntity> > selectedEdges =
                await ExecuteAsync(slicedSource, skip, cancellationToken);

            var moreItemsReturnedThanRequested = selectedEdges.Count > range.Count();
            var isSequenceFromStart            = range.Start == 0;

            selectedEdges = new SkipLastCollection <Edge <TEntity> >(
                selectedEdges,
                moreItemsReturnedThanRequested);

            ConnectionPageInfo pageInfo =
                CreatePageInfo(isSequenceFromStart, moreItemsReturnedThanRequested, selectedEdges);

            return(new Connection <TEntity>(selectedEdges, pageInfo, executeCount));
        }