diff --git a/SpotifyAPI.Web.Examples/Example.TokenSwap/Client/Program.cs b/SpotifyAPI.Web.Examples/Example.TokenSwap/Client/Program.cs index 8fe7cd54..3e69cac0 100644 --- a/SpotifyAPI.Web.Examples/Example.TokenSwap/Client/Program.cs +++ b/SpotifyAPI.Web.Examples/Example.TokenSwap/Client/Program.cs @@ -11,7 +11,7 @@ namespace Client private static readonly string clientId = Environment.GetEnvironmentVariable("SPOTIFY_CLIENT_ID"); private static EmbedIOAuthServer _server; - public static async Task Main(string[] args) + public static async Task Main() { _server = new EmbedIOAuthServer(new Uri("http://localhost:5000/callback"), 5000); await _server.Start(); diff --git a/SpotifyAPI.Web/Clients/Interfaces/IOAuthClient.cs b/SpotifyAPI.Web/Clients/Interfaces/IOAuthClient.cs index 7c490578..39816108 100644 --- a/SpotifyAPI.Web/Clients/Interfaces/IOAuthClient.cs +++ b/SpotifyAPI.Web/Clients/Interfaces/IOAuthClient.cs @@ -11,7 +11,7 @@ namespace SpotifyAPI.Web /// Requests a new token using client_ids and client_secrets. /// If the token is expired, simply call the funtion again to get a new token /// - /// + /// The request-model which contains required and optional parameters. /// /// https://developer.spotify.com/documentation/general/guides/authorization-guide/#client-credentials-flow /// @@ -21,7 +21,7 @@ namespace SpotifyAPI.Web /// /// Refresh an already received token via Authorization Code Auth /// - /// + /// The request-model which contains required and optional parameters. /// /// https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow /// @@ -31,11 +31,31 @@ namespace SpotifyAPI.Web /// /// Reequest an initial token via Authorization Code Auth /// - /// + /// The request-model which contains required and optional parameters. /// /// https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow /// /// Task RequestToken(AuthorizationCodeTokenRequest request); + + /// + /// Swaps out a received code with a access token using a remote server + /// + /// The request-model which contains required and optional parameters. + /// + /// https://developer.spotify.com/documentation/ios/guides/token-swap-and-refresh/ + /// + /// + Task RequestToken(TokenSwapTokenRequest request); + + /// + /// Gets a refreshed access token using an already received refresh token using a remote server + /// + /// + /// + /// https://developer.spotify.com/documentation/ios/guides/token-swap-and-refresh/ + /// + /// + Task RequestToken(TokenSwapRefreshRequest request); } } diff --git a/SpotifyAPI.Web/Clients/OAuthClient.cs b/SpotifyAPI.Web/Clients/OAuthClient.cs index 849e9b35..8176f368 100644 --- a/SpotifyAPI.Web/Clients/OAuthClient.cs +++ b/SpotifyAPI.Web/Clients/OAuthClient.cs @@ -15,26 +15,67 @@ namespace SpotifyAPI.Web [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062")] public OAuthClient(SpotifyClientConfig config) : base(ValidateConfig(config)) { } + /// + /// Requests a new token using client_ids and client_secrets. + /// If the token is expired, simply call the funtion again to get a new token + /// + /// The request-model which contains required and optional parameters. + /// + /// https://developer.spotify.com/documentation/general/guides/authorization-guide/#client-credentials-flow + /// + /// 1 public Task RequestToken(ClientCredentialsRequest request) { return RequestToken(request, API); } + /// + /// Refresh an already received token via Authorization Code Auth + /// + /// The request-model which contains required and optional parameters. + /// + /// https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow + /// + /// public Task RequestToken(AuthorizationCodeRefreshRequest request) { return RequestToken(request, API); } + /// + /// Reequest an initial token via Authorization Code Auth + /// + /// The request-model which contains required and optional parameters. + /// + /// https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow + /// + /// public Task RequestToken(AuthorizationCodeTokenRequest request) { return RequestToken(request, API); } + /// + /// Swaps out a received code with a access token using a remote server + /// + /// The request-model which contains required and optional parameters. + /// + /// https://developer.spotify.com/documentation/ios/guides/token-swap-and-refresh/ + /// + /// public Task RequestToken(TokenSwapTokenRequest request) { return RequestToken(request, API); } + /// + /// Gets a refreshed access token using an already received refresh token using a remote server + /// + /// + /// + /// https://developer.spotify.com/documentation/ios/guides/token-swap-and-refresh/ + /// + /// public Task RequestToken(TokenSwapRefreshRequest request) { return RequestToken(request, API); diff --git a/SpotifyAPI.Web/Exceptions/APITooManyRequestsException.cs b/SpotifyAPI.Web/Exceptions/APITooManyRequestsException.cs new file mode 100644 index 00000000..acfdac69 --- /dev/null +++ b/SpotifyAPI.Web/Exceptions/APITooManyRequestsException.cs @@ -0,0 +1,31 @@ +using System.Globalization; +using System.Runtime.Serialization; +using System; +using SpotifyAPI.Web.Http; + +namespace SpotifyAPI.Web +{ + [Serializable] + public class APITooManyRequestsException : APIException + { + public TimeSpan RetryAfter { get; } + + public APITooManyRequestsException(IResponse response) : base(response) + { + Ensure.ArgumentNotNull(response, nameof(response)); + + if (response.Headers.TryGetValue("Retry-After", out string retryAfter)) + { + RetryAfter = TimeSpan.FromSeconds(int.Parse(retryAfter, CultureInfo.InvariantCulture)); + } + } + + public APITooManyRequestsException() { } + + public APITooManyRequestsException(string message) : base(message) { } + + public APITooManyRequestsException(string message, Exception innerException) : base(message, innerException) { } + + protected APITooManyRequestsException(SerializationInfo info, StreamingContext context) : base(info, context) { } + } +} diff --git a/SpotifyAPI.Web/Http/APIConnector.cs b/SpotifyAPI.Web/Http/APIConnector.cs index 991cd5bd..3dfa7c76 100644 --- a/SpotifyAPI.Web/Http/APIConnector.cs +++ b/SpotifyAPI.Web/Http/APIConnector.cs @@ -283,6 +283,8 @@ namespace SpotifyAPI.Web.Http { case HttpStatusCode.Unauthorized: throw new APIUnauthorizedException(response); + case HttpStatusCode.TooManyRequests: + throw new APITooManyRequestsException(response); default: throw new APIException(response); } diff --git a/SpotifyAPI.Web/Http/Authenticators/AuthorizationCodeAuthenticator.cs b/SpotifyAPI.Web/Http/Authenticators/AuthorizationCodeAuthenticator.cs index e4059ebd..3c166c7f 100644 --- a/SpotifyAPI.Web/Http/Authenticators/AuthorizationCodeAuthenticator.cs +++ b/SpotifyAPI.Web/Http/Authenticators/AuthorizationCodeAuthenticator.cs @@ -9,8 +9,6 @@ namespace SpotifyAPI.Web.Http /// public class AuthorizationCodeAuthenticator : IAuthenticator { - public event EventHandler? TokenRefreshed; - /// /// Initiate a new instance. The token will be refreshed once it expires. /// The initialToken will be updated with the new values on refresh! @@ -26,6 +24,12 @@ namespace SpotifyAPI.Web.Http ClientSecret = clientSecret; } + /// + /// This event is called once a new refreshed token was aquired + /// + public event EventHandler? TokenRefreshed; + + /// /// The ClientID, defined in a spotify application in your Spotify Developer Dashboard ///