Pagination implementation adapted - Less methods,more defaults #451

This commit is contained in:
Jonas Dellinger 2020-05-31 15:56:49 +02:00
parent 7445d3ca0e
commit 5ae126699c
11 changed files with 567 additions and 100 deletions

View File

@ -59,7 +59,7 @@ namespace Example.CLI.PersistentConfig
var me = await spotify.UserProfile.Current();
Console.WriteLine($"Welcome {me.DisplayName} ({me.Id}), your authenticated!");
var playlists = await spotify.PaginateAll(() => spotify.Playlists.CurrentUsers());
var playlists = await spotify.PaginateAll(spotify.Playlists.CurrentUsers());
Console.WriteLine($"Total Playlists in your Account: {playlists.Count}");
_server.Dispose();

View File

@ -4,14 +4,60 @@ namespace SpotifyAPI.Web
{
public interface IShowsClient
{
/// <summary>
/// Get Spotify catalog information for a single show identified by its unique Spotify ID.
/// </summary>
/// <param name="showId">The Spotify ID for the show.</param>
/// <remarks>
/// https://developer.spotify.com/documentation/web-api/reference-beta/#endpoint-get-a-show
/// </remarks>
/// <returns></returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716")]
Task<FullShow> Get(string showId);
/// <summary>
/// Get Spotify catalog information for a single show identified by its unique Spotify ID.
/// </summary>
/// <param name="showId">The Spotify ID for the show.</param>
/// <param name="request">The request-model which contains required and optional parameters.</param>
/// <remarks>
/// https://developer.spotify.com/documentation/web-api/reference-beta/#endpoint-get-a-show
/// </remarks>
/// <returns></returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716")]
Task<FullShow> Get(string showId, ShowRequest request);
/// <summary>
/// Get Spotify catalog information for several shows based on their Spotify IDs.
/// </summary>
/// <param name="request">The request-model which contains required and optional parameters.</param>
/// <remarks>
/// https://developer.spotify.com/documentation/web-api/reference-beta/#endpoint-get-multiple-shows
/// </remarks>
/// <returns></returns>
Task<ShowsResponse> GetSeveral(ShowsRequest request);
/// <summary>
/// Get Spotify catalog information about an shows episodes.
/// Optional parameters can be used to limit the number of episodes returned.
/// </summary>
/// <param name="showId">The Spotify ID for the show.</param>
/// <remarks>
/// https://developer.spotify.com/documentation/web-api/reference-beta/#endpoint-get-a-shows-episodes
/// </remarks>
/// <returns></returns>
Task<Paging<SimpleEpisode>> GetEpisodes(string showId);
/// <summary>
/// Get Spotify catalog information about an shows episodes.
/// Optional parameters can be used to limit the number of episodes returned.
/// </summary>
/// <param name="showId">The Spotify ID for the show.</param>
/// <param name="request">The request-model which contains required and optional parameters.</param>
/// <remarks>
/// https://developer.spotify.com/documentation/web-api/reference-beta/#endpoint-get-a-shows-episodes
/// </remarks>
/// <returns></returns>
Task<Paging<SimpleEpisode>> GetEpisodes(string showId, ShowEpisodesRequest request);
}
}

View File

