diff --git a/SpotifyAPI.Web.Examples/Example.CLI.PersistentConfig/Program.cs b/SpotifyAPI.Web.Examples/Example.CLI.PersistentConfig/Program.cs index 76861994..67942070 100644 --- a/SpotifyAPI.Web.Examples/Example.CLI.PersistentConfig/Program.cs +++ b/SpotifyAPI.Web.Examples/Example.CLI.PersistentConfig/Program.cs @@ -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(); diff --git a/SpotifyAPI.Web/Clients/Interfaces/IShowsClient.cs b/SpotifyAPI.Web/Clients/Interfaces/IShowsClient.cs index ddb5e57c..a14ab7ae 100644 --- a/SpotifyAPI.Web/Clients/Interfaces/IShowsClient.cs +++ b/SpotifyAPI.Web/Clients/Interfaces/IShowsClient.cs @@ -4,14 +4,60 @@ namespace SpotifyAPI.Web { public interface IShowsClient { + /// + /// Get Spotify catalog information for a single show identified by its unique Spotify ID. + /// + /// The Spotify ID for the show. + /// + /// https://developer.spotify.com/documentation/web-api/reference-beta/#endpoint-get-a-show + /// + /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716")] Task Get(string showId); + + /// + /// Get Spotify catalog information for a single show identified by its unique Spotify ID. + /// + /// The Spotify ID for the show. + /// The request-model which contains required and optional parameters. + /// + /// https://developer.spotify.com/documentation/web-api/reference-beta/#endpoint-get-a-show + /// + /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716")] Task Get(string showId, ShowRequest request); + /// + /// Get Spotify catalog information for several shows based on their Spotify IDs. + /// + /// The request-model which contains required and optional parameters. + /// + /// https://developer.spotify.com/documentation/web-api/reference-beta/#endpoint-get-multiple-shows + /// + /// Task GetSeveral(ShowsRequest request); + /// + /// Get Spotify catalog information about an show’s episodes. + /// Optional parameters can be used to limit the number of episodes returned. + /// + /// The Spotify ID for the show. + /// + /// https://developer.spotify.com/documentation/web-api/reference-beta/#endpoint-get-a-shows-episodes + /// + /// Task> GetEpisodes(string showId); + + /// + /// Get Spotify catalog information about an show’s episodes. + /// Optional parameters can be used to limit the number of episodes returned. + /// + /// The Spotify ID for the show. + /// The request-model which contains required and optional parameters. + /// + /// https://developer.spotify.com/documentation/web-api/reference-beta/#endpoint-get-a-shows-episodes + /// + /// Task> GetEpisodes(string showId, ShowEpisodesRequest request); } } diff --git a/SpotifyAPI.Web/Clients/Interfaces/ISpotifyClient.cs b/SpotifyAPI.Web/Clients/Interfaces/ISpotifyClient.cs index abff6dde..40907235 100644 --- a/SpotifyAPI.Web/Clients/Interfaces/ISpotifyClient.cs +++ b/SpotifyAPI.Web/Clients/Interfaces/ISpotifyClient.cs @@ -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 { + /// + /// The default paginator used by the Paginator methods + /// + /// IPaginator DefaultPaginator { get; } + /// + /// Operations related to Spotify User Profiles + /// + /// IUserProfileClient UserProfile { get; } + /// + /// Operations related to Spotify Browse Endpoints + /// + /// IBrowseClient Browse { get; } + /// + /// Operations related to Spotify Shows + /// + /// IShowsClient Shows { get; } + /// + /// Operations related to Spotify Playlists + /// + /// IPlaylistsClient Playlists { get; } + /// + /// Operations related to Spotify Search + /// + /// ISearchClient Search { get; } + /// + /// Operations related to Spotify Follows + /// + /// IFollowClient Follow { get; } + /// + /// Operations related to Spotify Tracks + /// + /// ITracksClient Tracks { get; } + /// + /// Operations related to Spotify Player Endpoints + /// + /// IPlayerClient Player { get; } + /// + /// Operations related to Spotify Albums + /// + /// IAlbumsClient Albums { get; } + /// + /// Operations related to Spotify Artists + /// + /// IArtistsClient Artists { get; } + /// + /// Operations related to Spotify Personalization Endpoints + /// + /// IPersonalizationClient Personalization { get; } + /// + /// Operations related to Spotify Podcast Episodes + /// + /// IEpisodesClient Episodes { get; } + /// + /// Operations related to Spotify User Library + /// + /// ILibraryClient Library { get; } + /// + /// Returns the last response received by an API call. + /// + /// IResponse? LastResponse { get; } - Task> PaginateAll(Paging firstPage); - Task> PaginateAll(Paging firstPage, IPaginator paginator); - Task> PaginateAll(Func>> getFirstPage); - Task> PaginateAll(Func>> getFirstPage, IPaginator paginator); + /// + /// 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. + /// + /// The first page, will be included in the output list! + /// Optional. If not supplied, DefaultPaginator will be used + /// The Paging-Type + /// A list containing all fetched pages + Task> PaginateAll(Paging firstPage, IPaginator? paginator = default!); - Task> PaginateAll(Paging firstPage, Func> mapper); - Task> PaginateAll(Paging firstPage, Func> mapper, IPaginator paginator); - Task> PaginateAll(Func>> getFirstPage, Func> mapper); - Task> PaginateAll(Func>> getFirstPage, Func> mapper, IPaginator paginator); + /// + /// 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. + /// + /// A function to retrive the first page, will be included in the output list! + /// Optional. If not supplied, DefaultPaginator will be used + /// The Paging-Type + /// A list containing all fetched pages + Task> PaginateAll(Func>> getFirstPage, IPaginator? paginator = default!); + + /// + /// 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. + /// + /// A task to retrive the first page, will be included in the output list! + /// Optional. If not supplied, DefaultPaginator will be used + /// The Paging-Type + /// A list containing all fetched pages + Task> PaginateAll(Task> firstPageTask, IPaginator? paginator = default!); + + /// + /// 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. + /// + /// A first page, will be included in the output list! + /// A function which maps response objects to the next paging object + /// Optional. If not supplied, DefaultPaginator will be used + /// The Paging-Type + /// A list containing all fetched pages + Task> PaginateAll( + Paging firstPage, + Func> mapper, + IPaginator? paginator = default! + ); + + /// + /// 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. + /// + /// A function to retrive the first page, will be included in the output list! + /// A function which maps response objects to the next paging object + /// Optional. If not supplied, DefaultPaginator will be used + /// The Paging-Type + /// The Response-Type + /// + Task> PaginateAll( + Func>> getFirstPage, + Func> mapper, + IPaginator? paginator = default! + ); + + /// + /// 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. + /// + /// A Task to retrive the first page, will be included in the output list! + /// A function which maps response objects to the next paging object + /// Optional. If not supplied, DefaultPaginator will be used + /// The Paging-Type + /// The Response-Type + /// + Task> PaginateAll( + Task> firstPageTask, + Func> mapper, + IPaginator? paginator = default! + ); #if NETSTANDARD2_1 - IAsyncEnumerable Paginate(Paging firstPage); - IAsyncEnumerable Paginate(Paging firstPage, IPaginator paginator); - IAsyncEnumerable Paginate(Func>> getFirstPage); - IAsyncEnumerable Paginate(Func>> getFirstPage, IPaginator paginator); + /// + /// 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. + /// + /// A first page, will be included in the output list! + /// Optional. If not supplied, DefaultPaginator will be used + /// An optional Cancellation Token + /// The Paging-Type + /// An iterable IAsyncEnumerable + IAsyncEnumerable Paginate( + Paging firstPage, + IPaginator? paginator = default!, + CancellationToken cancellationToken = default! + ); - IAsyncEnumerable Paginate(Paging firstPage, Func> mapper); - IAsyncEnumerable Paginate(Paging firstPage, Func> mapper, IPaginator paginator); - IAsyncEnumerable Paginate(Func>> getFirstPage, Func> mapper); - IAsyncEnumerable Paginate(Func>> getFirstPage, Func> mapper, IPaginator paginator); + /// + /// 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. + /// + /// A Function to retrive the first page, will be included in the output list! + /// Optional. If not supplied, DefaultPaginator will be used + /// An optional Cancellation Token + /// The Paging-Type + /// An iterable IAsyncEnumerable + IAsyncEnumerable Paginate( + Func>> getFirstPage, + IPaginator? paginator = default!, + CancellationToken cancellationToken = default! + ); + + /// + /// 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. + /// + /// A Task to retrive the first page, will be included in the output list! + /// Optional. If not supplied, DefaultPaginator will be used + /// An optional Cancellation Token + /// The Paging-Type + /// An iterable IAsyncEnumerable + IAsyncEnumerable Paginate( + Task> firstPageTask, + IPaginator? paginator = default!, + CancellationToken cancellationToken = default! + ); + + /// + /// 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. + /// + /// A first page, will be included in the output list! + /// A function which maps response objects to the next paging object + /// Optional. If not supplied, DefaultPaginator will be used + /// An optional Cancellation Token + /// The Paging-Type + /// The Response-Type + /// + IAsyncEnumerable Paginate( + Paging firstPage, + Func> mapper, + IPaginator? paginator = default!, + CancellationToken cancellationToken = default! + ); + + /// + /// 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. + /// + /// A Function to retrive the first page, will be included in the output list! + /// A function which maps response objects to the next paging object + /// Optional. If not supplied, DefaultPaginator will be used + /// An optional Cancellation Token + /// The Paging-Type + /// The Response-Type + /// + IAsyncEnumerable Paginate( + Func>> getFirstPage, + Func> mapper, + IPaginator? paginator = default!, + CancellationToken cancellationToken = default! + ); + + /// + /// 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. + /// + /// A Task to retrive the first page, will be included in the output list! + /// A function which maps response objects to the next paging object + /// Optional. If not supplied, DefaultPaginator will be used + /// An optional Cancellation Token + /// The Paging-Type + /// The Response-Type + /// + IAsyncEnumerable Paginate( + Task> firstPageTask, + Func> mapper, + IPaginator? paginator = default!, + CancellationToken cancellationToken = default! + ); #endif } } diff --git a/SpotifyAPI.Web/Clients/SpotifyClient.cs b/SpotifyAPI.Web/Clients/SpotifyClient.cs index a30982a7..e5c958b3 100644 --- a/SpotifyAPI.Web/Clients/SpotifyClient.cs +++ b/SpotifyAPI.Web/Clients/SpotifyClient.cs @@ -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> PaginateAll(Paging firstPage) + /// + /// 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. + /// + /// The first page, will be included in the output list! + /// Optional. If not supplied, DefaultPaginator will be used + /// The Paging-Type + /// A list containing all fetched pages + public Task> PaginateAll(Paging firstPage, IPaginator? paginator = null) { - return DefaultPaginator.PaginateAll(firstPage, _apiConnector); + return (paginator ?? DefaultPaginator).PaginateAll(firstPage, _apiConnector); } - public Task> PaginateAll(Paging firstPage, IPaginator paginator) - { - Ensure.ArgumentNotNull(paginator, nameof(paginator)); - - return paginator.PaginateAll(firstPage, _apiConnector); - } - - public async Task> PaginateAll(Func>> getFirstPage) + /// + /// 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. + /// + /// A function to retrive the first page, will be included in the output list! + /// Optional. If not supplied, DefaultPaginator will be used + /// The Paging-Type + /// A list containing all fetched pages + public async Task> PaginateAll(Func>> 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> PaginateAll(Func>> getFirstPage, IPaginator paginator) + /// + /// 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. + /// + /// A task to retrive the first page, will be included in the output list! + /// Optional. If not supplied, DefaultPaginator will be used + /// The Paging-Type + /// A list containing all fetched pages + public async Task> PaginateAll(Task> 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> PaginateAll( - Paging firstPage, - Func> mapper - ) - { - return DefaultPaginator.PaginateAll(firstPage, mapper, _apiConnector); - } - - public async Task> PaginateAll( - Func>> getFirstPage, - Func> mapper - ) - { - Ensure.ArgumentNotNull(getFirstPage, nameof(getFirstPage)); - - return await DefaultPaginator.PaginateAll(await getFirstPage().ConfigureAwait(false), mapper, _apiConnector).ConfigureAwait(false); - } + /// + /// 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. + /// + /// A first page, will be included in the output list! + /// A function which maps response objects to the next paging object + /// Optional. If not supplied, DefaultPaginator will be used + /// The Paging-Type + /// A list containing all fetched pages public Task> PaginateAll( Paging firstPage, Func> mapper, - IPaginator paginator) + IPaginator? paginator = null + ) { - Ensure.ArgumentNotNull(paginator, nameof(paginator)); - - return paginator.PaginateAll(firstPage, mapper, _apiConnector); + return (paginator ?? DefaultPaginator).PaginateAll(firstPage, mapper, _apiConnector); } + + /// + /// 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. + /// + /// A function to retrive the first page, will be included in the output list! + /// A function which maps response objects to the next paging object + /// Optional. If not supplied, DefaultPaginator will be used + /// The Paging-Type + /// The Response-Type + /// public async Task> PaginateAll( Func>> getFirstPage, Func> 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); } + /// + /// 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. + /// + /// A Task to retrive the first page, will be included in the output list! + /// A function which maps response objects to the next paging object + /// Optional. If not supplied, DefaultPaginator will be used + /// The Paging-Type + /// The Response-Type + /// + public async Task> PaginateAll( + Task> firstPageTask, + Func> 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 Paginate(Paging firstPage) + + /// + /// 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. + /// + /// A first page, will be included in the output list! + /// Optional. If not supplied, DefaultPaginator will be used + /// An optional Cancellation Token + /// The Paging-Type + /// An iterable IAsyncEnumerable + public IAsyncEnumerable Paginate( + Paging firstPage, + IPaginator? paginator = null, + CancellationToken cancellationToken = default + ) { - return DefaultPaginator.Paginate(firstPage, _apiConnector); + return (paginator ?? DefaultPaginator).Paginate(firstPage, _apiConnector, cancellationToken); } - public IAsyncEnumerable Paginate(Paging firstPage, IPaginator paginator) + /// + /// 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. + /// + /// A Function to retrive the first page, will be included in the output list! + /// Optional. If not supplied, DefaultPaginator will be used + /// An optional Cancellation Token + /// The Paging-Type + /// An iterable IAsyncEnumerable + public async IAsyncEnumerable Paginate( + Func>> getFirstPage, + IPaginator? paginator = null, + [EnumeratorCancellation] CancellationToken cancellationToken = default + ) { - Ensure.ArgumentNotNull(paginator, nameof(paginator)); - return paginator.Paginate(firstPage, _apiConnector); - } - - public async IAsyncEnumerable Paginate(Func>> 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 Paginate(Func>> getFirstPage, IPaginator paginator) + /// + /// 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. + /// + /// A Task to retrive the first page, will be included in the output list! + /// Optional. If not supplied, DefaultPaginator will be used + /// An optional Cancellation Token + /// The Paging-Type + /// An iterable IAsyncEnumerable + public async IAsyncEnumerable Paginate( + Task> 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 Paginate(Paging firstPage, Func> mapper) - { - return DefaultPaginator.Paginate(firstPage, mapper, _apiConnector); - } - + /// + /// 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. + /// + /// A first page, will be included in the output list! + /// A function which maps response objects to the next paging object + /// Optional. If not supplied, DefaultPaginator will be used + /// An optional Cancellation Token + /// The Paging-Type + /// The Response-Type + /// public IAsyncEnumerable Paginate( Paging firstPage, Func> 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); } + /// + /// 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. + /// + /// A Function to retrive the first page, will be included in the output list! + /// A function which maps response objects to the next paging object + /// Optional. If not supplied, DefaultPaginator will be used + /// An optional Cancellation Token + /// The Paging-Type + /// The Response-Type + /// public async IAsyncEnumerable Paginate( Func>> getFirstPage, - Func> mapper - ) + Func> 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; } } + /// + /// 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. + /// + /// A Task to retrive the first page, will be included in the output list! + /// A function which maps response objects to the next paging object + /// Optional. If not supplied, DefaultPaginator will be used + /// An optional Cancellation Token + /// The Paging-Type + /// The Response-Type + /// public async IAsyncEnumerable Paginate( - Func>> getFirstPage, + Task> firstPageTask, Func> 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; } diff --git a/SpotifyAPI.Web/Models/Request/ShowEpisodesRequest.cs b/SpotifyAPI.Web/Models/Request/ShowEpisodesRequest.cs index 71c08cef..8f355ebe 100644 --- a/SpotifyAPI.Web/Models/Request/ShowEpisodesRequest.cs +++ b/SpotifyAPI.Web/Models/Request/ShowEpisodesRequest.cs @@ -2,12 +2,30 @@ namespace SpotifyAPI.Web { public class ShowEpisodesRequest : RequestParams { + /// + /// The maximum number of episodes to return. Default: 20. Minimum: 1. Maximum: 50. + /// + /// [QueryParam("limit")] public int? Limit { get; set; } + /// + /// The index of the first episode to return. + /// Default: 0 (the first object). Use with limit to get the next set of episodes. + /// + /// [QueryParam("offset")] public int? Offset { get; set; } + /// + /// 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. + /// + /// [QueryParam("market")] public string? Market { get; set; } } diff --git a/SpotifyAPI.Web/Models/Request/ShowRequest.cs b/SpotifyAPI.Web/Models/Request/ShowRequest.cs index be0726fa..e2cd1fba 100644 --- a/SpotifyAPI.Web/Models/Request/ShowRequest.cs +++ b/SpotifyAPI.Web/Models/Request/ShowRequest.cs @@ -2,6 +2,16 @@ namespace SpotifyAPI.Web { public class ShowRequest : RequestParams { + /// + /// 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. + /// + /// [QueryParam("market")] public string? Market { get; set; } } diff --git a/SpotifyAPI.Web/Models/Request/ShowsRequest.cs b/SpotifyAPI.Web/Models/Request/ShowsRequest.cs index eccf38fc..edccc421 100644 --- a/SpotifyAPI.Web/Models/Request/ShowsRequest.cs +++ b/SpotifyAPI.Web/Models/Request/ShowsRequest.cs @@ -4,6 +4,12 @@ namespace SpotifyAPI.Web { public class ShowsRequest : RequestParams { + /// + /// Get Spotify catalog information for several shows based on their Spotify IDs. + /// + /// + /// A comma-separated list of the Spotify IDs for the shows. Maximum: 50 IDs. + /// public ShowsRequest(IList ids) { Ensure.ArgumentNotNullOrEmptyList(ids, nameof(ids)); @@ -11,9 +17,23 @@ namespace SpotifyAPI.Web Ids = ids; } + /// + /// A comma-separated list of the Spotify IDs for the shows. Maximum: 50 IDs. + /// + /// [QueryParam("ids")] public IList Ids { get; } + /// + /// 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. + /// + /// [QueryParam("market")] public string? Market { get; set; } } diff --git a/SpotifyAPI.Web/Models/Response/CategoriesResponse.cs b/SpotifyAPI.Web/Models/Response/CategoriesResponse.cs index 92b2d6f4..ca63e5b1 100644 --- a/SpotifyAPI.Web/Models/Response/CategoriesResponse.cs +++ b/SpotifyAPI.Web/Models/Response/CategoriesResponse.cs @@ -2,7 +2,7 @@ namespace SpotifyAPI.Web { public class CategoriesResponse { - public Paging Categories { get; set; } = default!; + public Paging Categories { get; set; } = default!; } } diff --git a/SpotifyAPI.Web/Models/Response/CategoryPlaylistsResponse.cs b/SpotifyAPI.Web/Models/Response/CategoryPlaylistsResponse.cs index 1f895bc0..e65fa8ad 100644 --- a/SpotifyAPI.Web/Models/Response/CategoryPlaylistsResponse.cs +++ b/SpotifyAPI.Web/Models/Response/CategoryPlaylistsResponse.cs @@ -2,7 +2,7 @@ namespace SpotifyAPI.Web { public class CategoryPlaylistsResponse { - public Paging Playlists { get; set; } = default!; + public Paging Playlists { get; set; } = default!; } } diff --git a/SpotifyAPI.Web/Models/Response/FeaturedPlaylistsResponse.cs b/SpotifyAPI.Web/Models/Response/FeaturedPlaylistsResponse.cs index 7a97d741..f38b7346 100644 --- a/SpotifyAPI.Web/Models/Response/FeaturedPlaylistsResponse.cs +++ b/SpotifyAPI.Web/Models/Response/FeaturedPlaylistsResponse.cs @@ -3,7 +3,7 @@ namespace SpotifyAPI.Web public class FeaturedPlaylistsResponse { public string Message { get; set; } = default!; - public Paging Playlists { get; set; } = default!; + public Paging Playlists { get; set; } = default!; } } diff --git a/SpotifyAPI.Web/Models/Response/NewReleasesResponse.cs b/SpotifyAPI.Web/Models/Response/NewReleasesResponse.cs index 4d683c51..d9ec7aa6 100644 --- a/SpotifyAPI.Web/Models/Response/NewReleasesResponse.cs +++ b/SpotifyAPI.Web/Models/Response/NewReleasesResponse.cs @@ -3,7 +3,7 @@ namespace SpotifyAPI.Web public class NewReleasesResponse { public string Message { get; set; } = default!; - public Paging Albums { get; set; } = default!; + public Paging Albums { get; set; } = default!; } }