diff --git a/Octokit.Reactive/Clients/IObservablePullRequestReviewsClient.cs b/Octokit.Reactive/Clients/IObservablePullRequestReviewsClient.cs new file mode 100644 index 0000000000..92a2b17ade --- /dev/null +++ b/Octokit.Reactive/Clients/IObservablePullRequestReviewsClient.cs @@ -0,0 +1,187 @@ +using System; +using System.Reactive; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Pull Request Review API. + /// + /// + /// See the Review API documentation for more information. + /// + public interface IObservablePullRequestReviewsClient + { + /// + /// Gets reviews for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request + /// The owner of the repository + /// The name of the repository + /// The pull request number + IObservable GetAll(string owner, string name, int number); + + /// + /// Gets reviews for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request + /// The Id of the repository + /// The pull request number + IObservable GetAll(long repositoryId, int number); + + /// + /// Gets reviews for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request + /// 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 reviews for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request + /// The Id of the repository + /// The pull request number + /// Options for changing the API response + IObservable GetAll(long repositoryId, int number, ApiOptions options); + + /// + /// Gets a single pull request review by ID. + /// + /// https://developer.github.com/v3/pulls/reviews/#get-a-single-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + IObservable Get(string owner, string name, int number, long reviewId); + + /// + /// Gets a single pull request review by ID. + /// + /// https://developer.github.com/v3/pulls/reviews/#get-a-single-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + IObservable Get(long repositoryId, int number, long reviewId); + + /// + /// Creates a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#create-a-pull-request-review + /// The owner of the repository + /// The name of the repository + /// The Pull Request number + /// The review + IObservable Create(string owner, string name, int number, PullRequestReviewCreate review); + + /// + /// Creates a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#create-a-pull-request-review + /// The Id of the repository + /// The Pull Request number + /// The review + IObservable Create(long repositoryId, int number, PullRequestReviewCreate review); + + /// + /// Deletes a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#delete-a-pending-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + IObservable Delete(string owner, string name, int number, long reviewId); + + /// + /// Deletes a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#delete-a-pending-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + IObservable Delete(long repositoryId, int number, long reviewId); + + /// + /// Submits a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#submit-a-pull-request-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + /// The message and event being submitted for the review + IObservable Submit(string owner, string name, int number, long reviewId, PullRequestReviewSubmit submitMessage); + + /// + /// Submits a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#submit-a-pull-request-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + /// The message and event being submitted for the review + IObservable Submit(long repositoryId, int number, long reviewId, PullRequestReviewSubmit submitMessage); + + /// + /// Dismisses a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#dismiss-a-pull-request-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + /// The message indicating why the review was dismissed + IObservable Dismiss(string owner, string name, int number, long reviewId, PullRequestReviewDismiss dismissMessage); + + /// + /// Dismisses a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#dismiss-a-pull-request-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + /// The message indicating why the review was dismissed + IObservable Dismiss(long repositoryId, int number, long reviewId, PullRequestReviewDismiss dismissMessage); + + /// + /// Lists comments for a single review + /// + /// https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + IObservable GetAllComments(string owner, string name, int number, long reviewId); + + /// + /// Dismisses a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + IObservable GetAllComments(long repositoryId, int number, long reviewId); + + /// + /// Lists comments for a single review + /// + /// https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + IObservable GetAllComments(string owner, string name, int number, long reviewId, ApiOptions options); + + /// + /// Dismisses a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + IObservable GetAllComments(long repositoryId, int number, long reviewId, ApiOptions options); + } +} diff --git a/Octokit.Reactive/Clients/IObservablePullRequestsClient.cs b/Octokit.Reactive/Clients/IObservablePullRequestsClient.cs index 9df36fd39b..5b7fb37128 100644 --- a/Octokit.Reactive/Clients/IObservablePullRequestsClient.cs +++ b/Octokit.Reactive/Clients/IObservablePullRequestsClient.cs @@ -17,6 +17,11 @@ public interface IObservablePullRequestsClient [Obsolete("Please use IObservablePullRequestsClient.ReviewComment. This will be removed in a future version")] IObservablePullRequestReviewCommentsClient Comment { get; } + /// + /// Client for managing reviews. + /// + IObservablePullRequestReviewsClient Review { get; } + /// /// Client for managing review comments. /// diff --git a/Octokit.Reactive/Clients/ObservablePullRequestReviewsClient.cs b/Octokit.Reactive/Clients/ObservablePullRequestReviewsClient.cs new file mode 100644 index 0000000000..3debbc94be --- /dev/null +++ b/Octokit.Reactive/Clients/ObservablePullRequestReviewsClient.cs @@ -0,0 +1,292 @@ +using System; +using System.Reactive; +using System.Reactive.Threading.Tasks; +using Octokit.Reactive.Internal; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Pull Request Review API. + /// + /// + /// See the Review API documentation for more information. + /// + public class ObservablePullRequestReviewsClient : IObservablePullRequestReviewsClient + { + readonly IPullRequestReviewsClient _client; + readonly IConnection _connection; + + public ObservablePullRequestReviewsClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, nameof(client)); + + _client = client.PullRequest.Review; + _connection = client.Connection; + } + + /// + /// Creates a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#create-a-pull-request-review + /// The owner of the repository + /// The name of the repository + /// The Pull Request number + /// The review + public IObservable Create(string owner, string name, int number, PullRequestReviewCreate review) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name,nameof(name)); + Ensure.ArgumentNotNull(review, nameof(review)); + return _client.Create(owner, name, number, review).ToObservable(); + } + + /// + /// Creates a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#create-a-pull-request-review + /// The Id of the repository + /// The Pull Request number + /// The review + public IObservable Create(long repositoryId, int number, PullRequestReviewCreate review) + { + Ensure.ArgumentNotNull(review, nameof(review)); + + return _client.Create(repositoryId, number, review).ToObservable(); + } + + /// + /// Deletes a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#delete-a-pending-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + public IObservable Delete(string owner, string name, int number, long reviewId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name,nameof(name)); + + return _client.Delete(owner, name, number, reviewId).ToObservable(); + } + + /// + /// Deletes a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#delete-a-pending-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + public IObservable Delete(long repositoryId, int number, long reviewId) + { + return _client.Delete(repositoryId, number, reviewId).ToObservable(); + } + + /// + /// Dismisses a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#dismiss-a-pull-request-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + /// The message indicating why the review was dismissed + public IObservable Dismiss(string owner, string name, int number, long reviewId, PullRequestReviewDismiss dismissMessage) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name,nameof(name)); + Ensure.ArgumentNotNull(dismissMessage, nameof(dismissMessage)); + + return _client.Dismiss(owner, name, number, reviewId, dismissMessage).ToObservable(); + } + + /// + /// Dismisses a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#dismiss-a-pull-request-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + /// The message indicating why the review was dismissed + public IObservable Dismiss(long repositoryId, int number, long reviewId, PullRequestReviewDismiss dismissMessage) + { + Ensure.ArgumentNotNull(dismissMessage, nameof(dismissMessage)); + + return GetAll(repositoryId, number); + } + + /// + /// Gets reviews for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request + /// 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, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name,nameof(name)); + + return GetAll(owner, name, number, ApiOptions.None); + } + + /// + /// Gets reviews for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request + /// The Id of the repository + /// The pull request number + public IObservable GetAll(long repositoryId, int number) + { + return GetAll(repositoryId, number, ApiOptions.None); + } + + /// + /// Gets reviews for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request + /// 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, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name,nameof(name)); + Ensure.ArgumentNotNull(options, nameof(options)); + + return _connection.GetAndFlattenAllPages(ApiUrls.PullRequestReviews(owner, name, number), null, null, options); + } + + /// + /// Gets reviews for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request + /// 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, nameof(options)); + + return _connection.GetAndFlattenAllPages(ApiUrls.PullRequestReviews(repositoryId, number), null, null, options); + } + + /// + /// Gets a single pull request review by ID. + /// + /// https://developer.github.com/v3/pulls/reviews/#get-a-single-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + public IObservable Get(string owner, string name, int number, long reviewId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name,nameof(name)); + + return _client.Get(owner, name, number, reviewId).ToObservable(); + } + + /// + /// Gets a single pull request review by ID. + /// + /// https://developer.github.com/v3/pulls/reviews/#get-a-single-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + public IObservable Get(long repositoryId, int number, long reviewId) + { + return _client.Get(repositoryId, number, reviewId).ToObservable(); + } + + /// + /// Submits a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#submit-a-pull-request-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + /// The message and event being submitted for the review + public IObservable Submit(string owner, string name, int number, long reviewId, PullRequestReviewSubmit submitMessage) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name,nameof(name)); + Ensure.ArgumentNotNull(submitMessage, nameof(submitMessage)); + + return _client.Submit(owner, name, number, reviewId, submitMessage).ToObservable(); + } + + /// + /// Submits a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#submit-a-pull-request-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + /// The message and event being submitted for the review + public IObservable Submit(long repositoryId, int number, long reviewId, PullRequestReviewSubmit submitMessage) + { + Ensure.ArgumentNotNull(submitMessage, nameof(submitMessage)); + + return _client.Submit(repositoryId, number, reviewId, submitMessage).ToObservable(); + } + + /// + /// Lists comments for a single review + /// + /// https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + public IObservable GetAllComments(string owner, string name, int number, long reviewId) + { + return GetAllComments(owner, name, number, reviewId, ApiOptions.None); + } + + /// + /// Dismisses a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + public IObservable GetAllComments(long repositoryId, int number, long reviewId) + { + return GetAllComments(repositoryId, number, reviewId, ApiOptions.None); + } + + /// + /// Lists comments for a single review + /// + /// https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + public IObservable GetAllComments(string owner, string name, int number, long reviewId, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name,nameof(name)); + Ensure.ArgumentNotNull(options, nameof(options)); + + return _connection.GetAndFlattenAllPages(ApiUrls.PullRequestReviewComments(owner, name, number, reviewId), options); + } + + /// + /// Dismisses a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + public IObservable GetAllComments(long repositoryId, int number, long reviewId, ApiOptions options) + { + Ensure.ArgumentNotNull(options, nameof(options)); + + return _connection.GetAndFlattenAllPages(ApiUrls.PullRequestReviewComments(repositoryId, number, reviewId),options); + } + } +} diff --git a/Octokit.Reactive/Clients/ObservablePullRequestsClient.cs b/Octokit.Reactive/Clients/ObservablePullRequestsClient.cs index 30a3496afe..7787544c49 100644 --- a/Octokit.Reactive/Clients/ObservablePullRequestsClient.cs +++ b/Octokit.Reactive/Clients/ObservablePullRequestsClient.cs @@ -8,7 +8,7 @@ namespace Octokit.Reactive /// A client for GitHub's Pull Requests API. /// /// - /// See the Pull Requests API documentation for more information. + /// See the Pull Requests API documentation for more information. /// public class ObservablePullRequestsClient : IObservablePullRequestsClient { @@ -21,6 +21,11 @@ public class ObservablePullRequestsClient : IObservablePullRequestsClient [Obsolete("Please use ObservablePullRequestsClient.ReviewComment. This will be removed in a future version")] public IObservablePullRequestReviewCommentsClient Comment { get { return this.ReviewComment; } } + /// + /// Client for managing reviews. + /// + public IObservablePullRequestReviewsClient Review { get; private set; } + /// /// Client for managing review comments. /// @@ -37,6 +42,7 @@ public ObservablePullRequestsClient(IGitHubClient client) _client = client.Repository.PullRequest; _connection = client.Connection; + Review = new ObservablePullRequestReviewsClient(client); ReviewComment = new ObservablePullRequestReviewCommentsClient(client); ReviewRequest = new ObservablePullRequestReviewRequestsClient(client); } diff --git a/Octokit.Reactive/ObservableGitHubClient.cs b/Octokit.Reactive/ObservableGitHubClient.cs index e7d42acbd1..3745687f44 100644 --- a/Octokit.Reactive/ObservableGitHubClient.cs +++ b/Octokit.Reactive/ObservableGitHubClient.cs @@ -38,6 +38,7 @@ public ObservableGitHubClient(IGitHubClient gitHubClient) Oauth = new ObservableOauthClient(gitHubClient); Organization = new ObservableOrganizationsClient(gitHubClient); PullRequest = new ObservablePullRequestsClient(gitHubClient); + PullRequestReview = new ObservablePullRequestReviewsClient(gitHubClient); Repository = new ObservableRepositoriesClient(gitHubClient); User = new ObservableUsersClient(gitHubClient); Git = new ObservableGitDatabaseClient(gitHubClient); @@ -60,6 +61,7 @@ public IConnection Connection public IObservableOauthClient Oauth { get; private set; } public IObservableOrganizationsClient Organization { get; private set; } public IObservablePullRequestsClient PullRequest { get; private set; } + public IObservablePullRequestReviewsClient PullRequestReview { get; private set; } public IObservableRepositoriesClient Repository { get; private set; } public IObservableGistsClient Gist { get; private set; } public IObservableUsersClient User { get; private set; } diff --git a/Octokit.Tests.Integration/Clients/PullRequestReviewRequestsClientTests.cs b/Octokit.Tests.Integration/Clients/PullRequestReviewRequestsClientTests.cs index 3b638cb2b2..9d8ed3324c 100644 --- a/Octokit.Tests.Integration/Clients/PullRequestReviewRequestsClientTests.cs +++ b/Octokit.Tests.Integration/Clients/PullRequestReviewRequestsClientTests.cs @@ -44,9 +44,9 @@ public class TheGetAllMethod : PullRequestReviewRequestClientTestsBase [IntegrationTest] public async Task GetsNoRequestsWhenNoneExist() { - var pullRequestId = await CreateTheWorld(_github, _context, createReviewRequests: false); + var number = await CreateTheWorld(_github, _context, createReviewRequests: false); - var reviewRequests = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, pullRequestId); + var reviewRequests = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, number); Assert.NotNull(reviewRequests); Assert.Empty(reviewRequests); @@ -55,9 +55,9 @@ public async Task GetsNoRequestsWhenNoneExist() [IntegrationTest] public async Task GetsNoRequestsWhenNoneExistWithRepositoryId() { - var pullRequestId = await CreateTheWorld(_github, _context, createReviewRequests: false); + var number = await CreateTheWorld(_github, _context, createReviewRequests: false); - var reviewRequests = await _client.GetAll(_context.RepositoryId, pullRequestId); + var reviewRequests = await _client.GetAll(_context.RepositoryId, number); Assert.NotNull(reviewRequests); Assert.Empty(reviewRequests); @@ -66,9 +66,9 @@ public async Task GetsNoRequestsWhenNoneExistWithRepositoryId() [IntegrationTest] public async Task GetsRequests() { - var pullRequestId = await CreateTheWorld(_github, _context); + var number = await CreateTheWorld(_github, _context); - var reviewRequests = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, pullRequestId); + var reviewRequests = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, number); Assert.Equal(_collaboratorLogins, reviewRequests.Select(rr => rr.Login)); } @@ -76,9 +76,9 @@ public async Task GetsRequests() [IntegrationTest] public async Task GetsRequestsWithRepositoryId() { - var pullRequestId = await CreateTheWorld(_github, _context); + var number = await CreateTheWorld(_github, _context); - var reviewRequests = await _client.GetAll(_context.RepositoryId, pullRequestId); + var reviewRequests = await _client.GetAll(_context.RepositoryId, number); Assert.Equal(_collaboratorLogins, reviewRequests.Select(rr => rr.Login)); } @@ -86,7 +86,7 @@ public async Task GetsRequestsWithRepositoryId() [IntegrationTest] public async Task ReturnsCorrectCountOfReviewRequestsWithStart() { - var pullRequestId = await CreateTheWorld(_github, _context); + var number = await CreateTheWorld(_github, _context); var options = new ApiOptions { @@ -94,7 +94,7 @@ public async Task ReturnsCorrectCountOfReviewRequestsWithStart() PageCount = 1, StartPage = 2 }; - var reviewRequests = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, pullRequestId, options); + var reviewRequests = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, number, options); Assert.Equal(1, reviewRequests.Count); } @@ -102,7 +102,7 @@ public async Task ReturnsCorrectCountOfReviewRequestsWithStart() [IntegrationTest] public async Task ReturnsCorrectCountOfReviewRequestsWithStartWithRepositoryId() { - var pullRequestId = await CreateTheWorld(_github, _context); + var number = await CreateTheWorld(_github, _context); var options = new ApiOptions { @@ -110,7 +110,7 @@ public async Task ReturnsCorrectCountOfReviewRequestsWithStartWithRepositoryId() PageCount = 2, StartPage = 2 }; - var reviewRequests = await _client.GetAll(_context.RepositoryId, pullRequestId, options); + var reviewRequests = await _client.GetAll(_context.RepositoryId, number, options); Assert.Equal(1, reviewRequests.Count); } @@ -118,14 +118,14 @@ public async Task ReturnsCorrectCountOfReviewRequestsWithStartWithRepositoryId() [IntegrationTest] public async Task ReturnsDistinctResultsBasedOnStartPage() { - var pullRequestId = await CreateTheWorld(_github, _context); + var number = await CreateTheWorld(_github, _context); var startOptions = new ApiOptions { PageSize = 1, PageCount = 1 }; - var firstPage = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, pullRequestId, startOptions); + var firstPage = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, number, startOptions); var skipStartOptions = new ApiOptions { @@ -133,7 +133,7 @@ public async Task ReturnsDistinctResultsBasedOnStartPage() PageCount = 1, StartPage = 2 }; - var secondPage = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, pullRequestId, skipStartOptions); + var secondPage = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, number, skipStartOptions); Assert.Equal(1, firstPage.Count); Assert.Equal(1, secondPage.Count); @@ -143,14 +143,14 @@ public async Task ReturnsDistinctResultsBasedOnStartPage() [IntegrationTest] public async Task ReturnsDistinctResultsBasedOnStartPageWithRepositoryId() { - var pullRequestId = await CreateTheWorld(_github, _context); + var number = await CreateTheWorld(_github, _context); var startOptions = new ApiOptions { PageSize = 1, PageCount = 1 }; - var firstPage = await _client.GetAll(_context.RepositoryId, pullRequestId, startOptions); + var firstPage = await _client.GetAll(_context.RepositoryId, number, startOptions); var skipStartOptions = new ApiOptions { @@ -158,7 +158,7 @@ public async Task ReturnsDistinctResultsBasedOnStartPageWithRepositoryId() PageCount = 1, StartPage = 2 }; - var secondPage = await _client.GetAll(_context.RepositoryId, pullRequestId, skipStartOptions); + var secondPage = await _client.GetAll(_context.RepositoryId, number, skipStartOptions); Assert.Equal(1, firstPage.Count); Assert.Equal(1, secondPage.Count); @@ -171,12 +171,12 @@ public class TheDeleteMethod : PullRequestReviewRequestClientTestsBase [IntegrationTest] public async Task DeletesRequests() { - var pullRequestId = await CreateTheWorld(_github, _context); + var number = await CreateTheWorld(_github, _context); - var reviewRequestsBeforeDelete = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, pullRequestId); + var reviewRequestsBeforeDelete = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, number); var reviewRequestToCreate = new PullRequestReviewRequest(_collaboratorLogins); - await _client.Delete(_context.RepositoryOwner, _context.RepositoryName, pullRequestId, reviewRequestToCreate); - var reviewRequestsAfterDelete = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, pullRequestId); + await _client.Delete(_context.RepositoryOwner, _context.RepositoryName, number, reviewRequestToCreate); + var reviewRequestsAfterDelete = await _client.GetAll(_context.RepositoryOwner, _context.RepositoryName, number); Assert.NotEmpty(reviewRequestsBeforeDelete); Assert.Empty(reviewRequestsAfterDelete); @@ -185,12 +185,12 @@ public async Task DeletesRequests() [IntegrationTest] public async Task DeletesRequestsWithRepositoryId() { - var pullRequestId = await CreateTheWorld(_github, _context); + var number = await CreateTheWorld(_github, _context); - var reviewRequestsBeforeDelete = await _client.GetAll(_context.RepositoryId, pullRequestId); + var reviewRequestsBeforeDelete = await _client.GetAll(_context.RepositoryId, number); var reviewRequestToCreate = new PullRequestReviewRequest(_collaboratorLogins); - await _client.Delete(_context.RepositoryId, pullRequestId, reviewRequestToCreate); - var reviewRequestsAfterDelete = await _client.GetAll(_context.RepositoryId, pullRequestId); + await _client.Delete(_context.RepositoryId, number, reviewRequestToCreate); + var reviewRequestsAfterDelete = await _client.GetAll(_context.RepositoryId, number); Assert.NotEmpty(reviewRequestsBeforeDelete); Assert.Empty(reviewRequestsAfterDelete); @@ -202,10 +202,10 @@ public class TheCreateMethod : PullRequestReviewRequestClientTestsBase, IDisposa [IntegrationTest] public async Task CreatesRequests() { - var pullRequestId = await CreateTheWorld(_github, _context, createReviewRequests: false); + var number = await CreateTheWorld(_github, _context, createReviewRequests: false); var reviewRequestToCreate = new PullRequestReviewRequest(_collaboratorLogins); - var pr = await _client.Create(_context.RepositoryOwner, _context.RepositoryName, pullRequestId, reviewRequestToCreate); + var pr = await _client.Create(_context.RepositoryOwner, _context.RepositoryName, number, reviewRequestToCreate); Assert.Equal(_collaboratorLogins.ToList(), pr.RequestedReviewers.Select(rr => rr.Login)); } @@ -213,10 +213,10 @@ public async Task CreatesRequests() [IntegrationTest] public async Task CreatesRequestsWithRepositoryId() { - var pullRequestId = await CreateTheWorld(_github, _context, createReviewRequests: false); + var number = await CreateTheWorld(_github, _context, createReviewRequests: false); var reviewRequestToCreate = new PullRequestReviewRequest(_collaboratorLogins); - var pr = await _client.Create(_context.RepositoryId, pullRequestId, reviewRequestToCreate); + var pr = await _client.Create(_context.RepositoryId, number, reviewRequestToCreate); Assert.Equal(_collaboratorLogins.ToList(), pr.RequestedReviewers.Select(rr => rr.Login)); } diff --git a/Octokit.Tests.Integration/Clients/PullRequestReviewsClientTests.cs b/Octokit.Tests.Integration/Clients/PullRequestReviewsClientTests.cs new file mode 100644 index 0000000000..8505687cbd --- /dev/null +++ b/Octokit.Tests.Integration/Clients/PullRequestReviewsClientTests.cs @@ -0,0 +1,691 @@ +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 PullRequestReviewsClientTests +{ + public class TheGetAllMethod + { + private readonly IGitHubClient _github; + + public TheGetAllMethod() + { + _github = Helper.GetAuthenticatedClient(); + } + + [IntegrationTest] + public async Task GetsAllReviews() + { + var reviews = await _github.PullRequest.Review.GetAll("octokit", "octokit.net", 1648); + + Assert.NotNull(reviews); + Assert.NotEmpty(reviews); + Assert.True(reviews.Count > 1); + Assert.False(string.IsNullOrEmpty(reviews[0].Body)); + Assert.False(string.IsNullOrEmpty(reviews[0].CommitId)); + Assert.False(string.IsNullOrEmpty(reviews[0].User.Login)); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfReviewsWithoutStart() + { + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1 + }; + + var reviews = await _github.PullRequest.Review.GetAll("octokit", "octokit.net", 1648, options); + + Assert.Equal(1, reviews.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfReviewsWithStart() + { + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + var reviews = await _github.PullRequest.Review.GetAll("octokit", "octokit.net", 1648, options); + + Assert.Equal(1, reviews.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctReviewsBasedOnStartPage() + { + var startOptions = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + var firstPage = await _github.PullRequest.Review.GetAll("octokit", "octokit.net", 1648, startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _github.PullRequest.Review.GetAll("octokit", "octokit.net", 1648, skipStartOptions); + + Assert.Equal(1, firstPage.Count); + Assert.Equal(1, secondPage.Count); + Assert.NotEqual(firstPage.First().Id, secondPage.First().Id); + } + } + + public class TheGetMethod + { + private readonly IGitHubClient _github; + + public TheGetMethod() + { + _github = Helper.GetAuthenticatedClient(); + } + + [IntegrationTest] + public async Task GetsReview() + { + var review = await _github.PullRequest.Review.Get("octokit", "octokit.net", 1648, 54646850); + + Assert.NotNull(review); + Assert.False(string.IsNullOrEmpty(review.Body)); + Assert.False(string.IsNullOrEmpty(review.CommitId)); + Assert.False(string.IsNullOrEmpty(review.User.Login)); + } + } + + public class TheCreateMethod + { + private readonly IGitHubClient _github; + private readonly IPullRequestReviewsClient _client; + + private readonly IGitHubClient _github2; + + public TheCreateMethod() + { + _github = Helper.GetAuthenticatedClient(); + _client = _github.PullRequest.Review; + + _github2 = Helper.GetAuthenticatedClient(true); + } + + [DualAccountTest] + public async Task CanCreatePendingReview() + { + using (var context = await _github.CreateRepositoryContext("test-repo")) + { + await _github.CreateTheWorld(context.Repository); + var pullRequest = await _github2.CreatePullRequest(context.Repository); + + var body = "A review comment message"; + + var review = new PullRequestReviewCreate() + { + CommitId = pullRequest.Head.Sha, + Body = body, + Comments = new List + { + new DraftPullRequestReviewComment("comment 1", "README.md", 1), + new DraftPullRequestReviewComment("comment 2", "README.md", 2) + } + }; + + var createdReview = await _client.Create(context.RepositoryOwner, context.RepositoryName, pullRequest.Number, review); + + Assert.NotNull(createdReview); + Assert.Equal(body, createdReview.Body); + Assert.Equal(PullRequestReviewState.Pending, createdReview.State); + Assert.Equal(pullRequest.Head.Sha, createdReview.CommitId); + } + } + + [DualAccountTest] + public async Task CanCreatePendingReviewWithRepositoryId() + { + using (var context = await _github.CreateRepositoryContext("test-repo")) + { + await _github.CreateTheWorld(context.Repository); + var pullRequest = await _github2.CreatePullRequest(context.Repository); + + const string body = "A review comment message"; + + var review = new PullRequestReviewCreate() + { + CommitId = pullRequest.Head.Sha, + Body = body, + Comments = new List + { + new DraftPullRequestReviewComment("comment 1", "README.md", 1), + new DraftPullRequestReviewComment("comment 2", "README.md", 2) + } + }; + + var createdReview = await _client.Create(context.RepositoryId, pullRequest.Number, review); + + Assert.NotNull(createdReview); + Assert.Equal(body, createdReview.Body); + Assert.Equal(PullRequestReviewState.Pending, createdReview.State); + Assert.Equal(pullRequest.Head.Sha, createdReview.CommitId); + } + } + + [DualAccountTest] + public async Task CanCreateCommentedReview() + { + using (var context = await _github.CreateRepositoryContext("test-repo")) + { + await _github.CreateTheWorld(context.Repository); + var pullRequest = await _github2.CreatePullRequest(context.Repository); + + var body = "A review comment message"; + + var review = new PullRequestReviewCreate() + { + CommitId = pullRequest.Head.Sha, + Body = body, + Event = PullRequestReviewEvent.Comment, + Comments = new List + { + new DraftPullRequestReviewComment("comment 1", "README.md", 1), + new DraftPullRequestReviewComment("comment 2", "README.md", 2) + } + }; + + var createdReview = await _client.Create(context.RepositoryOwner, context.RepositoryName, pullRequest.Number, review); + + Assert.NotNull(createdReview); + Assert.Equal(body, createdReview.Body); + Assert.Equal(PullRequestReviewState.Commented, createdReview.State); + Assert.Equal(pullRequest.Head.Sha, createdReview.CommitId); + } + } + + [DualAccountTest] + public async Task CanCreateCommentedReviewWithRepositoryId() + { + using (var context = await _github.CreateRepositoryContext("test-repo")) + { + await _github.CreateTheWorld(context.Repository); + var pullRequest = await _github2.CreatePullRequest(context.Repository); + + const string body = "A review comment message"; + + var review = new PullRequestReviewCreate() + { + CommitId = pullRequest.Head.Sha, + Body = body, + Event = PullRequestReviewEvent.Comment, + Comments = new List + { + new DraftPullRequestReviewComment("comment 1", "README.md", 1), + new DraftPullRequestReviewComment("comment 2", "README.md", 2) + } + }; + + var createdReview = await _client.Create(context.RepositoryId, pullRequest.Number, review); + + Assert.NotNull(createdReview); + Assert.Equal(body, createdReview.Body); + Assert.Equal(PullRequestReviewState.Commented, createdReview.State); + Assert.Equal(pullRequest.Head.Sha, createdReview.CommitId); + } + } + + [DualAccountTest] + public async Task CanCreateChangesRequestedReview() + { + using (var context = await _github.CreateRepositoryContext("test-repo")) + { + await _github.CreateTheWorld(context.Repository); + var pullRequest = await _github2.CreatePullRequest(context.Repository); + + var body = "A review comment message"; + + var review = new PullRequestReviewCreate() + { + CommitId = pullRequest.Head.Sha, + Body = body, + Event = PullRequestReviewEvent.RequestChanges, + Comments = new List + { + new DraftPullRequestReviewComment("comment 1", "README.md", 1), + new DraftPullRequestReviewComment("comment 2", "README.md", 2) + } + }; + + var createdReview = await _client.Create(context.RepositoryOwner, context.RepositoryName, pullRequest.Number, review); + + Assert.NotNull(createdReview); + Assert.Equal(body, createdReview.Body); + Assert.Equal(PullRequestReviewState.ChangesRequested, createdReview.State); + Assert.Equal(pullRequest.Head.Sha, createdReview.CommitId); + } + } + + [DualAccountTest] + public async Task CanCreateChangesRequestedReviewWithRepositoryId() + { + using (var context = await _github.CreateRepositoryContext("test-repo")) + { + await _github.CreateTheWorld(context.Repository); + var pullRequest = await _github2.CreatePullRequest(context.Repository); + + const string body = "A review comment message"; + + var review = new PullRequestReviewCreate() + { + CommitId = pullRequest.Head.Sha, + Body = body, + Event = PullRequestReviewEvent.RequestChanges, + Comments = new List + { + new DraftPullRequestReviewComment("comment 1", "README.md", 1), + new DraftPullRequestReviewComment("comment 2", "README.md", 2) + } + }; + + var createdReview = await _client.Create(context.RepositoryId, pullRequest.Number, review); + + Assert.NotNull(createdReview); + Assert.Equal(body, createdReview.Body); + Assert.Equal(PullRequestReviewState.ChangesRequested, createdReview.State); + Assert.Equal(pullRequest.Head.Sha, createdReview.CommitId); + } + } + + [DualAccountTest] + public async Task CanCreateApprovedReview() + { + using (var context = await _github.CreateRepositoryContext("test-repo")) + { + await _github.CreateTheWorld(context.Repository); + var pullRequest = await _github2.CreatePullRequest(context.Repository); + + var body = "A review comment message"; + + var review = new PullRequestReviewCreate() + { + CommitId = pullRequest.Head.Sha, + Body = body, + Event = PullRequestReviewEvent.Approve, + Comments = new List + { + new DraftPullRequestReviewComment("comment 1", "README.md", 1), + new DraftPullRequestReviewComment("comment 2", "README.md", 2) + } + }; + + var createdReview = await _client.Create(context.RepositoryOwner, context.RepositoryName, pullRequest.Number, review); + + Assert.NotNull(createdReview); + Assert.Equal(body, createdReview.Body); + Assert.Equal(PullRequestReviewState.Approved, createdReview.State); + Assert.Equal(pullRequest.Head.Sha, createdReview.CommitId); + } + } + + [DualAccountTest] + public async Task CanCreateApprovedReviewWithRepositoryId() + { + using (var context = await _github.CreateRepositoryContext("test-repo")) + { + await _github.CreateTheWorld(context.Repository); + var pullRequest = await _github2.CreatePullRequest(context.Repository); + + const string body = "A review comment message"; + + var review = new PullRequestReviewCreate() + { + CommitId = pullRequest.Head.Sha, + Body = body, + Event = PullRequestReviewEvent.Approve, + Comments = new List + { + new DraftPullRequestReviewComment("comment 1", "README.md", 1), + new DraftPullRequestReviewComment("comment 2", "README.md", 2) + } + }; + + var createdReview = await _client.Create(context.RepositoryId, pullRequest.Number, review); + + Assert.NotNull(createdReview); + Assert.Equal(body, createdReview.Body); + Assert.Equal(PullRequestReviewState.Approved, createdReview.State); + Assert.Equal(pullRequest.Head.Sha, createdReview.CommitId); + } + } + } + + public class TheDeleteMethod + { + private readonly IGitHubClient _github; + private readonly IPullRequestReviewsClient _client; + + private readonly IGitHubClient _github2; + + public TheDeleteMethod() + { + _github = Helper.GetAuthenticatedClient(); + _client = _github.PullRequest.Review; + + _github2 = Helper.GetAuthenticatedClient(true); + } + + [DualAccountTest] + public async Task CanDeleteReview() + { + using (var context = await _github.CreateRepositoryContext("test-repo")) + { + await _github.CreateTheWorld(context.Repository); + var pullRequest = await _github2.CreatePullRequest(context.Repository); + var createdReview = await _github.CreatePullRequestReview(context.Repository, pullRequest.Number, "A pending review"); + + await _client.Delete(context.RepositoryOwner, context.RepositoryName, pullRequest.Number, createdReview.Id); + + var retrievedReviews = await _client.GetAll(context.RepositoryOwner, context.RepositoryName, pullRequest.Number); + + Assert.False(retrievedReviews.Any(x => x.Id == createdReview.Id)); + } + } + + [DualAccountTest] + public async Task CanDeleteReviewWithRepositoryId() + { + using (var context = await _github.CreateRepositoryContext("test-repo")) + { + await _github.CreateTheWorld(context.Repository); + var pullRequest = await _github2.CreatePullRequest(context.Repository); + var createdReview = await _github.CreatePullRequestReview(context.Repository, pullRequest.Number, "A pending review"); + + await _client.Delete(context.RepositoryId, pullRequest.Number, createdReview.Id); + + var retrievedReviews = await _client.GetAll(context.RepositoryId, pullRequest.Number); + + Assert.False(retrievedReviews.Any(x => x.Id == createdReview.Id)); + } + } + } + + public class TheDismissMethod + { + private readonly IGitHubClient _github; + private readonly IPullRequestReviewsClient _client; + + private readonly IGitHubClient _github2; + + public TheDismissMethod() + { + _github = Helper.GetAuthenticatedClient(); + _client = _github.PullRequest.Review; + + _github2 = Helper.GetAuthenticatedClient(true); + } + + [DualAccountTest] + public async Task CanDismissReview() + { + using (var context = await _github.CreateRepositoryContext("test-repo")) + { + await _github.CreateTheWorld(context.Repository); + var pullRequest = await _github2.CreatePullRequest(context.Repository); + var createdReview = await _github.CreatePullRequestReview(context.Repository, pullRequest.Number, "A pending review", PullRequestReviewEvent.RequestChanges); + + var dismissedReview = await _client.Dismiss(context.RepositoryOwner, context.RepositoryName, pullRequest.Number, createdReview.Id, new PullRequestReviewDismiss { Message = "No soup for you!" }); + + Assert.Equal(PullRequestReviewState.Dismissed, dismissedReview.State); + } + } + + [DualAccountTest] + public async Task CanDismissReviewWithRepositoryId() + { + using (var context = await _github.CreateRepositoryContext("test-repo")) + { + await _github.CreateTheWorld(context.Repository); + var pullRequest = await _github2.CreatePullRequest(context.Repository); + var createdReview = await _github.CreatePullRequestReview(context.Repository, pullRequest.Number, "A pending review", PullRequestReviewEvent.RequestChanges); + + var dismissedReview = await _client.Dismiss(context.RepositoryId, pullRequest.Number, createdReview.Id, new PullRequestReviewDismiss { Message = "No soup for you!" }); + + Assert.Equal(PullRequestReviewState.Dismissed, dismissedReview.State); + } + } + } + + public class TheGetAllCommentsMethod + { + private readonly IGitHubClient _github; + + public TheGetAllCommentsMethod() + { + _github = Helper.GetAuthenticatedClient(); + } + + [IntegrationTest] + public async Task GetsAllComments() + { + var comments = await _github.PullRequest.Review.GetAllComments("octokit", "octokit.net", 1648, 54646850); + + Assert.NotNull(comments); + Assert.NotEmpty(comments); + Assert.True(comments.Count > 1); + foreach (var comment in comments) + { + Assert.False(string.IsNullOrEmpty(comment.Body)); + Assert.False(string.IsNullOrEmpty(comment.CommitId)); + Assert.False(string.IsNullOrEmpty(comment.User.Login)); + } + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfCommentsWithoutStart() + { + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1 + }; + + var comments = await _github.PullRequest.Review.GetAllComments("octokit", "octokit.net", 1648, 54646850, options); + + Assert.Equal(1, comments.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfCommentsWithStart() + { + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + var comments = await _github.PullRequest.Review.GetAllComments("octokit", "octokit.net", 1648, 54646850, options); + + Assert.Equal(1, comments.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctCommentsBasedOnStartPage() + { + var startOptions = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + var firstPage = await _github.PullRequest.Review.GetAllComments("octokit", "octokit.net", 1648, 54646850, startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _github.PullRequest.Review.GetAllComments("octokit", "octokit.net", 1648, 54646850, skipStartOptions); + + Assert.Equal(1, firstPage.Count); + Assert.Equal(1, secondPage.Count); + Assert.NotEqual(firstPage.First().Id, secondPage.First().Id); + } + } + + public class TheSubmitMethod + { + private readonly IGitHubClient _github; + private readonly IPullRequestReviewsClient _client; + + private readonly IGitHubClient _github2; + + public TheSubmitMethod() + { + _github = Helper.GetAuthenticatedClient(); + _client = _github.PullRequest.Review; + + _github2 = Helper.GetAuthenticatedClient(true); + } + + [DualAccountTest] + public async Task CanSubmitCommentedReview() + { + using (var context = await _github.CreateRepositoryContext("test-repo")) + { + await _github.CreateTheWorld(context.Repository); + var pullRequest = await _github2.CreatePullRequest(context.Repository); + var createdReview = await _github.CreatePullRequestReview(context.Repository, pullRequest.Number, "A pending review"); + + var submitMessage = new PullRequestReviewSubmit + { + Body = "Roger roger!", + Event = PullRequestReviewEvent.Comment + }; + var submittedReview = await _client.Submit(context.RepositoryOwner, context.RepositoryName, pullRequest.Number, createdReview.Id, submitMessage); + + Assert.Equal("Roger roger!", submittedReview.Body); + Assert.Equal(PullRequestReviewState.Commented, submittedReview.State); + } + } + + [DualAccountTest] + public async Task CanSubmitCommentedReviewWithRepositoryId() + { + using (var context = await _github.CreateRepositoryContext("test-repo")) + { + await _github.CreateTheWorld(context.Repository); + var pullRequest = await _github2.CreatePullRequest(context.Repository); + var createdReview = await _github.CreatePullRequestReview(context.Repository, pullRequest.Number, "A pending review"); + + var submitMessage = new PullRequestReviewSubmit + { + Body = "Roger roger!", + Event = PullRequestReviewEvent.Comment + }; + var submittedReview = await _client.Submit(context.RepositoryId, pullRequest.Number, createdReview.Id, submitMessage); + + Assert.Equal("Roger roger!", submittedReview.Body); + Assert.Equal(PullRequestReviewState.Commented, submittedReview.State); + } + } + + [DualAccountTest] + public async Task CanSubmitChangesRequestedReview() + { + using (var context = await _github.CreateRepositoryContext("test-repo")) + { + await _github.CreateTheWorld(context.Repository); + var pullRequest = await _github2.CreatePullRequest(context.Repository); + var createdReview = await _github.CreatePullRequestReview(context.Repository, pullRequest.Number, "A pending review"); + + var submitMessage = new PullRequestReviewSubmit + { + Body = "Roger roger!", + Event = PullRequestReviewEvent.RequestChanges + }; + var submittedReview = await _client.Submit(context.RepositoryOwner, context.RepositoryName, pullRequest.Number, createdReview.Id, submitMessage); + + Assert.Equal("Roger roger!", submittedReview.Body); + Assert.Equal(PullRequestReviewState.ChangesRequested, submittedReview.State); + } + } + + [DualAccountTest] + public async Task CanSubmitChangesRequestedReviewWithRepositoryId() + { + using (var context = await _github.CreateRepositoryContext("test-repo")) + { + await _github.CreateTheWorld(context.Repository); + var pullRequest = await _github2.CreatePullRequest(context.Repository); + var createdReview = await _github.CreatePullRequestReview(context.Repository, pullRequest.Number, "A pending review"); + + var submitMessage = new PullRequestReviewSubmit + { + Body = "Roger roger!", + Event = PullRequestReviewEvent.RequestChanges + }; + var submittedReview = await _client.Submit(context.RepositoryId, pullRequest.Number, createdReview.Id, submitMessage); + + Assert.Equal("Roger roger!", submittedReview.Body); + Assert.Equal(PullRequestReviewState.ChangesRequested, submittedReview.State); + } + } + + [DualAccountTest] + public async Task CanSubmitApprovedReview() + { + using (var context = await _github.CreateRepositoryContext("test-repo")) + { + await _github.CreateTheWorld(context.Repository); + var pullRequest = await _github2.CreatePullRequest(context.Repository); + var createdReview = await _github.CreatePullRequestReview(context.Repository, pullRequest.Number, "A pending review"); + + var submitMessage = new PullRequestReviewSubmit + { + Body = "Roger roger!", + Event = PullRequestReviewEvent.Approve + }; + var submittedReview = await _client.Submit(context.RepositoryOwner, context.RepositoryName, pullRequest.Number, createdReview.Id, submitMessage); + + Assert.Equal("Roger roger!", submittedReview.Body); + Assert.Equal(PullRequestReviewState.Approved, submittedReview.State); + } + } + + [DualAccountTest] + public async Task CanSubmitApprovedReviewWithRepositoryId() + { + using (var context = await _github.CreateRepositoryContext("test-repo")) + { + await _github.CreateTheWorld(context.Repository); + var pullRequest = await _github2.CreatePullRequest(context.Repository); + var createdReview = await _github.CreatePullRequestReview(context.Repository, pullRequest.Number, "A pending review"); + + var submitMessage = new PullRequestReviewSubmit + { + Body = "Roger roger!", + Event = PullRequestReviewEvent.Approve + }; + var submittedReview = await _client.Submit(context.RepositoryId, pullRequest.Number, createdReview.Id, submitMessage); + + Assert.Equal("Roger roger!", submittedReview.Body); + Assert.Equal(PullRequestReviewState.Approved, submittedReview.State); + } + } + } +} \ No newline at end of file diff --git a/Octokit.Tests.Integration/Helper.cs b/Octokit.Tests.Integration/Helper.cs index 8682f78769..a18fabca8c 100644 --- a/Octokit.Tests.Integration/Helper.cs +++ b/Octokit.Tests.Integration/Helper.cs @@ -27,6 +27,18 @@ public static class Helper return new Credentials(githubUsername, githubPassword); }); + static readonly Lazy _credentialsSecondUserThunk = new Lazy(() => + { + var githubUsername = Environment.GetEnvironmentVariable("OCTOKIT_GITHUBUSERNAME_2"); + + var githubPassword = Environment.GetEnvironmentVariable("OCTOKIT_GITHUBPASSWORD_2"); + + if (githubUsername == null || githubPassword == null) + return null; + + return new Credentials(githubUsername, githubPassword); + }); + static readonly Lazy _oauthApplicationCredentials = new Lazy(() => { var applicationClientId = ClientId; @@ -78,6 +90,8 @@ static Helper() /// public static Credentials Credentials { get { return _credentialsThunk.Value; } } + public static Credentials CredentialsSecondUser { get { return _credentialsSecondUserThunk.Value; } } + public static Credentials ApplicationCredentials { get { return _oauthApplicationCredentials.Value; } } public static Credentials BasicAuthCredentials { get { return _basicAuthCredentials.Value; } } @@ -193,11 +207,11 @@ public static Stream LoadFixture(string fileName) return stream; } - public static IGitHubClient GetAuthenticatedClient() + public static IGitHubClient GetAuthenticatedClient(bool useSecondUser = false) { return new GitHubClient(new ProductHeaderValue("OctokitTests"), TargetUrl) { - Credentials = Credentials + Credentials = useSecondUser ? CredentialsSecondUser : Credentials }; } diff --git a/Octokit.Tests.Integration/Helpers/DualAccountTestAttribute.cs b/Octokit.Tests.Integration/Helpers/DualAccountTestAttribute.cs new file mode 100644 index 0000000000..9d93604da5 --- /dev/null +++ b/Octokit.Tests.Integration/Helpers/DualAccountTestAttribute.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Linq; +using Xunit; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Octokit.Tests.Integration +{ + public class DualAccountTestDiscoverer : IXunitTestCaseDiscoverer + { + readonly IMessageSink diagnosticMessageSink; + + public DualAccountTestDiscoverer(IMessageSink diagnosticMessageSink) + { + this.diagnosticMessageSink = diagnosticMessageSink; + } + + public IEnumerable Discover(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo factAttribute) + { + if (Helper.CredentialsSecondUser == null) + { + return Enumerable.Empty(); + } + + return new[] { new XunitTestCase(diagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), testMethod) }; + } + } + + [XunitTestCaseDiscoverer("Octokit.Tests.Integration.DualAccountTestDiscoverer", "Octokit.Tests.Integration")] + public class DualAccountTestAttribute : FactAttribute + { + } +} diff --git a/Octokit.Tests.Integration/Helpers/RepositorySetupHelper.cs b/Octokit.Tests.Integration/Helpers/RepositorySetupHelper.cs index 682353c299..3bfa7f4d02 100644 --- a/Octokit.Tests.Integration/Helpers/RepositorySetupHelper.cs +++ b/Octokit.Tests.Integration/Helpers/RepositorySetupHelper.cs @@ -54,11 +54,34 @@ public static async Task CreateTheWorld(this IGitHubClient client, Re await client.Git.Reference.Update(repository.Owner.Login, repository.Name, "heads/master", new ReferenceUpdate(newMaster.Sha)); // create new commit for feature branch - var featureBranchTree = await client.CreateTree(repository, new Dictionary { { "README.md", "I am overwriting this blob with something new" } }); + var featureBranchTree = await client.CreateTree(repository, new Dictionary { { "README.md", "I am overwriting this blob with something new\nand a second line too" } }); var featureBranchCommit = await client.CreateCommit(repository, "this is the commit to merge into the pull request", featureBranchTree.Sha, newMaster.Sha); // create branch return await client.Git.Reference.Create(repository.Owner.Login, repository.Name, new NewReference("refs/heads/my-branch", featureBranchCommit.Sha)); } + + public static async Task CreatePullRequest(this IGitHubClient client, Repository repository, string branch = "my-branch") + { + var pullRequest = new NewPullRequest("Nice title for the pull request", branch, "master"); + var createdPullRequest = await client.PullRequest.Create(repository.Owner.Login, repository.Name, pullRequest); + + return createdPullRequest; + } + + public static async Task CreatePullRequestReview(this IGitHubClient client, Repository repository, int number, string body, PullRequestReviewEvent? @event = null, string commitId = null, List comments = null) + { + var review = new PullRequestReviewCreate() + { + CommitId = commitId, + Body = body, + Event = @event, + Comments = comments + }; + + var createdReview = await client.PullRequest.Review.Create(repository.Owner.Login, repository.Name, number, review); + + return createdReview; + } } } diff --git a/Octokit.Tests/Clients/PullRequestReviewsClientTests.cs b/Octokit.Tests/Clients/PullRequestReviewsClientTests.cs new file mode 100644 index 0000000000..ad82d10734 --- /dev/null +++ b/Octokit.Tests/Clients/PullRequestReviewsClientTests.cs @@ -0,0 +1,464 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using NSubstitute; +using Octokit; +using Octokit.Tests; +using Xunit; + +public class PullRequestReviewsClientTests +{ + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new PullRequestReviewCommentsClient(null)); + } + + [Fact] + public void PullRequestReviewCreateEnsuresArgumentsValue() + { + string body = "body"; + string path = "path"; + PullRequestReviewEvent evt = PullRequestReviewEvent.Approve; + int position = 1; + + var comment = new DraftPullRequestReviewComment(body, path, position); + + var review = new PullRequestReviewCreate() + { + Body = body, + Event = evt + }; + + review.Comments.Add(comment); + + + Assert.Equal(body, review.Body); + Assert.Equal(evt, review.Event); + Assert.NotEmpty(review.Comments); + } + } + + public class TheGetAllMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + await client.GetAll("owner", "name", 7); + + connection.Received().GetAll(Arg.Is(u => u.ToString() == "repos/owner/name/pulls/7/reviews"), null, Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlWithRepositoryId() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + await client.GetAll(1, 7); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "repositories/1/pulls/7/reviews"), Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlWithApiOptions() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(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/reviews"), null, options); + } + + [Fact] + public async Task RequestsCorrectUrlWithApiOptionsWithRepositoryId() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + var options = new ApiOptions + { + StartPage = 1, + PageCount = 1, + PageSize = 1 + }; + + await client.GetAll(1, 7, options); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "repositories/1/pulls/7/reviews"), options); + } + + [Fact] + public async Task EnsuresNotNullArguments() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(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(1, 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)); + } + } + + public class TheGetMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + client.Get("owner", "name", 53, 2); + + connection.Received().Get( + Arg.Is(u => u.ToString() == "repos/owner/name/pulls/53/reviews/2")); + } + + [Fact] + public void RequestsCorrectUrlWithRepositoryId() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + client.Get(1, 53, 2); + + connection.Received().Get(Arg.Is(u => u.ToString() == "repositories/1/pulls/53/reviews/2")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new PullRequestReviewsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.Get(null, "name", 1, 1)); + await Assert.ThrowsAsync(() => client.Get("owner", null, 1, 1)); + + await Assert.ThrowsAsync(() => client.Get("", "name", 1, 1)); + await Assert.ThrowsAsync(() => client.Get("owner", "", 1, 1)); + } + } + + public class TheCreateMethod + { + [Fact] + public void PostsToCorrectUrl() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + var comment = new DraftPullRequestReviewComment("Comment content", "file.css", 7); + + var review = new PullRequestReviewCreate() + { + CommitId = "commit", + Body = "body", + Event = PullRequestReviewEvent.Approve + }; + review.Comments.Add(comment); + + client.Create("fakeOwner", "fakeRepoName", 13, review); + + connection.Received().Post(Arg.Is(u => u.ToString() == "repos/fakeOwner/fakeRepoName/pulls/13/reviews"), + review, null, null); + } + + [Fact] + public void PostsToCorrectUrlWithRepositoryId() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + var comment = new DraftPullRequestReviewComment("Comment content", "file.css", 7); + + var review = new PullRequestReviewCreate() + { + CommitId = "commit", + Body = "body", + Event = PullRequestReviewEvent.Approve + }; + review.Comments.Add(comment); + + client.Create(1, 13, review); + + connection.Received().Post(Arg.Is(u => u.ToString() == "repositories/1/pulls/13/reviews"), + review, null, null); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + string body = "Comment content"; + string path = "file.css"; + int position = 7; + + var comment = new DraftPullRequestReviewComment(body, path, position); + + var review = new PullRequestReviewCreate() + { + CommitId = "commit", + Body = "body", + Event = PullRequestReviewEvent.Approve + }; + + review.Comments.Add(comment); + + await Assert.ThrowsAsync(() => client.Create(null, "fakeRepoName", 1, review)); + await Assert.ThrowsAsync(() => client.Create("fakeOwner", null, 1, review)); + await Assert.ThrowsAsync(() => client.Create("fakeOwner", "fakeRepoName", 1, null)); + + await Assert.ThrowsAsync(() => client.Create("", "fakeRepoName", 1, review)); + await Assert.ThrowsAsync(() => client.Create("fakeOwner", "", 1, review)); + } + } + + public class TheDeleteMethod + { + [Fact] + public async Task PostsToCorrectUrl() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + await client.Delete("owner", "name", 13, 13); + + connection.Received().Delete(Arg.Is(u => u.ToString() == "repos/owner/name/pulls/13/reviews/13")); + } + + [Fact] + public async Task PostsToCorrectUrlWithRepositoryId() + { + + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + await client.Delete(1, 13, 13); + + connection.Received().Delete(Arg.Is(u => u.ToString() == "repositories/1/pulls/13/reviews/13")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + await Assert.ThrowsAsync(() => client.Delete(null, "name", 1, 1)); + await Assert.ThrowsAsync(() => client.Delete("owner", null, 1, 1)); + + await Assert.ThrowsAsync(() => client.Delete("", "name", 1, 1)); + await Assert.ThrowsAsync(() => client.Delete("owner", "", 1, 1)); + } + } + + public class TheDismissMethod + { + [Fact] + public async Task PostsToCorrectUrl() + { + + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + var dismissMessage = new PullRequestReviewDismiss() + { + Message = "test message" + }; + await client.Dismiss("owner", "name", 13, 13, dismissMessage); + + connection.Received().Put(Arg.Is(u => u.ToString() == "repos/owner/name/pulls/13/reviews/13/dismissals"), dismissMessage); + } + + [Fact] + public async Task PostsToCorrectUrlWithRepositoryId() + { + + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + var dismissMessage = new PullRequestReviewDismiss() + { + Message = "test message" + }; + await client.Dismiss(1, 13, 13, dismissMessage); + + connection.Received().Put(Arg.Is(u => u.ToString() == "repositories/1/pulls/13/reviews/13/dismissals"), dismissMessage); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + var dismissMessage = new PullRequestReviewDismiss() + { + Message = "test message" + }; + + await Assert.ThrowsAsync(() => client.Dismiss(null, "name", 1, 1, dismissMessage)); + await Assert.ThrowsAsync(() => client.Dismiss("owner", null, 1, 1, dismissMessage)); + await Assert.ThrowsAsync(() => client.Dismiss("owner", "name", 1, 1, null)); + + await Assert.ThrowsAsync(() => client.Dismiss("", "name", 1, 1, dismissMessage)); + await Assert.ThrowsAsync(() => client.Dismiss("owner", "", 1, 1, dismissMessage)); + } + } + + public class TheGetAllCommentsMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + await client.GetAllComments("owner", "name", 13, 13); + + connection.Received().GetAll(Arg.Is(u => u.ToString() == "repos/owner/name/pulls/13/reviews/13/comments"), null, Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlWithRepositoryId() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + await client.GetAllComments(1, 13, 13); + + connection.Received().GetAll(Arg.Is(u => u.ToString() == "repositories/1/pulls/13/reviews/13/comments"), null, Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlWithApiOptions() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + var options = new ApiOptions + { + StartPage = 1, + PageCount = 1, + PageSize = 1 + }; + + await client.GetAllComments("owner", "name", 13, 13, options); + + connection.Received().GetAll(Arg.Is(u => u.ToString() == "repos/owner/name/pulls/13/reviews/13/comments"), null, options); + } + + [Fact] + public async Task RequestsCorrectUrlWithApiOptionsWithRepositoryId() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + var options = new ApiOptions + { + StartPage = 1, + PageCount = 1, + PageSize = 1 + }; + + await client.GetAllComments(1, 13, 13, options); + + connection.Received().GetAll(Arg.Is(u => u.ToString() == "repositories/1/pulls/13/reviews/13/comments"), null, Args.ApiOptions); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + await Assert.ThrowsAsync(() => client.GetAllComments(null, "name", 1, 1)); + await Assert.ThrowsAsync(() => client.GetAllComments("owner", null, 1, 1)); + + await Assert.ThrowsAsync(() => client.GetAllComments("", "name", 1, 1)); + await Assert.ThrowsAsync(() => client.GetAllComments("owner", "", 1, 1)); + } + } + + public class TheSubmitMethod + { + [Fact] + public async Task PostsToCorrectUrl() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + var submitMessage = new PullRequestReviewSubmit() + { + Body = "string", + Event = PullRequestReviewEvent.Approve + }; + await client.Submit("owner", "name", 13, 13, submitMessage); + + connection.Received().Post(Arg.Is(u => u.ToString() == "repos/owner/name/pulls/13/reviews/13/events"), submitMessage, null, null); + } + + [Fact] + public async Task PostsToCorrectUrlWithRepositoryId() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + var submitMessage = new PullRequestReviewSubmit() + { + Body = "string", + Event = PullRequestReviewEvent.Approve + }; + await client.Submit(1, 13, 13, submitMessage); + + connection.Received().Post(Arg.Is(u => u.ToString() == "repositories/1/pulls/13/reviews/13/events"), submitMessage, null, null); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new PullRequestReviewsClient(connection); + + var submitMessage = new PullRequestReviewSubmit() + { + Body = "string", + Event = PullRequestReviewEvent.Approve + }; + + await Assert.ThrowsAsync(() => client.Submit(null, "name", 1, 1, submitMessage)); + await Assert.ThrowsAsync(() => client.Submit("owner", null, 1, 1, submitMessage)); + await Assert.ThrowsAsync(() => client.Submit("owner", "name", 1, 1, null)); + + await Assert.ThrowsAsync(() => client.Submit("", "name", 1, 1, submitMessage)); + await Assert.ThrowsAsync(() => client.Submit("owner", "", 1, 1, submitMessage)); + } + } +} diff --git a/Octokit.Tests/Reactive/ObservablePullRequestReviewCommentsClientTests.cs b/Octokit.Tests/Reactive/ObservablePullRequestReviewCommentsClientTests.cs index 4afdee43b4..9266da83e6 100644 --- a/Octokit.Tests/Reactive/ObservablePullRequestReviewCommentsClientTests.cs +++ b/Octokit.Tests/Reactive/ObservablePullRequestReviewCommentsClientTests.cs @@ -179,8 +179,6 @@ public async Task RequestsCorrectUrlMultiWithRepositoryId() } ); - var acceptHeader = "application/vnd.github.squirrel-girl-preview"; - var gitHubClient = Substitute.For(); gitHubClient.Connection.Get>(firstPageUrl, Args.EmptyDictionary, null) .Returns(Task.Factory.StartNew>>(() => firstPageResponse)); diff --git a/Octokit.Tests/Reactive/ObservablePullRequestReviewsClientTests.cs b/Octokit.Tests/Reactive/ObservablePullRequestReviewsClientTests.cs new file mode 100644 index 0000000000..8f327776e4 --- /dev/null +++ b/Octokit.Tests/Reactive/ObservablePullRequestReviewsClientTests.cs @@ -0,0 +1,679 @@ +using System; +using System.Collections.Generic; +using System.Reactive.Linq; +using System.Threading.Tasks; +using NSubstitute; +using Octokit.Internal; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservablePullRequestReviewsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws( + () => new ObservablePullRequestReviewsClient(null)); + } + } + + static IResponse CreateResponseWithApiInfo(IDictionary links) + { + var response = Substitute.For(); + response.ApiInfo.Returns(new ApiInfo(links, new List(), new List(), "etag", new RateLimit(new Dictionary()))); + return response; + } + + public class TheGetAllMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + client.GetAll("fake", "repo", 1); + + gitHubClient.Received().PullRequest.Review.GetAll("fake", "repo", 1); + } + + [Fact] + public void RequestsCorrectUrlWithRepositoryId() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + client.GetAll(1, 1); + + gitHubClient.Received().PullRequest.Review.GetAll(1, 1); + } + + [Fact] + public void RequestsCorrectUrlWithApiOptions() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + client.GetAll("fake", "repo", 1, options); + + gitHubClient.Received().PullRequest.Review.GetAll("fake", "repo", 1, options); + } + + [Fact] + public void RequestsCorrectUrlWithApiOptionsWithRepositoryId() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + client.GetAll(1, 1, options); + + gitHubClient.Received().PullRequest.Review.GetAll(1, 1, options); + } + + [Fact] + public async Task RequestsCorrectUrlMulti() + { + + var firstPageUrl = new Uri("repos/owner/name/pulls/7/reviews", UriKind.Relative); + var secondPageUrl = new Uri("https://example.com/page/2"); + var firstPageLinks = new Dictionary { { "next", secondPageUrl } }; + var firstPageResponse = new ApiResponse> + ( + CreateResponseWithApiInfo(firstPageLinks), + new List + { + new PullRequestReview(1), + new PullRequestReview(2), + new PullRequestReview(3) + }); + var thirdPageUrl = new Uri("https://example.com/page/3"); + var secondPageLinks = new Dictionary { { "next", thirdPageUrl } }; + var secondPageResponse = new ApiResponse> + ( + CreateResponseWithApiInfo(secondPageLinks), + new List + { + new PullRequestReview(4), + new PullRequestReview(5), + new PullRequestReview(6) + } + ); + var lastPageResponse = new ApiResponse> + ( + new Response(), + new List + { + new PullRequestReview(7) + } + ); + + var gitHubClient = Substitute.For(); + + gitHubClient.Connection.Get>(firstPageUrl, Args.EmptyDictionary, null) + .Returns(Task.Factory.StartNew>>(() => firstPageResponse)); + gitHubClient.Connection.Get>(secondPageUrl, Args.EmptyDictionary, null) + .Returns(Task.Factory.StartNew>>(() => secondPageResponse)); + gitHubClient.Connection.Get>(thirdPageUrl, Args.EmptyDictionary, null) + .Returns(Task.Factory.StartNew>>(() => lastPageResponse)); + + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + var results = await client.GetAll("owner", "name", 7).ToArray(); + + Assert.Equal(7, results.Length); + gitHubClient.Connection.Received(1).Get>(firstPageUrl, Args.EmptyDictionary, null); + gitHubClient.Connection.Received(1).Get>(secondPageUrl, Args.EmptyDictionary, null); + gitHubClient.Connection.Received(1).Get>(thirdPageUrl, Args.EmptyDictionary, null); + } + + [Fact] + public async Task RequestsCorrectUrlMultiWithRepositoryId() + { + var firstPageUrl = new Uri("repositories/1/pulls/7/reviews", UriKind.Relative); + var secondPageUrl = new Uri("https://example.com/page/2"); + var firstPageLinks = new Dictionary { { "next", secondPageUrl } }; + var firstPageResponse = new ApiResponse> + ( + CreateResponseWithApiInfo(firstPageLinks), + new List + { + new PullRequestReview(1), + new PullRequestReview(2), + new PullRequestReview(3) + }); + var thirdPageUrl = new Uri("https://example.com/page/3"); + var secondPageLinks = new Dictionary { { "next", thirdPageUrl } }; + var secondPageResponse = new ApiResponse> + ( + CreateResponseWithApiInfo(secondPageLinks), + new List + { + new PullRequestReview(4), + new PullRequestReview(5), + new PullRequestReview(6) + } + ); + var lastPageResponse = new ApiResponse> + ( + new Response(), + new List + { + new PullRequestReview(7) + } + ); + + var gitHubClient = Substitute.For(); + + gitHubClient.Connection.Get>(firstPageUrl, Args.EmptyDictionary, null) + .Returns(Task.Factory.StartNew>>(() => firstPageResponse)); + gitHubClient.Connection.Get>(secondPageUrl, Args.EmptyDictionary, null) + .Returns(Task.Factory.StartNew>>(() => secondPageResponse)); + gitHubClient.Connection.Get>(thirdPageUrl, Args.EmptyDictionary, null) + .Returns(Task.Factory.StartNew>>(() => lastPageResponse)); + + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + var results = await client.GetAll(1, 7).ToArray(); + + Assert.Equal(7, results.Length); + gitHubClient.Connection.Received(1).Get>(firstPageUrl, Args.EmptyDictionary, null); + gitHubClient.Connection.Received(1).Get>(secondPageUrl, Args.EmptyDictionary, null); + gitHubClient.Connection.Received(1).Get>(thirdPageUrl, Args.EmptyDictionary, null); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(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(1, 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)); + + } + } + + public class TheGetMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + client.Get("owner", "name", 53, 2); + + gitHubClient.Received().PullRequest.Review.Get("owner", "name", 53, 2); + } + + [Fact] + public void RequestsCorrectUrlWithRepositoryId() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + client.Get(1, 53, 2); + + gitHubClient.Received().PullRequest.Review.Get(1, 53, 2); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservablePullRequestReviewsClient(Substitute.For()); + + Assert.Throws(() => client.Get(null, "name", 1, 1)); + Assert.Throws(() => client.Get("owner", null, 1, 1)); + + Assert.Throws(() => client.Get("", "name", 1, 1)); + Assert.Throws(() => client.Get("owner", "", 1, 1)); + } + } + + public class TheCreateMethod + { + [Fact] + public void PostsToCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + var comment = new DraftPullRequestReviewComment("Comment content", "file.css", 7); + var review = new PullRequestReviewCreate() + { + CommitId = "commit", + Body = "body", + Event = PullRequestReviewEvent.Approve + }; + + review.Comments.Add(comment); + + client.Create("owner", "name", 53, review); + + gitHubClient.Received().PullRequest.Review.Create("owner", "name", 53, review); + } + + [Fact] + public void PostsToCorrectUrlWithRepositoryId() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + var comment = new DraftPullRequestReviewComment("Comment content", "file.css", 7); + var review = new PullRequestReviewCreate() + { + CommitId = "commit", + Body = "body", + Event = PullRequestReviewEvent.Approve + }; + + review.Comments.Add(comment); + + client.Create(1, 13, review); + + gitHubClient.Received().PullRequest.Review.Create(1, 53, review); + } + + [Fact] + public void EnsuresNonNullArguments() + { + var client = new ObservablePullRequestReviewsClient(Substitute.For()); + + string body = "Comment content"; + string path = "file.css"; + int position = 7; + + var comment = new DraftPullRequestReviewComment(body, path, position); + + var review = new PullRequestReviewCreate() + { + CommitId = "commit", + Body = "body", + Event = PullRequestReviewEvent.Approve + }; + + review.Comments.Add(comment); + + Assert.Throws(() => client.Create(null, "fakeRepoName", 1, review)); + Assert.Throws(() => client.Create("fakeOwner", null, 1, review)); + Assert.Throws(() => client.Create("fakeOwner", "fakeRepoName", 1, null)); + + Assert.Throws(() => client.Create("", "fakeRepoName", 1, review)); + Assert.Throws(() => client.Create("fakeOwner", "", 1, review)); + } + } + + public class TheDeleteMethod + { + [Fact] + public void PostsToCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + client.Delete("owner", "name", 13, 13); + + gitHubClient.Received().PullRequest.Review.Delete("owner", "name", 13, 13); + } + + [Fact] + public void PostsToCorrectUrlWithRepositoryId() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + client.Delete(1, 13, 13); + + gitHubClient.Received().PullRequest.Review.Delete(1, 13, 13); + } + + [Fact] + public void EnsuresNonNullArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + Assert.Throws(() => client.Delete(null, "name", 1, 1)); + Assert.Throws(() => client.Delete("owner", null, 1, 1)); + + Assert.Throws(() => client.Delete("", "name", 1, 1)); + Assert.Throws(() => client.Delete("owner", "", 1, 1)); + } + } + + public class TheDismissMethod + { + [Fact] + public void PostsToCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + var dismissMessage = new PullRequestReviewDismiss() + { + Message = "test message" + }; + + client.Dismiss("owner", "name", 13, 13, dismissMessage); + + gitHubClient.Received().PullRequest.Review.Dismiss("owner", "name", 13, 13, dismissMessage); + } + + [Fact] + public void PostsToCorrectUrlWithRepositoryId() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + var dismissMessage = new PullRequestReviewDismiss() + { + Message = "test message" + }; + + client.Dismiss(1, 13, 13, dismissMessage); + + gitHubClient.Received().PullRequest.Review.Dismiss(1, 13, 13, dismissMessage); + } + + [Fact] + public void EnsuresNonNullArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + var dismissMessage = new PullRequestReviewDismiss() + { + Message = "test message" + }; + + Assert.Throws(() => client.Dismiss(null, "name", 1, 1, dismissMessage)); + Assert.Throws(() => client.Dismiss("owner", null, 1, 1, dismissMessage)); + Assert.Throws(() => client.Dismiss("owner", "name", 1, 1, null)); + + Assert.Throws(() => client.Dismiss("", "name", 1, 1, dismissMessage)); + Assert.Throws(() => client.Dismiss("owner", "", 1, 1, dismissMessage)); + } + } + + public class TheGetAllCommentsMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + client.GetAllComments("fake", "repo", 1, 1); + + gitHubClient.Received().PullRequest.Review.GetAllComments("fake", "repo", 1, 1); + } + + [Fact] + public void RequestsCorrectUrlWithRepositoryId() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + client.GetAllComments(1, 1, 1); + + gitHubClient.Received().PullRequest.Review.GetAllComments(1, 1, 1); + } + + [Fact] + public void RequestsCorrectUrlWithApiOptions() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + client.GetAllComments("fake", "repo", 1, 1, options); + + gitHubClient.Received().PullRequest.Review.GetAllComments("fake", "repo", 1, 1, options); + } + + [Fact] + public void RequestsCorrectUrlWithApiOptionsWithRepositoryId() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + client.GetAllComments(1, 1, 1, options); + + gitHubClient.Received().PullRequest.Review.GetAllComments(1, 1, 1, options); + } + + [Fact] + public async Task RequestsCorrectUrlMulti() + { + var firstPageUrl = new Uri("repos/owner/name/pulls/7/reviews/1/comments", UriKind.Relative); + var secondPageUrl = new Uri("https://example.com/page/2"); + var firstPageLinks = new Dictionary { { "next", secondPageUrl } }; + var firstPageResponse = new ApiResponse> + ( + CreateResponseWithApiInfo(firstPageLinks), + new List + { + new PullRequestReviewComment(1), + new PullRequestReviewComment(2), + new PullRequestReviewComment(3) + }); + var thirdPageUrl = new Uri("https://example.com/page/3"); + var secondPageLinks = new Dictionary { { "next", thirdPageUrl } }; + var secondPageResponse = new ApiResponse> + ( + CreateResponseWithApiInfo(secondPageLinks), + new List + { + new PullRequestReviewComment(4), + new PullRequestReviewComment(5), + new PullRequestReviewComment(6) + } + ); + var lastPageResponse = new ApiResponse> + ( + new Response(), + new List + { + new PullRequestReviewComment(7) + } + ); + + var gitHubClient = Substitute.For(); + + gitHubClient.Connection.Get>(firstPageUrl, Args.EmptyDictionary, null) + .Returns(Task.Factory.StartNew>>(() => firstPageResponse)); + gitHubClient.Connection.Get>(secondPageUrl, Args.EmptyDictionary, null) + .Returns(Task.Factory.StartNew>>(() => secondPageResponse)); + gitHubClient.Connection.Get>(thirdPageUrl, Args.EmptyDictionary, null) + .Returns(Task.Factory.StartNew>>(() => lastPageResponse)); + + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + var results = await client.GetAllComments("owner", "name", 7, 1).ToArray(); + + Assert.Equal(7, results.Length); + gitHubClient.Connection.Received(1).Get>(firstPageUrl, Args.EmptyDictionary, null); + gitHubClient.Connection.Received(1).Get>(secondPageUrl, Args.EmptyDictionary, null); + gitHubClient.Connection.Received(1).Get>(thirdPageUrl, Args.EmptyDictionary, null); + } + + [Fact] + public async Task RequestsCorrectUrlMultiWithRepositoryId() + { + var firstPageUrl = new Uri("repositories/1/pulls/7/reviews/1/comments", UriKind.Relative); + var secondPageUrl = new Uri("https://example.com/page/2"); + var firstPageLinks = new Dictionary { { "next", secondPageUrl } }; + var firstPageResponse = new ApiResponse> + ( + CreateResponseWithApiInfo(firstPageLinks), + new List + { + new PullRequestReviewComment(1), + new PullRequestReviewComment(2), + new PullRequestReviewComment(3) + }); + var thirdPageUrl = new Uri("https://example.com/page/3"); + var secondPageLinks = new Dictionary { { "next", thirdPageUrl } }; + var secondPageResponse = new ApiResponse> + ( + CreateResponseWithApiInfo(secondPageLinks), + new List + { + new PullRequestReviewComment(4), + new PullRequestReviewComment(5), + new PullRequestReviewComment(6) + } + ); + var lastPageResponse = new ApiResponse> + ( + new Response(), + new List + { + new PullRequestReviewComment(7) + } + ); + + var gitHubClient = Substitute.For(); + + gitHubClient.Connection.Get>(firstPageUrl, Args.EmptyDictionary, null) + .Returns(Task.Factory.StartNew>>(() => firstPageResponse)); + gitHubClient.Connection.Get>(secondPageUrl, Args.EmptyDictionary, null) + .Returns(Task.Factory.StartNew>>(() => secondPageResponse)); + gitHubClient.Connection.Get>(thirdPageUrl, Args.EmptyDictionary, null) + .Returns(Task.Factory.StartNew>>(() => lastPageResponse)); + + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + var results = await client.GetAllComments(1, 7, 1).ToArray(); + + Assert.Equal(7, results.Length); + gitHubClient.Connection.Received(1).Get>(firstPageUrl, Args.EmptyDictionary, null); + gitHubClient.Connection.Received(1).Get>(secondPageUrl, Args.EmptyDictionary, null); + gitHubClient.Connection.Received(1).Get>(thirdPageUrl, Args.EmptyDictionary, null); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + Assert.Throws(() => client.GetAllComments(null, "name", 1, 1)); + Assert.Throws(() => client.GetAllComments("owner", null, 1, 1)); + + Assert.Throws(() => client.GetAllComments(null, "name", 1, 1, ApiOptions.None)); + Assert.Throws(() => client.GetAllComments("owner", null, 1, 1, ApiOptions.None)); + Assert.Throws(() => client.GetAllComments("owner", "name", 1, 1, null)); + + Assert.Throws(() => client.GetAllComments(1, 1, 1, null)); + + Assert.Throws(() => client.GetAllComments("", "name", 1, 1)); + Assert.Throws(() => client.GetAllComments("owner", "", 1, 1)); + + Assert.Throws(() => client.GetAllComments("", "name", 1, 1, ApiOptions.None)); + Assert.Throws(() => client.GetAllComments("owner", "", 1, 1, ApiOptions.None)); + + } + } + + public class TheSubmitMethod + { + [Fact] + public void PostsToCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + var submitMessage = new PullRequestReviewSubmit() + { + Body = "string", + Event = PullRequestReviewEvent.Approve + }; + + client.Submit("owner", "name", 13, 13, submitMessage); + + gitHubClient.Received().PullRequest.Review.Submit("owner", "name", 13, 13, submitMessage); + } + + [Fact] + public void PostsToCorrectUrlWithRepositoryId() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + var submitMessage = new PullRequestReviewSubmit() + { + Body = "string", + Event = PullRequestReviewEvent.Approve + }; + + client.Submit(1, 13, 13, submitMessage); + + gitHubClient.Received().PullRequest.Review.Submit(1, 13, 13, submitMessage); + } + + [Fact] + public void EnsuresNonNullArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewsClient(gitHubClient); + + var submitMessage = new PullRequestReviewSubmit() + { + Body = "string", + Event = PullRequestReviewEvent.Approve + }; + + Assert.Throws(() => client.Submit(null, "name", 1, 1, submitMessage)); + Assert.Throws(() => client.Submit("owner", null, 1, 1, submitMessage)); + Assert.Throws(() => client.Submit("owner", "name", 1, 1, null)); + + Assert.Throws(() => client.Submit("", "name", 1, 1, submitMessage)); + Assert.Throws(() => client.Submit("owner", "", 1, 1, submitMessage)); + } + } + } +} diff --git a/Octokit/Clients/IPullRequestReviewsClient.cs b/Octokit/Clients/IPullRequestReviewsClient.cs new file mode 100644 index 0000000000..49b262aa2f --- /dev/null +++ b/Octokit/Clients/IPullRequestReviewsClient.cs @@ -0,0 +1,189 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Pull Request Review API. + /// + /// + /// See the Review API documentation for more information. + /// + public interface IPullRequestReviewsClient + { + /// + /// Gets reviews for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request + /// The owner of the repository + /// The name of the repository + /// The pull request number + Task> GetAll(string owner, string name, int number); + + /// + /// Gets reviews for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request + /// The Id of the repository + /// The pull request number + Task> GetAll(long repositoryId, int number); + + /// + /// Gets reviews for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request + /// 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 reviews for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request + /// The Id of the repository + /// The pull request number + /// Options for changing the API response + Task> GetAll(long repositoryId, int number, ApiOptions options); + + /// + /// Gets a single pull request review by ID. + /// + /// https://developer.github.com/v3/pulls/reviews/#get-a-single-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + Task Get(string owner, string name, int number, long reviewId); + + /// + /// Gets a single pull request review by ID. + /// + /// https://developer.github.com/v3/pulls/reviews/#get-a-single-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + Task Get(long repositoryId, int number, long reviewId); + + /// + /// Creates a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#create-a-pull-request-review + /// The owner of the repository + /// The name of the repository + /// The Pull Request number + /// The review + Task Create(string owner, string name, int number, PullRequestReviewCreate review); + + /// + /// Creates a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#create-a-pull-request-review + /// The Id of the repository + /// The Pull Request number + /// The review + Task Create(long repositoryId, int number, PullRequestReviewCreate review); + + /// + /// Deletes a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#delete-a-pending-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + Task Delete(string owner, string name, int number, long reviewId); + + /// + /// Deletes a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#delete-a-pending-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + Task Delete(long repositoryId, int number, long reviewId); + + /// + /// Submits a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#submit-a-pull-request-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + /// The message and event being submitted for the review + Task Submit(string owner, string name, int number, long reviewId, PullRequestReviewSubmit submitMessage); + + /// + /// Submits a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#submit-a-pull-request-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + /// The message and event being submitted for the review + Task Submit(long repositoryId, int number, long reviewId, PullRequestReviewSubmit submitMessage); + + /// + /// Dismisses a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#dismiss-a-pull-request-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + /// The message indicating why the review was dismissed + Task Dismiss(string owner, string name, int number, long reviewId, PullRequestReviewDismiss dismissMessage); + + /// + /// Dismisses a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#dismiss-a-pull-request-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + /// The message indicating why the review was dismissed + Task Dismiss(long repositoryId, int number, long reviewId, PullRequestReviewDismiss dismissMessage); + + /// + /// Lists comments for a single review + /// + /// https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + Task> GetAllComments(string owner, string name, int number, long reviewId); + + /// + /// Lists comments for a single review + /// + /// https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + Task> GetAllComments(long repositoryId, int number, long reviewId); + + /// + /// Lists comments for a single review + /// + /// https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + /// Options for changing the API response + Task> GetAllComments(string owner, string name, int number, long reviewId, ApiOptions options); + + /// + /// Lists comments for a single review + /// + /// https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + /// Options for changing the API response + Task> GetAllComments(long repositoryId, int number, long reviewId, ApiOptions options); + } +} \ No newline at end of file diff --git a/Octokit/Clients/IPullRequestsClient.cs b/Octokit/Clients/IPullRequestsClient.cs index 3eb893ec2b..bd211312c6 100644 --- a/Octokit/Clients/IPullRequestsClient.cs +++ b/Octokit/Clients/IPullRequestsClient.cs @@ -19,6 +19,11 @@ public interface IPullRequestsClient [Obsolete("Please use IPullRequestsClient.ReviewComment instead. This method will be removed in a future version")] IPullRequestReviewCommentsClient Comment { get; } + /// + /// Client for managing reviews. + /// + IPullRequestReviewsClient Review { get; } + /// /// Client for managing review comments. /// diff --git a/Octokit/Clients/PullRequestReviewsClient.cs b/Octokit/Clients/PullRequestReviewsClient.cs new file mode 100644 index 0000000000..de41bdb649 --- /dev/null +++ b/Octokit/Clients/PullRequestReviewsClient.cs @@ -0,0 +1,301 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Pull Request Review API. + /// + /// + /// See the Review API documentation for more information. + /// + public class PullRequestReviewsClient : ApiClient, IPullRequestReviewsClient + { + public PullRequestReviewsClient(IApiConnection apiConnection) + : base(apiConnection) + { + } + + /// + /// Gets reviews for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request + /// 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, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return GetAll(owner, name, number, ApiOptions.None); + } + + /// + /// Gets reviews for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request + /// The Id of the repository + /// The pull request number + public Task> GetAll(long repositoryId, int number) + { + return GetAll(repositoryId, number, ApiOptions.None); + } + + /// + /// Gets reviews for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request + /// 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, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(options, nameof(options)); + + var endpoint = ApiUrls.PullRequestReviews(owner, name, number); + return ApiConnection.GetAll(endpoint, null, options); + } + + /// + /// Gets reviews for a specified pull request. + /// + /// https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request + /// 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, nameof(options)); + + var endpoint = ApiUrls.PullRequestReviews(repositoryId, number); + return ApiConnection.GetAll(endpoint, options); + } + + /// + /// Gets a single pull request review by ID. + /// + /// https://developer.github.com/v3/pulls/reviews/#get-a-single-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + public Task Get(string owner, string name, int number, long reviewId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + var endpoint = ApiUrls.PullRequestReview(owner, name, number, reviewId); + return ApiConnection.Get(endpoint); + } + + /// + /// Gets a single pull request review by ID. + /// + /// https://developer.github.com/v3/pulls/reviews/#get-a-single-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + public Task Get(long repositoryId, int number, long reviewId) + { + var endpoint = ApiUrls.PullRequestReview(repositoryId, number, reviewId); + return ApiConnection.Get(endpoint); + } + + /// + /// Creates a review. + /// + /// https://developer.github.com/v3/pulls/reviews/#create-a-pull-request-review + /// The owner of the repository + /// The name of the repository + /// The Pull Request number + /// The review + public Task Create(string owner, string name, int number, PullRequestReviewCreate review) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(review, nameof(review)); + + var endpoint = ApiUrls.PullRequestReviews(owner, name, number); + return ApiConnection.Post(endpoint, review, null, null); + } + + /// + /// Creates a review. + /// + /// https://developer.github.com/v3/pulls/reviews/#create-a-pull-request-review + /// The Id of the repository + /// The Pull Request number + /// The review + public Task Create(long repositoryId, int number, PullRequestReviewCreate review) + { + Ensure.ArgumentNotNull(review, nameof(review)); + + var endpoint = ApiUrls.PullRequestReviews(repositoryId, number); + return ApiConnection.Post(endpoint, review, null, null); + } + + /// + /// Deletes a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#delete-a-pending-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + public Task Delete(string owner, string name, int number, long reviewId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + var endpoint = ApiUrls.PullRequestReview(owner, name, number, reviewId); + return ApiConnection.Delete(endpoint); + } + + /// + /// Deletes a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#delete-a-pending-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + public Task Delete(long repositoryId, int number, long reviewId) + { + var endpoint = ApiUrls.PullRequestReview(repositoryId, number, reviewId); + return ApiConnection.Delete(endpoint); + } + + /// + /// Dismisses a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#dismiss-a-pull-request-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + /// The message indicating why the review was dismissed + public Task Dismiss(string owner, string name, int number, long reviewId, PullRequestReviewDismiss dismissMessage) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(dismissMessage, nameof(dismissMessage)); + + var endpoint = ApiUrls.PullRequestReviewDismissal(owner, name, number, reviewId); + return ApiConnection.Put(endpoint, dismissMessage); + } + + /// + /// Dismisses a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#dismiss-a-pull-request-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + /// The message indicating why the review was dismissed + public Task Dismiss(long repositoryId, int number, long reviewId, PullRequestReviewDismiss dismissMessage) + { + Ensure.ArgumentNotNull(dismissMessage, nameof(dismissMessage)); + + var endpoint = ApiUrls.PullRequestReviewDismissal(repositoryId, number, reviewId); + return ApiConnection.Put(endpoint, dismissMessage); + } + + /// + /// Submits a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#submit-a-pull-request-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + /// The message and event being submitted for the review + public Task Submit(string owner, string name, int number, long reviewId, PullRequestReviewSubmit submitMessage) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(submitMessage, nameof(submitMessage)); + + var endpoint = ApiUrls.PullRequestReviewSubmit(owner, name, number, reviewId); + return ApiConnection.Post(endpoint, submitMessage, null, null); + } + + /// + /// Submits a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#submit-a-pull-request-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + /// The message and event being submitted for the review + public Task Submit(long repositoryId, int number, long reviewId, PullRequestReviewSubmit submitMessage) + { + Ensure.ArgumentNotNull(submitMessage, nameof(submitMessage)); + + var endpoint = ApiUrls.PullRequestReviewSubmit(repositoryId, number, reviewId); + return ApiConnection.Post(endpoint, submitMessage, null, null); + } + + /// + /// Lists comments for a single review + /// + /// https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + public Task> GetAllComments(string owner, string name, int number, long reviewId) + { + return GetAllComments(owner, name, number, reviewId, ApiOptions.None); + } + + /// + /// Dismisses a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + public Task> GetAllComments(long repositoryId, int number, long reviewId) + { + return GetAllComments(repositoryId, number, reviewId, ApiOptions.None); + } + + /// + /// Lists comments for a single review + /// + /// https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + /// Options for changing the API response + public Task> GetAllComments(string owner, string name, int number, long reviewId, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + var endpoint = ApiUrls.PullRequestReviewComments(owner, name, number, reviewId); + return ApiConnection.GetAll(endpoint, null, options); + } + + /// + /// Dismisses a pull request review. + /// + /// https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review + /// The Id of the repository + /// The pull request number + /// The pull request review number + /// Options for changing the API response + public Task> GetAllComments(long repositoryId, int number, long reviewId, ApiOptions options) + { + var endpoint = ApiUrls.PullRequestReviewComments(repositoryId, number, reviewId); + + return ApiConnection.GetAll(endpoint, null, options); + } + } +} diff --git a/Octokit/Clients/PullRequestsClient.cs b/Octokit/Clients/PullRequestsClient.cs index 4b712f35c8..fd73e8ef45 100644 --- a/Octokit/Clients/PullRequestsClient.cs +++ b/Octokit/Clients/PullRequestsClient.cs @@ -9,12 +9,13 @@ namespace Octokit /// A client for GitHub's Pull Requests API. /// /// - /// See the Pull Requests API documentation for more information. + /// See the Pull Requests API documentation for more information. /// public class PullRequestsClient : ApiClient, IPullRequestsClient { public PullRequestsClient(IApiConnection apiConnection) : base(apiConnection) { + Review = new PullRequestReviewsClient(apiConnection); ReviewComment = new PullRequestReviewCommentsClient(apiConnection); ReviewRequest = new PullRequestReviewRequestsClient(apiConnection); } @@ -25,6 +26,11 @@ public PullRequestsClient(IApiConnection apiConnection) : base(apiConnection) [Obsolete("Please use PullRequestsClient.ReviewComment instead. This method will be removed in a future version")] public IPullRequestReviewCommentsClient Comment { get { return this.ReviewComment; } } + /// + /// Client for managing reviews. + /// + public IPullRequestReviewsClient Review { get; set; } + /// /// Client for managing review comments. /// @@ -43,8 +49,8 @@ public PullRequestsClient(IApiConnection apiConnection) : base(apiConnection) /// public Task Get(string owner, string name, int number) { - Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); - Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); return ApiConnection.Get(ApiUrls.PullRequest(owner, name, number)); } @@ -70,8 +76,8 @@ public Task Get(long repositoryId, int number) /// The name of the repository public Task> GetAllForRepository(string owner, string name) { - Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); - Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); return GetAllForRepository(owner, name, new PullRequestRequest(), ApiOptions.None); } @@ -99,9 +105,9 @@ public Task> GetAllForRepository(long repositoryId) /// Options for changing the API response public Task> GetAllForRepository(string owner, string name, ApiOptions options) { - Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); - Ensure.ArgumentNotNullOrEmptyString(name, "name"); - Ensure.ArgumentNotNull(options, "options"); + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(options, nameof(options)); return GetAllForRepository(owner, name, new PullRequestRequest(), options); } @@ -116,7 +122,7 @@ public Task> GetAllForRepository(string owner, string /// Options for changing the API response public Task> GetAllForRepository(long repositoryId, ApiOptions options) { - Ensure.ArgumentNotNull(options, "options"); + Ensure.ArgumentNotNull(options, nameof(options)); return GetAllForRepository(repositoryId, new PullRequestRequest(), options); } @@ -132,9 +138,9 @@ public Task> GetAllForRepository(long repositoryId, A /// Used to filter and sort the list of pull requests returned public Task> GetAllForRepository(string owner, string name, PullRequestRequest request) { - Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); - Ensure.ArgumentNotNullOrEmptyString(name, "name"); - Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(request, nameof(request)); return GetAllForRepository(owner, name, request, ApiOptions.None); } @@ -149,7 +155,7 @@ public Task> GetAllForRepository(string owner, string /// Used to filter and sort the list of pull requests returned public Task> GetAllForRepository(long repositoryId, PullRequestRequest request) { - Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(request, nameof(request)); return GetAllForRepository(repositoryId, request, ApiOptions.None); } @@ -166,10 +172,10 @@ public Task> GetAllForRepository(long repositoryId, P /// Options for changing the API response public Task> GetAllForRepository(string owner, string name, PullRequestRequest request, ApiOptions options) { - Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); - Ensure.ArgumentNotNullOrEmptyString(name, "name"); - Ensure.ArgumentNotNull(request, "request"); - Ensure.ArgumentNotNull(options, "options"); + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(request, nameof(request)); + Ensure.ArgumentNotNull(options, nameof(options)); return ApiConnection.GetAll(ApiUrls.PullRequests(owner, name), request.ToParametersDictionary(), options); @@ -186,8 +192,8 @@ public Task> GetAllForRepository(string owner, string /// Options for changing the API response public Task> GetAllForRepository(long repositoryId, PullRequestRequest request, ApiOptions options) { - Ensure.ArgumentNotNull(request, "request"); - Ensure.ArgumentNotNull(options, "options"); + Ensure.ArgumentNotNull(request, nameof(request)); + Ensure.ArgumentNotNull(options, nameof(options)); return ApiConnection.GetAll(ApiUrls.PullRequests(repositoryId), request.ToParametersDictionary(), options); @@ -202,9 +208,9 @@ public Task> GetAllForRepository(long repositoryId, P /// A instance describing the new PullRequest to create public Task Create(string owner, string name, NewPullRequest newPullRequest) { - Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); - Ensure.ArgumentNotNullOrEmptyString(name, "name"); - Ensure.ArgumentNotNull(newPullRequest, "newPullRequest"); + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(newPullRequest, nameof(newPullRequest)); return ApiConnection.Post(ApiUrls.PullRequests(owner, name), newPullRequest); } @@ -217,7 +223,7 @@ public Task Create(string owner, string name, NewPullRequest newPul /// A instance describing the new PullRequest to create public Task Create(long repositoryId, NewPullRequest newPullRequest) { - Ensure.ArgumentNotNull(newPullRequest, "newPullRequest"); + Ensure.ArgumentNotNull(newPullRequest, nameof(newPullRequest)); return ApiConnection.Post(ApiUrls.PullRequests(repositoryId), newPullRequest); } @@ -233,9 +239,9 @@ public Task Create(long repositoryId, NewPullRequest newPullRequest /// public Task Update(string owner, string name, int number, PullRequestUpdate pullRequestUpdate) { - Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); - Ensure.ArgumentNotNullOrEmptyString(name, "name"); - Ensure.ArgumentNotNull(pullRequestUpdate, "pullRequestUpdate"); + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(pullRequestUpdate, nameof(pullRequestUpdate)); return ApiConnection.Patch(ApiUrls.PullRequest(owner, name, number), pullRequestUpdate); } @@ -250,7 +256,7 @@ public Task Update(string owner, string name, int number, PullReque /// public Task Update(long repositoryId, int number, PullRequestUpdate pullRequestUpdate) { - Ensure.ArgumentNotNull(pullRequestUpdate, "pullRequestUpdate"); + Ensure.ArgumentNotNull(pullRequestUpdate, nameof(pullRequestUpdate)); return ApiConnection.Patch(ApiUrls.PullRequest(repositoryId, number), pullRequestUpdate); } @@ -265,9 +271,9 @@ public Task Update(long repositoryId, int number, PullRequestUpdate /// A instance describing a pull request merge public async Task Merge(string owner, string name, int number, MergePullRequest mergePullRequest) { - Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); - Ensure.ArgumentNotNullOrEmptyString(name, "name"); - Ensure.ArgumentNotNull(mergePullRequest, "mergePullRequest"); + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(mergePullRequest, nameof(mergePullRequest)); try { @@ -299,7 +305,7 @@ public async Task Merge(string owner, string name, int number, /// A instance describing a pull request merge public async Task Merge(long repositoryId, int number, MergePullRequest mergePullRequest) { - Ensure.ArgumentNotNull(mergePullRequest, "mergePullRequest"); + Ensure.ArgumentNotNull(mergePullRequest, nameof(mergePullRequest)); try { @@ -331,8 +337,8 @@ public async Task Merge(long repositoryId, int number, MergePu /// The pull request number public async Task Merged(string owner, string name, int number) { - Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); - Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); try { @@ -375,8 +381,8 @@ public async Task Merged(long repositoryId, int number) /// The pull request number public Task> Commits(string owner, string name, int number) { - Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); - Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); return ApiConnection.GetAll(ApiUrls.PullRequestCommits(owner, name, number)); } @@ -401,8 +407,8 @@ public Task> Commits(long repositoryId, int num /// The pull request number public Task> Files(string owner, string name, int number) { - Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); - Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); return ApiConnection.GetAll(ApiUrls.PullRequestFiles(owner, name, number)); } diff --git a/Octokit/GitHubClient.cs b/Octokit/GitHubClient.cs index a00614c4f5..cf86a5c659 100644 --- a/Octokit/GitHubClient.cs +++ b/Octokit/GitHubClient.cs @@ -205,7 +205,7 @@ public Uri BaseAddress /// Refer to the API documentation for more information: https://developer.github.com/v3/pulls/ /// public IPullRequestsClient PullRequest { get; private set; } - + /// /// Access GitHub's Repositories API. /// diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index b8de422983..96304dcae0 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -1339,6 +1339,18 @@ public static Uri PullRequestReviewComments(string owner, string name, int numbe return "repos/{0}/{1}/pulls/{2}/comments".FormatUri(owner, name, number); } + /// + /// Returns the for the reviews opf a specified pull request + /// + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The + public static Uri PullRequestReviews(string owner, string name, int number) + { + return "repos/{0}/{1}/pulls/{2}/reviews".FormatUri(owner, name, number); + } + /// /// Returns the for the specified pull request review comment. /// @@ -1351,6 +1363,106 @@ public static Uri PullRequestReviewComment(string owner, string name, int number return "repos/{0}/{1}/pulls/comments/{2}".FormatUri(owner, name, number); } + /// + /// Returns the for the specified pull request review. + /// + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + /// The + public static Uri PullRequestReview(string owner, string name, int number, long reviewId) + { + return "repos/{0}/{1}/pulls/{2}/reviews/{3}".FormatUri(owner, name, number, reviewId); + } + + /// + /// Returns the for dismissing a specified pull request review + /// + /// The Id of the repository + /// The pull request number + /// The pull request review number + /// The + public static Uri PullRequestReviewDismissal(long repositoryId, int number, long reviewId) + { + return "repositories/{0}/pulls/{1}/reviews/{2}/dismissals".FormatUri(repositoryId, number, reviewId); + } + + /// + /// Returns the for dismissing a specified pull request review + /// + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + /// The + public static Uri PullRequestReviewDismissal(string owner, string name, int number, long reviewId) + { + return "repos/{0}/{1}/pulls/{2}/reviews/{3}/dismissals".FormatUri(owner, name, number, reviewId); + } + + /// + /// Returns the for submitting a pull request review + /// + /// The Id of the repository + /// The pull request number + /// The pull request review number + /// The that + public static Uri PullRequestReviewSubmit(long repositoryId, int number, long reviewId) + { + return "repositories/{0}/pulls/{1}/reviews/{2}/events".FormatUri(repositoryId, number, reviewId); + } + + /// + /// Returns the for submitting a pull request review + /// + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + /// The + public static Uri PullRequestReviewSubmit(string owner, string name, int number, long reviewId) + { + return "repos/{0}/{1}/pulls/{2}/reviews/{3}/events".FormatUri(owner, name, number, reviewId); + } + + /// + /// Returns the for submitting a pull request review + /// + /// The Id of the repository + /// The pull request number + /// The pull request review number + /// The that + public static Uri PullRequestReviewComments(long repositoryId, int number, long reviewId) + { + return "repositories/{0}/pulls/{1}/reviews/{2}/comments".FormatUri(repositoryId, number, reviewId); + } + + /// + /// Returns the for submitting a pull request review + /// + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The pull request review number + /// The + public static Uri PullRequestReviewComments(string owner, string name, int number, long reviewId) + { + return "repos/{0}/{1}/pulls/{2}/reviews/{3}/comments".FormatUri(owner, name, number, reviewId); + } + + /// + /// Returns the for a specified pull request review. + /// + /// The Id of the repository + /// The pull request number + /// The pull request review number + /// The + public static Uri PullRequestReview(long repositoryId, int number, long reviewId) + { + return "repositories/{0}/pulls/{1}/reviews/{2}".FormatUri(repositoryId, number, reviewId); + } + /// /// Returns the for the reaction of a specified pull request review comment. /// @@ -2815,6 +2927,17 @@ public static Uri PullRequestReviewComments(long repositoryId, int number) return "repositories/{0}/pulls/{1}/comments".FormatUri(repositoryId, number); } + /// + /// Returns the for the reviews of a specified pull request + /// + /// The Id of the repository + /// The pull request number + /// The that + public static Uri PullRequestReviews(long repositoryId, int number) + { + return "repositories/{0}/pulls/{1}/reviews".FormatUri(repositoryId, number); + } + /// /// Returns the for the pull request review comments on a specified repository. /// diff --git a/Octokit/Models/Request/DraftPullRequestReviewComment.cs b/Octokit/Models/Request/DraftPullRequestReviewComment.cs new file mode 100644 index 0000000000..26a84e1b93 --- /dev/null +++ b/Octokit/Models/Request/DraftPullRequestReviewComment.cs @@ -0,0 +1,48 @@ +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + /// + /// A draft comment that is part of a Pull Request Review + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class DraftPullRequestReviewComment : RequestParameters + { + /// + /// Creates a draft comment + /// + /// The text of the comment + /// The relative path of the file to comment on + /// The line index in the diff to comment on + public DraftPullRequestReviewComment(string body, string path, int position) + { + Ensure.ArgumentNotNullOrEmptyString(body, "body"); + Ensure.ArgumentNotNullOrEmptyString(path, "path"); + + Body = body; + Path = path; + Position = position; + } + + /// + /// The text of the comment. + /// + public string Body { get; private set; } + + /// + /// The relative path of the file to comment on. + /// + public string Path { get; private set; } + + /// + /// The line index in the diff to comment on. + /// + public int Position { get; private set; } + + internal string DebuggerDisplay + { + get { return string.Format(CultureInfo.InvariantCulture, "Path: {0}, Position: {1}", Path, Position); } + } + } +} diff --git a/Octokit/Models/Request/PullRequestReviewCreate.cs b/Octokit/Models/Request/PullRequestReviewCreate.cs new file mode 100644 index 0000000000..47960bc49e --- /dev/null +++ b/Octokit/Models/Request/PullRequestReviewCreate.cs @@ -0,0 +1,47 @@ +using System.Diagnostics; +using System.Globalization; +using Octokit.Internal; +using System.Collections.Generic; + +namespace Octokit +{ + /// + /// Used to create a pull request review + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class PullRequestReviewCreate : RequestParameters + { + public PullRequestReviewCreate() + { + Comments = new List(); + } + + /// + /// The Commit ID which the review is being created for. Default is the latest. + /// + public string CommitId { get; set; } + + /// + /// The body of the review message + /// + public string Body { get; set; } + + /// + /// The review event - Approve, Request Changes, Comment or leave blank (null) for Pending. + /// + public PullRequestReviewEvent? Event { get; set; } + + /// + /// List of comments to include with this review + /// + public List Comments { get; set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "CommitId: {0}, Body: {1} ", CommitId, Body); + } + } + } +} diff --git a/Octokit/Models/Request/PullRequestReviewDismiss.cs b/Octokit/Models/Request/PullRequestReviewDismiss.cs new file mode 100644 index 0000000000..854b0900bd --- /dev/null +++ b/Octokit/Models/Request/PullRequestReviewDismiss.cs @@ -0,0 +1,31 @@ +using System.Diagnostics; +using System.Globalization; +using Octokit.Internal; +using System.Collections.Generic; + +namespace Octokit +{ + /// + /// Used to dismiss a pull request review + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class PullRequestReviewDismiss : RequestParameters + { + public PullRequestReviewDismiss() + { + } + + /// + /// The message explaining why this review is being dismissed + /// + public string Message { get; set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Message: {0} ", Message); + } + } + } +} diff --git a/Octokit/Models/Request/PullRequestReviewSubmit.cs b/Octokit/Models/Request/PullRequestReviewSubmit.cs new file mode 100644 index 0000000000..b673e3ae90 --- /dev/null +++ b/Octokit/Models/Request/PullRequestReviewSubmit.cs @@ -0,0 +1,57 @@ +using System.Diagnostics; +using System.Globalization; +using Octokit.Internal; +using System.Collections.Generic; + +namespace Octokit +{ + /// + /// Used to submit a pending pull request review + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class PullRequestReviewSubmit : RequestParameters + { + public PullRequestReviewSubmit() + { + } + + /// + /// The body of the review message + /// + public string Body { get; set; } + + /// + /// The review event - Approve, Request Changes, Comment + /// + public PullRequestReviewEvent Event { get; set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Event: {0} ", Event); + } + } + } + + public enum PullRequestReviewEvent + { + /// + /// The review is approved + /// + [Parameter(Value = "APPROVE")] + Approve, + + /// + /// The review requests changes that must be addressed before merging + /// + [Parameter(Value = "REQUEST_CHANGES")] + RequestChanges, + + /// + /// The review provides comment without explicit approval + /// + [Parameter(Value = "COMMENT")] + Comment + } +} diff --git a/Octokit/Models/Response/PullRequestReview.cs b/Octokit/Models/Response/PullRequestReview.cs new file mode 100644 index 0000000000..8a8cd1970f --- /dev/null +++ b/Octokit/Models/Response/PullRequestReview.cs @@ -0,0 +1,87 @@ +using System; +using System.Diagnostics; +using System.Globalization; +using Octokit.Internal; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class PullRequestReview + { + public PullRequestReview() { } + + public PullRequestReview(long id) + { + Id = id; + } + + public PullRequestReview(long id, string commitId, User user, string body, string htmlUrl, string pullRequestUrl, PullRequestReviewState state) + { + Id = id; + CommitId = commitId; + User = user; + Body = body; + HtmlUrl = htmlUrl; + PullRequestUrl = pullRequestUrl; + State = state; + } + + /// + /// The review Id. + /// + public long Id { get; protected set; } + + /// + /// The state of the review + /// + public StringEnum State { get; protected set; } + + /// + /// The commit Id the review is associated with. + /// + public string CommitId { get; protected set; } + + /// + /// The user that created the review. + /// + public User User { get; protected set; } + + /// + /// The text of the review. + /// + public string Body { get; protected set; } + + /// + /// The URL for this review on Github.com + /// + public string HtmlUrl { get; protected set; } + + /// + /// The URL for the pull request via the API. + /// + public string PullRequestUrl { get; protected set; } + + internal string DebuggerDisplay + { + get { return string.Format(CultureInfo.InvariantCulture, "Id: {0}, State: {1}, User: {2}", Id, State.DebuggerDisplay, User.DebuggerDisplay); } + } + } + + public enum PullRequestReviewState + { + [Parameter(Value = "APPROVED")] + Approved, + + [Parameter(Value = "CHANGES_REQUESTED")] + ChangesRequested, + + [Parameter(Value = "COMMENTED")] + Commented, + + [Parameter(Value = "DISMISSED")] + Dismissed, + + [Parameter(Value = "PENDING")] + Pending + } +} diff --git a/Octokit/Models/Response/StringEnum.cs b/Octokit/Models/Response/StringEnum.cs index be9be44f08..d928e686f1 100644 --- a/Octokit/Models/Response/StringEnum.cs +++ b/Octokit/Models/Response/StringEnum.cs @@ -45,7 +45,16 @@ public TEnum Value internal string DebuggerDisplay { - get { return StringValue; } + get + { + TEnum value; + if (TryParse(out value)) + { + return value.ToString(); + } + + return StringValue; + } } public bool TryParse(out TEnum value) diff --git a/script/configure-integration-tests.ps1 b/script/configure-integration-tests.ps1 index 0bfcc1f186..ba8c8b4851 100644 --- a/script/configure-integration-tests.ps1 +++ b/script/configure-integration-tests.ps1 @@ -79,9 +79,15 @@ Write-Host "You should use a test account when running the Octokit integration t Write-Host Write-Host -VerifyEnvironmentVariable "account name" "OCTOKIT_GITHUBUSERNAME" -VerifyEnvironmentVariable "account password" "OCTOKIT_GITHUBPASSWORD" $true -VerifyEnvironmentVariable "OAuth token" "OCTOKIT_OAUTHTOKEN" +VerifyEnvironmentVariable "test account name" "OCTOKIT_GITHUBUSERNAME" +VerifyEnvironmentVariable "test account password" "OCTOKIT_GITHUBPASSWORD" $true +VerifyEnvironmentVariable "test account OAuth token" "OCTOKIT_OAUTHTOKEN" + +if (AskYesNoQuestion "Some tests require a second test account, do you want to set one up?" "OCTOKIT_PRIVATEREPOSITORIES") +{ + VerifyEnvironmentVariable "Second test account name" "OCTOKIT_GITHUBUSERNAME_2" + VerifyEnvironmentVariable "Second account password" "OCTOKIT_GITHUBPASSWORD_2" +} AskYesNoQuestion "Do you have private repositories associated with your test account?" "OCTOKIT_PRIVATEREPOSITORIES" | Out-Null