@ -1,3 +1,4 @@
using System.Threading;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
@ -7,56 +8,296 @@ namespace SpotifyAPI.Web
{
public interface ISpotifyClient
{
/// <summary>
/// The default paginator used by the Paginator methods
/// </summary>
/// <value></value>
IPaginator DefaultPaginator { get; }
/// <summary>
/// Operations related to Spotify User Profiles
/// </summary>
/// <value></value>
IUserProfileClient UserProfile { get; }
/// <summary>
/// Operations related to Spotify Browse Endpoints
/// </summary>
/// <value></value>
IBrowseClient Browse { get; }
/// <summary>
/// Operations related to Spotify Shows
/// </summary>
/// <value></value>
IShowsClient Shows { get; }
/// <summary>
/// Operations related to Spotify Playlists
/// </summary>
/// <value></value>
IPlaylistsClient Playlists { get; }
/// <summary>
/// Operations related to Spotify Search
/// </summary>
/// <value></value>
ISearchClient Search { get; }
/// <summary>
/// Operations related to Spotify Follows
/// </summary>
/// <value></value>
IFollowClient Follow { get; }
/// <summary>
/// Operations related to Spotify Tracks
/// </summary>
/// <value></value>
ITracksClient Tracks { get; }
/// <summary>
/// Operations related to Spotify Player Endpoints
/// </summary>
/// <value></value>
IPlayerClient Player { get; }
/// <summary>
/// Operations related to Spotify Albums
/// </summary>
/// <value></value>
IAlbumsClient Albums { get; }
/// <summary>
/// Operations related to Spotify Artists
/// </summary>
/// <value></value>
IArtistsClient Artists { get; }
/// <summary>
/// Operations related to Spotify Personalization Endpoints
/// </summary>
/// <value></value>
IPersonalizationClient Personalization { get; }
/// <summary>
/// Operations related to Spotify Podcast Episodes
/// </summary>
/// <value></value>
IEpisodesClient Episodes { get; }
/// <summary>
/// Operations related to Spotify User Library
/// </summary>
/// <value></value>
ILibraryClient Library { get; }
/// <summary>
/// Returns the last response received by an API call.
/// </summary>
/// <value></value>
IResponse? LastResponse { get; }
Task<IList<T>> PaginateAll<T>(Paging<T> firstPage);
Task<IList<T>> PaginateAll<T>(Paging<T> firstPage, IPaginator paginator);
Task<IList<T>> PaginateAll<T>(Func<Task<Paging<T>>> getFirstPage);
Task<IList<T>> PaginateAll<T>(Func<Task<Paging<T>>> getFirstPage, IPaginator paginator);
/// <summary>
/// Fetches all pages and returns them grouped in a list.
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="firstPage">The first page, will be included in the output list!</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <returns>A list containing all fetched pages</returns>
Task<IList<T>> PaginateAll<T>(Paging<T> firstPage, IPaginator? paginator = default!);
Task<IList<T>> PaginateAll<T, TNext>(Paging<T, TNext> firstPage, Func<TNext, Paging<T, TNext>> mapper);
Task<IList<T>> PaginateAll<T, TNext>(Paging<T, TNext> firstPage, Func<TNext, Paging<T, TNext>> mapper, IPaginator paginator);
Task<IList<T>> PaginateAll<T, TNext>(Func<Task<Paging<T, TNext>>> getFirstPage, Func<TNext, Paging<T, TNext>> mapper);
Task<IList<T>> PaginateAll<T, TNext>(Func<Task<Paging<T, TNext>>> getFirstPage, Func<TNext, Paging<T, TNext>> mapper, IPaginator paginator);
/// <summary>
/// Fetches all pages and returns them grouped in a list.
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="getFirstPage">A function to retrive the first page, will be included in the output list!</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <returns>A list containing all fetched pages</returns>
Task<IList<T>> PaginateAll<T>(Func<Task<Paging<T>>> getFirstPage, IPaginator? paginator = default!);
/// <summary>
/// Fetches all pages and returns them grouped in a list.
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="firstPageTask">A task to retrive the first page, will be included in the output list!</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <returns>A list containing all fetched pages</returns>
Task<IList<T>> PaginateAll<T>(Task<Paging<T>> firstPageTask, IPaginator? paginator = default!);
/// <summary>
/// Fetches all pages and returns them grouped in a list.
/// Some responses (e.g search response) have the pagination nested in a JSON Property.
/// To workaround this limitation, the mapper is required and needs to point to the correct next pagination.
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="firstPage">A first page, will be included in the output list!</param>
/// <param name="mapper">A function which maps response objects to the next paging object</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <returns>A list containing all fetched pages</returns>
Task<IList<T>> PaginateAll<T, TNext>(
Paging<T, TNext> firstPage,
Func<TNext, Paging<T, TNext>> mapper,
IPaginator? paginator = default!
);
/// <summary>
/// Fetches all pages and returns them grouped in a list.
/// Some responses (e.g search response) have the pagination nested in a JSON Property.
/// To workaround this limitation, the mapper is required and needs to point to the correct next pagination.
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="getFirstPage">A function to retrive the first page, will be included in the output list!</param>
/// <param name="mapper">A function which maps response objects to the next paging object</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <typeparam name="TNext">The Response-Type</typeparam>
/// <returns></returns>
Task<IList<T>> PaginateAll<T, TNext>(
Func<Task<Paging<T, TNext>>> getFirstPage,
Func<TNext, Paging<T, TNext>> mapper,
IPaginator? paginator = default!
);
/// <summary>
/// Fetches all pages and returns them grouped in a list.
/// Some responses (e.g search response) have the pagination nested in a JSON Property.
/// To workaround this limitation, the mapper is required and needs to point to the correct next pagination.
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="firstPageTask">A Task to retrive the first page, will be included in the output list!</param>
/// <param name="mapper">A function which maps response objects to the next paging object</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <typeparam name="TNext">The Response-Type</typeparam>
/// <returns></returns>
Task<IList<T>> PaginateAll<T, TNext>(
Task<Paging<T, TNext>> firstPageTask,
Func<TNext, Paging<T, TNext>> mapper,
IPaginator? paginator = default!
);
#if NETSTANDARD2_1
IAsyncEnumerable<T> Paginate<T>(Paging<T> firstPage);
IAsyncEnumerable<T> Paginate<T>(Paging<T> firstPage, IPaginator paginator);
IAsyncEnumerable<T> Paginate<T>(Func<Task<Paging<T>>> getFirstPage);
IAsyncEnumerable<T> Paginate<T>(Func<Task<Paging<T>>> getFirstPage, IPaginator paginator);
/// <summary>
/// Paginate through pages by using IAsyncEnumerable, introduced in C# 8
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="firstPage">A first page, will be included in the output list!</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <param name="cancellationToken">An optional Cancellation Token</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <returns>An iterable IAsyncEnumerable</returns>
IAsyncEnumerable<T> Paginate<T>(
Paging<T> firstPage,
IPaginator? paginator = default!,
CancellationToken cancellationToken = default!
);
IAsyncEnumerable<T> Paginate<T, TNext>(Paging<T, TNext> firstPage, Func<TNext, Paging<T, TNext>> mapper);
IAsyncEnumerable<T> Paginate<T, TNext>(Paging<T, TNext> firstPage, Func<TNext, Paging<T, TNext>> mapper, IPaginator paginator);
IAsyncEnumerable<T> Paginate<T, TNext>(Func<Task<Paging<T, TNext>>> getFirstPage, Func<TNext, Paging<T, TNext>> mapper);
IAsyncEnumerable<T> Paginate<T, TNext>(Func<Task<Paging<T, TNext>>> getFirstPage, Func<TNext, Paging<T, TNext>> mapper, IPaginator paginator);
/// <summary>
/// Paginate through pages by using IAsyncEnumerable, introduced in C# 8
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="getFirstPage">A Function to retrive the first page, will be included in the output list!</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <param name="cancellationToken">An optional Cancellation Token</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <returns>An iterable IAsyncEnumerable</returns>
IAsyncEnumerable<T> Paginate<T>(
Func<Task<Paging<T>>> getFirstPage,
IPaginator? paginator = default!,
CancellationToken cancellationToken = default!
);
/// <summary>
/// Paginate through pages by using IAsyncEnumerable, introduced in C# 8
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="firstPageTask">A Task to retrive the first page, will be included in the output list!</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <param name="cancellationToken">An optional Cancellation Token</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <returns>An iterable IAsyncEnumerable</returns>
IAsyncEnumerable<T> Paginate<T>(
Task<Paging<T>> firstPageTask,
IPaginator? paginator = default!,
CancellationToken cancellationToken = default!
);
/// <summary>
/// Paginate through pages by using IAsyncEnumerable, introduced in C# 8
/// Some responses (e.g search response) have the pagination nested in a JSON Property.
/// To workaround this limitation, the mapper is required and needs to point to the correct next pagination.
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="firstPage">A first page, will be included in the output list!</param>
/// <param name="mapper">A function which maps response objects to the next paging object</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <param name="cancellationToken">An optional Cancellation Token</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <typeparam name="TNext">The Response-Type</typeparam>
/// <returns></returns>
IAsyncEnumerable<T> Paginate<T, TNext>(
Paging<T, TNext> firstPage,
Func<TNext, Paging<T, TNext>> mapper,
IPaginator? paginator = default!,
CancellationToken cancellationToken = default!
);
/// <summary>
/// Paginate through pages by using IAsyncEnumerable, introduced in C# 8
/// Some responses (e.g search response) have the pagination nested in a JSON Property.
/// To workaround this limitation, the mapper is required and needs to point to the correct next pagination.
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="getFirstPage">A Function to retrive the first page, will be included in the output list!</param>
/// <param name="mapper">A function which maps response objects to the next paging object</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <param name="cancellationToken">An optional Cancellation Token</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <typeparam name="TNext">The Response-Type</typeparam>
/// <returns></returns>
IAsyncEnumerable<T> Paginate<T, TNext>(
Func<Task<Paging<T, TNext>>> getFirstPage,
Func<TNext, Paging<T, TNext>> mapper,
IPaginator? paginator = default!,
CancellationToken cancellationToken = default!
);
/// <summary>
/// Paginate through pages by using IAsyncEnumerable, introduced in C# 8
/// Some responses (e.g search response) have the pagination nested in a JSON Property.
/// To workaround this limitation, the mapper is required and needs to point to the correct next pagination.
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="firstPageTask">A Task to retrive the first page, will be included in the output list!</param>
/// <param name="mapper">A function which maps response objects to the next paging object</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <param name="cancellationToken">An optional Cancellation Token</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <typeparam name="TNext">The Response-Type</typeparam>
/// <returns></returns>
IAsyncEnumerable<T> Paginate<T, TNext>(
Task<Paging<T, TNext>> firstPageTask,
Func<TNext, Paging<T, TNext>> mapper,
IPaginator? paginator = default!,
CancellationToken cancellationToken = default!
);
#endif
}
}

