diff --git a/Octokit.Reactive/Clients/IObservablePullRequestReviewRequestsClient.cs b/Octokit.Reactive/Clients/IObservablePullRequestReviewRequestsClient.cs new file mode 100644 index 0000000000..c1831760d1 --- /dev/null +++ b/Octokit.Reactive/Clients/IObservablePullRequestReviewRequestsClient.cs @@ -0,0 +1,88 @@ +using System; +using System.Reactive; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Pull Request Review Requests API. + /// + /// + /// See the Review Requests API documentation for more information. + /// + public interface IObservablePullRequestReviewRequestsClient + { + /// + /// Gets review requests for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#list-review-requests + /// The owner of the repository + /// The name of the repository + /// The pull request number + IObservable GetAll(string owner, string name, int number); + + /// + /// Gets review requests for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#list-review-requests + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// Options for changing the API response + IObservable GetAll(string owner, string name, int number, ApiOptions options); + + /// + /// Gets review requests for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#list-review-requests + /// The Id of the repository + /// The pull request number + IObservable GetAll(long repositoryId, int number); + + /// + /// Gets review requests for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#list-review-requests + /// The Id of the repository + /// The pull request number + /// Options for changing the API response + IObservable GetAll(long repositoryId, int number, ApiOptions options); + + /// + /// Creates review requests on a pull request for specified users. + /// + /// https://developer.github.com/v3/pulls/review_requests/#create-a-review-request + /// The owner of the repository + /// The name of the repository + /// The Pull Request number + /// List of logins of user will be requested for review + IObservable Create(string owner, string name, int number, PullRequestReviewRequest users); + + /// + /// Creates review requests on a pull request for specified users. + /// + /// https://developer.github.com/v3/pulls/review_requests/#create-a-review-request + /// The Id of the repository + /// The Pull Request number + /// List of logins of user will be requested for review + IObservable Create(long repositoryId, int number, PullRequestReviewRequest users); + + /// + /// Deletes review request for given users on a pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#delete-a-review-request + /// The owner of the repository + /// The name of the repository + /// The pull request review comment number + /// List of logins of users that will be not longer requested for review + IObservable Delete(string owner, string name, int number, PullRequestReviewRequest users); + + /// + /// Deletes review request for given users on a pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#delete-a-review-request + /// The Id of the repository + /// The pull request review comment number + /// List of logins of users that will be not longer requested for review + IObservable Delete(long repositoryId, int number, PullRequestReviewRequest users); + } +} \ No newline at end of file diff --git a/Octokit.Reactive/Clients/IObservablePullRequestsClient.cs b/Octokit.Reactive/Clients/IObservablePullRequestsClient.cs index ad5dd7c97a..9df36fd39b 100644 --- a/Octokit.Reactive/Clients/IObservablePullRequestsClient.cs +++ b/Octokit.Reactive/Clients/IObservablePullRequestsClient.cs @@ -22,6 +22,11 @@ public interface IObservablePullRequestsClient /// IObservablePullRequestReviewCommentsClient ReviewComment { get; } + /// + /// Client for managing review requests. + /// + IObservablePullRequestReviewRequestsClient ReviewRequest { get; } + /// /// Gets a single Pull Request by number. /// diff --git a/Octokit.Reactive/Clients/ObservablePullRequestReviewRequestsClient.cs b/Octokit.Reactive/Clients/ObservablePullRequestReviewRequestsClient.cs new file mode 100644 index 0000000000..1a26b9263b --- /dev/null +++ b/Octokit.Reactive/Clients/ObservablePullRequestReviewRequestsClient.cs @@ -0,0 +1,140 @@ +using System; +using System.Reactive; +using System.Reactive.Threading.Tasks; +using Octokit.Reactive.Internal; + +namespace Octokit.Reactive +{ + public class ObservablePullRequestReviewRequestsClient : IObservablePullRequestReviewRequestsClient + { + readonly IPullRequestReviewRequestsClient _client; + readonly IConnection _connection; + + public ObservablePullRequestReviewRequestsClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, "client"); + + _client = client.PullRequest.ReviewRequest; + _connection = client.Connection; + } + + /// + /// Gets review requests for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#list-review-requests + /// The owner of the repository + /// The name of the repository + /// The pull request number + public IObservable GetAll(string owner, string name, int number) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return _connection.GetAndFlattenAllPages(ApiUrls.PullRequestReviewRequests(owner, name, number), null, AcceptHeaders.PullRequestReviewsApiPreview); + } + + /// + /// Gets review requests for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#list-review-requests + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// Options for changing the API response + public IObservable GetAll(string owner, string name, int number, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.PullRequestReviewRequests(owner, name, number), null, AcceptHeaders.PullRequestReviewsApiPreview, options); + } + + /// + /// Gets review requests for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#list-review-requests + /// The Id of the repository + /// The pull request number + public IObservable GetAll(long repositoryId, int number) + { + return _connection.GetAndFlattenAllPages(ApiUrls.PullRequestReviewRequests(repositoryId, number), null, AcceptHeaders.PullRequestReviewsApiPreview); + } + + /// + /// Gets review requests for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#list-review-requests + /// The Id of the repository + /// The pull request number + /// Options for changing the API response + public IObservable GetAll(long repositoryId, int number, ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.PullRequestReviewRequests(repositoryId, number), null, AcceptHeaders.PullRequestReviewsApiPreview, options); + } + + /// + /// Creates review requests on a pull request for specified users. + /// + /// https://developer.github.com/v3/pulls/review_requests/#create-a-review-request + /// The owner of the repository + /// The name of the repository + /// The Pull Request number + /// List of logins of user will be requested for review + public IObservable Create(string owner, string name, int number, PullRequestReviewRequest users) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(users, "users"); + + return _client.Create(owner, name, number, users).ToObservable(); + } + + /// + /// Creates review requests on a pull request for specified users. + /// + /// https://developer.github.com/v3/pulls/review_requests/#create-a-review-request + /// The Id of the repository + /// The Pull Request number + /// List of logins of user will be requested for review + public IObservable Create(long repositoryId, int number, PullRequestReviewRequest users) + { + Ensure.ArgumentNotNull(users, "users"); + + return _client.Create(repositoryId, number, users).ToObservable(); + } + + /// + /// Deletes review request for given users on a pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#delete-a-review-request + /// The owner of the repository + /// The name of the repository + /// The pull request review comment number + /// List of logins of users that will be not longer requested for review + public IObservable Delete(string owner, string name, int number, PullRequestReviewRequest users) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(users, "users"); + + return _client.Delete(owner, name, number, users).ToObservable(); + } + + /// + /// Deletes review request for given users on a pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#delete-a-review-request + /// The Id of the repository + /// The pull request review comment number + /// List of logins of users that will be not longer requested for review + public IObservable Delete(long repositoryId, int number, PullRequestReviewRequest users) + { + Ensure.ArgumentNotNull(users, "users"); + + return _client.Delete(repositoryId, number, users).ToObservable(); + } + } +} \ No newline at end of file diff --git a/Octokit.Reactive/Clients/ObservablePullRequestsClient.cs b/Octokit.Reactive/Clients/ObservablePullRequestsClient.cs index 912d0fe293..30a3496afe 100644 --- a/Octokit.Reactive/Clients/ObservablePullRequestsClient.cs +++ b/Octokit.Reactive/Clients/ObservablePullRequestsClient.cs @@ -26,6 +26,11 @@ public class ObservablePullRequestsClient : IObservablePullRequestsClient /// public IObservablePullRequestReviewCommentsClient ReviewComment { get; private set; } + /// + /// Client for managing review requests. + /// + public IObservablePullRequestReviewRequestsClient ReviewRequest { get; private set; } + public ObservablePullRequestsClient(IGitHubClient client) { Ensure.ArgumentNotNull(client, "client"); @@ -33,6 +38,7 @@ public ObservablePullRequestsClient(IGitHubClient client) _client = client.Repository.PullRequest; _connection = client.Connection; ReviewComment = new ObservablePullRequestReviewCommentsClient(client); + ReviewRequest = new ObservablePullRequestReviewRequestsClient(client); } /// diff --git a/Octokit.Tests.Integration/Clients/PullRequestReviewRequestsClientTests.cs b/Octokit.Tests.Integration/Clients/PullRequestReviewRequestsClientTests.cs new file mode 100644 index 0000000000..3b638cb2b2 --- /dev/null +++ b/Octokit.Tests.Integration/Clients/PullRequestReviewRequestsClientTests.cs @@ -0,0 +1,296 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Octokit; +using Octokit.Tests.Integration; +using Octokit.Tests.Integration.Helpers; +using Xunit; + +public class PullRequestReviewRequestsClientTests +{ + private static readonly string[] _collaboratorLogins = { + "octokitnet-test1", + "octokitnet-test2" + }; + + public class PullRequestReviewRequestClientTestsBase : IDisposable + { + internal readonly IGitHubClient _github; + internal readonly IPullRequestReviewRequestsClient _client; + internal readonly RepositoryContext _context; + + public PullRequestReviewRequestClientTestsBase() + { + _github = Helper.GetAuthenticatedClient(); + + _client = _github.PullRequest.ReviewRequest; + + _context = _github.CreateRepositoryContext("test-repo").Result; + + Task.WaitAll(_collaboratorLogins.Select(AddCollaborator).ToArray()); + } + + private Task AddCollaborator(string collaborator) => _github.Repository.Collaborator.Add(_context.RepositoryOwner, _context.RepositoryName, collaborator); + + public void Dispose() + { + _context.Dispose(); + } + } + + public class TheGetAllMethod : PullRequestReviewRequestClientTestsBase + { + [IntegrationTest] + public async Task GetsNoRequestsWhenNoneExist() + { + var pullRequestId = await CreateTheWorld(_github, _context, createReviewRequests: false); + + var reviewRequests = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, pullRequestId); + + Assert.NotNull(reviewRequests); + Assert.Empty(reviewRequests); + } + + [IntegrationTest] + public async Task GetsNoRequestsWhenNoneExistWithRepositoryId() + { + var pullRequestId = await CreateTheWorld(_github, _context, createReviewRequests: false); + + var reviewRequests = await _client.GetAll(_context.RepositoryId, pullRequestId); + + Assert.NotNull(reviewRequests); + Assert.Empty(reviewRequests); + } + + [IntegrationTest] + public async Task GetsRequests() + { + var pullRequestId = await CreateTheWorld(_github, _context); + + var reviewRequests = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, pullRequestId); + + Assert.Equal(_collaboratorLogins, reviewRequests.Select(rr => rr.Login)); + } + + [IntegrationTest] + public async Task GetsRequestsWithRepositoryId() + { + var pullRequestId = await CreateTheWorld(_github, _context); + + var reviewRequests = await _client.GetAll(_context.RepositoryId, pullRequestId); + + Assert.Equal(_collaboratorLogins, reviewRequests.Select(rr => rr.Login)); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfReviewRequestsWithStart() + { + var pullRequestId = await CreateTheWorld(_github, _context); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + var reviewRequests = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, pullRequestId, options); + + Assert.Equal(1, reviewRequests.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfReviewRequestsWithStartWithRepositoryId() + { + var pullRequestId = await CreateTheWorld(_github, _context); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 2, + StartPage = 2 + }; + var reviewRequests = await _client.GetAll(_context.RepositoryId, pullRequestId, options); + + Assert.Equal(1, reviewRequests.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctResultsBasedOnStartPage() + { + var pullRequestId = await CreateTheWorld(_github, _context); + + var startOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + var firstPage = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, pullRequestId, startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + var secondPage = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, pullRequestId, skipStartOptions); + + Assert.Equal(1, firstPage.Count); + Assert.Equal(1, secondPage.Count); + Assert.NotEqual(firstPage[0].Id, secondPage[0].Id); + } + + [IntegrationTest] + public async Task ReturnsDistinctResultsBasedOnStartPageWithRepositoryId() + { + var pullRequestId = await CreateTheWorld(_github, _context); + + var startOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + var firstPage = await _client.GetAll(_context.RepositoryId, pullRequestId, startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + var secondPage = await _client.GetAll(_context.RepositoryId, pullRequestId, skipStartOptions); + + Assert.Equal(1, firstPage.Count); + Assert.Equal(1, secondPage.Count); + Assert.NotEqual(firstPage[0].Id, secondPage[0].Id); + } + } + + public class TheDeleteMethod : PullRequestReviewRequestClientTestsBase + { + [IntegrationTest] + public async Task DeletesRequests() + { + var pullRequestId = await CreateTheWorld(_github, _context); + + var reviewRequestsBeforeDelete = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, pullRequestId); + var reviewRequestToCreate = new PullRequestReviewRequest(_collaboratorLogins); + await _client.Delete(_context.RepositoryOwner, _context.RepositoryName, pullRequestId, reviewRequestToCreate); + var reviewRequestsAfterDelete = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, pullRequestId); + + Assert.NotEmpty(reviewRequestsBeforeDelete); + Assert.Empty(reviewRequestsAfterDelete); + } + + [IntegrationTest] + public async Task DeletesRequestsWithRepositoryId() + { + var pullRequestId = await CreateTheWorld(_github, _context); + + var reviewRequestsBeforeDelete = await _client.GetAll(_context.RepositoryId, pullRequestId); + var reviewRequestToCreate = new PullRequestReviewRequest(_collaboratorLogins); + await _client.Delete(_context.RepositoryId, pullRequestId, reviewRequestToCreate); + var reviewRequestsAfterDelete = await _client.GetAll(_context.RepositoryId, pullRequestId); + + Assert.NotEmpty(reviewRequestsBeforeDelete); + Assert.Empty(reviewRequestsAfterDelete); + } + } + + public class TheCreateMethod : PullRequestReviewRequestClientTestsBase, IDisposable + { + [IntegrationTest] + public async Task CreatesRequests() + { + var pullRequestId = await CreateTheWorld(_github, _context, createReviewRequests: false); + var reviewRequestToCreate = new PullRequestReviewRequest(_collaboratorLogins); + + var pr = await _client.Create(_context.RepositoryOwner, _context.RepositoryName, pullRequestId, reviewRequestToCreate); + + Assert.Equal(_collaboratorLogins.ToList(), pr.RequestedReviewers.Select(rr => rr.Login)); + } + + [IntegrationTest] + public async Task CreatesRequestsWithRepositoryId() + { + var pullRequestId = await CreateTheWorld(_github, _context, createReviewRequests: false); + var reviewRequestToCreate = new PullRequestReviewRequest(_collaboratorLogins); + + var pr = await _client.Create(_context.RepositoryId, pullRequestId, reviewRequestToCreate); + + Assert.Equal(_collaboratorLogins.ToList(), pr.RequestedReviewers.Select(rr => rr.Login)); + } + } + + static async Task CreateTheWorld(IGitHubClient github, RepositoryContext context, bool createReviewRequests = true) + { + var master = await github.Git.Reference.Get(context.RepositoryOwner, context.RepositoryName, "heads/master"); + + // create new commit for master branch + var newMasterTree = await CreateTree(github, context, new Dictionary { { "README.md", "Hello World!" } }); + var newMaster = await CreateCommit(github, context, "baseline for pull request", newMasterTree.Sha, master.Object.Sha); + + // update master + await github.Git.Reference.Update(context.RepositoryOwner, context.RepositoryName, "heads/master", new ReferenceUpdate(newMaster.Sha)); + + // create new commit for feature branch + var featureBranchTree = await CreateTree(github, context, new Dictionary { { "README.md", "I am overwriting this blob with something new" } }); + var featureBranchCommit = await CreateCommit(github, context, "this is the commit to merge into the pull request", featureBranchTree.Sha, newMaster.Sha); + + var featureBranchTree2 = await CreateTree(github, context, new Dictionary { { "README.md", "I am overwriting this blob with something new a 2nd time" } }); + var featureBranchCommit2 = await CreateCommit(github, context, "this is a 2nd commit to merge into the pull request", featureBranchTree2.Sha, featureBranchCommit.Sha); + + // create branch + await github.Git.Reference.Create(context.RepositoryOwner, context.RepositoryName, new NewReference("refs/heads/my-branch", featureBranchCommit2.Sha)); + + // create a pull request + var pullRequest = new NewPullRequest("Nice title for the pull request", "my-branch", "master"); + var createdPullRequest = await github.PullRequest.Create(context.RepositoryOwner, context.RepositoryName, pullRequest); + + // Create review requests (optional) + if (createReviewRequests) + { + var reviewRequest = new PullRequestReviewRequest(_collaboratorLogins); + await github.PullRequest.ReviewRequest.Create(context.RepositoryOwner, context.RepositoryName, createdPullRequest.Number, reviewRequest); + } + + return createdPullRequest.Number; + } + + static async Task CreateTree(IGitHubClient github, RepositoryContext context, IEnumerable> treeContents) + { + var collection = new List(); + + foreach (var c in treeContents) + { + var baselineBlob = new NewBlob + { + Content = c.Value, + Encoding = EncodingType.Utf8 + }; + var baselineBlobResult = await github.Git.Blob.Create(context.RepositoryOwner, context.RepositoryName, baselineBlob); + + collection.Add(new NewTreeItem + { + Type = TreeType.Blob, + Mode = FileMode.File, + Path = c.Key, + Sha = baselineBlobResult.Sha + }); + } + + var newTree = new NewTree(); + foreach (var item in collection) + { + newTree.Tree.Add(item); + } + + return await github.Git.Tree.Create(context.RepositoryOwner, context.RepositoryName, newTree); + } + + static async Task CreateCommit(IGitHubClient github, RepositoryContext context, string message, string sha, string parent) + { + var newCommit = new NewCommit(message, sha, parent); + return await github.Git.Commit.Create(context.RepositoryOwner, context.RepositoryName, newCommit); + } +} \ No newline at end of file diff --git a/Octokit.Tests/Clients/PullRequestReviewRequestsClientTests.cs b/Octokit.Tests/Clients/PullRequestReviewRequestsClientTests.cs new file mode 100644 index 0000000000..78c7ad934f --- /dev/null +++ b/Octokit.Tests/Clients/PullRequestReviewRequestsClientTests.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using NSubstitute; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class PullRequestReviewRequestsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new PullRequestReviewRequestsClient(null)); + } + } + + public class TheGetAlltMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new PullRequestReviewRequestsClient(connection); + + await client.GetAll("owner", "name", 7); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "repos/owner/name/pulls/7/requested_reviewers"), + null, + "application/vnd.github.black-cat-preview+json"); + } + + [Fact] + public async Task RequestsCorrectUrlWithRepositoryId() + { + var connection = Substitute.For(); + var client = new PullRequestReviewRequestsClient(connection); + + await client.GetAll(42, 7); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "repositories/42/pulls/7/requested_reviewers"), + null, + "application/vnd.github.black-cat-preview+json"); + } + + [Fact] + public async Task RequestsCorrectUrlWithApiOptions() + { + var connection = Substitute.For(); + var client = new PullRequestReviewRequestsClient(connection); + + var options = new ApiOptions + { + StartPage = 1, + PageCount = 1, + PageSize = 1 + }; + + await client.GetAll("owner", "name", 7, options); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "repos/owner/name/pulls/7/requested_reviewers"), + null, + "application/vnd.github.black-cat-preview+json", + options); + } + + [Fact] + public async Task RequestsCorrectUrlWithApiOptionsWithRepositoryId() + { + var connection = Substitute.For(); + var client = new PullRequestReviewRequestsClient(connection); + + var options = new ApiOptions + { + StartPage = 1, + PageCount = 1, + PageSize = 1 + }; + + await client.GetAll(42, 7, options); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "repositories/42/pulls/7/requested_reviewers"), + null, + "application/vnd.github.black-cat-preview+json", + options); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new PullRequestReviewRequestsClient(connection); + + await Assert.ThrowsAsync(() => client.GetAll(null, "name", 1)); + await Assert.ThrowsAsync(() => client.GetAll("owner", null, 1)); + + await Assert.ThrowsAsync(() => client.GetAll(null, "name", 1, ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAll("owner", null, 1, ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAll("owner", "name", 1, null)); + + + await Assert.ThrowsAsync(() => client.GetAll("", "name", 1)); + await Assert.ThrowsAsync(() => client.GetAll("owner", "", 1)); + + await Assert.ThrowsAsync(() => client.GetAll("", "name", 1, ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAll("owner", "", 1, ApiOptions.None)); + + await Assert.ThrowsAsync(() => client.GetAll(42, 1, null)); + } + } + + public class TheCreateMethod + { + [Fact] + public void PostsToCorrectUrl() + { + var connection = Substitute.For(); + var client = new PullRequestReviewRequestsClient(connection); + + IReadOnlyList fakeReviewers = new List { "zxc", "asd"}; + var pullRequestReviewRequest = new PullRequestReviewRequest(fakeReviewers); + + client.Create("fakeOwner", "fakeRepoName", 13, pullRequestReviewRequest); + + connection.Connection.Received().Post( + Arg.Is(u => u.ToString() == "repos/fakeOwner/fakeRepoName/pulls/13/requested_reviewers"), + pullRequestReviewRequest, + "application/vnd.github.black-cat-preview+json", + null); + } + + [Fact] + public void PostsToCorrectUrlWithRepositoryId() + { + var connection = Substitute.For(); + var client = new PullRequestReviewRequestsClient(connection); + IReadOnlyList fakeReviewers = new List { "zxc", "asd" }; + var pullRequestReviewRequest = new PullRequestReviewRequest(fakeReviewers); + + client.Create(42, 13, pullRequestReviewRequest); + + connection.Connection.Received().Post( + Arg.Is(u => u.ToString() == "repositories/42/pulls/13/requested_reviewers"), + pullRequestReviewRequest, + "application/vnd.github.black-cat-preview+json", + null); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new PullRequestReviewRequestsClient(connection); + + IReadOnlyList fakeReviewers = new List { "zxc", "asd" }; + var pullRequestReviewRequest = new PullRequestReviewRequest(fakeReviewers); + + await Assert.ThrowsAsync(() => client.Create(null, "fakeRepoName", 1, pullRequestReviewRequest)); + await Assert.ThrowsAsync(() => client.Create("fakeOwner", null, 1, pullRequestReviewRequest)); + await Assert.ThrowsAsync(() => client.Create("fakeOwner", "fakeRepoName", 1, null)); + await Assert.ThrowsAsync(() => client.Create(1, 1, null)); + + await Assert.ThrowsAsync(() => client.Create("", "fakeRepoName", 1, pullRequestReviewRequest)); + await Assert.ThrowsAsync(() => client.Create("fakeOwner", "", 1, pullRequestReviewRequest)); + } + } + + public class TheDeleteMethod + { + [Fact] + public async Task PostsToCorrectUrl() + { + var connection = Substitute.For(); + var client = new PullRequestReviewRequestsClient(connection); + + IReadOnlyList fakeReviewers = new List { "zxc", "asd" }; + var pullRequestReviewRequest = new PullRequestReviewRequest(fakeReviewers); + + await client.Delete("owner", "name", 13, pullRequestReviewRequest); + + connection.Received().Delete( + Arg.Is(u => u.ToString() == "repos/owner/name/pulls/13/requested_reviewers"), + pullRequestReviewRequest, + "application/vnd.github.black-cat-preview+json"); + } + + [Fact] + public async Task PostsToCorrectUrlWithRepositoryId() + { + var connection = Substitute.For(); + var client = new PullRequestReviewRequestsClient(connection); + + IReadOnlyList fakeReviewers = new List { "zxc", "asd" }; + var pullRequestReviewRequest = new PullRequestReviewRequest(fakeReviewers); + + await client.Delete(43, 13, pullRequestReviewRequest); + + connection.Received().Delete( + Arg.Is(u => u.ToString() == "repositories/43/pulls/13/requested_reviewers"), + pullRequestReviewRequest, + "application/vnd.github.black-cat-preview+json"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new PullRequestReviewRequestsClient(connection); + + IReadOnlyList fakeReviewers = new List { "zxc", "asd" }; + var pullRequestReviewRequest = new PullRequestReviewRequest(fakeReviewers); + + await Assert.ThrowsAsync(() => client.Delete(null, "name", 1, pullRequestReviewRequest)); + await Assert.ThrowsAsync(() => client.Delete("owner", null, 1, pullRequestReviewRequest)); + await Assert.ThrowsAsync(() => client.Delete("owner", "name", 1, null)); + await Assert.ThrowsAsync(() => client.Delete(1, 1, null)); + + await Assert.ThrowsAsync(() => client.Delete("", "name", 1, pullRequestReviewRequest)); + await Assert.ThrowsAsync(() => client.Delete("owner", "", 1, pullRequestReviewRequest)); + } + } + } +} diff --git a/Octokit.Tests/Reactive/ObservablePullRequestReviewRequestsClientTests.cs b/Octokit.Tests/Reactive/ObservablePullRequestReviewRequestsClientTests.cs new file mode 100644 index 0000000000..7d98870154 --- /dev/null +++ b/Octokit.Tests/Reactive/ObservablePullRequestReviewRequestsClientTests.cs @@ -0,0 +1,205 @@ +using System; +using System.Collections.Generic; +using System.Reactive.Linq; +using System.Threading.Tasks; +using NSubstitute; +using Octokit.Reactive; +using Octokit.Reactive.Internal; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservablePullRequestReviewRequestsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws( + () => new ObservablePullRequestReviewRequestsClient(null)); + } + } + + public class TheGetAlltMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewRequestsClient(gitHubClient); + + client.GetAll("owner", "name", 7); + + gitHubClient.Received().PullRequest.ReviewRequest.GetAll("owner", "name", 7); + } + + [Fact] + public async Task RequestsCorrectUrlWithRepositoryId() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewRequestsClient(gitHubClient); + + client.GetAll(42, 7); + + gitHubClient.Received().PullRequest.ReviewRequest.GetAll(42, 7); + } + + [Fact] + public async Task RequestsCorrectUrlWithApiOptions() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewRequestsClient(gitHubClient); + + var options = new ApiOptions + { + StartPage = 1, + PageCount = 1, + PageSize = 1 + }; + + client.GetAll("owner", "name", 7, options); + + gitHubClient.Received().PullRequest.ReviewRequest.GetAll("owner", "name", 7, options); + } + + [Fact] + public async Task RequestsCorrectUrlWithApiOptionsWithRepositoryId() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewRequestsClient(gitHubClient); + + var options = new ApiOptions + { + StartPage = 1, + PageCount = 1, + PageSize = 1 + }; + + client.GetAll(42, 7, options); + + gitHubClient.Received().PullRequest.ReviewRequest.GetAll(42, 7, options); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewRequestsClient(gitHubClient); + + Assert.Throws(() => client.GetAll(null, "name", 1)); + Assert.Throws(() => client.GetAll("owner", null, 1)); + + Assert.Throws(() => client.GetAll(null, "name", 1, ApiOptions.None)); + Assert.Throws(() => client.GetAll("owner", null, 1, ApiOptions.None)); + Assert.Throws(() => client.GetAll("owner", "name", 1, null)); + + Assert.Throws(() => client.GetAll("", "name", 1)); + Assert.Throws(() => client.GetAll("owner", "", 1)); + + Assert.Throws(() => client.GetAll("", "name", 1, ApiOptions.None)); + Assert.Throws(() => client.GetAll("owner", "", 1, ApiOptions.None)); + + Assert.Throws(() => client.GetAll(42, 1, null)); + } + } + + public class TheCreateMethod + { + [Fact] + public void PostsToCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewRequestsClient(gitHubClient); + + IReadOnlyList fakeReviewers = new List { "zxc", "asd" }; + var pullRequestReviewRequest = new PullRequestReviewRequest(fakeReviewers); + + client.Create("fakeOwner", "fakeRepoName", 13, pullRequestReviewRequest); + + gitHubClient.Received().PullRequest.ReviewRequest.Create("fakeOwner", "fakeRepoName", 13, pullRequestReviewRequest); + } + + [Fact] + public void PostsToCorrectUrlWithRepositoryId() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewRequestsClient(gitHubClient); + + IReadOnlyList fakeReviewers = new List { "zxc", "asd" }; + var pullRequestReviewRequest = new PullRequestReviewRequest(fakeReviewers); + + client.Create(42, 13, pullRequestReviewRequest); + + gitHubClient.Received().PullRequest.ReviewRequest.Create(42, 13, pullRequestReviewRequest); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewRequestsClient(gitHubClient); + + IReadOnlyList fakeReviewers = new List { "zxc", "asd" }; + var pullRequestReviewRequest = new PullRequestReviewRequest(fakeReviewers); + + Assert.Throws(() => client.Create(null, "fakeRepoName", 1, pullRequestReviewRequest)); + Assert.Throws(() => client.Create("fakeOwner", null, 1, pullRequestReviewRequest)); + Assert.Throws(() => client.Create("fakeOwner", "fakeRepoName", 1, null)); + Assert.Throws(() => client.Create(42, 1, null)); + + Assert.Throws(() => client.Create("", "fakeRepoName", 1, pullRequestReviewRequest)); + Assert.Throws(() => client.Create("fakeOwner", "", 1, pullRequestReviewRequest)); + } + } + + public class TheDeleteMethod + { + [Fact] + public async Task PostsToCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewRequestsClient(gitHubClient); + + IReadOnlyList fakeReviewers = new List { "zxc", "asd" }; + var pullRequestReviewRequest = new PullRequestReviewRequest(fakeReviewers); + + await client.Delete("owner", "name", 13, pullRequestReviewRequest); + + gitHubClient.Received().PullRequest.ReviewRequest.Delete("owner", "name", 13, pullRequestReviewRequest); + } + + [Fact] + public async Task PostsToCorrectUrlWithRepositoryId() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewRequestsClient(gitHubClient); + + IReadOnlyList fakeReviewers = new List { "zxc", "asd" }; + var pullRequestReviewRequest = new PullRequestReviewRequest(fakeReviewers); + + await client.Delete(42, 13, pullRequestReviewRequest); + + gitHubClient.Received().PullRequest.ReviewRequest.Delete(42, 13, pullRequestReviewRequest); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewRequestsClient(gitHubClient); + + IReadOnlyList fakeReviewers = new List { "zxc", "asd" }; + var pullRequestReviewRequest = new PullRequestReviewRequest(fakeReviewers); + + Assert.Throws(() => client.Delete(null, "name", 1, pullRequestReviewRequest)); + Assert.Throws(() => client.Delete("owner", null, 1, pullRequestReviewRequest)); + Assert.Throws(() => client.Delete("owner", "name", 1, null)); + Assert.Throws(() => client.Delete(42, 1, null)); + + Assert.Throws(() => client.Delete("", "name", 1, pullRequestReviewRequest)); + Assert.Throws(() => client.Delete("owner", "", 1, pullRequestReviewRequest)); + } + } + } +} \ No newline at end of file diff --git a/Octokit/Clients/IPullRequestReviewRequestsClient.cs b/Octokit/Clients/IPullRequestReviewRequestsClient.cs new file mode 100644 index 0000000000..55d7973fa3 --- /dev/null +++ b/Octokit/Clients/IPullRequestReviewRequestsClient.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Pull Request Review Requests API. + /// + /// + /// See the Review Requests API documentation for more information. + /// + public interface IPullRequestReviewRequestsClient + { + /// + /// Gets review requests for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#list-review-requests + /// The owner of the repository + /// The name of the repository + /// The pull request number + Task> GetAll(string owner, string name, int number); + + /// + /// Gets review requests for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#list-review-requests + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// Options for changing the API response + Task> GetAll(string owner, string name, int number, ApiOptions options); + + /// + /// Gets review requests for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#list-review-requests + /// The Id of the repository + /// The pull request number + Task> GetAll(long repositoryId, int number); + + /// + /// Gets review requests for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#list-review-requests + /// The Id of the repository + /// The pull request number + /// Options for changing the API response + Task> GetAll(long repositoryId, int number, ApiOptions options); + + /// + /// Creates review requests on a pull request for specified users. + /// + /// https://developer.github.com/v3/pulls/review_requests/#create-a-review-request + /// The owner of the repository + /// The name of the repository + /// The Pull Request number + /// List of logins of user will be requested for review + Task Create(string owner, string name, int number, PullRequestReviewRequest users); + + /// + /// Creates review requests on a pull request for specified users. + /// + /// https://developer.github.com/v3/pulls/review_requests/#create-a-review-request + /// The Id of the repository + /// The Pull Request number + /// List of logins of user will be requested for review + Task Create(long repositoryId, int number, PullRequestReviewRequest users); + + /// + /// Deletes review request for given users on a pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#delete-a-review-request + /// The owner of the repository + /// The name of the repository + /// The pull request review comment number + /// List of logins of users that will be not longer requested for review + Task Delete(string owner, string name, int number, PullRequestReviewRequest users); + + /// + /// Deletes review request for given users on a pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#delete-a-review-request + /// The Id of the repository + /// The pull request review comment number + /// List of logins of users that will be not longer requested for review + Task Delete(long repositoryId, int number, PullRequestReviewRequest users); + } +} \ No newline at end of file diff --git a/Octokit/Clients/IPullRequestsClient.cs b/Octokit/Clients/IPullRequestsClient.cs index ccb1315856..3eb893ec2b 100644 --- a/Octokit/Clients/IPullRequestsClient.cs +++ b/Octokit/Clients/IPullRequestsClient.cs @@ -24,6 +24,11 @@ public interface IPullRequestsClient /// IPullRequestReviewCommentsClient ReviewComment { get; } + /// + /// Client for managing review requests. + /// + IPullRequestReviewRequestsClient ReviewRequest { get; } + /// /// Get a pull request by number. /// diff --git a/Octokit/Clients/PullRequestReviewRequestsClient.cs b/Octokit/Clients/PullRequestReviewRequestsClient.cs new file mode 100644 index 0000000000..233b993b75 --- /dev/null +++ b/Octokit/Clients/PullRequestReviewRequestsClient.cs @@ -0,0 +1,155 @@ +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Pull Request Review Requests API. + /// + /// + /// See the Review Requests API documentation for more information. + /// + public class PullRequestReviewRequestsClient : ApiClient, IPullRequestReviewRequestsClient + { + public PullRequestReviewRequestsClient(IApiConnection apiConnection) + : base(apiConnection) + { + } + + /// + /// Gets review requests for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#list-review-requests + /// The owner of the repository + /// The name of the repository + /// The pull request number + public Task> GetAll(string owner, string name, int number) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return ApiConnection.GetAll(ApiUrls.PullRequestReviewRequests(owner, name, number), null, AcceptHeaders.PullRequestReviewsApiPreview); + } + + /// + /// Gets review requests for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#list-review-requests + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// Options for changing the API response + public Task> GetAll(string owner, string name, int number, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.PullRequestReviewRequests(owner, name, number), null, AcceptHeaders.PullRequestReviewsApiPreview, options); + } + + /// + /// Gets review requests for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#list-review-requests + /// The Id of the repository + /// The pull request number + public Task> GetAll(long repositoryId, int number) + { + return ApiConnection.GetAll(ApiUrls.PullRequestReviewRequests(repositoryId, number), null, AcceptHeaders.PullRequestReviewsApiPreview); + } + + /// + /// Gets review requests for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#list-review-requests + /// The Id of the repository + /// The pull request number + /// Options for changing the API response + public Task> GetAll(long repositoryId, int number, ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.PullRequestReviewRequests(repositoryId, number), null, AcceptHeaders.PullRequestReviewsApiPreview, options); + } + + /// + /// Creates review requests on a pull request for specified users. + /// + /// https://developer.github.com/v3/pulls/review_requests/#create-a-review-request + /// The owner of the repository + /// The name of the repository + /// The Pull Request number + /// List of logins of user will be requested for review + public async Task Create(string owner, string name, int number, PullRequestReviewRequest users) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(users, "users"); + + var endpoint = ApiUrls.PullRequestReviewRequests(owner, name, number); + var response = await ApiConnection.Connection.Post(endpoint, users, AcceptHeaders.PullRequestReviewsApiPreview, null).ConfigureAwait(false); + + if (response.HttpResponse.StatusCode != HttpStatusCode.Created) + { + throw new ApiException("Invalid Status Code returned. Expected a 201", response.HttpResponse.StatusCode); + } + + return response.Body; + } + + /// + /// Creates review requests on a pull request for specified users. + /// + /// https://developer.github.com/v3/pulls/review_requests/#create-a-review-request + /// The Id of the repository + /// The Pull Request number + /// List of logins of user will be requested for review + public async Task Create(long repositoryId, int number, PullRequestReviewRequest users) + { + Ensure.ArgumentNotNull(users, "users"); + + var endpoint = ApiUrls.PullRequestReviewRequests(repositoryId, number); + var response = await ApiConnection.Connection.Post(endpoint, users, AcceptHeaders.PullRequestReviewsApiPreview, null).ConfigureAwait(false); + + if (response.HttpResponse.StatusCode != HttpStatusCode.Created) + { + throw new ApiException("Invalid Status Code returned. Expected a 201", response.HttpResponse.StatusCode); + } + + return response.Body; + } + + /// + /// Deletes review request for given users on a pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#delete-a-review-request + /// The owner of the repository + /// The name of the repository + /// The pull request review comment number + /// List of logins of users that will be not longer requested for review + public Task Delete(string owner, string name, int number, PullRequestReviewRequest users) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(users, "users"); + + return ApiConnection.Delete(ApiUrls.PullRequestReviewRequests(owner, name, number), users, AcceptHeaders.PullRequestReviewsApiPreview); + } + + /// + /// Deletes review request for given users on a pull request. + /// + /// https://developer.github.com/v3/pulls/review_requests/#delete-a-review-request + /// The Id of the repository + /// The pull request review comment number + /// List of logins of users that will be not longer requested for review + public Task Delete(long repositoryId, int number, PullRequestReviewRequest users) + { + Ensure.ArgumentNotNull(users, "users"); + + return ApiConnection.Delete(ApiUrls.PullRequestReviewRequests(repositoryId, number), users, AcceptHeaders.PullRequestReviewsApiPreview); + } + } +} \ No newline at end of file diff --git a/Octokit/Clients/PullRequestsClient.cs b/Octokit/Clients/PullRequestsClient.cs index 81ea33ffed..4b712f35c8 100644 --- a/Octokit/Clients/PullRequestsClient.cs +++ b/Octokit/Clients/PullRequestsClient.cs @@ -16,6 +16,7 @@ public class PullRequestsClient : ApiClient, IPullRequestsClient public PullRequestsClient(IApiConnection apiConnection) : base(apiConnection) { ReviewComment = new PullRequestReviewCommentsClient(apiConnection); + ReviewRequest = new PullRequestReviewRequestsClient(apiConnection); } /// @@ -29,6 +30,11 @@ public PullRequestsClient(IApiConnection apiConnection) : base(apiConnection) /// public IPullRequestReviewCommentsClient ReviewComment { get; set; } + /// + /// Client for managing review requests. + /// + public IPullRequestReviewRequestsClient ReviewRequest { get; set; } + /// /// Get a pull request by number. /// diff --git a/Octokit/Helpers/AcceptHeaders.cs b/Octokit/Helpers/AcceptHeaders.cs index 20e01e51e6..1e53eb0e14 100644 --- a/Octokit/Helpers/AcceptHeaders.cs +++ b/Octokit/Helpers/AcceptHeaders.cs @@ -42,5 +42,7 @@ public static class AcceptHeaders public const string IssueTimelineApiPreview = "application/vnd.github.mockingbird-preview"; public const string RepositoryTrafficApiPreview = "application/vnd.github.spiderman-preview"; + + public const string PullRequestReviewsApiPreview = "application/vnd.github.black-cat-preview+json"; } } diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index 587914da51..00a430bbbe 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -3358,5 +3358,28 @@ public static Uri RepositoryTrafficClones(long repositoryId) { return "repositories/{0}/traffic/clones".FormatUri(repositoryId); } + + /// + /// Returns the for pull request review requests. + /// + /// The owner of repo + /// The name of repo + /// The pull request number + /// The for pull request review requests. + public static Uri PullRequestReviewRequests(string owner, string repo, int number) + { + return "repos/{0}/{1}/pulls/{2}/requested_reviewers".FormatUri(owner, repo, number); + } + + /// + /// Returns the for pull request review requests. + /// + /// The id of the repository + /// The pull request number + /// The for pull request review requests. + public static Uri PullRequestReviewRequests(long repositoryId, int number) + { + return "repositories/{0}/pulls/{1}/requested_reviewers".FormatUri(repositoryId, number); + } } } diff --git a/Octokit/Models/Request/PullRequestReviewRequest.cs b/Octokit/Models/Request/PullRequestReviewRequest.cs new file mode 100644 index 0000000000..ae0ede36f3 --- /dev/null +++ b/Octokit/Models/Request/PullRequestReviewRequest.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + /// + /// Used to add and delete pull request review requests. + /// + /// + /// API: https://developer.github.com/v3/pulls/review_requests/#create-a-review-request + /// API: https://developer.github.com/v3/pulls/review_requests/#delete-a-review-request + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class PullRequestReviewRequest + { + public PullRequestReviewRequest(IReadOnlyList reviewers) + { + Reviewers = reviewers; + } + + public IReadOnlyList Reviewers { get; set; } + + internal string DebuggerDisplay + { + get { return string.Format(CultureInfo.InvariantCulture, "Reviewers: {0}", string.Join(", ", Reviewers)); } + } + } +} \ No newline at end of file diff --git a/Octokit/Models/Response/PullRequest.cs b/Octokit/Models/Response/PullRequest.cs index fb9e7e9893..bca78fdcd3 100644 --- a/Octokit/Models/Response/PullRequest.cs +++ b/Octokit/Models/Response/PullRequest.cs @@ -15,7 +15,7 @@ public PullRequest(int number) Number = number; } - public PullRequest(long id, string url, string htmlUrl, string diffUrl, string patchUrl, string issueUrl, string statusesUrl, int number, ItemState state, string title, string body, DateTimeOffset createdAt, DateTimeOffset updatedAt, DateTimeOffset? closedAt, DateTimeOffset? mergedAt, GitReference head, GitReference @base, User user, User assignee, IReadOnlyList assignees, bool? mergeable, User mergedBy, string mergeCommitSha, int comments, int commits, int additions, int deletions, int changedFiles, Milestone milestone, bool locked) + public PullRequest(long id, string url, string htmlUrl, string diffUrl, string patchUrl, string issueUrl, string statusesUrl, int number, ItemState state, string title, string body, DateTimeOffset createdAt, DateTimeOffset updatedAt, DateTimeOffset? closedAt, DateTimeOffset? mergedAt, GitReference head, GitReference @base, User user, User assignee, IReadOnlyList assignees, bool? mergeable, User mergedBy, string mergeCommitSha, int comments, int commits, int additions, int deletions, int changedFiles, Milestone milestone, bool locked, IReadOnlyList requestedReviewers) { Id = id; Url = url; @@ -47,6 +47,7 @@ public PullRequest(long id, string url, string htmlUrl, string diffUrl, string p ChangedFiles = changedFiles; Milestone = milestone; Locked = locked; + RequestedReviewers = requestedReviewers; } /// @@ -211,6 +212,11 @@ public bool Merged /// public bool Locked { get; protected set; } + /// + /// Users requested for review + /// + public IReadOnlyList RequestedReviewers { get; protected set; } + internal string DebuggerDisplay { get { return string.Format(CultureInfo.InvariantCulture, "Number: {0} State: {1}", Number, State); }