From 40397fa4f6a49eb0c7aa4405ebec45a135541ffc Mon Sep 17 00:00:00 2001 From: Jonas Dellinger Date: Tue, 5 May 2020 05:26:37 +0200 Subject: [PATCH] Added paginator and some more ConfigureAwaits --- SpotifyAPI.Web.Tests/Http/APIConnectorTest.cs | 4 +-- SpotifyAPI.Web/Clients/FollowClient.cs | 20 ++++++++--- .../Clients/Interfaces/IPaginator.cs | 13 +++++++ .../Clients/Interfaces/ISpotifyClient.cs | 11 ++++++ SpotifyAPI.Web/Clients/PlaylistsClient.cs | 6 ++-- SpotifyAPI.Web/Clients/SimplePaginator.cs | 35 +++++++++++++++++++ SpotifyAPI.Web/Clients/SpotifyClient.cs | 30 ++++++++++++++++ SpotifyAPI.Web/Clients/SpotifyClientConfig.cs | 25 ++++++++++--- SpotifyAPI.Web/Http/APIConnector.cs | 10 +++--- 9 files changed, 134 insertions(+), 20 deletions(-) create mode 100644 SpotifyAPI.Web/Clients/Interfaces/IPaginator.cs create mode 100644 SpotifyAPI.Web/Clients/SimplePaginator.cs diff --git a/SpotifyAPI.Web.Tests/Http/APIConnectorTest.cs b/SpotifyAPI.Web.Tests/Http/APIConnectorTest.cs index fee42f62..7eb7df11 100644 --- a/SpotifyAPI.Web.Tests/Http/APIConnectorTest.cs +++ b/SpotifyAPI.Web.Tests/Http/APIConnectorTest.cs @@ -44,7 +44,7 @@ namespace SpotifyAPI.Web.Tests retryHandler.Object, null ); - await apiConnector.SendAPIRequest(new Uri("/me", UriKind.Relative), HttpMethod.Get); + await apiConnector.SendAPIRequest(new Uri("/me", UriKind.Relative), HttpMethod.Get).ConfigureAwait(false); authenticator.Verify(a => a.Apply(It.IsAny()), Times.Once); httpClient.Verify(h => h.DoRequest(It.IsAny()), Times.Once); @@ -86,7 +86,7 @@ namespace SpotifyAPI.Web.Tests retryHandler.Object, null ); - await apiConnector.SendAPIRequest(new Uri("/me", UriKind.Relative), HttpMethod.Get); + await apiConnector.SendAPIRequest(new Uri("/me", UriKind.Relative), HttpMethod.Get).ConfigureAwait(false); serializer.Verify(s => s.SerializeRequest(It.IsAny()), Times.Once); authenticator.Verify(a => a.Apply(It.IsAny()), Times.Exactly(2)); diff --git a/SpotifyAPI.Web/Clients/FollowClient.cs b/SpotifyAPI.Web/Clients/FollowClient.cs index 5045966f..96fe3988 100644 --- a/SpotifyAPI.Web/Clients/FollowClient.cs +++ b/SpotifyAPI.Web/Clients/FollowClient.cs @@ -29,7 +29,9 @@ namespace SpotifyAPI.Web { Ensure.ArgumentNotNull(request, nameof(request)); - var statusCode = await API.Put(URLs.CurrentUserFollower(), request.BuildQueryParams(), request.BuildBodyParams()); + var statusCode = await API + .Put(URLs.CurrentUserFollower(), request.BuildQueryParams(), request.BuildBodyParams()) + .ConfigureAwait(false); return statusCode == HttpStatusCode.OK; } @@ -37,7 +39,9 @@ namespace SpotifyAPI.Web { Ensure.ArgumentNotNullOrEmptyString(playlistId, nameof(playlistId)); - var statusCode = await API.Put(URLs.PlaylistFollowers(playlistId), null, null); + var statusCode = await API + .Put(URLs.PlaylistFollowers(playlistId), null, null) + .ConfigureAwait(false); return statusCode == HttpStatusCode.OK; } @@ -46,7 +50,9 @@ namespace SpotifyAPI.Web Ensure.ArgumentNotNullOrEmptyString(playlistId, nameof(playlistId)); Ensure.ArgumentNotNull(request, nameof(request)); - var statusCode = await API.Put(URLs.PlaylistFollowers(playlistId), null, request.BuildBodyParams()); + var statusCode = await API + .Put(URLs.PlaylistFollowers(playlistId), null, request.BuildBodyParams()) + .ConfigureAwait(false); return statusCode == HttpStatusCode.OK; } @@ -68,7 +74,9 @@ namespace SpotifyAPI.Web { Ensure.ArgumentNotNull(request, nameof(request)); - var statusCode = await API.Delete(URLs.CurrentUserFollower(), request.BuildQueryParams(), request.BuildBodyParams()); + var statusCode = await API + .Delete(URLs.CurrentUserFollower(), request.BuildQueryParams(), request.BuildBodyParams()) + .ConfigureAwait(false); return statusCode == HttpStatusCode.NoContent; } @@ -76,7 +84,9 @@ namespace SpotifyAPI.Web { Ensure.ArgumentNotNullOrEmptyString(playlistId, nameof(playlistId)); - var statusCode = await API.Delete(URLs.PlaylistFollowers(playlistId), null, null); + var statusCode = await API + .Delete(URLs.PlaylistFollowers(playlistId), null, null) + .ConfigureAwait(false); return statusCode == HttpStatusCode.OK; } } diff --git a/SpotifyAPI.Web/Clients/Interfaces/IPaginator.cs b/SpotifyAPI.Web/Clients/Interfaces/IPaginator.cs new file mode 100644 index 00000000..0a92405f --- /dev/null +++ b/SpotifyAPI.Web/Clients/Interfaces/IPaginator.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using SpotifyAPI.Web.Http; + +namespace SpotifyAPI.Web +{ + public interface IPaginator + { + Task> Paginate(Paging firstPage, IAPIConnector connector); + Task> Paginate(Func>> getFirstPage, IAPIConnector connector); + } +} diff --git a/SpotifyAPI.Web/Clients/Interfaces/ISpotifyClient.cs b/SpotifyAPI.Web/Clients/Interfaces/ISpotifyClient.cs index 4fd46c8d..e0c36d3a 100644 --- a/SpotifyAPI.Web/Clients/Interfaces/ISpotifyClient.cs +++ b/SpotifyAPI.Web/Clients/Interfaces/ISpotifyClient.cs @@ -1,7 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + namespace SpotifyAPI.Web { public interface ISpotifyClient { + IPaginator DefaultPaginator { get; } + IUserProfileClient UserProfile { get; } IBrowseClient Browse { get; } @@ -15,5 +21,10 @@ namespace SpotifyAPI.Web IFollowClient Follow { get; } ITracksClient Tracks { get; } + + Task> Paginate(Paging firstPage); + Task> Paginate(Func>> getFirstPage); + Task> Paginate(Paging firstPage, IPaginator paginator); + Task> Paginate(Func>> getFirstPage, IPaginator paginator); } } diff --git a/SpotifyAPI.Web/Clients/PlaylistsClient.cs b/SpotifyAPI.Web/Clients/PlaylistsClient.cs index b17a72b8..6b74e146 100644 --- a/SpotifyAPI.Web/Clients/PlaylistsClient.cs +++ b/SpotifyAPI.Web/Clients/PlaylistsClient.cs @@ -55,7 +55,7 @@ namespace SpotifyAPI.Web Ensure.ArgumentNotNullOrEmptyString(playlistId, nameof(playlistId)); Ensure.ArgumentNotNullOrEmptyString(base64Jpg, nameof(base64Jpg)); - var statusCode = await API.PutRaw(URLs.PlaylistImages(playlistId), null, base64Jpg); + var statusCode = await API.PutRaw(URLs.PlaylistImages(playlistId), null, base64Jpg).ConfigureAwait(false); return statusCode == HttpStatusCode.Accepted; } @@ -102,7 +102,7 @@ namespace SpotifyAPI.Web Ensure.ArgumentNotNullOrEmptyString(playlistId, nameof(playlistId)); Ensure.ArgumentNotNull(request, nameof(request)); - var statusCode = await API.Put(URLs.PlaylistTracks(playlistId), null, request.BuildBodyParams()); + var statusCode = await API.Put(URLs.PlaylistTracks(playlistId), null, request.BuildBodyParams()).ConfigureAwait(false); return statusCode == HttpStatusCode.Created; } @@ -123,7 +123,7 @@ namespace SpotifyAPI.Web Ensure.ArgumentNotNullOrEmptyString(playlistId, nameof(playlistId)); Ensure.ArgumentNotNull(request, nameof(request)); - var statusCode = await API.Put(URLs.Playlist(playlistId), null, request.BuildBodyParams()); + var statusCode = await API.Put(URLs.Playlist(playlistId), null, request.BuildBodyParams()).ConfigureAwait(false); return statusCode == HttpStatusCode.OK; } diff --git a/SpotifyAPI.Web/Clients/SimplePaginator.cs b/SpotifyAPI.Web/Clients/SimplePaginator.cs new file mode 100644 index 00000000..1a280339 --- /dev/null +++ b/SpotifyAPI.Web/Clients/SimplePaginator.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using SpotifyAPI.Web.Http; + +namespace SpotifyAPI.Web +{ + public class SimplePaginator : IPaginator + { + protected bool ShouldContinue(List results, Paging page) + { + return true; + } + + public async Task> Paginate(Paging firstPage, IAPIConnector connector) + { + var page = firstPage; + var results = new List(); + results.AddRange(firstPage.Items); + while (page.Next != null && ShouldContinue(results, page)) + { + page = await connector.Get>(new Uri(page.Next, UriKind.Absolute)).ConfigureAwait(false); + results.AddRange(page.Items); + } + + return results; + } + + public async Task> Paginate(Func>> getFirstPage, IAPIConnector connector) + { + var firstPage = await getFirstPage().ConfigureAwait(false); + return await Paginate(firstPage, connector).ConfigureAwait(false); + } + } +} diff --git a/SpotifyAPI.Web/Clients/SpotifyClient.cs b/SpotifyAPI.Web/Clients/SpotifyClient.cs index 9f476372..fd10aea3 100644 --- a/SpotifyAPI.Web/Clients/SpotifyClient.cs +++ b/SpotifyAPI.Web/Clients/SpotifyClient.cs @@ -1,3 +1,6 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; using SpotifyAPI.Web.Http; namespace SpotifyAPI.Web @@ -15,6 +18,7 @@ namespace SpotifyAPI.Web Ensure.ArgumentNotNull(config, nameof(config)); _apiConnector = config.CreateAPIConnector(); + DefaultPaginator = config.Paginator; UserProfile = new UserProfileClient(_apiConnector); Browse = new BrowseClient(_apiConnector); Shows = new ShowsClient(_apiConnector); @@ -24,6 +28,8 @@ namespace SpotifyAPI.Web Tracks = new TracksClient(_apiConnector); } + public IPaginator DefaultPaginator { get; } + public IUserProfileClient UserProfile { get; } public IBrowseClient Browse { get; } @@ -37,5 +43,29 @@ namespace SpotifyAPI.Web public IFollowClient Follow { get; } public ITracksClient Tracks { get; } + + public Task> Paginate(Paging firstPage) + { + return DefaultPaginator.Paginate(firstPage, _apiConnector); + } + + public Task> Paginate(Paging firstPage, IPaginator paginator) + { + Ensure.ArgumentNotNull(paginator, nameof(paginator)); + + return paginator.Paginate(firstPage, _apiConnector); + } + + public Task> Paginate(Func>> getFirstPage) + { + return DefaultPaginator.Paginate(getFirstPage, _apiConnector); + } + + public Task> Paginate(Func>> getFirstPage, IPaginator paginator) + { + Ensure.ArgumentNotNull(paginator, nameof(paginator)); + + return paginator.Paginate(getFirstPage, _apiConnector); + } } } diff --git a/SpotifyAPI.Web/Clients/SpotifyClientConfig.cs b/SpotifyAPI.Web/Clients/SpotifyClientConfig.cs index 1d42be28..a872f113 100644 --- a/SpotifyAPI.Web/Clients/SpotifyClientConfig.cs +++ b/SpotifyAPI.Web/Clients/SpotifyClientConfig.cs @@ -10,6 +10,8 @@ namespace SpotifyAPI.Web public IJSONSerializer JSONSerializer { get; } public IHTTPClient HTTPClient { get; } public IHTTPLogger HTTPLogger { get; } + public IPaginator Paginator { get; set; } + public IRetryHandler RetryHandler { get; } /// @@ -23,13 +25,15 @@ namespace SpotifyAPI.Web /// /// /// + /// public SpotifyClientConfig( Uri baseAddress, IAuthenticator authenticator, IJSONSerializer jsonSerializer, IHTTPClient httpClient, IRetryHandler retryHandler, - IHTTPLogger httpLogger + IHTTPLogger httpLogger, + IPaginator paginator ) { BaseAddress = baseAddress; @@ -38,6 +42,7 @@ namespace SpotifyAPI.Web HTTPClient = httpClient; RetryHandler = retryHandler; HTTPLogger = httpLogger; + Paginator = paginator; } internal IAPIConnector CreateAPIConnector() @@ -59,17 +64,26 @@ namespace SpotifyAPI.Web public SpotifyClientConfig WithRetryHandler(IRetryHandler retryHandler) { - return new SpotifyClientConfig(BaseAddress, Authenticator, JSONSerializer, HTTPClient, retryHandler, HTTPLogger); + return new SpotifyClientConfig( + BaseAddress, Authenticator, JSONSerializer, HTTPClient, retryHandler, HTTPLogger, Paginator); } public SpotifyClientConfig WithAuthenticator(IAuthenticator authenticator) { - return new SpotifyClientConfig(BaseAddress, authenticator, JSONSerializer, HTTPClient, RetryHandler, HTTPLogger); + return new SpotifyClientConfig( + BaseAddress, authenticator, JSONSerializer, HTTPClient, RetryHandler, HTTPLogger, Paginator); } public SpotifyClientConfig WithHTTPLogger(IHTTPLogger httpLogger) { - return new SpotifyClientConfig(BaseAddress, Authenticator, JSONSerializer, HTTPClient, RetryHandler, httpLogger); + return new SpotifyClientConfig( + BaseAddress, Authenticator, JSONSerializer, HTTPClient, RetryHandler, httpLogger, Paginator); + } + + public SpotifyClientConfig WithPaginator(IPaginator paginator) + { + return new SpotifyClientConfig( + BaseAddress, Authenticator, JSONSerializer, HTTPClient, RetryHandler, HTTPLogger, paginator); } public static SpotifyClientConfig CreateDefault(string token, string tokenType = "Bearer") @@ -96,7 +110,8 @@ namespace SpotifyAPI.Web new NewtonsoftJSONSerializer(), new NetHttpClient(), null, - null + null, + new SimplePaginator() ); } } diff --git a/SpotifyAPI.Web/Http/APIConnector.cs b/SpotifyAPI.Web/Http/APIConnector.cs index b5dc6eee..49360268 100644 --- a/SpotifyAPI.Web/Http/APIConnector.cs +++ b/SpotifyAPI.Web/Http/APIConnector.cs @@ -59,7 +59,7 @@ namespace SpotifyAPI.Web.Http { Ensure.ArgumentNotNull(uri, nameof(uri)); - var response = await SendAPIRequestDetailed(uri, HttpMethod.Delete, parameters, body); + var response = await SendAPIRequestDetailed(uri, HttpMethod.Delete, parameters, body).ConfigureAwait(false); return response.StatusCode; } @@ -102,7 +102,7 @@ namespace SpotifyAPI.Web.Http { Ensure.ArgumentNotNull(uri, nameof(uri)); - var response = await SendAPIRequestDetailed(uri, HttpMethod.Post, parameters, body); + var response = await SendAPIRequestDetailed(uri, HttpMethod.Post, parameters, body).ConfigureAwait(false); return response.StatusCode; } @@ -131,7 +131,7 @@ namespace SpotifyAPI.Web.Http { Ensure.ArgumentNotNull(uri, nameof(uri)); - var response = await SendAPIRequestDetailed(uri, HttpMethod.Put, parameters, body); + var response = await SendAPIRequestDetailed(uri, HttpMethod.Put, parameters, body).ConfigureAwait(false); return response.StatusCode; } @@ -139,7 +139,7 @@ namespace SpotifyAPI.Web.Http { Ensure.ArgumentNotNull(uri, nameof(uri)); - var response = await SendRawRequest(uri, HttpMethod.Put, parameters, body); + var response = await SendRawRequest(uri, HttpMethod.Put, parameters, body).ConfigureAwait(false); return response.StatusCode; } @@ -171,7 +171,7 @@ namespace SpotifyAPI.Web.Http private async Task> DoSerializedRequest(IRequest request) { _jsonSerializer.SerializeRequest(request); - var response = await DoRequest(request); + var response = await DoRequest(request).ConfigureAwait(false); return _jsonSerializer.DeserializeResponse(response); }