View File

@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using SpotifyAPI.Web.Http;
using System.Runtime.CompilerServices;
namespace SpotifyAPI.Web
{
@ -80,157 +82,287 @@ namespace SpotifyAPI.Web
public IResponse? LastResponse { get; private set; }
public Task<IList<T>> PaginateAll<T>(Paging<T> firstPage)
/// <summary>
/// Fetches all pages and returns them grouped in a list.
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="firstPage">The first page, will be included in the output list!</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <returns>A list containing all fetched pages</returns>
public Task<IList<T>> PaginateAll<T>(Paging<T> firstPage, IPaginator? paginator = null)
{
return DefaultPaginator.PaginateAll(firstPage, _apiConnector);
return (paginator ?? DefaultPaginator).PaginateAll(firstPage, _apiConnector);
}
public Task<IList<T>> PaginateAll<T>(Paging<T> firstPage, IPaginator paginator)
{
Ensure.ArgumentNotNull(paginator, nameof(paginator));
return paginator.PaginateAll(firstPage, _apiConnector);
}
public async Task<IList<T>> PaginateAll<T>(Func<Task<Paging<T>>> getFirstPage)
/// <summary>
/// Fetches all pages and returns them grouped in a list.
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="getFirstPage">A function to retrive the first page, will be included in the output list!</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <returns>A list containing all fetched pages</returns>
public async Task<IList<T>> PaginateAll<T>(Func<Task<Paging<T>>> getFirstPage, IPaginator? paginator = null)
{
Ensure.ArgumentNotNull(getFirstPage, nameof(getFirstPage));
return await DefaultPaginator.PaginateAll(
await getFirstPage().ConfigureAwait(false), _apiConnector
).ConfigureAwait(false);
var firstPage = await getFirstPage().ConfigureAwait(false);
return await (paginator ?? DefaultPaginator).PaginateAll(firstPage, _apiConnector).ConfigureAwait(false);
}
public async Task<IList<T>> PaginateAll<T>(Func<Task<Paging<T>>> getFirstPage, IPaginator paginator)
/// <summary>
/// Fetches all pages and returns them grouped in a list.
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="firstPageTask">A task to retrive the first page, will be included in the output list!</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <returns>A list containing all fetched pages</returns>
public async Task<IList<T>> PaginateAll<T>(Task<Paging<T>> firstPageTask, IPaginator? paginator = null)
{
Ensure.ArgumentNotNull(getFirstPage, nameof(getFirstPage));
Ensure.ArgumentNotNull(paginator, nameof(paginator));
Ensure.ArgumentNotNull(firstPageTask, nameof(firstPageTask));
return await paginator.PaginateAll(
await getFirstPage().ConfigureAwait(false), _apiConnector
).ConfigureAwait(false);
var firstPage = await firstPageTask.ConfigureAwait(false);
return await (paginator ?? DefaultPaginator).PaginateAll(firstPage, _apiConnector).ConfigureAwait(false);
}
public Task<IList<T>> PaginateAll<T, TNext>(
Paging<T, TNext> firstPage,
Func<TNext, Paging<T, TNext>> mapper
)
{
return DefaultPaginator.PaginateAll(firstPage, mapper, _apiConnector);
}
public async Task<IList<T>> PaginateAll<T, TNext>(
Func<Task<Paging<T, TNext>>> getFirstPage,
Func<TNext, Paging<T, TNext>> mapper
)
{
Ensure.ArgumentNotNull(getFirstPage, nameof(getFirstPage));
return await DefaultPaginator.PaginateAll(await getFirstPage().ConfigureAwait(false), mapper, _apiConnector).ConfigureAwait(false);
}
/// <summary>
/// Fetches all pages and returns them grouped in a list.
/// Some responses (e.g search response) have the pagination nested in a JSON Property.
/// To workaround this limitation, the mapper is required and needs to point to the correct next pagination.
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="firstPage">A first page, will be included in the output list!</param>
/// <param name="mapper">A function which maps response objects to the next paging object</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <returns>A list containing all fetched pages</returns>
public Task<IList<T>> PaginateAll<T, TNext>(
Paging<T, TNext> firstPage,
Func<TNext, Paging<T, TNext>> mapper,
IPaginator paginator)
IPaginator? paginator = null
)
{
Ensure.ArgumentNotNull(paginator, nameof(paginator));
return paginator.PaginateAll(firstPage, mapper, _apiConnector);
return (paginator ?? DefaultPaginator).PaginateAll(firstPage, mapper, _apiConnector);
}
/// <summary>
/// Fetches all pages and returns them grouped in a list.
/// Some responses (e.g search response) have the pagination nested in a JSON Property.
/// To workaround this limitation, the mapper is required and needs to point to the correct next pagination.
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="getFirstPage">A function to retrive the first page, will be included in the output list!</param>
/// <param name="mapper">A function which maps response objects to the next paging object</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <typeparam name="TNext">The Response-Type</typeparam>
/// <returns></returns>
public async Task<IList<T>> PaginateAll<T, TNext>(
Func<Task<Paging<T, TNext>>> getFirstPage,
Func<TNext, Paging<T, TNext>> mapper,
IPaginator paginator
IPaginator? paginator = null
)
{
Ensure.ArgumentNotNull(getFirstPage, nameof(getFirstPage));
Ensure.ArgumentNotNull(paginator, nameof(paginator));
return await paginator.PaginateAll(
await getFirstPage().ConfigureAwait(false), mapper, _apiConnector
).ConfigureAwait(false);
var firstPage = await getFirstPage().ConfigureAwait(false);
return await (paginator ?? DefaultPaginator).PaginateAll(firstPage, mapper, _apiConnector).ConfigureAwait(false);
}
/// <summary>
/// Fetches all pages and returns them grouped in a list.
/// Some responses (e.g search response) have the pagination nested in a JSON Property.
/// To workaround this limitation, the mapper is required and needs to point to the correct next pagination.
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="firstPageTask">A Task to retrive the first page, will be included in the output list!</param>
/// <param name="mapper">A function which maps response objects to the next paging object</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <typeparam name="TNext">The Response-Type</typeparam>
/// <returns></returns>
public async Task<IList<T>> PaginateAll<T, TNext>(
Task<Paging<T, TNext>> firstPageTask,
Func<TNext, Paging<T, TNext>> mapper,
IPaginator? paginator = null
)
{
Ensure.ArgumentNotNull(firstPageTask, nameof(firstPageTask));
var firstPage = await firstPageTask.ConfigureAwait(false);
return await (paginator ?? DefaultPaginator).PaginateAll(firstPage, mapper, _apiConnector).ConfigureAwait(false);
}
#if NETSTANDARD2_1
public IAsyncEnumerable<T> Paginate<T>(Paging<T> firstPage)
/// <summary>
/// Paginate through pages by using IAsyncEnumerable, introduced in C# 8
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="firstPage">A first page, will be included in the output list!</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <param name="cancellationToken">An optional Cancellation Token</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <returns>An iterable IAsyncEnumerable</returns>
public IAsyncEnumerable<T> Paginate<T>(
Paging<T> firstPage,
IPaginator? paginator = null,
CancellationToken cancellationToken = default
)
{
return DefaultPaginator.Paginate(firstPage, _apiConnector);
return (paginator ?? DefaultPaginator).Paginate(firstPage, _apiConnector, cancellationToken);
}
public IAsyncEnumerable<T> Paginate<T>(Paging<T> firstPage, IPaginator paginator)
/// <summary>
/// Paginate through pages by using IAsyncEnumerable, introduced in C# 8
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="getFirstPage">A Function to retrive the first page, will be included in the output list!</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <param name="cancellationToken">An optional Cancellation Token</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <returns>An iterable IAsyncEnumerable</returns>
public async IAsyncEnumerable<T> Paginate<T>(
Func<Task<Paging<T>>> getFirstPage,
IPaginator? paginator = null,
[EnumeratorCancellation] CancellationToken cancellationToken = default
)
{
Ensure.ArgumentNotNull(paginator, nameof(paginator));
return paginator.Paginate(firstPage, _apiConnector);
}
public async IAsyncEnumerable<T> Paginate<T>(Func<Task<Paging<T>>> getFirstPage)
{
Ensure.ArgumentNotNull(getFirstPage, nameof(getFirstPage));
var firstPage = await getFirstPage().ConfigureAwait(false);
await foreach (var item in DefaultPaginator.Paginate(firstPage, _apiConnector))
await foreach (var item in (paginator ?? DefaultPaginator)
.Paginate(firstPage, _apiConnector)
.WithCancellation(cancellationToken)
)
{
yield return item;
}
}
public async IAsyncEnumerable<T> Paginate<T>(Func<Task<Paging<T>>> getFirstPage, IPaginator paginator)
/// <summary>
/// Paginate through pages by using IAsyncEnumerable, introduced in C# 8
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="firstPageTask">A Task to retrive the first page, will be included in the output list!</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <param name="cancellationToken">An optional Cancellation Token</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <returns>An iterable IAsyncEnumerable</returns>
public async IAsyncEnumerable<T> Paginate<T>(
Task<Paging<T>> firstPageTask,
IPaginator? paginator = null,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
Ensure.ArgumentNotNull(getFirstPage, nameof(getFirstPage));
Ensure.ArgumentNotNull(paginator, nameof(paginator));
Ensure.ArgumentNotNull(firstPageTask, nameof(firstPageTask));
var firstPage = await getFirstPage().ConfigureAwait(false);
await foreach (var item in DefaultPaginator.Paginate(firstPage, _apiConnector))
var firstPage = await firstPageTask.ConfigureAwait(false);
await foreach (var item in (paginator ?? DefaultPaginator)
.Paginate(firstPage, _apiConnector)
.WithCancellation(cancellationToken)
)
{
yield return item;
}
}
public IAsyncEnumerable<T> Paginate<T, TNext>(Paging<T, TNext> firstPage, Func<TNext, Paging<T, TNext>> mapper)
{
return DefaultPaginator.Paginate(firstPage, mapper, _apiConnector);
}
/// <summary>
/// Paginate through pages by using IAsyncEnumerable, introduced in C# 8
/// Some responses (e.g search response) have the pagination nested in a JSON Property.
/// To workaround this limitation, the mapper is required and needs to point to the correct next pagination.
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="firstPage">A first page, will be included in the output list!</param>
/// <param name="mapper">A function which maps response objects to the next paging object</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <param name="cancellationToken">An optional Cancellation Token</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <typeparam name="TNext">The Response-Type</typeparam>
/// <returns></returns>
public IAsyncEnumerable<T> Paginate<T, TNext>(
Paging<T, TNext> firstPage,
Func<TNext, Paging<T, TNext>> mapper,
IPaginator paginator
IPaginator? paginator = null,
CancellationToken cancellationToken = default
)
{
Ensure.ArgumentNotNull(paginator, nameof(paginator));
return paginator.Paginate(firstPage, mapper, _apiConnector);
return (paginator ?? DefaultPaginator).Paginate(firstPage, mapper, _apiConnector, cancellationToken);
}
/// <summary>
/// Paginate through pages by using IAsyncEnumerable, introduced in C# 8
/// Some responses (e.g search response) have the pagination nested in a JSON Property.
/// To workaround this limitation, the mapper is required and needs to point to the correct next pagination.
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="getFirstPage">A Function to retrive the first page, will be included in the output list!</param>
/// <param name="mapper">A function which maps response objects to the next paging object</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <param name="cancellationToken">An optional Cancellation Token</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <typeparam name="TNext">The Response-Type</typeparam>
/// <returns></returns>
public async IAsyncEnumerable<T> Paginate<T, TNext>(
Func<Task<Paging<T, TNext>>> getFirstPage,
Func<TNext, Paging<T, TNext>> mapper
)
Func<TNext, Paging<T, TNext>> mapper,
IPaginator? paginator = null,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
Ensure.ArgumentNotNull(getFirstPage, nameof(getFirstPage));
var firstPage = await getFirstPage().ConfigureAwait(false);
await foreach (var item in DefaultPaginator.Paginate(firstPage, mapper, _apiConnector))
await foreach (var item in (paginator ?? DefaultPaginator)
.Paginate(firstPage, mapper, _apiConnector)
.WithCancellation(cancellationToken)
)
{
yield return item;
}
}
/// <summary>
/// Paginate through pages by using IAsyncEnumerable, introduced in C# 8
/// Some responses (e.g search response) have the pagination nested in a JSON Property.
/// To workaround this limitation, the mapper is required and needs to point to the correct next pagination.
/// The default paginator will fetch all available resources without a delay between requests.
/// This can drain your request limit quite fast, so consider using a custom paginator with delays.
/// </summary>
/// <param name="firstPageTask">A Task to retrive the first page, will be included in the output list!</param>
/// <param name="mapper">A function which maps response objects to the next paging object</param>
/// <param name="paginator">Optional. If not supplied, DefaultPaginator will be used</param>
/// <param name="cancellationToken">An optional Cancellation Token</param>
/// <typeparam name="T">The Paging-Type</typeparam>
/// <typeparam name="TNext">The Response-Type</typeparam>
/// <returns></returns>
public async IAsyncEnumerable<T> Paginate<T, TNext>(
Func<Task<Paging<T, TNext>>> getFirstPage,
Task<Paging<T, TNext>> firstPageTask,
Func<TNext, Paging<T, TNext>> mapper,
IPaginator paginator
)
IPaginator? paginator = null,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
Ensure.ArgumentNotNull(getFirstPage, nameof(getFirstPage));
Ensure.ArgumentNotNull(paginator, nameof(paginator));
Ensure.ArgumentNotNull(firstPageTask, nameof(firstPageTask));
var firstPage = await getFirstPage().ConfigureAwait(false);
await foreach (var item in paginator.Paginate(firstPage, mapper, _apiConnector))
var firstPage = await firstPageTask.ConfigureAwait(false);
await foreach (var item in (paginator ?? DefaultPaginator)
.Paginate(firstPage, mapper, _apiConnector)
.WithCancellation(cancellationToken)
)
{
yield return item;
}

View File

@ -2,12 +2,30 @@ namespace SpotifyAPI.Web
{
public class ShowEpisodesRequest : RequestParams
{
/// <summary>
/// The maximum number of episodes to return. Default: 20. Minimum: 1. Maximum: 50.
/// </summary>
/// <value></value>
[QueryParam("limit")]
public int? Limit { get; set; }
/// <summary>
/// The index of the first episode to return.
/// Default: 0 (the first object). Use with limit to get the next set of episodes.
/// </summary>
/// <value></value>
[QueryParam("offset")]
public int? Offset { get; set; }
/// <summary>
/// An ISO 3166-1 alpha-2 country code. If a country code is specified, only shows and episodes
/// that are available in that market will be returned.
/// If a valid user access token is specified in the request header,
/// the country associated with the user account will take priority over this parameter.
/// Note: If neither market or user country are provided, the content is considered unavailable for the client.
/// Users can view the country that is associated with their account in the account settings.
/// </summary>
/// <value></value>
[QueryParam("market")]
public string? Market { get; set; }
}

View File

@ -2,6 +2,16 @@ namespace SpotifyAPI.Web
{
public class ShowRequest : RequestParams
{
/// <summary>
/// An ISO 3166-1 alpha-2 country code. If a country code is specified,
/// only shows and episodes that are available in that market will be returned.
/// If a valid user access token is specified in the request header,
/// the country associated with the user account will take priority over this parameter.
/// Note: If neither market or user country are provided, the content
/// is considered unavailable for the client.
/// Users can view the country that is associated with their account in the account settings.
/// </summary>
/// <value></value>
[QueryParam("market")]
public string? Market { get; set; }
}

View File

@ -4,6 +4,12 @@ namespace SpotifyAPI.Web
{
public class ShowsRequest : RequestParams
{
/// <summary>
/// Get Spotify catalog information for several shows based on their Spotify IDs.
/// </summary>
/// <param name="ids">
/// A comma-separated list of the Spotify IDs for the shows. Maximum: 50 IDs.
/// </param>
public ShowsRequest(IList<string> ids)
{
Ensure.ArgumentNotNullOrEmptyList(ids, nameof(ids));
@ -11,9 +17,23 @@ namespace SpotifyAPI.Web
Ids = ids;
}
/// <summary>
/// A comma-separated list of the Spotify IDs for the shows. Maximum: 50 IDs.
/// </summary>
/// <value></value>
[QueryParam("ids")]
public IList<string> Ids { get; }
/// <summary>
/// An ISO 3166-1 alpha-2 country code. If a country code is specified, only shows and episodes
/// that are available in that market will be returned.
/// If a valid user access token is specified in the request header,
/// the country associated with the user account will take priority over this parameter.
/// Note: If neither market or user country are provided,
/// the content is considered unavailable for the client.
/// Users can view the country that is associated with their account in the account settings.
/// </summary>
/// <value></value>
[QueryParam("market")]
public string? Market { get; set; }
}

View File

@ -2,7 +2,7 @@ namespace SpotifyAPI.Web
{
public class CategoriesResponse
{
public Paging<Category> Categories { get; set; } = default!;
public Paging<Category, CategoriesResponse> Categories { get; set; } = default!;
}
}

View File

@ -2,7 +2,7 @@ namespace SpotifyAPI.Web
{
public class CategoryPlaylistsResponse
{
public Paging<SimplePlaylist> Playlists { get; set; } = default!;
public Paging<SimplePlaylist, CategoryPlaylistsResponse> Playlists { get; set; } = default!;
}
}

View File

@ -3,7 +3,7 @@ namespace SpotifyAPI.Web
public class FeaturedPlaylistsResponse
{
public string Message { get; set; } = default!;
public Paging<SimplePlaylist> Playlists { get; set; } = default!;
public Paging<SimplePlaylist, FeaturedPlaylistsResponse> Playlists { get; set; } = default!;
}
}

View File

@ -3,7 +3,7 @@ namespace SpotifyAPI.Web
public class NewReleasesResponse
{
public string Message { get; set; } = default!;
public Paging<SimpleAlbum> Albums { get; set; } = default!;
public Paging<SimpleAlbum, NewReleasesResponse> Albums { get; set; } = default!;
}
}