Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Align total count on connection & collection segment (#4960) #5087

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 Connection(
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 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 async ValueTask<Connection<TEntity>> ApplyPaginationAsync(
}

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 async ValueTask<Connection<TEntity>> ApplyPaginationAsync(
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 CollectionSegment(
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 CollectionSegment(
/// <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 CollectionSegment(
/// <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="CollectionSegmentCountType{T}" />.
/// </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 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 async ValueTask<CollectionSegment<TEntity>> ApplyPaginationAsync(

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);
}
}
Loading