Skip to content

Commit

Permalink
Align total count on connection & collection segment (#4960)
Browse files Browse the repository at this point in the history
  • Loading branch information
PascalSenn committed May 18, 2022
1 parent ee288d0 commit f202519
Show file tree
Hide file tree
Showing 17 changed files with 351 additions and 104 deletions.
63 changes: 61 additions & 2 deletions src/HotChocolate/Core/src/Types.CursorPagination/Connection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,25 @@

namespace HotChocolate.Types.Pagination;

/// <summary>
/// The connection represents one section of a dataset / collection.
/// </summary>
public class Connection : IPage
{
private readonly Func<CancellationToken, ValueTask<int>> _getTotalCount;

/// <summary>
/// Initializes <see cref="Connection" />.
/// </summary>
/// <param name="edges">
/// The edges that belong to this connection.
/// </param>
/// <param name="info">
/// Additional information about this connection.
/// </param>
/// <param name="getTotalCount">
/// A delegate to request the the total count.
/// </param>
public Connection(
IReadOnlyCollection<IEdge> edges,
ConnectionPageInfo info,
Expand All @@ -22,15 +37,59 @@ public class Connection : IPage
throw new ArgumentNullException(nameof(info));
}

/// <summary>
/// Initializes <see cref="Connection" />.
/// </summary>
/// <param name="edges">
/// The edges that belong to this connection.
/// </param>
/// <param name="info">
/// Additional information about this connection.
/// </param>
/// <param name="totalCount">
/// The total count of items of this connection
/// </param>
public Connection(
IReadOnlyCollection<IEdge> edges,
ConnectionPageInfo info,
int totalCount = 0)
{
_getTotalCount = _ => new(totalCount);
Edges = edges ??
throw new ArgumentNullException(nameof(edges));
Info = info ??
throw new ArgumentNullException(nameof(info));
}

/// <summary>
/// The edges that belong to this connection.
/// </summary>
public IReadOnlyCollection<IEdge> Edges { get; }

/// <summary>
/// The items that belong to this connection.
/// </summary>
IReadOnlyCollection<object> IPage.Items => Edges;

/// <summary>
/// Information about pagination in a connection.
/// </summary>
public ConnectionPageInfo Info { get; }

/// <summary>
/// Information about pagination in a connection.
/// </summary>
IPageInfo IPage.Info => Info;

public ValueTask<int> GetTotalCountAsync(
CancellationToken cancellationToken) =>
/// <summary>
/// Requests the total count of the data set / collection that is being paged.
/// </summary>
/// <param name="cancellationToken">
/// The cancellation token.
/// </param>
/// <returns>
/// The total count of the data set / collection.
/// </returns>
public ValueTask<int> GetTotalCountAsync(CancellationToken cancellationToken) =>
_getTotalCount(cancellationToken);
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,49 @@
namespace HotChocolate.Types.Pagination;

/// <summary>
/// Represents the connection paging page info.
/// This class provides additional information about pagination in a connection.
/// </summary>
public class ConnectionPageInfo : IPageInfo
{
/// <summary>
/// Initializes <see cref="ConnectionPageInfo" />.
/// </summary>
/// <param name="hasNextPage"></param>
/// <param name="hasPreviousPage"></param>
/// <param name="startCursor"></param>
/// <param name="endCursor"></param>
public ConnectionPageInfo(
bool hasNextPage,
bool hasPreviousPage,
string? startCursor,
string? endCursor,
int? totalCount = null)
string? endCursor)
{
HasNextPage = hasNextPage;
HasPreviousPage = hasPreviousPage;
StartCursor = startCursor;
EndCursor = endCursor;
TotalCount = totalCount;
}

/// <summary>
/// <c>true</c> if there is another page after the current one.
/// <c>false</c> if this page is the last page of the current data set / collection.
/// </summary>
public bool HasNextPage { get; }

/// <summary>
/// <c>true</c> if there is before this page.
/// <c>false</c> if this page is the first page in the current data set / collection.
/// </summary>
public bool HasPreviousPage { get; }

/// <summary>
/// When paginating backwards, the cursor to continue.
/// </summary>
public string? StartCursor { get; }

/// <summary>
/// When paginating forwards, the cursor to continue.
/// </summary>
public string? EndCursor { get; }

public int? TotalCount { get; }
}
39 changes: 39 additions & 0 deletions src/HotChocolate/Core/src/Types.CursorPagination/Connection~1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,23 @@

namespace HotChocolate.Types.Pagination;

/// <summary>
/// The connection represents one section of a dataset / collection.
/// </summary>
public class Connection<T> : Connection
{
/// <summary>
/// Initializes <see cref="Connection{T}" />.
/// </summary>
/// <param name="edges">
/// The edges that belong to this connection.
/// </param>
/// <param name="info">
/// Additional information about this connection.
/// </param>
/// <param name="getTotalCount">
/// A delegate to request the the total count.
/// </param>
public Connection(
IReadOnlyCollection<Edge<T>> edges,
ConnectionPageInfo info,
Expand All @@ -16,5 +31,29 @@ public class Connection<T> : Connection
Edges = edges;
}

/// <summary>
/// Initializes <see cref="Connection{T}" />.
/// </summary>
/// <param name="edges">
/// The edges that belong to this connection.
/// </param>
/// <param name="info">
/// Additional information about this connection.
/// </param>
/// <param name="totalCount">
/// The total count of items of this connection
/// </param>
public Connection(
IReadOnlyCollection<Edge<T>> edges,
ConnectionPageInfo info,
int totalCount = 0)
: base(edges, info, totalCount)
{
Edges = edges;
}

/// <summary>
/// The edges that belong to this connection.
/// </summary>
public new IReadOnlyCollection<Edge<T>> Edges { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ public abstract class CursorPaginationAlgorithm<TQuery, TEntity> where TQuery :
}

var maxElementCount = int.MaxValue;
Func<CancellationToken, ValueTask<int>> executeCount = totalCount is null ?
ct => CountAsync(query, ct)
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`
Expand All @@ -68,9 +68,9 @@ public abstract class CursorPaginationAlgorithm<TQuery, TEntity> where TQuery :
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);
// in case we already know the total count, we set the totalCount parameter
// so that we do not have have to fetch the count twice
executeCount = _ => new(count);
}

CursorPagingRange range = SliceRange(arguments, maxElementCount);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,30 @@ public class CollectionSegment : IPage
throw new ArgumentNullException(nameof(getTotalCount));
}

/// <summary>
/// Initializes <see cref="CollectionSegment" />.
/// </summary>
/// <param name="items">
/// The items that belong to this page.
/// </param>
/// <param name="info">
/// Additional information about this page.
/// </param>
/// <param name="totalCount">
/// The total count of the data set / collection that is being paged.
/// </param>
public CollectionSegment(
IReadOnlyCollection<object> items,
CollectionSegmentInfo info,
int totalCount = 0)
{
_getTotalCount = _ => new(totalCount);
Items = items ??
throw new ArgumentNullException(nameof(items));
Info = info ??
throw new ArgumentNullException(nameof(info));
}

/// <summary>
/// The items that belong to this page.
/// </summary>
Expand All @@ -45,7 +69,12 @@ public class CollectionSegment : IPage
/// <summary>
/// Gets more information about this page.
/// </summary>
public IPageInfo Info { get; }
public CollectionSegmentInfo Info { get; }

/// <summary>
/// Gets more information about this page.
/// </summary>
IPageInfo IPage.Info => Info;

/// <summary>
/// Requests the total count of the data set / collection that is being paged.
Expand All @@ -56,7 +85,6 @@ public class CollectionSegment : IPage
/// <returns>
/// The total count of the data set / collection.
/// </returns>
public ValueTask<int> GetTotalCountAsync(
CancellationToken cancellationToken) =>
public ValueTask<int> GetTotalCountAsync(CancellationToken cancellationToken) =>
_getTotalCount(cancellationToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ public class CollectionSegmentInfo : IPageInfo
/// <summary>
/// Initializes <see cref="CollectionSegmentInfo" />.
/// </summary>
/// <param name="hasNextPage"></param>
/// <param name="hasPreviousPage"></param>
public CollectionSegmentInfo(
bool hasNextPage,
bool hasPreviousPage)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,27 @@ public class CollectionSegment<T> : CollectionSegment
Items = items;
}

/// <summary>
/// Initializes <see cref="CollectionSegment" />.
/// </summary>
/// <param name="items">
/// The items that belong to this page.
/// </param>
/// <param name="info">
/// Additional information about this page.
/// </param>
/// <param name="totalCount">
/// The total count of the data set / collection that is being paged.
/// </param>
public CollectionSegment(
IReadOnlyCollection<T> items,
CollectionSegmentInfo info,
int totalCount = 0)
: base(new CollectionWrapper(items), info, totalCount)
{
Items = items;
}

/// <summary>
/// The items that belong to this page.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,7 @@ public abstract class OffsetPaginationAlgorithm<TQuery, TEntity>

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

return new CollectionSegment<TEntity>(
items,
pageInfo,
getTotalCount);
return new CollectionSegment<TEntity>( items, pageInfo, getTotalCount);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,29 @@
using Xunit;

namespace HotChocolate.Types.Pagination
namespace HotChocolate.Types.Pagination;

public class ConnectionPageInfoTests
{
public class ConnectionPageInfoTests
[InlineData(true, true, "a", "b")]
[InlineData(true, false, "a", "b")]
[InlineData(false, true, "a", "b")]
[InlineData(true, true, null, "b")]
[InlineData(true, true, "a", null)]
[Theory]
public void CreatePageInfo_ArgumentsArePassedCorrectly(
bool hasNextPage,
bool hasPreviousPage,
string startCursor,
string endCursor)
{
[InlineData(true, true, "a", "b", null)]
[InlineData(true, false, "a", "b", null)]
[InlineData(false, true, "a", "b", null)]
[InlineData(true, true, null, "b", null)]
[InlineData(true, true, "a", null, null)]
[InlineData(true, true, "a", "b", 1)]
[InlineData(true, false, "a", "b", 2)]
[InlineData(false, true, "a", "b", 3)]
[InlineData(true, true, null, "b", 4)]
[InlineData(true, true, "a", null, 5)]
[Theory]
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);
// arrange
// act
var pageInfo = new ConnectionPageInfo(hasNextPage, hasPreviousPage, startCursor, endCursor);

// 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);
}
// assert
Assert.Equal(hasNextPage, pageInfo.HasNextPage);
Assert.Equal(hasPreviousPage, pageInfo.HasPreviousPage);
Assert.Equal(startCursor, pageInfo.StartCursor);
Assert.Equal(endCursor, pageInfo.EndCursor);
}
}

0 comments on commit f202519

Please sign in to comment.