From 171b3c520a35af7d3fd80c48718623dafb7c474c Mon Sep 17 00:00:00 2001 From: Prayank Mathur Date: Fri, 25 Mar 2016 11:26:37 +0530 Subject: [PATCH] Implement Lock/Unlock Issue --- .../Clients/IObservableIssuesClient.cs | 23 ++++++- .../Clients/ObservableIssuesClient.cs | 33 ++++++++++ .../Clients/IssuesClientTests.cs | 20 +++++++ .../Reactive/ObservableIssuesClientTests.cs | 17 ++++++ Octokit.Tests/Clients/IssuesClientTests.cs | 52 ++++++++++++++++ .../Reactive/ObservableIssuesClientTests.cs | 60 +++++++++++++++++-- Octokit/Clients/IIssuesClient.cs | 24 +++++++- Octokit/Clients/IssuesClient.cs | 32 ++++++++++ Octokit/Helpers/ApiUrls.cs | 12 ++++ 9 files changed, 265 insertions(+), 8 deletions(-) diff --git a/Octokit.Reactive/Clients/IObservableIssuesClient.cs b/Octokit.Reactive/Clients/IObservableIssuesClient.cs index 008d9955b0..cae13e02a1 100644 --- a/Octokit.Reactive/Clients/IObservableIssuesClient.cs +++ b/Octokit.Reactive/Clients/IObservableIssuesClient.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Reactive; namespace Octokit.Reactive { @@ -156,5 +157,25 @@ public interface IObservableIssuesClient /// /// IObservable Update(string owner, string name, int number, IssueUpdate issueUpdate); + + /// + /// Locks an issue for the specified repository. Issue owners and users with push access can lock an issue. + /// + /// https://developer.github.com/v3/issues/#lock-an-issue + /// The owner of the repository + /// The name of the repository + /// The issue number + /// + IObservable LockIssue(string owner, string name, int number); + + /// + /// Unlocks an issue for the specified repository. Issue owners and users with push access can unlock an issue. + /// + /// https://developer.github.com/v3/issues/#unlock-an-issue + /// The owner of the repository + /// The name of the repository + /// The issue number + /// + IObservable UnlockIssue(string owner, string name, int number); } -} +} \ No newline at end of file diff --git a/Octokit.Reactive/Clients/ObservableIssuesClient.cs b/Octokit.Reactive/Clients/ObservableIssuesClient.cs index 4571696334..47d2b9f07b 100644 --- a/Octokit.Reactive/Clients/ObservableIssuesClient.cs +++ b/Octokit.Reactive/Clients/ObservableIssuesClient.cs @@ -1,6 +1,7 @@ using System; using System.Reactive.Threading.Tasks; using Octokit.Reactive.Internal; +using System.Reactive; namespace Octokit.Reactive { @@ -222,5 +223,37 @@ public IObservable Update(string owner, string name, int number, IssueUpd return _client.Update(owner, name, number, issueUpdate).ToObservable(); } + + /// + /// Locks an issue for the specified repository. Issue owners and users with push access can lock an issue. + /// + /// https://developer.github.com/v3/issues/#lock-an-issue + /// The owner of the repository + /// The name of the repository + /// The issue number + /// + public IObservable LockIssue(string owner, string name, int number) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return _client.LockIssue(owner, name, number).ToObservable(); + } + + /// + /// Unlocks an issue for the specified repository. Issue owners and users with push access can unlock an issue. + /// + /// https://developer.github.com/v3/issues/#unlock-an-issue + /// The owner of the repository + /// The name of the repository + /// The issue number + /// + public IObservable UnlockIssue(string owner, string name, int number) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return _client.UnlockIssue(owner, name, number).ToObservable(); + } } } diff --git a/Octokit.Tests.Integration/Clients/IssuesClientTests.cs b/Octokit.Tests.Integration/Clients/IssuesClientTests.cs index 73284093a5..fec271d0bf 100644 --- a/Octokit.Tests.Integration/Clients/IssuesClientTests.cs +++ b/Octokit.Tests.Integration/Clients/IssuesClientTests.cs @@ -62,6 +62,26 @@ public async Task CanCreateRetrieveAndCloseIssue() } [IntegrationTest] + public async Task CanLockAndUnlockIssue() + { + var newIssue = new NewIssue("a test issue") { Body = "A new unassigned issue" }; + var issue = await _issuesClient.Create(_context.RepositoryOwner, _context.RepositoryName, newIssue); + var retrieved = await _issuesClient.Get(_context.RepositoryOwner, _context.RepositoryName, issue.Number); + + Assert.Equal(false, issue.Locked); + + await _issuesClient.LockIssue(_context.RepositoryOwner, _context.RepositoryName, issue.Number); + retrieved = await _issuesClient.Get(_context.RepositoryOwner, _context.RepositoryName, issue.Number); + Assert.NotNull(retrieved); + Assert.Equal(true, issue.Locked); + + await _issuesClient.UnlockIssue(_context.RepositoryOwner, _context.RepositoryName, issue.Number); + retrieved = await _issuesClient.Get(_context.RepositoryOwner, _context.RepositoryName, issue.Number); + Assert.NotNull(retrieved); + Assert.Equal(false, issue.Locked); + } + + [IntegrationTest] public async Task CanListOpenIssuesWithDefaultSort() { var newIssue1 = new NewIssue("A test issue1") { Body = "A new unassigned issue" }; diff --git a/Octokit.Tests.Integration/Reactive/ObservableIssuesClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableIssuesClientTests.cs index 5eb94af756..1b40a7ac92 100644 --- a/Octokit.Tests.Integration/Reactive/ObservableIssuesClientTests.cs +++ b/Octokit.Tests.Integration/Reactive/ObservableIssuesClientTests.cs @@ -72,6 +72,23 @@ public async Task CanCreateAndUpdateIssues() Assert.Equal("Modified integration test issue", updateResult.Title); } + [IntegrationTest] + public async Task CanLockAndUnlockIssues() + { + var newIssue = new NewIssue("Integration Test Issue"); + + var createResult = await _client.Create(_context.RepositoryOwner, _context.RepositoryName, newIssue); + Assert.Equal(false, createResult.Locked); + + await _client.LockIssue(_context.RepositoryOwner, _context.RepositoryName, createResult.Number); + var lockResult = await _client.Get(_context.RepositoryOwner, _context.RepositoryName, createResult.Number); + Assert.Equal(true, lockResult.Locked); + + await _client.UnlockIssue(_context.RepositoryOwner, _context.RepositoryName, createResult.Number); + var unlockIssueResult = await _client.Get(_context.RepositoryOwner, _context.RepositoryName, createResult.Number); + Assert.Equal(false, unlockIssueResult.Locked); + } + public void Dispose() { _context.Dispose(); diff --git a/Octokit.Tests/Clients/IssuesClientTests.cs b/Octokit.Tests/Clients/IssuesClientTests.cs index 2f3b759b19..f4086d6833 100644 --- a/Octokit.Tests/Clients/IssuesClientTests.cs +++ b/Octokit.Tests/Clients/IssuesClientTests.cs @@ -185,6 +185,58 @@ public async Task EnsuresArgumentsNotNull() } } + public class TheLockIssueMethod + { + [Fact] + public void PostsToCorrectUrl() + { + var connection = Substitute.For(); + var client = new IssuesClient(connection); + + client.LockIssue("fake", "repo", 42); + + connection.Received().Put(Arg.Is(u => u.ToString() == "repos/fake/repo/issues/42/lock")); + } + + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var connection = Substitute.For(); + var client = new IssuesClient(connection); + + await Assert.ThrowsAsync(() => client.LockIssue(null, "name", 1)); + await Assert.ThrowsAsync(() => client.LockIssue("", "name", 1)); + await Assert.ThrowsAsync(() => client.LockIssue("owner", null, 1)); + await Assert.ThrowsAsync(() => client.LockIssue("owner", "", 1)); + } + } + + public class TheUnlockIssueMethod + { + [Fact] + public void PostsToCorrectUrl() + { + var connection = Substitute.For(); + var client = new IssuesClient(connection); + + client.UnlockIssue("fake", "repo", 42); + + connection.Received().Delete(Arg.Is(u => u.ToString() == "repos/fake/repo/issues/42/lock")); + } + + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var connection = Substitute.For(); + var client = new IssuesClient(connection); + + await Assert.ThrowsAsync(() => client.UnlockIssue(null, "name", 1)); + await Assert.ThrowsAsync(() => client.UnlockIssue("", "name", 1)); + await Assert.ThrowsAsync(() => client.UnlockIssue("owner", null, 1)); + await Assert.ThrowsAsync(() => client.UnlockIssue("owner", "", 1)); + } + } + public class TheCtor { [Fact] diff --git a/Octokit.Tests/Reactive/ObservableIssuesClientTests.cs b/Octokit.Tests/Reactive/ObservableIssuesClientTests.cs index aa23f958fd..61afa771cd 100644 --- a/Octokit.Tests/Reactive/ObservableIssuesClientTests.cs +++ b/Octokit.Tests/Reactive/ObservableIssuesClientTests.cs @@ -329,11 +329,61 @@ public void EnsuresArgumentsNotNull() var gitHubClient = Substitute.For(); var client = new ObservableIssuesClient(gitHubClient); - Assert.Throws(() => client.Create(null, "name", new NewIssue("title"))); - Assert.Throws(() => client.Create("", "name", new NewIssue("x"))); - Assert.Throws(() => client.Create("owner", null, new NewIssue("x"))); - Assert.Throws(() => client.Create("owner", "", new NewIssue("x"))); - Assert.Throws(() => client.Create("owner", "name", null)); + Assert.Throws(() => client.Update(null, "name", 42, new IssueUpdate())); + Assert.Throws(() => client.Update("", "name", 42, new IssueUpdate())); + Assert.Throws(() => client.Update("owner", null, 42, new IssueUpdate())); + Assert.Throws(() => client.Update("owner", "", 42, new IssueUpdate())); + Assert.Throws(() => client.Update("owner", "name", 42, null)); + } + } + + public class TheLockIssueMethod + { + [Fact] + public void LockIssue() + { + var gitHubClient = Substitute.For(); + var client = new ObservableIssuesClient(gitHubClient); + + client.LockIssue("fake", "repo", 42); + gitHubClient.Issue.Received().LockIssue("fake", "repo", 42); + } + + [Fact] + public void EnsuresArgumentsNotNull() + { + var gitHubClient = Substitute.For(); + var client = new ObservableIssuesClient(gitHubClient); + + Assert.Throws(() => client.LockIssue(null, "name", 42)); + Assert.Throws(() => client.LockIssue("", "name", 42)); + Assert.Throws(() => client.LockIssue("owner", null, 42)); + Assert.Throws(() => client.LockIssue("owner", "", 42)); + } + } + + public class TheUnlockIssueMethod + { + [Fact] + public void UnlockIssue() + { + var gitHubClient = Substitute.For(); + var client = new ObservableIssuesClient(gitHubClient); + + client.UnlockIssue("fake", "repo", 42); + gitHubClient.Issue.Received().UnlockIssue("fake", "repo", 42); + } + + [Fact] + public void EnsuresArgumentsNotNull() + { + var gitHubClient = Substitute.For(); + var client = new ObservableIssuesClient(gitHubClient); + + Assert.Throws(() => client.UnlockIssue(null, "name", 42)); + Assert.Throws(() => client.UnlockIssue("", "name", 42)); + Assert.Throws(() => client.UnlockIssue("owner", null, 42)); + Assert.Throws(() => client.UnlockIssue("owner", "", 42)); } } diff --git a/Octokit/Clients/IIssuesClient.cs b/Octokit/Clients/IIssuesClient.cs index bc0e362cb9..f6f61dea4a 100644 --- a/Octokit/Clients/IIssuesClient.cs +++ b/Octokit/Clients/IIssuesClient.cs @@ -152,10 +152,10 @@ public interface IIssuesClient Task Create(string owner, string name, NewIssue newIssue); /// - /// Creates an issue for the specified repository. Any user with pull access to a repository can create an + /// Updates an issue for the specified repository. Any user with pull access to a repository can update an /// issue. /// - /// http://developer.github.com/v3/issues/#create-an-issue + /// http://developer.github.com/v3/issues/#edit-an-issue /// The owner of the repository /// The name of the repository /// The issue number @@ -163,5 +163,25 @@ public interface IIssuesClient /// /// Task Update(string owner, string name, int number, IssueUpdate issueUpdate); + + /// + /// Locks an issue for the specified repository. Issue owners and users with push access can lock an issue. + /// + /// https://developer.github.com/v3/issues/#lock-an-issue + /// The owner of the repository + /// The name of the repository + /// The issue number + /// + Task LockIssue(string owner, string name, int number); + + /// + /// Unlocks an issue for the specified repository. Issue owners and users with push access can unlock an issue. + /// + /// https://developer.github.com/v3/issues/#unlock-an-issue + /// The owner of the repository + /// The name of the repository + /// The issue number + /// + Task UnlockIssue(string owner, string name, int number); } } diff --git a/Octokit/Clients/IssuesClient.cs b/Octokit/Clients/IssuesClient.cs index 8b924245bb..be4a9641b6 100644 --- a/Octokit/Clients/IssuesClient.cs +++ b/Octokit/Clients/IssuesClient.cs @@ -225,5 +225,37 @@ public Task Update(string owner, string name, int number, IssueUpdate iss return ApiConnection.Patch(ApiUrls.Issue(owner, name, number), issueUpdate); } + + /// + /// Locks an issue for the specified repository. Issue owners and users with push access can lock an issue. + /// + /// https://developer.github.com/v3/issues/#lock-an-issue + /// The owner of the repository + /// The name of the repository + /// The issue number + /// + public Task LockIssue(string owner, string name, int number) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return ApiConnection.Put(ApiUrls.IssueLock(owner, name, number)); + } + + /// + /// Unlocks an issue for the specified repository. Issue owners and users with push access can unlock an issue. + /// + /// https://developer.github.com/v3/issues/#unlock-an-issue + /// The owner of the repository + /// The name of the repository + /// The issue number + /// + public Task UnlockIssue(string owner, string name, int number) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return ApiConnection.Delete(ApiUrls.IssueLock(owner, name, number)); + } } } diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index bb34ce4624..73e99aa3bc 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -294,6 +294,18 @@ public static Uri Issue(string owner, string name, int number) return "repos/{0}/{1}/issues/{2}".FormatUri(owner, name, number); } + /// + /// Returns the for the specified issue to be locked/unlocked. + /// + /// The owner of the repository + /// The name of the repository + /// The issue number + /// + public static Uri IssueLock(string owner, string name, int number) + { + return "repos/{0}/{1}/issues/{2}/lock".FormatUri(owner, name, number); + } + /// /// Returns the for the comments for all issues in a specific repo. ///