Skip to content

Allow clientside paging #974

@svrooij

Description

@svrooij

Is your feature request related to a problem? Please describe the problem.

The SDK only supports "server-side" paging using the PageIterator this parsed the NextDataLink and allows to get multiple pages on the server side.

For instance the List MobileApps endpoint.

Describe the solution you'd like.

But what if I want to show some page to user, and not all pages.

I would like to do:

var apps = await graphServiceClient.DeviceAppManagement.MobileApps.GetFromNextLinkAsync(Uri nextDataLink, CancellationToken cancellationToken);

Then I can pass the nextDataLink to the user and allow very efficient loading of pages.

I also considered parsing the NextDataLink uri and extracting the $SkipToken query parameter, but this is not supported:

var apps = await graphServiceClient.DeviceAppManagement.MobileApps.GetAsync(req => {
  req.QueryParameters.SkipToken = "..."; // This does not exists 🙄
}, cancellationToken);

Additional context?

You have a lot of "list" endpoints. You can get all items server side using a PageIterator, but you cannot easily make the client browse a list efficiently.

I think that every endpoint that returns a SomethingCollectionResponse should also have a GetAsync(Uri nextLink, CancellationToken cancellationToken) overload!

Otherwise we are all forced to start creating Extensions for the client that does something like:

private async Task InterpageIterateAsync(CancellationToken token)
{
State = PagingState.InterpageIteration;
// Get the next page if it is available and queue the items for processing.
if (!string.IsNullOrEmpty(Nextlink) || !string.IsNullOrEmpty(Deltalink))
{
// Call the MSGraph API to get the next page of results and set that page as the currentPage.
var nextPageRequestInformation = new RequestInformation
{
HttpMethod = Method.GET,
UrlTemplate = string.IsNullOrEmpty(Nextlink) ? Deltalink : Nextlink,
};
// if we have a request configurator, modify the request as desired then execute it to get the next page
nextPageRequestInformation = _requestConfigurator == null ? nextPageRequestInformation : _requestConfigurator(nextPageRequestInformation);
_currentPage = await _requestAdapter.SendAsync<TCollectionPage>(nextPageRequestInformation, (parseNode) => new TCollectionPage(), _errorMapping, token);
var pageItems = ExtractEntityListFromParsable(_currentPage);
// Add all of the items returned in the response to the queue.
if (pageItems != null && pageItems.Count > 0)
{
foreach (TEntity entity in pageItems)
{
_pageItemQueue.Enqueue(entity);
}
}
}
// Detect nextLink loop
if (!string.IsNullOrEmpty(Nextlink) && Nextlink.Equals(ExtractNextLinkFromParsable(_currentPage)))
{
throw new ServiceException($"Detected nextLink loop. Nextlink value: {Nextlink}");
}
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions