Add paging support for skip + take format

This commit is contained in:
Sipke Schoorstra 2023-07-10 16:10:00 +02:00
parent 938df75664
commit 4d9ef959d4
11 changed files with 69 additions and 19 deletions

View file

@ -28,11 +28,11 @@ public interface IWorkflowInstancesApi
/// Returns a workflow instance.
/// </summary>
/// <param name="workflowInstanceId">The ID of the workflow instance for which to return the journal.</param>
/// <param name="page">The page number.</param>
/// <param name="pageSize">The page size.</param>
/// <param name="skip">The number of records to skip.</param>
/// <param name="take">The number of records to return.</param>
/// <param name="cancellationToken">The cancellation token.</param>
[Get("/workflow-instances/{workflowInstanceId}/journal")]
Task<PagedListResponse<ExecutionLogRecord>> GetJournalAsync(string workflowInstanceId, int? page = default, int? pageSize = default, CancellationToken cancellationToken = default);
Task<PagedListResponse<ExecutionLogRecord>> GetJournalAsync(string workflowInstanceId, int? skip = default, int? take = default, CancellationToken cancellationToken = default);
/// <summary>
/// Deletes a workflow instance.

View file

@ -3,23 +3,64 @@ namespace Elsa.Common.Models;
/// <summary>
/// Represents pagination arguments.
/// </summary>
/// <param name="Page">The zero-based page number.</param>
/// <param name="PageSize">The number of items per page.</param>
public record PageArgs(int? Page, int? PageSize)
public record PageArgs
{
/// <summary>
/// Creates pagination arguments from a page number and page size.
/// </summary>
/// <param name="page">The zero-based page number.</param>
/// <param name="pageSize">The number of items per page.</param>
public static PageArgs FromPage(int? page, int? pageSize) => new() { Offset = page * pageSize, Limit = pageSize };
/// <summary>
/// Creates pagination arguments from an offset and limit.
/// </summary>
/// <param name="offset">The number of items to skip.</param>
/// <param name="limit">The number of items to take.</param>
public static PageArgs FromRange(int? offset, int? limit) => new() { Offset = offset, Limit = limit };
/// <summary>
/// Creates pagination arguments from page and page size or offset and limit.
/// </summary>
/// <param name="page">The zero-based page number.</param>
/// <param name="pageSize">The number of items per page.</param>
/// <param name="offset">The number of items to skip.</param>
/// <param name="limit">The number of items to take.</param>
/// <exception cref="ArgumentException">Thrown when neither page and pageSize nor offset and limit are specified.</exception>
public static PageArgs From(int? page, int? pageSize, int? offset, int? limit)
{
if(page != null && pageSize != null)
return FromPage(page, pageSize);
if(offset != null && limit != null)
return FromRange(offset, limit);
throw new ArgumentException("Either page and pageSize or offset and limit must be specified.");
}
/// <summary>
/// Gets the offset of the page.
/// </summary>
public int? Offset => Page * PageSize;
public int? Offset { get; set; }
/// <summary>
/// Gets the limit of the page.
/// Gets or sets the limit of the page.
/// </summary>
public int? Limit => PageSize;
public int? Limit { get; set; }
/// <summary>
/// Gets the zero-based page number.
/// </summary>
public int? Page => Offset.HasValue && Limit.HasValue ? Offset / Limit : null;
/// <summary>
/// Gets the number of items per page.
/// </summary>
public int? PageSize => Limit;
/// <summary>
/// Returns pagination arguments for the next page.
/// </summary>
/// <returns>The arguments for the next page.</returns>
public PageArgs Next() => this with { Page = Page + 1 };
public PageArgs Next() => this with { Offset = Page + 1 };
}

View file

@ -30,7 +30,7 @@ public class ElasticStore<T> where T : class
/// </summary>
public async Task<IEnumerable<T>> SearchAsync(Action<SearchRequestDescriptor<T>> search, CancellationToken cancellationToken = default)
{
var page = new PageArgs(0, 1000);
var page = PageArgs.FromPage(0, 1000);
var collectedItems = new List<T>();
while(true)

View file

@ -29,7 +29,7 @@ public class ElasticWorkflowInstanceStore : IWorkflowInstanceStore
/// <inheritdoc />
public async Task<WorkflowInstance?> FindAsync(WorkflowInstanceFilter filter, CancellationToken cancellationToken = default)
{
var result = await _store.SearchAsync(d => Filter(d, filter), new PageArgs(0, 1), cancellationToken);
var result = await _store.SearchAsync(d => Filter(d, filter), PageArgs.FromRange(0, 1), cancellationToken);
return result.Items.FirstOrDefault();
}

View file

@ -40,14 +40,14 @@ public class ElasticWorkflowExecutionLogStore : IWorkflowExecutionLogStore
/// <inheritdoc />
public async Task<WorkflowExecutionLogRecord?> FindAsync(WorkflowExecutionLogRecordFilter filter, CancellationToken cancellationToken = default)
{
var result = await _store.SearchAsync(d => Filter(d, filter), new PageArgs(0, 1), cancellationToken);
var result = await _store.SearchAsync(d => Filter(d, filter), PageArgs.FromRange(0, 1), cancellationToken);
return result.Items.FirstOrDefault();
}
/// <inheritdoc />
public async Task<WorkflowExecutionLogRecord?> FindAsync<TOrderBy>(WorkflowExecutionLogRecordFilter filter, WorkflowExecutionLogRecordOrder<TOrderBy> order, CancellationToken cancellationToken = default)
{
var result = await _store.SearchAsync(d => Sort(Filter(d, filter), order), new PageArgs(0, 1), cancellationToken);
var result = await _store.SearchAsync(d => Sort(Filter(d, filter), order), PageArgs.FromRange(0, 1), cancellationToken);
return result.Items.FirstOrDefault();
}

View file

@ -274,7 +274,6 @@ public class Store<TDbContext, TEntity> where TDbContext : DbContext where TEnti
var set = dbContext.Set<TEntity>();
var queryable = query(set.AsQueryable());
queryable = query(queryable);
var entities = await queryable.ToListAsync(cancellationToken);
if (onLoading != null)

View file

@ -8,7 +8,7 @@ public class Request
public int? Page { get; set; }
public int? PageSize { get; set; }
public PageArgs ToPageArgs() => new(Page, PageSize);
public PageArgs ToPageArgs() => PageArgs.FromPage(Page, PageSize);
}
public class Response

View file

@ -27,7 +27,7 @@ internal class List : ElsaEndpoint<Request, Response>
public override async Task<Response> ExecuteAsync(Request request, CancellationToken cancellationToken)
{
var pageArgs = new PageArgs(request.Page, request.PageSize);
var pageArgs = PageArgs.FromPage(request.Page, request.PageSize);
var filter = CreateFilter(request);
var summaries = await FindAsync(request, filter, pageArgs, cancellationToken);

View file

@ -30,7 +30,7 @@ internal class Get : ElsaEndpoint<Request, Response>
/// <inheritdoc />
public override async Task<Response> ExecuteAsync(Request request, CancellationToken cancellationToken)
{
var pageArgs = new PageArgs(request.Page, request.PageSize);
var pageArgs = PageArgs.From(request.Page, request.PageSize, request.Skip, request.Take);
var filter = new WorkflowExecutionLogRecordFilter { WorkflowInstanceId = request.WorkflowInstanceId };
var order = new WorkflowExecutionLogRecordOrder<long>(x => x.Sequence, OrderDirection.Ascending);
var pageOfRecords = await _store.FindManyAsync(filter, pageArgs, order, cancellationToken);

View file

@ -23,6 +23,16 @@ internal class Request
/// The size of the page to get.
/// </summary>
public int? PageSize { get; set; }
/// <summary>
/// The number of records to skip.
/// </summary>
public int? Skip { get; set; }
/// <summary>
/// The number of records to take.
/// </summary>
public int? Take { get; set; }
}
internal class Response

View file

@ -28,7 +28,7 @@ internal class List : ElsaEndpoint<Request, Response>
public override async Task<Response> ExecuteAsync(Request request, CancellationToken cancellationToken)
{
var pageArgs = new PageArgs(request.Page, request.PageSize);
var pageArgs = PageArgs.FromPage(request.Page, request.PageSize);
var filter = new WorkflowInstanceFilter
{