From 34ac57ccab4ae599ce6a5f2562b0b415197a9dbc Mon Sep 17 00:00:00 2001 From: Haacked Date: Tue, 18 Feb 2014 22:41:19 -0800 Subject: [PATCH] Add exception for exceeding the private quota --- .../Clients/RepositoriesClientTests.cs | 41 ++++++++++++++- .../Clients/RepositoriesClientTests.cs | 27 +++++++++- Octokit/Clients/RepositoriesClient.cs | 11 +++- ...PrivateRepositoryQuotaExceededException.cs | 52 +++++++++++++++++++ Octokit/Octokit-Mono.csproj | 1 + Octokit/Octokit-MonoAndroid.csproj | 1 + Octokit/Octokit-Monotouch.csproj | 1 + Octokit/Octokit-netcore45.csproj | 1 + Octokit/Octokit.csproj | 1 + 9 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 Octokit/Exceptions/PrivateRepositoryQuotaExceededException.cs diff --git a/Octokit.Tests.Integration/Clients/RepositoriesClientTests.cs b/Octokit.Tests.Integration/Clients/RepositoriesClientTests.cs index 59bd6636..4d94f810 100644 --- a/Octokit.Tests.Integration/Clients/RepositoriesClientTests.cs +++ b/Octokit.Tests.Integration/Clients/RepositoriesClientTests.cs @@ -9,7 +9,7 @@ using Octokit.Tests.Helpers; public class RepositoriesClientTests { - public class TheCreateMethodForUser + public class TheCreateMethodForUser : IDisposable { [IntegrationTest] public async Task CreatesANewPublicRepository() @@ -287,6 +287,45 @@ public class RepositoriesClientTests Helper.DeleteRepo(createdRepository); } } + + [IntegrationTest] + public async Task ThrowsPrivateRepositoryQuotaExceededExceptionWhenOverQuota() + { + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) + { + Credentials = Helper.Credentials + }; + + for (int i = 0; i < 5; i++) + { + var repoName = Helper.MakeNameWithTimestamp("private-repo" + i); + var repository = new NewRepository { Name = repoName, Private = true }; + await github.Repository.Create(repository); + } + + var thrown = await AssertEx.Throws( + async () => await github.Repository.Create(new NewRepository { Name = "x-private", Private = true })); + Assert.NotNull(thrown); + } + + // Clean up the repos. + public void Dispose() + { + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) + { + Credentials = Helper.Credentials + }; + var repositories = github.Repository.GetAllForCurrent().Result; + + foreach (var repository in repositories) + { + try + { + github.Repository.Delete(repository.Owner.Login, repository.Name).Wait(); + } + catch (Exception) { } + } + } } public class TheCreateMethodForOrganization diff --git a/Octokit.Tests/Clients/RepositoriesClientTests.cs b/Octokit.Tests/Clients/RepositoriesClientTests.cs index 8a1c4f68..06505535 100644 --- a/Octokit.Tests/Clients/RepositoriesClientTests.cs +++ b/Octokit.Tests/Clients/RepositoriesClientTests.cs @@ -76,13 +76,38 @@ namespace Octokit.Tests.Clients .Returns>(_ => { throw new ApiValidationException(response); }); var client = new RepositoriesClient(connection); - var exception = await AssertEx.Throws(async () => await client.Create(newRepository)); + var exception = await AssertEx.Throws( + async () => await client.Create(newRepository)); Assert.False(exception.OwnerIsOrganization); Assert.Equal("haacked", exception.Owner); Assert.Equal("aName", exception.RepositoryName); Assert.Equal(new Uri("https://github.com/haacked/aName"), exception.ExistingRepositoryWebUrl); } + + [Fact] + public async Task ThrowsExceptionWhenPrivateRepositoryQuotaExceeded() + { + var newRepository = new NewRepository { Name = "aName", Private = true }; + var response = Substitute.For(); + response.StatusCode.Returns((HttpStatusCode)422); + response.Body.Returns(@"{""message"":""Validation Failed"",""documentation_url"":" + + @"""http://developer.github.com/v3/repos/#create"",""errors"":[{""resource"":""Repository""," + + @"""code"":""custom"",""field"":""name"",""message"":" + + @"""name can't be private. You are over your quota.""}]}"); + var credentials = new Credentials("haacked", "pwd"); + var connection = Substitute.For(); + connection.Connection.BaseAddress.Returns(GitHubClient.GitHubApiUrl); + connection.Connection.Credentials.Returns(credentials); + connection.Post(Args.Uri, newRepository) + .Returns>(_ => { throw new ApiValidationException(response); }); + var client = new RepositoriesClient(connection); + + var exception = await AssertEx.Throws( + async () => await client.Create(newRepository)); + + Assert.NotNull(exception); + } } public class TheCreateMethodForOrganization diff --git a/Octokit/Clients/RepositoriesClient.cs b/Octokit/Clients/RepositoriesClient.cs index 6d55d27c..8d65e1eb 100644 --- a/Octokit/Clients/RepositoriesClient.cs +++ b/Octokit/Clients/RepositoriesClient.cs @@ -73,9 +73,11 @@ namespace Octokit } catch (ApiValidationException e) { + string errorMessage = e.ApiError.FirstErrorMessageSafe(); + if (String.Equals( "name already exists on this account", - e.ApiError.FirstErrorMessageSafe(), + errorMessage, StringComparison.OrdinalIgnoreCase)) { string owner = organizationLogin ?? Connection.Credentials.Login; @@ -89,6 +91,13 @@ namespace Octokit organizationLogin != null, baseAddress, e); } + if (String.Equals( + "name can't be private. You are over your quota.", + errorMessage, + StringComparison.OrdinalIgnoreCase)) + { + throw new PrivateRepositoryQuotaExceededException(e); + } throw; } } diff --git a/Octokit/Exceptions/PrivateRepositoryQuotaExceededException.cs b/Octokit/Exceptions/PrivateRepositoryQuotaExceededException.cs new file mode 100644 index 00000000..b21ff1f7 --- /dev/null +++ b/Octokit/Exceptions/PrivateRepositoryQuotaExceededException.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// Exception thrown when creating a private repository, but the user's private quota is or would be exceeded + /// by creating it. + /// +#if !NETFX_CORE + [Serializable] +#endif + [SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors", + Justification = "These exceptions are specific to the GitHub API and not general purpose exceptions")] + public class PrivateRepositoryQuotaExceededException : ApiValidationException + { + /// + /// Constructs an instance of PrivateRepositoryQuotaExceededException. + /// + /// The login of the owner of the existing repository + /// The name of the existing repository + /// True if the owner is an organization + /// The base address of the repository. + /// The inner validation exception. + public PrivateRepositoryQuotaExceededException(ApiValidationException innerException) + : base(innerException) + { + } + + public override string Message + { + get + { + // TODO: Would be nice to show the actual numbers, but that requires another request. + return "You are currently at your limit of private repositories. Either delete a private repository " + + "you no longer use or upgrade your account to a plan that allows for more private repositories."; + } + } + +#if !NETFX_CORE + protected PrivateRepositoryQuotaExceededException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + } +} \ No newline at end of file diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index 48e9274c..3b1379b5 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -292,6 +292,7 @@ + \ No newline at end of file diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index 583f0e21..2951004b 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -303,6 +303,7 @@ + \ No newline at end of file diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index 8fb6a1c6..d26efb29 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -298,6 +298,7 @@ + \ No newline at end of file diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index 3d1b4f18..9eeedb90 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -290,6 +290,7 @@ + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index c4695663..626b1818 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -66,6 +66,7 @@ Properties\SolutionInfo.cs +