Skip to content

[FEAT]: Add "Done for You" Pagination #19

@StevanFreeborn

Description

@StevanFreeborn

Feature Proposal: "Done for You" Pagination with IAsyncEnumerable

Summary

Enhance the OnspringClient by introducing methods that utilize the IAsyncEnumerable<T> type to handle automatic pagination for endpoints that return paged responses. This feature will simplify working with paginated data by abstracting the pagination logic away from the user, allowing developers to consume all items from an endpoint using asynchronous enumeration.

Note

The IAsyncEnumerable<T> interface was introduced in C#8 and in order to allow using this feature in packages such as ours that target both netstandard2.0 and netstandard2.1 Microsoft publishes this package that you can include as a dependency to use this feature.

Motivation

Currently, working with paginated responses from the Onspring API requires developers to manually handle pagination by iterating over pages, making multiple API calls, and aggregating results. This approach adds complexity and boilerplate code, which could be avoided by leveraging IAsyncEnumerable<T> for automatic pagination.

Introducing these methods will:

  • Simplify developer workflows by abstracting pagination logic.
  • Reduce boilerplate code required to interact with paginated endpoints.
  • Enable seamless integration with modern .NET async programming patterns (e.g., await foreach).

Proposed Solution

Add new methods to the OnspringClient that return IAsyncEnumerable<T> for endpoints that support paginated responses. These methods will:

  • Automatically handle fetching additional pages until all data is retrieved.
  • Stream results back to the caller using the IAsyncEnumerable<T> type.

For example, if the existing method to fetch a page of apps is:

Task<ApiResponse<GetPagedAppsResponse>> GetAppsAsync(PagingRequest pagingRequest = null);

The proposed method would look like:

IAsyncEnumerable<ApiResponse<GetPagedAppsResponse>> GetAllAppsAsync(int pageSize = 50);

Usage Example

var appsResponses = onspringClient.GetAllAppsAsync();

await foreach (var response in appsResponses)
{
    if (response.IsSuccessful)
    {
        foreach (var app in response.Value.Items)
        {
            Console.WriteLine($"{app.Id}, {app.Name}");
        }
    }
    else
    {
        Console.WriteLine($"Error: {response.Message}");
    }
}

Implementation Details

  • Add internal helper method to OnspringClient to encapsulate the pagination logic.
  • Add methods to OnspringClient to support fetching all pages of particular entities

Example Helper Implementation

private async IAsyncEnumerable<ApiResponse<T>> GetAllPagesAsync<T>(Func<int, Task<ApiResponse<T>>> callback) where T : PagedResponse
{
    var initialResponse = await callback(1);

    yield return initialResponse;

    var totalPages = initialResponse.Value.TotalPages;
    var nextPage = initialResponse.Value.PageNumber + 1;

    while (nextPage <= totalPages)
    {
        var response = await callback(nextPage);
        nextPage++;

        yield return response;
    }
}

Impact

This feature adds new methods and does not alter existing ones. The new methods would essentially call those that already exist but handle paging through the responses and yielding each response up one at a time.

This would necessitate creating a base PagedResponse model that we then extend from to derive the existing PagedResponse<T> model, but this change is low risk.

Alternatives Considered

  • Continue requiring users to handle pagination manually.
  • Introduce helper utilities outside of the OnspringClient to handle pagination. However, embedding this functionality directly in the client simplifies usage and improves the developer experience.

Additional Context

This feature aligns with modern .NET development practices, leveraging IAsyncEnumerable<T> to provide more intuitive and efficient data processing. It will enhance the usability of the SDK and make it more appealing for developers integrating with Onspring.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions