From d88f180cd6038268207cc3f3983d220c6396b6a4 Mon Sep 17 00:00:00 2001 From: Mark Taylor Date: Fri, 24 Jul 2015 21:30:25 +0100 Subject: [PATCH 1/5] Added GetRateLimits to MiscellaneousClient --- .../Clients/IObservableMiscellaneousClient.cs | 9 +++ .../Clients/ObservableMiscellaneousClient.cs | 11 +++ .../Clients/MiscellaneousClientTests.cs | 42 ++++++++++++ .../Clients/MiscellaneousClientTests.cs | 67 +++++++++++++++++++ Octokit/Clients/IMiscellaneousClient.cs | 8 +++ Octokit/Clients/MiscellaneousClient.cs | 15 +++++ Octokit/Models/Response/MiscRateLimits.cs | 43 ++++++++++++ Octokit/Models/Response/ResourceRateLimit.cs | 55 +++++++++++++++ Octokit/Models/Response/ResourceRateLimits.cs | 42 ++++++++++++ Octokit/Octokit-Mono.csproj | 3 + Octokit/Octokit-MonoAndroid.csproj | 5 +- Octokit/Octokit-Monotouch.csproj | 5 +- Octokit/Octokit-Portable.csproj | 3 + Octokit/Octokit-netcore45.csproj | 3 + Octokit/Octokit.csproj | 3 + 15 files changed, 312 insertions(+), 2 deletions(-) create mode 100644 Octokit/Models/Response/MiscRateLimits.cs create mode 100644 Octokit/Models/Response/ResourceRateLimit.cs create mode 100644 Octokit/Models/Response/ResourceRateLimits.cs diff --git a/Octokit.Reactive/Clients/IObservableMiscellaneousClient.cs b/Octokit.Reactive/Clients/IObservableMiscellaneousClient.cs index 3116b40978..2b5a8b64d1 100644 --- a/Octokit.Reactive/Clients/IObservableMiscellaneousClient.cs +++ b/Octokit.Reactive/Clients/IObservableMiscellaneousClient.cs @@ -50,5 +50,14 @@ public interface IObservableMiscellaneousClient /// /// A that includes the license key, text, and attributes of the license. IObservable GetLicense(string key); + + /// + /// Gets API Rate Limits (API service rather than header info). + /// + /// Thrown when a general API error occurs. + /// An of Rate Limits. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + IObservable GetRateLimits(); + } } diff --git a/Octokit.Reactive/Clients/ObservableMiscellaneousClient.cs b/Octokit.Reactive/Clients/ObservableMiscellaneousClient.cs index befd0de374..8f0a4ae38a 100644 --- a/Octokit.Reactive/Clients/ObservableMiscellaneousClient.cs +++ b/Octokit.Reactive/Clients/ObservableMiscellaneousClient.cs @@ -74,5 +74,16 @@ public IObservable GetLicense(string key) { return _client.GetLicense(key).ToObservable(); } + + /// + /// Gets API Rate Limits (API service rather than header info). + /// + /// Thrown when a general API error occurs. + /// An of Rate Limits. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public IObservable GetRateLimits() + { + return _client.GetRateLimits().ToObservable(); + } } } diff --git a/Octokit.Tests.Integration/Clients/MiscellaneousClientTests.cs b/Octokit.Tests.Integration/Clients/MiscellaneousClientTests.cs index 256a73f3a4..451b30c6f8 100644 --- a/Octokit.Tests.Integration/Clients/MiscellaneousClientTests.cs +++ b/Octokit.Tests.Integration/Clients/MiscellaneousClientTests.cs @@ -70,4 +70,46 @@ public async Task CanRetrieveListOfLicenses() Assert.Equal("MIT License", result.Name); } } + + public class TheGetResourceRateLimitsMethod + { + [IntegrationTest] + public async Task CanRetrieveResourceRateLimits() + { + var github = Helper.GetAuthenticatedClient(); + + var result = await github.Miscellaneous.GetRateLimits(); + + // Test the high level object + Assert.NotNull(result); + + // Test the resources level + Assert.NotNull(result.Resources); + + // Test the core limits + Assert.NotNull(result.Resources.Core); + Assert.True(result.Resources.Core.Limit > 0); + Assert.True(result.Resources.Core.Remaining > -1); + Assert.True(result.Resources.Core.Remaining <= result.Resources.Core.Limit); + Assert.True(result.Resources.Core.Reset > 0); + Assert.NotNull(result.Resources.Core.ResetAsDateTimeOffset); + + // Test the search limits + Assert.NotNull(result.Resources.Search); + Assert.True(result.Resources.Search.Limit > 0); + Assert.True(result.Resources.Search.Remaining > -1); + Assert.True(result.Resources.Search.Remaining <= result.Resources.Search.Limit); + Assert.True(result.Resources.Search.Reset > 0); + Assert.NotNull(result.Resources.Search.ResetAsDateTimeOffset); + + // Test the depreciated rate limits + Assert.NotNull(result.Rate); + Assert.True(result.Rate.Limit > 0); + Assert.True(result.Rate.Remaining > -1); + Assert.True(result.Rate.Remaining <= result.Rate.Limit); + Assert.True(result.Resources.Search.Reset > 0); + Assert.NotNull(result.Resources.Search.ResetAsDateTimeOffset); + + } + } } diff --git a/Octokit.Tests/Clients/MiscellaneousClientTests.cs b/Octokit.Tests/Clients/MiscellaneousClientTests.cs index 0b397c7e30..48fe696bb0 100644 --- a/Octokit.Tests/Clients/MiscellaneousClientTests.cs +++ b/Octokit.Tests/Clients/MiscellaneousClientTests.cs @@ -4,6 +4,7 @@ using NSubstitute; using Octokit.Internal; using Xunit; +using System.Globalization; namespace Octokit.Tests.Clients { @@ -58,6 +59,72 @@ public async Task RequestsTheEmojiEndpoint() } } + public class TheGetResourceRateLimitsMethod + { + [Fact] + public async Task RequestsTheRecourceRateLimitEndpoint() + { + IApiResponse response = new ApiResponse + ( + new Response(), + new MiscRateLimits( + new ResourceRateLimits( + new ResourceRateLimit(5000, 4999, 1372700873), + new ResourceRateLimit(30, 18, 1372700873) + ), + new ResourceRateLimit(100, 75, 1372700873) + ) + ); + var connection = Substitute.For(); + connection.Get(Args.Uri, null, null).Returns(Task.FromResult(response)); + var client = new MiscellaneousClient(connection); + + var result = await client.GetRateLimits(); + + // Test the high level object + Assert.NotNull(result); + + // Test the resource object + Assert.NotNull(result.Resources); + + // Test the core limits + Assert.NotNull(result.Resources.Core); + Assert.Equal(5000, result.Resources.Core.Limit); + Assert.Equal(4999, result.Resources.Core.Remaining); + Assert.Equal(1372700873, result.Resources.Core.Reset); + var expectedReset = DateTimeOffset.ParseExact( + "Mon 01 Jul 2013 5:47:53 PM -00:00", + "ddd dd MMM yyyy h:mm:ss tt zzz", + CultureInfo.InvariantCulture); + Assert.Equal(expectedReset, result.Resources.Core.ResetAsDateTimeOffset); + + // Test the search limits + Assert.NotNull(result.Resources.Search); + Assert.Equal(30, result.Resources.Search.Limit); + Assert.Equal(18, result.Resources.Search.Remaining); + Assert.Equal(1372700873, result.Resources.Search.Reset); + expectedReset = DateTimeOffset.ParseExact( + "Mon 01 Jul 2013 5:47:53 PM -00:00", + "ddd dd MMM yyyy h:mm:ss tt zzz", + CultureInfo.InvariantCulture); + Assert.Equal(expectedReset, result.Resources.Search.ResetAsDateTimeOffset); + + // Test the depreciated rate limits + Assert.NotNull(result.Rate); + Assert.Equal(100, result.Rate.Limit); + Assert.Equal(75, result.Rate.Remaining); + Assert.Equal(1372700873, result.Rate.Reset); + expectedReset = DateTimeOffset.ParseExact( + "Mon 01 Jul 2013 5:47:53 PM -00:00", + "ddd dd MMM yyyy h:mm:ss tt zzz", + CultureInfo.InvariantCulture); + Assert.Equal(expectedReset, result.Rate.ResetAsDateTimeOffset); + + connection.Received() + .Get(Arg.Is(u => u.ToString() == "rate_limit"), null, null); + } + } + public class TheCtor { [Fact] diff --git a/Octokit/Clients/IMiscellaneousClient.cs b/Octokit/Clients/IMiscellaneousClient.cs index 04153a319a..75b40a31c1 100644 --- a/Octokit/Clients/IMiscellaneousClient.cs +++ b/Octokit/Clients/IMiscellaneousClient.cs @@ -59,5 +59,13 @@ public interface IMiscellaneousClient /// /// A that includes the license key, text, and attributes of the license. Task GetLicense(string key); + + /// + /// Gets API Rate Limits (API service rather than header info). + /// + /// Thrown when a general API error occurs. + /// An of Rate Limits. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + Task GetRateLimits(); } } diff --git a/Octokit/Clients/MiscellaneousClient.cs b/Octokit/Clients/MiscellaneousClient.cs index 05025a3666..f4fec16ae4 100644 --- a/Octokit/Clients/MiscellaneousClient.cs +++ b/Octokit/Clients/MiscellaneousClient.cs @@ -116,5 +116,20 @@ public async Task GetLicense(string key) .ConfigureAwait(false); return response.Body; } + + /// + /// Gets API Rate Limits (API service rather than header info). + /// + /// Thrown when a general API error occurs. + /// An of Rate Limits. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public async Task GetRateLimits() + { + var endpoint = new Uri("rate_limit", UriKind.Relative); + var response = await _connection.Get(endpoint, null, null).ConfigureAwait(false); + //var response = await _connection.Get(endpoint, null, null).ConfigureAwait(false); + return response.Body; + //return null; + } } } \ No newline at end of file diff --git a/Octokit/Models/Response/MiscRateLimits.cs b/Octokit/Models/Response/MiscRateLimits.cs new file mode 100644 index 0000000000..1a0ac28576 --- /dev/null +++ b/Octokit/Models/Response/MiscRateLimits.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class MiscRateLimits + { + public MiscRateLimits() {} + + public MiscRateLimits(ResourceRateLimits resources, ResourceRateLimit rate) + { + Ensure.ArgumentNotNull(resources, "resource"); + Ensure.ArgumentNotNull(rate, "rate"); + + Resources = resources; + Rate = rate; + } + + /// + /// Object of resources rate limits + /// + public ResourceRateLimits Resources { get; private set; } + + /// + /// Legacy rate limit - to be depreciated - https://developer.github.com/v3/rate_limit/#deprecation-notice + /// + public ResourceRateLimit Rate { get; private set; } + + internal string DebuggerDisplay + { + get + { + return Resources == null ? "No rates found" : String.Format(CultureInfo.InvariantCulture, Resources.DebuggerDisplay); + } + } + } +} diff --git a/Octokit/Models/Response/ResourceRateLimit.cs b/Octokit/Models/Response/ResourceRateLimit.cs new file mode 100644 index 0000000000..15995e878f --- /dev/null +++ b/Octokit/Models/Response/ResourceRateLimit.cs @@ -0,0 +1,55 @@ +using System; +using System.Diagnostics; +using System.Globalization; + +using Octokit.Helpers; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class ResourceRateLimit + { + public ResourceRateLimit() { } + + public ResourceRateLimit(int limit, int remaining, long reset) + { + Ensure.ArgumentNotNull(limit, "limit"); + Ensure.ArgumentNotNull(remaining, "remaining"); + Ensure.ArgumentNotNull(reset, "reset"); + + Limit = limit; + Remaining = remaining; + Reset = reset; + } + + + /// + /// The maximum number of requests that the consumer is permitted to make per hour. + /// + public int Limit { get; private set; } + + /// + /// The number of requests remaining in the current rate limit window. + /// + public int Remaining { get; private set; } + + /// + /// The date and time at which the current rate limit window resets - in UTC epoch seconds + /// + public long Reset { get; private set; } + + /// + /// The date and time at which the current rate limit window resets - as DateTimeOffset + /// + public DateTimeOffset ResetAsDateTimeOffset { get { return Reset.FromUnixTime(); } } + + + internal string DebuggerDisplay + { + get + { + return String.Format(CultureInfo.InvariantCulture, "Limit {0}, Remaining {1}, Reset {2} ", Limit, Remaining, Reset); + } + } + } +} diff --git a/Octokit/Models/Response/ResourceRateLimits.cs b/Octokit/Models/Response/ResourceRateLimits.cs new file mode 100644 index 0000000000..70c5d2cb58 --- /dev/null +++ b/Octokit/Models/Response/ResourceRateLimits.cs @@ -0,0 +1,42 @@ +using System; +using System.Diagnostics; +using System.Globalization; + +using Octokit.Helpers; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class ResourceRateLimits + { + public ResourceRateLimits() {} + + public ResourceRateLimits(ResourceRateLimit core, ResourceRateLimit search) + { + Ensure.ArgumentNotNull(core, "core"); + Ensure.ArgumentNotNull(search, "search"); + + Core = core; + Search = search; + } + + /// + /// Rate limits for core API (rate limit for everything except Search API) + /// + public ResourceRateLimit Core { get; private set; } + + /// + /// Rate Limits for Search API + /// + public ResourceRateLimit Search { get; private set; } + + internal string DebuggerDisplay + { + get + { + return String.Format(CultureInfo.InvariantCulture, "Core: {0}; Search: {1} ", Core.DebuggerDisplay, Search.DebuggerDisplay); + } + } + } + +} diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index 42a9449406..c3e9f989b7 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -396,6 +396,9 @@ + + + \ No newline at end of file diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index 8077c4c47e..fb756b17aa 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -1,4 +1,4 @@ - + Debug @@ -412,6 +412,9 @@ + + + \ No newline at end of file diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index 61fb07ffa9..e82708dd58 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -1,4 +1,4 @@ - + Debug @@ -405,6 +405,9 @@ + + + diff --git a/Octokit/Octokit-Portable.csproj b/Octokit/Octokit-Portable.csproj index 59b0edd083..f358632189 100644 --- a/Octokit/Octokit-Portable.csproj +++ b/Octokit/Octokit-Portable.csproj @@ -394,6 +394,9 @@ + + + diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index 2b01180517..fc2a20fa0f 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -398,6 +398,9 @@ + + + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index 6b0fed0029..bb4ef867a4 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -116,8 +116,11 @@ + + + From e092a109eae71b706092489ccee867181a2763df Mon Sep 17 00:00:00 2001 From: Mark Taylor Date: Sun, 26 Jul 2015 22:03:19 +0100 Subject: [PATCH 2/5] Additional changes to support rate_limit API call --- .../Clients/IObservableMiscellaneousClient.cs | 4 +- .../Clients/ObservableMiscellaneousClient.cs | 4 +- .../Clients/MiscellaneousClientTests.cs | 12 +++--- .../Clients/MiscellaneousClientTests.cs | 28 ++++++------- Octokit/Clients/IMiscellaneousClient.cs | 4 +- Octokit/Clients/MiscellaneousClient.cs | 8 ++-- Octokit/Helpers/UnixTimeStampExtensions.cs | 9 ++++ Octokit/Http/RateLimit.cs | 31 ++++++++++++++ ...ateLimits.cs => MiscellaneousRateLimit.cs} | 10 ++--- Octokit/Models/Response/ResourceRateLimit.cs | 37 ++++++---------- Octokit/Models/Response/ResourceRateLimits.cs | 42 ------------------- Octokit/Octokit-Mono.csproj | 3 +- Octokit/Octokit-MonoAndroid.csproj | 3 +- Octokit/Octokit-Monotouch.csproj | 3 +- Octokit/Octokit-Portable.csproj | 3 +- Octokit/Octokit-netcore45.csproj | 3 +- Octokit/Octokit.csproj | 3 +- 17 files changed, 92 insertions(+), 115 deletions(-) rename Octokit/Models/Response/{MiscRateLimits.cs => MiscellaneousRateLimit.cs} (76%) delete mode 100644 Octokit/Models/Response/ResourceRateLimits.cs diff --git a/Octokit.Reactive/Clients/IObservableMiscellaneousClient.cs b/Octokit.Reactive/Clients/IObservableMiscellaneousClient.cs index 2b5a8b64d1..56ac658a8c 100644 --- a/Octokit.Reactive/Clients/IObservableMiscellaneousClient.cs +++ b/Octokit.Reactive/Clients/IObservableMiscellaneousClient.cs @@ -55,9 +55,9 @@ public interface IObservableMiscellaneousClient /// Gets API Rate Limits (API service rather than header info). /// /// Thrown when a general API error occurs. - /// An of Rate Limits. + /// An of Rate Limits. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] - IObservable GetRateLimits(); + IObservable GetRateLimits(); } } diff --git a/Octokit.Reactive/Clients/ObservableMiscellaneousClient.cs b/Octokit.Reactive/Clients/ObservableMiscellaneousClient.cs index 8f0a4ae38a..0675437f56 100644 --- a/Octokit.Reactive/Clients/ObservableMiscellaneousClient.cs +++ b/Octokit.Reactive/Clients/ObservableMiscellaneousClient.cs @@ -79,9 +79,9 @@ public IObservable GetLicense(string key) /// Gets API Rate Limits (API service rather than header info). /// /// Thrown when a general API error occurs. - /// An of Rate Limits. + /// An of Rate Limits. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] - public IObservable GetRateLimits() + public IObservable GetRateLimits() { return _client.GetRateLimits().ToObservable(); } diff --git a/Octokit.Tests.Integration/Clients/MiscellaneousClientTests.cs b/Octokit.Tests.Integration/Clients/MiscellaneousClientTests.cs index 451b30c6f8..cc37a98abd 100644 --- a/Octokit.Tests.Integration/Clients/MiscellaneousClientTests.cs +++ b/Octokit.Tests.Integration/Clients/MiscellaneousClientTests.cs @@ -91,24 +91,24 @@ public async Task CanRetrieveResourceRateLimits() Assert.True(result.Resources.Core.Limit > 0); Assert.True(result.Resources.Core.Remaining > -1); Assert.True(result.Resources.Core.Remaining <= result.Resources.Core.Limit); - Assert.True(result.Resources.Core.Reset > 0); - Assert.NotNull(result.Resources.Core.ResetAsDateTimeOffset); + Assert.True(result.Resources.Core.ResetAsUtcEpochSeconds > 0); + Assert.NotNull(result.Resources.Core.Reset); // Test the search limits Assert.NotNull(result.Resources.Search); Assert.True(result.Resources.Search.Limit > 0); Assert.True(result.Resources.Search.Remaining > -1); Assert.True(result.Resources.Search.Remaining <= result.Resources.Search.Limit); - Assert.True(result.Resources.Search.Reset > 0); - Assert.NotNull(result.Resources.Search.ResetAsDateTimeOffset); + Assert.True(result.Resources.Search.ResetAsUtcEpochSeconds > 0); + Assert.NotNull(result.Resources.Search.Reset); // Test the depreciated rate limits Assert.NotNull(result.Rate); Assert.True(result.Rate.Limit > 0); Assert.True(result.Rate.Remaining > -1); Assert.True(result.Rate.Remaining <= result.Rate.Limit); - Assert.True(result.Resources.Search.Reset > 0); - Assert.NotNull(result.Resources.Search.ResetAsDateTimeOffset); + Assert.True(result.Resources.Search.ResetAsUtcEpochSeconds > 0); + Assert.NotNull(result.Resources.Search.Reset); } } diff --git a/Octokit.Tests/Clients/MiscellaneousClientTests.cs b/Octokit.Tests/Clients/MiscellaneousClientTests.cs index 48fe696bb0..7e1c030836 100644 --- a/Octokit.Tests/Clients/MiscellaneousClientTests.cs +++ b/Octokit.Tests/Clients/MiscellaneousClientTests.cs @@ -64,19 +64,19 @@ public class TheGetResourceRateLimitsMethod [Fact] public async Task RequestsTheRecourceRateLimitEndpoint() { - IApiResponse response = new ApiResponse + IApiResponse response = new ApiResponse ( new Response(), - new MiscRateLimits( - new ResourceRateLimits( - new ResourceRateLimit(5000, 4999, 1372700873), - new ResourceRateLimit(30, 18, 1372700873) + new MiscellaneousRateLimit( + new ResourceRateLimit( + new RateLimit(5000, 4999, 1372700873), + new RateLimit(30, 18, 1372700873) ), - new ResourceRateLimit(100, 75, 1372700873) + new RateLimit(100, 75, 1372700873) ) ); var connection = Substitute.For(); - connection.Get(Args.Uri, null, null).Returns(Task.FromResult(response)); + connection.Get(Args.Uri, null, null).Returns(Task.FromResult(response)); var client = new MiscellaneousClient(connection); var result = await client.GetRateLimits(); @@ -91,37 +91,37 @@ public async Task RequestsTheRecourceRateLimitEndpoint() Assert.NotNull(result.Resources.Core); Assert.Equal(5000, result.Resources.Core.Limit); Assert.Equal(4999, result.Resources.Core.Remaining); - Assert.Equal(1372700873, result.Resources.Core.Reset); + Assert.Equal(1372700873, result.Resources.Core.ResetAsUtcEpochSeconds); var expectedReset = DateTimeOffset.ParseExact( "Mon 01 Jul 2013 5:47:53 PM -00:00", "ddd dd MMM yyyy h:mm:ss tt zzz", CultureInfo.InvariantCulture); - Assert.Equal(expectedReset, result.Resources.Core.ResetAsDateTimeOffset); + Assert.Equal(expectedReset, result.Resources.Core.Reset); // Test the search limits Assert.NotNull(result.Resources.Search); Assert.Equal(30, result.Resources.Search.Limit); Assert.Equal(18, result.Resources.Search.Remaining); - Assert.Equal(1372700873, result.Resources.Search.Reset); + Assert.Equal(1372700873, result.Resources.Search.ResetAsUtcEpochSeconds); expectedReset = DateTimeOffset.ParseExact( "Mon 01 Jul 2013 5:47:53 PM -00:00", "ddd dd MMM yyyy h:mm:ss tt zzz", CultureInfo.InvariantCulture); - Assert.Equal(expectedReset, result.Resources.Search.ResetAsDateTimeOffset); + Assert.Equal(expectedReset, result.Resources.Search.Reset); // Test the depreciated rate limits Assert.NotNull(result.Rate); Assert.Equal(100, result.Rate.Limit); Assert.Equal(75, result.Rate.Remaining); - Assert.Equal(1372700873, result.Rate.Reset); + Assert.Equal(1372700873, result.Rate.ResetAsUtcEpochSeconds); expectedReset = DateTimeOffset.ParseExact( "Mon 01 Jul 2013 5:47:53 PM -00:00", "ddd dd MMM yyyy h:mm:ss tt zzz", CultureInfo.InvariantCulture); - Assert.Equal(expectedReset, result.Rate.ResetAsDateTimeOffset); + Assert.Equal(expectedReset, result.Rate.Reset); connection.Received() - .Get(Arg.Is(u => u.ToString() == "rate_limit"), null, null); + .Get(Arg.Is(u => u.ToString() == "rate_limit"), null, null); } } diff --git a/Octokit/Clients/IMiscellaneousClient.cs b/Octokit/Clients/IMiscellaneousClient.cs index 75b40a31c1..ef3a1696a1 100644 --- a/Octokit/Clients/IMiscellaneousClient.cs +++ b/Octokit/Clients/IMiscellaneousClient.cs @@ -64,8 +64,8 @@ public interface IMiscellaneousClient /// Gets API Rate Limits (API service rather than header info). /// /// Thrown when a general API error occurs. - /// An of Rate Limits. + /// An of Rate Limits. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] - Task GetRateLimits(); + Task GetRateLimits(); } } diff --git a/Octokit/Clients/MiscellaneousClient.cs b/Octokit/Clients/MiscellaneousClient.cs index f4fec16ae4..100116563d 100644 --- a/Octokit/Clients/MiscellaneousClient.cs +++ b/Octokit/Clients/MiscellaneousClient.cs @@ -121,15 +121,13 @@ public async Task GetLicense(string key) /// Gets API Rate Limits (API service rather than header info). /// /// Thrown when a general API error occurs. - /// An of Rate Limits. + /// An of Rate Limits. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] - public async Task GetRateLimits() + public async Task GetRateLimits() { var endpoint = new Uri("rate_limit", UriKind.Relative); - var response = await _connection.Get(endpoint, null, null).ConfigureAwait(false); - //var response = await _connection.Get(endpoint, null, null).ConfigureAwait(false); + var response = await _connection.Get(endpoint, null, null).ConfigureAwait(false); return response.Body; - //return null; } } } \ No newline at end of file diff --git a/Octokit/Helpers/UnixTimeStampExtensions.cs b/Octokit/Helpers/UnixTimeStampExtensions.cs index 397c155fd2..70ec92b32b 100644 --- a/Octokit/Helpers/UnixTimeStampExtensions.cs +++ b/Octokit/Helpers/UnixTimeStampExtensions.cs @@ -18,5 +18,14 @@ public static DateTimeOffset FromUnixTime(this long unixTime) { return new DateTimeOffset(unixTime * TimeSpan.TicksPerSecond + unixEpochTicks, TimeSpan.Zero); } + + /// + /// Convert with UTC offset to a Unix tick + /// + /// Date Time with UTC offset + public static long ToUnixTime(this DateTimeOffset date) + { + return (date.Ticks - unixEpochTicks) / TimeSpan.TicksPerSecond; + } } } diff --git a/Octokit/Http/RateLimit.cs b/Octokit/Http/RateLimit.cs index 44b572da79..f6621b574d 100644 --- a/Octokit/Http/RateLimit.cs +++ b/Octokit/Http/RateLimit.cs @@ -2,17 +2,22 @@ using System.Collections.Generic; using System.Runtime.Serialization; using Octokit.Helpers; +using System.Diagnostics; +using System.Globalization; +using Octokit.Internal; namespace Octokit { #if !NETFX_CORE [Serializable] #endif + [DebuggerDisplay("{DebuggerDisplay,nq}")] public class RateLimit #if !NETFX_CORE : ISerializable #endif { + public RateLimit() {} public RateLimit(IDictionary responseHeaders) { @@ -23,6 +28,17 @@ public RateLimit(IDictionary responseHeaders) Reset = GetHeaderValueAsInt32Safe(responseHeaders, "X-RateLimit-Reset").FromUnixTime(); } + public RateLimit(int limit, int remaining, long reset) + { + Ensure.ArgumentNotNull(limit, "limit"); + Ensure.ArgumentNotNull(remaining, "remaining"); + Ensure.ArgumentNotNull(reset, "reset"); + + Limit = limit; + Remaining = remaining; + Reset = reset.FromUnixTime(); + } + /// /// The maximum number of requests that the consumer is permitted to make per hour. /// @@ -36,8 +52,16 @@ public RateLimit(IDictionary responseHeaders) /// /// The date and time at which the current rate limit window resets /// + [ParameterAttribute(Key = "ignoreThisField")] public DateTimeOffset Reset { get; private set; } + /// + /// The date and time at which the current rate limit window resets - in UTC epoch seconds + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [ParameterAttribute(Key = "reset")] + public long ResetAsUtcEpochSeconds { get { return Reset.ToUnixTime(); } private set { Reset = value.FromUnixTime(); } } + static long GetHeaderValueAsInt32Safe(IDictionary responseHeaders, string key) { string value; @@ -66,5 +90,12 @@ public virtual void GetObjectData(SerializationInfo info, StreamingContext conte info.AddValue("Reset", Reset.Ticks); } #endif + internal string DebuggerDisplay + { + get + { + return String.Format(CultureInfo.InvariantCulture, "Limit {0}, Remaining {1}, Reset {2} ", Limit, Remaining, Reset); + } + } } } diff --git a/Octokit/Models/Response/MiscRateLimits.cs b/Octokit/Models/Response/MiscellaneousRateLimit.cs similarity index 76% rename from Octokit/Models/Response/MiscRateLimits.cs rename to Octokit/Models/Response/MiscellaneousRateLimit.cs index 1a0ac28576..707e0d5393 100644 --- a/Octokit/Models/Response/MiscRateLimits.cs +++ b/Octokit/Models/Response/MiscellaneousRateLimit.cs @@ -9,11 +9,11 @@ namespace Octokit { [DebuggerDisplay("{DebuggerDisplay,nq}")] - public class MiscRateLimits + public class MiscellaneousRateLimit { - public MiscRateLimits() {} + public MiscellaneousRateLimit() {} - public MiscRateLimits(ResourceRateLimits resources, ResourceRateLimit rate) + public MiscellaneousRateLimit(ResourceRateLimit resources, RateLimit rate) { Ensure.ArgumentNotNull(resources, "resource"); Ensure.ArgumentNotNull(rate, "rate"); @@ -25,12 +25,12 @@ public MiscRateLimits(ResourceRateLimits resources, ResourceRateLimit rate) /// /// Object of resources rate limits /// - public ResourceRateLimits Resources { get; private set; } + public ResourceRateLimit Resources { get; private set; } /// /// Legacy rate limit - to be depreciated - https://developer.github.com/v3/rate_limit/#deprecation-notice /// - public ResourceRateLimit Rate { get; private set; } + public RateLimit Rate { get; private set; } internal string DebuggerDisplay { diff --git a/Octokit/Models/Response/ResourceRateLimit.cs b/Octokit/Models/Response/ResourceRateLimit.cs index 15995e878f..99bd74f137 100644 --- a/Octokit/Models/Response/ResourceRateLimit.cs +++ b/Octokit/Models/Response/ResourceRateLimit.cs @@ -9,47 +9,34 @@ namespace Octokit [DebuggerDisplay("{DebuggerDisplay,nq}")] public class ResourceRateLimit { - public ResourceRateLimit() { } + public ResourceRateLimit() {} - public ResourceRateLimit(int limit, int remaining, long reset) + public ResourceRateLimit(RateLimit core, RateLimit search) { - Ensure.ArgumentNotNull(limit, "limit"); - Ensure.ArgumentNotNull(remaining, "remaining"); - Ensure.ArgumentNotNull(reset, "reset"); + Ensure.ArgumentNotNull(core, "core"); + Ensure.ArgumentNotNull(search, "search"); - Limit = limit; - Remaining = remaining; - Reset = reset; + Core = core; + Search = search; } - /// - /// The maximum number of requests that the consumer is permitted to make per hour. + /// Rate limits for core API (rate limit for everything except Search API) /// - public int Limit { get; private set; } + public RateLimit Core { get; private set; } /// - /// The number of requests remaining in the current rate limit window. + /// Rate Limits for Search API /// - public int Remaining { get; private set; } - - /// - /// The date and time at which the current rate limit window resets - in UTC epoch seconds - /// - public long Reset { get; private set; } - - /// - /// The date and time at which the current rate limit window resets - as DateTimeOffset - /// - public DateTimeOffset ResetAsDateTimeOffset { get { return Reset.FromUnixTime(); } } - + public RateLimit Search { get; private set; } internal string DebuggerDisplay { get { - return String.Format(CultureInfo.InvariantCulture, "Limit {0}, Remaining {1}, Reset {2} ", Limit, Remaining, Reset); + return String.Format(CultureInfo.InvariantCulture, "Core: {0}; Search: {1} ", Core.DebuggerDisplay, Search.DebuggerDisplay); } } } + } diff --git a/Octokit/Models/Response/ResourceRateLimits.cs b/Octokit/Models/Response/ResourceRateLimits.cs deleted file mode 100644 index 70c5d2cb58..0000000000 --- a/Octokit/Models/Response/ResourceRateLimits.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Diagnostics; -using System.Globalization; - -using Octokit.Helpers; - -namespace Octokit -{ - [DebuggerDisplay("{DebuggerDisplay,nq}")] - public class ResourceRateLimits - { - public ResourceRateLimits() {} - - public ResourceRateLimits(ResourceRateLimit core, ResourceRateLimit search) - { - Ensure.ArgumentNotNull(core, "core"); - Ensure.ArgumentNotNull(search, "search"); - - Core = core; - Search = search; - } - - /// - /// Rate limits for core API (rate limit for everything except Search API) - /// - public ResourceRateLimit Core { get; private set; } - - /// - /// Rate Limits for Search API - /// - public ResourceRateLimit Search { get; private set; } - - internal string DebuggerDisplay - { - get - { - return String.Format(CultureInfo.InvariantCulture, "Core: {0}; Search: {1} ", Core.DebuggerDisplay, Search.DebuggerDisplay); - } - } - } - -} diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index c3e9f989b7..d2d5776f92 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -396,9 +396,8 @@ - - + \ No newline at end of file diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index fb756b17aa..81444b5fd2 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -412,9 +412,8 @@ - - + \ No newline at end of file diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index e82708dd58..2e68fd1044 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -405,9 +405,8 @@ - - + diff --git a/Octokit/Octokit-Portable.csproj b/Octokit/Octokit-Portable.csproj index f358632189..042dcc3542 100644 --- a/Octokit/Octokit-Portable.csproj +++ b/Octokit/Octokit-Portable.csproj @@ -394,9 +394,8 @@ - - + diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index fc2a20fa0f..f0827838a3 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -398,9 +398,8 @@ - - + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index bb4ef867a4..09b0b40a0e 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -116,10 +116,9 @@ - + - From d5d264d05bc6b5a3195d0cfa4850aa18de596d0e Mon Sep 17 00:00:00 2001 From: Mark Taylor Date: Sun, 26 Jul 2015 22:43:04 +0100 Subject: [PATCH 3/5] Added changes to handle NETFX_CORE properly (I hope) --- Octokit/Http/RateLimit.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Octokit/Http/RateLimit.cs b/Octokit/Http/RateLimit.cs index f6621b574d..6fde0f3cd1 100644 --- a/Octokit/Http/RateLimit.cs +++ b/Octokit/Http/RateLimit.cs @@ -10,8 +10,8 @@ namespace Octokit { #if !NETFX_CORE [Serializable] -#endif [DebuggerDisplay("{DebuggerDisplay,nq}")] +#endif public class RateLimit #if !NETFX_CORE : ISerializable @@ -52,14 +52,18 @@ public RateLimit(int limit, int remaining, long reset) /// /// The date and time at which the current rate limit window resets /// +#if !NETFX_CORE [ParameterAttribute(Key = "ignoreThisField")] +#endif public DateTimeOffset Reset { get; private set; } /// /// The date and time at which the current rate limit window resets - in UTC epoch seconds /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] +#if !NETFX_CORE [ParameterAttribute(Key = "reset")] +#endif public long ResetAsUtcEpochSeconds { get { return Reset.ToUnixTime(); } private set { Reset = value.FromUnixTime(); } } static long GetHeaderValueAsInt32Safe(IDictionary responseHeaders, string key) @@ -90,12 +94,18 @@ public virtual void GetObjectData(SerializationInfo info, StreamingContext conte info.AddValue("Reset", Reset.Ticks); } #endif + +#if !NETFX_CORE internal string DebuggerDisplay +#else + public string DebuggerDisplay +#endif { get { return String.Format(CultureInfo.InvariantCulture, "Limit {0}, Remaining {1}, Reset {2} ", Limit, Remaining, Reset); } } + } } From 040571861c89dc8fc1ede60c7dd17575cef5b916 Mon Sep 17 00:00:00 2001 From: Mark Taylor Date: Mon, 27 Jul 2015 00:25:26 +0100 Subject: [PATCH 4/5] Fixes to project files after corrupting them with merge --- Octokit/Octokit-Mono.csproj | 3 --- Octokit/Octokit-MonoAndroid.csproj | 3 --- Octokit/Octokit-Monotouch.csproj | 3 --- Octokit/Octokit-Portable.csproj | 3 --- Octokit/Octokit-netcore45.csproj | 3 --- 5 files changed, 15 deletions(-) diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index f6cc5f9dfc..64daa00563 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -396,12 +396,9 @@ -<<<<<<< HEAD -======= ->>>>>>> upstream/master \ No newline at end of file diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index e26da391c3..9002587a63 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -412,12 +412,9 @@ -<<<<<<< HEAD -======= ->>>>>>> upstream/master \ No newline at end of file diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index 48f31ea53a..607ec59518 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -405,12 +405,9 @@ -<<<<<<< HEAD -======= ->>>>>>> upstream/master diff --git a/Octokit/Octokit-Portable.csproj b/Octokit/Octokit-Portable.csproj index 22a0bff41c..74f211dfd1 100644 --- a/Octokit/Octokit-Portable.csproj +++ b/Octokit/Octokit-Portable.csproj @@ -395,12 +395,9 @@ -<<<<<<< HEAD -======= ->>>>>>> upstream/master diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index 48ba9a2dc6..944bb68159 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -399,12 +399,9 @@ -<<<<<<< HEAD -======= ->>>>>>> upstream/master From 4d9cc4ff09d78d66f575982feeca72fc9e171942 Mon Sep 17 00:00:00 2001 From: Mark Taylor Date: Mon, 27 Jul 2015 14:07:15 +0100 Subject: [PATCH 5/5] Additional changes following review --- .../Clients/MiscellaneousClientTests.cs | 9 --------- Octokit.Tests/Clients/MiscellaneousClientTests.cs | 9 --------- Octokit/Http/RateLimit.cs | 10 +--------- 3 files changed, 1 insertion(+), 27 deletions(-) diff --git a/Octokit.Tests.Integration/Clients/MiscellaneousClientTests.cs b/Octokit.Tests.Integration/Clients/MiscellaneousClientTests.cs index cc37a98abd..149c49b262 100644 --- a/Octokit.Tests.Integration/Clients/MiscellaneousClientTests.cs +++ b/Octokit.Tests.Integration/Clients/MiscellaneousClientTests.cs @@ -80,14 +80,7 @@ public async Task CanRetrieveResourceRateLimits() var result = await github.Miscellaneous.GetRateLimits(); - // Test the high level object - Assert.NotNull(result); - - // Test the resources level - Assert.NotNull(result.Resources); - // Test the core limits - Assert.NotNull(result.Resources.Core); Assert.True(result.Resources.Core.Limit > 0); Assert.True(result.Resources.Core.Remaining > -1); Assert.True(result.Resources.Core.Remaining <= result.Resources.Core.Limit); @@ -95,7 +88,6 @@ public async Task CanRetrieveResourceRateLimits() Assert.NotNull(result.Resources.Core.Reset); // Test the search limits - Assert.NotNull(result.Resources.Search); Assert.True(result.Resources.Search.Limit > 0); Assert.True(result.Resources.Search.Remaining > -1); Assert.True(result.Resources.Search.Remaining <= result.Resources.Search.Limit); @@ -103,7 +95,6 @@ public async Task CanRetrieveResourceRateLimits() Assert.NotNull(result.Resources.Search.Reset); // Test the depreciated rate limits - Assert.NotNull(result.Rate); Assert.True(result.Rate.Limit > 0); Assert.True(result.Rate.Remaining > -1); Assert.True(result.Rate.Remaining <= result.Rate.Limit); diff --git a/Octokit.Tests/Clients/MiscellaneousClientTests.cs b/Octokit.Tests/Clients/MiscellaneousClientTests.cs index 7e1c030836..98eaac131a 100644 --- a/Octokit.Tests/Clients/MiscellaneousClientTests.cs +++ b/Octokit.Tests/Clients/MiscellaneousClientTests.cs @@ -81,14 +81,7 @@ public async Task RequestsTheRecourceRateLimitEndpoint() var result = await client.GetRateLimits(); - // Test the high level object - Assert.NotNull(result); - - // Test the resource object - Assert.NotNull(result.Resources); - // Test the core limits - Assert.NotNull(result.Resources.Core); Assert.Equal(5000, result.Resources.Core.Limit); Assert.Equal(4999, result.Resources.Core.Remaining); Assert.Equal(1372700873, result.Resources.Core.ResetAsUtcEpochSeconds); @@ -99,7 +92,6 @@ public async Task RequestsTheRecourceRateLimitEndpoint() Assert.Equal(expectedReset, result.Resources.Core.Reset); // Test the search limits - Assert.NotNull(result.Resources.Search); Assert.Equal(30, result.Resources.Search.Limit); Assert.Equal(18, result.Resources.Search.Remaining); Assert.Equal(1372700873, result.Resources.Search.ResetAsUtcEpochSeconds); @@ -110,7 +102,6 @@ public async Task RequestsTheRecourceRateLimitEndpoint() Assert.Equal(expectedReset, result.Resources.Search.Reset); // Test the depreciated rate limits - Assert.NotNull(result.Rate); Assert.Equal(100, result.Rate.Limit); Assert.Equal(75, result.Rate.Remaining); Assert.Equal(1372700873, result.Rate.ResetAsUtcEpochSeconds); diff --git a/Octokit/Http/RateLimit.cs b/Octokit/Http/RateLimit.cs index 6fde0f3cd1..6d30371164 100644 --- a/Octokit/Http/RateLimit.cs +++ b/Octokit/Http/RateLimit.cs @@ -10,8 +10,8 @@ namespace Octokit { #if !NETFX_CORE [Serializable] - [DebuggerDisplay("{DebuggerDisplay,nq}")] #endif + [DebuggerDisplay("{DebuggerDisplay,nq}")] public class RateLimit #if !NETFX_CORE : ISerializable @@ -52,18 +52,14 @@ public RateLimit(int limit, int remaining, long reset) /// /// The date and time at which the current rate limit window resets /// -#if !NETFX_CORE [ParameterAttribute(Key = "ignoreThisField")] -#endif public DateTimeOffset Reset { get; private set; } /// /// The date and time at which the current rate limit window resets - in UTC epoch seconds /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] -#if !NETFX_CORE [ParameterAttribute(Key = "reset")] -#endif public long ResetAsUtcEpochSeconds { get { return Reset.ToUnixTime(); } private set { Reset = value.FromUnixTime(); } } static long GetHeaderValueAsInt32Safe(IDictionary responseHeaders, string key) @@ -95,11 +91,7 @@ public virtual void GetObjectData(SerializationInfo info, StreamingContext conte } #endif -#if !NETFX_CORE internal string DebuggerDisplay -#else - public string DebuggerDisplay -#endif { get {